Compare commits
14 Commits
v4.0-beta2
...
v4.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
| 627a00beb4 | |||
| e00ed13ad1 | |||
| 893fa6bd90 | |||
| 512188c8dd | |||
| aae6761809 | |||
| c3f055d0c4 | |||
|
|
04d5df20d1 | ||
|
|
665eca0699 | ||
|
|
9a1534464f | ||
|
|
f856fc6fac | ||
| 4af8e73303 | |||
| 23239f1fec | |||
| 853e4d6fde | |||
| 14a37ad16e |
@@ -15,8 +15,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 498
|
||||
versionName '4.0-beta2'
|
||||
versionCode 499
|
||||
versionName '4.0-rc1'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -60,6 +60,7 @@ android {
|
||||
'-opt-in=kotlinx.coroutines.FlowPreview',
|
||||
'-opt-in=kotlin.contracts.ExperimentalContracts',
|
||||
'-opt-in=coil.annotation.ExperimentalCoilApi',
|
||||
'-opt-in=com.google.android.material.badge.ExperimentalBadgeUtils',
|
||||
]
|
||||
}
|
||||
lint {
|
||||
@@ -82,15 +83,15 @@ afterEvaluate {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:b3a9c5fcda') {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:b1990c7918') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.activity:activity-ktx:1.5.1'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.2'
|
||||
implementation 'androidx.activity:activity-ktx:1.6.0'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.3'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-service:2.5.1'
|
||||
@@ -101,7 +102,7 @@ dependencies {
|
||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
|
||||
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha05'
|
||||
implementation 'com.google.android.material:material:1.7.0-rc01'
|
||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.5.1'
|
||||
@@ -117,8 +118,8 @@ dependencies {
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2'
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
|
||||
|
||||
implementation "com.google.dagger:hilt-android:2.43.2"
|
||||
kapt "com.google.dagger:hilt-compiler:2.43.2"
|
||||
implementation "com.google.dagger:hilt-android:2.44"
|
||||
kapt "com.google.dagger:hilt-compiler:2.44"
|
||||
implementation 'androidx.hilt:hilt-work:1.0.0'
|
||||
kapt 'androidx.hilt:hilt-compiler:1.0.0'
|
||||
|
||||
@@ -146,6 +147,6 @@ dependencies {
|
||||
androidTestImplementation 'androidx.room:room-testing:2.4.3'
|
||||
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.14.0'
|
||||
|
||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.43.2'
|
||||
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.43.2'
|
||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.44'
|
||||
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.44'
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@ import android.app.Service
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
|
||||
abstract class CoroutineIntentService : BaseService() {
|
||||
|
||||
@@ -21,11 +23,13 @@ abstract class CoroutineIntentService : BaseService() {
|
||||
return Service.START_REDELIVER_INTENT
|
||||
}
|
||||
|
||||
private fun launchCoroutine(intent: Intent?, startId: Int) = lifecycleScope.launch {
|
||||
private fun launchCoroutine(intent: Intent?, startId: Int) = lifecycleScope.launch(errorHandler(startId)) {
|
||||
mutex.withLock {
|
||||
try {
|
||||
withContext(dispatcher) {
|
||||
processIntent(intent)
|
||||
if (intent != null) {
|
||||
withContext(dispatcher) {
|
||||
processIntent(startId, intent)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stopSelf(startId)
|
||||
@@ -33,5 +37,12 @@ abstract class CoroutineIntentService : BaseService() {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract suspend fun processIntent(intent: Intent?)
|
||||
protected abstract suspend fun processIntent(startId: Int, intent: Intent)
|
||||
|
||||
protected abstract fun onError(startId: Int, error: Throwable)
|
||||
|
||||
private fun errorHandler(startId: Int) = CoroutineExceptionHandler { _, throwable ->
|
||||
throwable.printStackTraceDebug()
|
||||
onError(startId, throwable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,6 @@ import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.network.DoHProvider
|
||||
@@ -26,6 +20,14 @@ import org.koitharu.kotatsu.utils.ext.getEnumValue
|
||||
import org.koitharu.kotatsu.utils.ext.observe
|
||||
import org.koitharu.kotatsu.utils.ext.putEnumValue
|
||||
import org.koitharu.kotatsu.utils.ext.toUriOrNull
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Collections
|
||||
import java.util.EnumSet
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
@@ -206,6 +208,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
val isReaderBarEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READER_BAR, true)
|
||||
|
||||
val isReaderSliderEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READER_SLIDER, true)
|
||||
|
||||
val dnsOverHttps: DoHProvider
|
||||
get() = prefs.getEnumValue(KEY_DOH, DoHProvider.NONE)
|
||||
|
||||
@@ -328,6 +333,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_INCOGNITO_MODE = "incognito"
|
||||
const val KEY_SYNC = "sync"
|
||||
const val KEY_READER_BAR = "reader_bar"
|
||||
const val KEY_READER_SLIDER = "reader_slider"
|
||||
const val KEY_SHORTCUTS = "dynamic_shortcuts"
|
||||
const val KEY_READER_TAPS_LTR = "reader_taps_ltr"
|
||||
const val KEY_LOCAL_LIST_ORDER = "local_order"
|
||||
|
||||
@@ -54,7 +54,7 @@ class MangaErrorDialog : AlertDialogFragment<DialogMangaErrorBinding>() {
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.report) { _, _ ->
|
||||
dismiss()
|
||||
error.report(TAG)
|
||||
error.report()
|
||||
}.setTitle(R.string.error_occurred)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,10 @@ import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.MangaIntent
|
||||
@@ -37,12 +35,17 @@ import org.koitharu.kotatsu.core.ui.MangaErrorDialog
|
||||
import org.koitharu.kotatsu.databinding.ActivityDetailsBinding
|
||||
import org.koitharu.kotatsu.details.ui.model.HistoryInfo
|
||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.list.ui.adapter.bindBadge
|
||||
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import org.koitharu.kotatsu.utils.ViewBadge
|
||||
import org.koitharu.kotatsu.utils.ext.assistedViewModels
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.isReportable
|
||||
import org.koitharu.kotatsu.utils.ext.setNavigationBarTransparentCompat
|
||||
import org.koitharu.kotatsu.utils.ext.textAndVisible
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DetailsActivity :
|
||||
@@ -60,7 +63,7 @@ class DetailsActivity :
|
||||
@Inject
|
||||
lateinit var shortcutsUpdater: ShortcutsUpdater
|
||||
|
||||
private var badge: BadgeDrawable? = null
|
||||
private lateinit var viewBadge: ViewBadge
|
||||
|
||||
private val viewModel: DetailsViewModel by assistedViewModels {
|
||||
viewModelFactory.create(MangaIntent(intent))
|
||||
@@ -83,6 +86,7 @@ class DetailsActivity :
|
||||
}
|
||||
binding.buttonRead.setOnClickListener(this)
|
||||
binding.buttonDropdown.setOnClickListener(this)
|
||||
viewBadge = ViewBadge(binding.buttonRead, this)
|
||||
|
||||
chaptersMenuProvider = if (binding.layoutBottom != null) {
|
||||
val bsMediator = ChaptersBottomSheetMediator(checkNotNull(binding.layoutBottom))
|
||||
@@ -151,6 +155,7 @@ class DetailsActivity :
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.button_dropdown -> showBranchPopupMenu()
|
||||
}
|
||||
}
|
||||
@@ -188,10 +193,12 @@ class DetailsActivity :
|
||||
ExceptionResolver.canResolve(e) -> {
|
||||
resolveError(e)
|
||||
}
|
||||
|
||||
manga == null -> {
|
||||
Toast.makeText(this, e.getDisplayMessage(resources), Toast.LENGTH_LONG).show()
|
||||
finishAfterTransition()
|
||||
}
|
||||
|
||||
else -> {
|
||||
val snackbar = makeSnackbar(
|
||||
e.getDisplayMessage(resources),
|
||||
@@ -242,7 +249,7 @@ class DetailsActivity :
|
||||
}
|
||||
|
||||
private fun onNewChaptersChanged(newChapters: Int) {
|
||||
badge = binding.buttonRead.bindBadge(badge, newChapters)
|
||||
viewBadge.counter = newChapters
|
||||
}
|
||||
|
||||
fun showChapterMissingDialog(chapterId: Long) {
|
||||
|
||||
@@ -34,7 +34,7 @@ class DownloadsActivity : BaseActivity<ActivityDownloadsBinding>() {
|
||||
setContentView(ActivityDownloadsBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
val adapter = DownloadsAdapter(lifecycleScope, coil)
|
||||
val spacing = resources.getDimensionPixelOffset(R.dimen.grid_spacing)
|
||||
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
||||
binding.recyclerView.addItemDecoration(SpacingItemDecoration(spacing))
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
@@ -16,7 +16,12 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.transformWhile
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -85,7 +90,9 @@ class DownloadService : BaseService() {
|
||||
|
||||
override fun onDestroy() {
|
||||
unregisterReceiver(controlReceiver)
|
||||
wakeLock.release()
|
||||
if (wakeLock.isHeld) {
|
||||
wakeLock.release()
|
||||
}
|
||||
isRunning = false
|
||||
super.onDestroy()
|
||||
}
|
||||
@@ -169,6 +176,7 @@ class DownloadService : BaseService() {
|
||||
val cancelId = intent.getIntExtra(EXTRA_CANCEL_ID, 0)
|
||||
jobs[cancelId]?.cancel()
|
||||
}
|
||||
|
||||
ACTION_DOWNLOAD_RESUME -> {
|
||||
val cancelId = intent.getIntExtra(EXTRA_CANCEL_ID, 0)
|
||||
jobs[cancelId]?.resume()
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
|
||||
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID
|
||||
import kotlin.text.Typography.dagger
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FavouritesActivity : BaseActivity<ActivityContainerBinding>() {
|
||||
@@ -28,6 +29,7 @@ class FavouritesActivity : BaseActivity<ActivityContainerBinding>() {
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = FavouritesListFragment.newInstance(intent.getLongExtra(EXTRA_CATEGORY_ID, NO_ID))
|
||||
replace(R.id.container, fragment)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import kotlin.text.Typography.dagger
|
||||
|
||||
@AndroidEntryPoint
|
||||
class HistoryActivity :
|
||||
@@ -28,6 +29,7 @@ class HistoryActivity :
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = HistoryListFragment.newInstance()
|
||||
replace(R.id.container, fragment)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
@file:SuppressLint("UnsafeOptInUsageError")
|
||||
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import androidx.annotation.CheckResult
|
||||
import androidx.core.view.doOnNextLayout
|
||||
@@ -42,4 +39,4 @@ private fun initBadge(anchor: View): BadgeDrawable {
|
||||
private fun BadgeDrawable.align() {
|
||||
horizontalOffset = intrinsicWidth
|
||||
verticalOffset = intrinsicHeight
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,12 @@ import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.local.domain.importer.MangaImporter
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.utils.PendingIntentCompat
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import org.koitharu.kotatsu.utils.ext.asArrayList
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.ext.referer
|
||||
import org.koitharu.kotatsu.utils.ext.report
|
||||
import org.koitharu.kotatsu.utils.ext.toBitmapOrNull
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -48,8 +53,8 @@ class ImportService : CoroutineIntentService() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override suspend fun processIntent(intent: Intent?) {
|
||||
val uris = intent?.getParcelableArrayListExtra<Uri>(EXTRA_URIS)
|
||||
override suspend fun processIntent(startId: Int, intent: Intent) {
|
||||
val uris = intent.getParcelableArrayListExtra<Uri>(EXTRA_URIS)
|
||||
if (uris.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
@@ -69,6 +74,10 @@ class ImportService : CoroutineIntentService() {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
}
|
||||
|
||||
override fun onError(startId: Int, error: Throwable) {
|
||||
error.report()
|
||||
}
|
||||
|
||||
private suspend fun importImpl(uri: Uri): Manga {
|
||||
val importer = importerFactory.create(uri)
|
||||
return importer.import(uri)
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
|
||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -34,8 +35,8 @@ class LocalChaptersRemoveService : CoroutineIntentService() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override suspend fun processIntent(intent: Intent?) {
|
||||
val manga = intent?.getParcelableExtraCompat<ParcelableManga>(EXTRA_MANGA)?.manga ?: return
|
||||
override suspend fun processIntent(startId: Int, intent: Intent) {
|
||||
val manga = intent.getParcelableExtraCompat<ParcelableManga>(EXTRA_MANGA)?.manga ?: return
|
||||
val chaptersIds = intent.getLongArrayExtra(EXTRA_CHAPTERS_IDS)?.toSet() ?: return
|
||||
startForeground()
|
||||
val mangaWithChapters = localMangaRepository.getDetails(manga)
|
||||
@@ -47,6 +48,21 @@ class LocalChaptersRemoveService : CoroutineIntentService() {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
}
|
||||
|
||||
override fun onError(startId: Int, error: Throwable) {
|
||||
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.error_occurred))
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setDefaults(0)
|
||||
.setColor(ContextCompat.getColor(this, R.color.blue_primary_dark))
|
||||
.setSilent(true)
|
||||
.setContentText(error.getDisplayMessage(resources))
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
nm.notify(NOTIFICATION_ID + startId, notification)
|
||||
}
|
||||
|
||||
private fun startForeground() {
|
||||
val title = getString(R.string.local_manga_processing)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
|
||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||
import org.koitharu.kotatsu.utils.VoiceInputContract
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import kotlin.text.Typography.dagger
|
||||
|
||||
private const val TAG_SEARCH = "search"
|
||||
|
||||
@@ -127,10 +128,12 @@ class MainActivity :
|
||||
binding.searchView.clearFocus()
|
||||
when {
|
||||
fragment != null -> supportFragmentManager.commit {
|
||||
setReorderingAllowed(true)
|
||||
remove(fragment)
|
||||
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
runOnCommit { onSearchClosed() }
|
||||
}
|
||||
|
||||
else -> super.onBackPressed()
|
||||
}
|
||||
}
|
||||
@@ -173,6 +176,7 @@ class MainActivity :
|
||||
if (v?.id == R.id.searchView && hasFocus) {
|
||||
if (fragment == null) {
|
||||
supportFragmentManager.commit {
|
||||
setReorderingAllowed(true)
|
||||
add(R.id.container, SearchSuggestionFragment.newInstance(), TAG_SEARCH)
|
||||
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
runOnCommit { onSearchOpened() }
|
||||
|
||||
@@ -85,6 +85,7 @@ class MainNavigationDelegate(
|
||||
|
||||
private fun setPrimaryFragment(fragment: Fragment) {
|
||||
fragmentManager.beginTransaction()
|
||||
.setReorderingAllowed(true)
|
||||
.replace(R.id.container, fragment, TAG_PRIMARY)
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.commit()
|
||||
|
||||
@@ -49,7 +49,10 @@ class ProtectActivity :
|
||||
startActivity(intent)
|
||||
finishAfterTransition()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (!useFingerprint()) {
|
||||
binding.editPassword.requestFocus()
|
||||
}
|
||||
|
||||
@@ -9,15 +9,23 @@ import android.transition.Fade
|
||||
import android.transition.Slide
|
||||
import android.transition.TransitionManager
|
||||
import android.transition.TransitionSet
|
||||
import android.view.*
|
||||
import android.view.Gravity
|
||||
import android.view.KeyEvent
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.*
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -42,7 +50,16 @@ import org.koitharu.kotatsu.settings.SettingsActivity
|
||||
import org.koitharu.kotatsu.utils.GridTouchHelper
|
||||
import org.koitharu.kotatsu.utils.IdlingDetector
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import org.koitharu.kotatsu.utils.ext.assistedViewModels
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat
|
||||
import org.koitharu.kotatsu.utils.ext.hasGlobalPoint
|
||||
import org.koitharu.kotatsu.utils.ext.isReportable
|
||||
import org.koitharu.kotatsu.utils.ext.observeWithPrevious
|
||||
import org.koitharu.kotatsu.utils.ext.postDelayed
|
||||
import org.koitharu.kotatsu.utils.ext.report
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReaderActivity :
|
||||
@@ -145,6 +162,7 @@ class ReaderActivity :
|
||||
R.id.action_settings -> {
|
||||
startActivity(SettingsActivity.newReaderSettingsIntent(this))
|
||||
}
|
||||
|
||||
R.id.action_chapters -> {
|
||||
ChaptersBottomSheet.show(
|
||||
supportFragmentManager,
|
||||
@@ -152,6 +170,7 @@ class ReaderActivity :
|
||||
viewModel.getCurrentState()?.chapterId ?: 0L,
|
||||
)
|
||||
}
|
||||
|
||||
R.id.action_pages_thumbs -> {
|
||||
val pages = viewModel.getCurrentChapterPages()
|
||||
if (!pages.isNullOrEmpty()) {
|
||||
@@ -165,6 +184,7 @@ class ReaderActivity :
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
R.id.action_bookmark -> {
|
||||
if (viewModel.isBookmarkAdded.value == true) {
|
||||
viewModel.removeBookmark()
|
||||
@@ -172,11 +192,13 @@ class ReaderActivity :
|
||||
viewModel.addBookmark()
|
||||
}
|
||||
}
|
||||
|
||||
R.id.action_options -> {
|
||||
viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState())
|
||||
val currentMode = readerManager.currentMode ?: return false
|
||||
ReaderConfigBottomSheet.show(supportFragmentManager, currentMode)
|
||||
}
|
||||
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
@@ -364,7 +386,7 @@ class ReaderActivity :
|
||||
binding.toastView.showTemporary(uiState.chapterName, TOAST_DURATION)
|
||||
}
|
||||
}
|
||||
if (uiState.totalPages > 1 && uiState.currentPage < uiState.totalPages) {
|
||||
if (uiState.isSliderAvailable()) {
|
||||
binding.slider.valueTo = uiState.totalPages.toFloat() - 1
|
||||
binding.slider.value = uiState.currentPage.toFloat()
|
||||
binding.slider.isVisible = true
|
||||
@@ -383,7 +405,7 @@ class ReaderActivity :
|
||||
if (ExceptionResolver.canResolve(exception)) {
|
||||
tryResolve(exception)
|
||||
} else {
|
||||
exception.report("ReaderActivity::onError")
|
||||
exception.report()
|
||||
}
|
||||
} else {
|
||||
onCancel(dialog)
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.databinding.DialogReaderConfigBinding
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
|
||||
@Deprecated("Not in use")
|
||||
class ReaderConfigDialog :
|
||||
AlertDialogFragment<DialogReaderConfigBinding>(),
|
||||
CheckableButtonGroup.OnCheckedChangeListener {
|
||||
|
||||
private lateinit var mode: ReaderMode
|
||||
|
||||
override fun onInflateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
) = DialogReaderConfigBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mode = arguments?.getInt(ARG_MODE)
|
||||
?.let { ReaderMode.valueOf(it) }
|
||||
?: ReaderMode.STANDARD
|
||||
}
|
||||
|
||||
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
|
||||
return super.onBuildDialog(builder)
|
||||
.setTitle(R.string.read_mode)
|
||||
.setPositiveButton(R.string.done, null)
|
||||
.setCancelable(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.buttonStandard.isChecked = mode == ReaderMode.STANDARD
|
||||
binding.buttonReversed.isChecked = mode == ReaderMode.REVERSED
|
||||
binding.buttonWebtoon.isChecked = mode == ReaderMode.WEBTOON
|
||||
|
||||
binding.checkableGroup.onCheckedChangeListener = this
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
(
|
||||
(parentFragment as? Callback)
|
||||
?: (activity as? Callback)
|
||||
)?.onReaderModeChanged(mode)
|
||||
super.onDismiss(dialog)
|
||||
}
|
||||
|
||||
override fun onCheckedChanged(group: CheckableButtonGroup, checkedId: Int) {
|
||||
mode = when (checkedId) {
|
||||
R.id.button_standard -> ReaderMode.STANDARD
|
||||
R.id.button_webtoon -> ReaderMode.WEBTOON
|
||||
R.id.button_reversed -> ReaderMode.REVERSED
|
||||
else -> return
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
|
||||
fun onReaderModeChanged(mode: ReaderMode)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "ReaderConfigDialog"
|
||||
private const val ARG_MODE = "mode"
|
||||
|
||||
fun show(fm: FragmentManager, mode: ReaderMode) = ReaderConfigDialog().withArgs(1) {
|
||||
putInt(ARG_MODE, mode.id)
|
||||
}.show(fm, TAG)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
import org.koitharu.kotatsu.reader.ui.pager.reversed.ReversedReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonReaderFragment
|
||||
import java.util.*
|
||||
import java.util.EnumMap
|
||||
|
||||
class ReaderManager(
|
||||
private val fragmentManager: FragmentManager,
|
||||
@@ -35,11 +35,15 @@ class ReaderManager(
|
||||
fun replace(newMode: ReaderMode) {
|
||||
val readerClass = requireNotNull(modeMap[newMode])
|
||||
fragmentManager.commit {
|
||||
setReorderingAllowed(true)
|
||||
replace(containerResId, readerClass, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun replace(reader: BaseReader<*>) {
|
||||
fragmentManager.commit { replace(containerResId, reader) }
|
||||
fragmentManager.commit {
|
||||
setReorderingAllowed(true)
|
||||
replace(containerResId, reader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,22 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import java.util.*
|
||||
import javax.inject.Provider
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
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.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.MangaDataRepository
|
||||
import org.koitharu.kotatsu.base.domain.MangaIntent
|
||||
@@ -21,7 +33,11 @@ import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
||||
import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.*
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.core.prefs.ScreenshotsPolicy
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsLiveData
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.parsers.exception.NotFoundException
|
||||
@@ -39,6 +55,8 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||
import org.koitharu.kotatsu.utils.ext.requireValue
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import java.util.Date
|
||||
import javax.inject.Provider
|
||||
|
||||
private const val BOUNDS_PAGE_OFFSET = 2
|
||||
private const val PREFETCH_LIMIT = 10
|
||||
@@ -116,6 +134,10 @@ class ReaderViewModel @AssistedInject constructor(
|
||||
|
||||
init {
|
||||
loadImpl()
|
||||
settings.observe()
|
||||
.onEach { key ->
|
||||
if (key == AppSettings.KEY_READER_SLIDER) notifyStateChanged()
|
||||
}.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
@@ -348,6 +370,7 @@ class ReaderViewModel @AssistedInject constructor(
|
||||
chaptersTotal = chapters.size(),
|
||||
totalPages = if (chapter != null) chaptersLoader.getPagesCount(chapter.id) else 0,
|
||||
currentPage = state?.page ?: 0,
|
||||
isSliderEnabled = settings.isReaderSliderEnabled,
|
||||
)
|
||||
uiState.postValue(newState)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,13 @@ data class ReaderUiState(
|
||||
val chaptersTotal: Int,
|
||||
val currentPage: Int,
|
||||
val totalPages: Int,
|
||||
private val isSliderEnabled: Boolean,
|
||||
) {
|
||||
|
||||
fun isSliderAvailable(): Boolean {
|
||||
return isSliderEnabled && totalPages > 1 && currentPage < totalPages
|
||||
}
|
||||
|
||||
fun computePercent(): Float {
|
||||
val ppc = 1f / chaptersTotal
|
||||
val chapterIndex = chapterNumber - 1
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
|
||||
import org.koitharu.kotatsu.utils.ext.getParcelableExtraCompat
|
||||
import kotlin.text.Typography.dagger
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MangaListActivity :
|
||||
@@ -41,6 +42,7 @@ class MangaListActivity :
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = if (source == MangaSource.LOCAL) {
|
||||
LocalListFragment.newInstance()
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.databinding.ActivitySearchBinding
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel
|
||||
import org.koitharu.kotatsu.utils.ext.showKeyboard
|
||||
import kotlin.text.Typography.dagger
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SearchActivity : BaseActivity<ActivitySearchBinding>(), SearchView.OnQueryTextListener {
|
||||
@@ -61,6 +62,7 @@ class SearchActivity : BaseActivity<ActivitySearchBinding>(), SearchView.OnQuery
|
||||
}
|
||||
title = query
|
||||
supportFragmentManager.commit {
|
||||
setReorderingAllowed(true)
|
||||
replace(R.id.container, SearchFragment.newInstance(source, q))
|
||||
}
|
||||
binding.searchView.clearFocus()
|
||||
|
||||
@@ -91,8 +91,8 @@ class ToolsFragment :
|
||||
binding.cardUpdate.root.isVisible = false
|
||||
return
|
||||
}
|
||||
binding.cardUpdate.textPrimary.text = getString(R.string.app_update_available_s, version.name)
|
||||
binding.cardUpdate.textSecondary.text = version.description
|
||||
binding.cardUpdate.textSecondary.text = getString(R.string.new_version_s, version.name)
|
||||
binding.cardUpdate.textChangelog.text = version.description
|
||||
binding.cardUpdate.root.isVisible = true
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import kotlin.text.Typography.dagger
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SuggestionsActivity :
|
||||
@@ -28,6 +29,7 @@ class SuggestionsActivity :
|
||||
val fm = supportFragmentManager
|
||||
if (fm.findFragmentById(R.id.container) == null) {
|
||||
fm.commit {
|
||||
setReorderingAllowed(true)
|
||||
val fragment = SuggestionsFragment.newInstance()
|
||||
replace(R.id.container, fragment)
|
||||
}
|
||||
|
||||
62
app/src/main/java/org/koitharu/kotatsu/utils/ViewBadge.kt
Normal file
62
app/src/main/java/org/koitharu/kotatsu/utils/ViewBadge.kt
Normal file
@@ -0,0 +1,62 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import android.view.View
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.badge.BadgeUtils
|
||||
|
||||
class ViewBadge(
|
||||
private val anchor: View,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) : View.OnLayoutChangeListener, DefaultLifecycleObserver {
|
||||
|
||||
private var badgeDrawable: BadgeDrawable? = null
|
||||
|
||||
var counter: Int
|
||||
get() = badgeDrawable?.number ?: 0
|
||||
set(value) {
|
||||
val badge = badgeDrawable ?: initBadge()
|
||||
badge.number = value
|
||||
badge.isVisible = value > 0
|
||||
}
|
||||
|
||||
init {
|
||||
lifecycleOwner.lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
override fun onLayoutChange(
|
||||
v: View?,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int,
|
||||
oldLeft: Int,
|
||||
oldTop: Int,
|
||||
oldRight: Int,
|
||||
oldBottom: Int,
|
||||
) {
|
||||
val badge = badgeDrawable ?: return
|
||||
BadgeUtils.setBadgeDrawableBounds(badge, anchor, null)
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
clearBadge()
|
||||
}
|
||||
|
||||
private fun initBadge(): BadgeDrawable {
|
||||
val badge = BadgeDrawable.create(anchor.context)
|
||||
anchor.addOnLayoutChangeListener(this)
|
||||
BadgeUtils.attachBadgeDrawable(badge, anchor)
|
||||
badgeDrawable = badge
|
||||
return badge
|
||||
}
|
||||
|
||||
private fun clearBadge() {
|
||||
val badge = badgeDrawable ?: return
|
||||
anchor.removeOnLayoutChangeListener(this)
|
||||
BadgeUtils.detachBadgeDrawable(badge, anchor)
|
||||
badgeDrawable = null
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ import android.content.res.Resources
|
||||
import androidx.collection.arraySetOf
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import okio.FileNotFoundException
|
||||
import okio.IOException
|
||||
import org.acra.ktx.sendWithAcra
|
||||
import org.json.JSONException
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.CaughtException
|
||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||
@@ -20,6 +22,8 @@ import org.koitharu.kotatsu.parsers.exception.ParseException
|
||||
import java.net.SocketTimeoutException
|
||||
import java.net.UnknownHostException
|
||||
|
||||
private const val MSG_NO_SPACE_LEFT = "No space left on device"
|
||||
|
||||
fun Throwable.getDisplayMessage(resources: Resources): String = when (this) {
|
||||
is AuthRequiredException -> resources.getString(R.string.auth_required)
|
||||
is CloudFlareProtectedException -> resources.getString(R.string.captcha_required)
|
||||
@@ -41,20 +45,28 @@ fun Throwable.getDisplayMessage(resources: Resources): String = when (this) {
|
||||
|
||||
is WrongPasswordException -> resources.getString(R.string.wrong_password)
|
||||
is NotFoundException -> resources.getString(R.string.not_found_404)
|
||||
is IOException -> getDisplayMessage(message, resources) ?: localizedMessage
|
||||
else -> localizedMessage
|
||||
} ?: resources.getString(R.string.error_occurred)
|
||||
|
||||
private fun getDisplayMessage(msg: String?, resources: Resources): String? = when {
|
||||
msg.isNullOrEmpty() -> null
|
||||
msg.contains(MSG_NO_SPACE_LEFT) -> resources.getString(R.string.error_no_space_left)
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun Throwable.isReportable(): Boolean {
|
||||
return this is Error || this.javaClass in reportableExceptions
|
||||
}
|
||||
|
||||
fun Throwable.report(message: String?) {
|
||||
val exception = CaughtException(this, message)
|
||||
fun Throwable.report() {
|
||||
val exception = CaughtException(this, "${javaClass.simpleName}($message)")
|
||||
exception.sendWithAcra()
|
||||
}
|
||||
|
||||
private val reportableExceptions = arraySetOf<Class<*>>(
|
||||
ParseException::class.java,
|
||||
JSONException::class.java,
|
||||
RuntimeException::class.java,
|
||||
IllegalStateException::class.java,
|
||||
IllegalArgumentException::class.java,
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/list_spacing"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
android:id="@+id/checkableGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:layout_marginHorizontal="?attr/dialogPreferredPadding"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
@@ -50,8 +51,7 @@
|
||||
style="?materialAlertDialogTitleTextStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="?attr/dialogPreferredPadding"
|
||||
android:paddingRight="?attr/dialogPreferredPadding"
|
||||
android:paddingHorizontal="?attr/dialogPreferredPadding"
|
||||
android:singleLine="true"
|
||||
android:text="@string/grid_size"
|
||||
android:visibility="gone"
|
||||
@@ -72,4 +72,4 @@
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/margin_normal"
|
||||
android:paddingHorizontal="?attr/dialogPreferredPadding"
|
||||
android:paddingTop="@dimen/margin_normal">
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -11,10 +11,8 @@
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||
android:paddingBottom="6dp"
|
||||
android:paddingHorizontal="?attr/dialogPreferredPadding"
|
||||
android:paddingVertical="16dp"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
tools:text="@string/onboard_text" />
|
||||
|
||||
@@ -28,4 +26,4 @@
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_source_locale" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
android:padding="?attr/dialogPreferredPadding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
@@ -31,4 +31,4 @@
|
||||
android:textColor="?android:textColorSecondary"
|
||||
tools:text="Subtitle" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup
|
||||
android:id="@+id/checkableGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_standard"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/standard"
|
||||
app:icon="@drawable/ic_book_page" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_reversed"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/right_to_left"
|
||||
app:icon="@drawable/ic_reader_rtl" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_webtoon"
|
||||
style="@style/Widget.Kotatsu.ToggleButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/webtoon"
|
||||
app:icon="@drawable/ic_script" />
|
||||
|
||||
</org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/reader_mode_hint"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -2,6 +2,7 @@
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/card_update"
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
android:layout_width="match_parent"
|
||||
@@ -47,6 +48,22 @@
|
||||
app:layout_constraintStart_toEndOf="@id/icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/textPrimary" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textChangelog"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_normal"
|
||||
android:layout_marginTop="@dimen/margin_small"
|
||||
android:background="@drawable/bg_badge_empty"
|
||||
android:fontFamily="monospace"
|
||||
android:padding="8dp"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/textSecondary"
|
||||
tools:text="- Fixes\n- Improvements" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_download"
|
||||
style="@style/Widget.Material3.Button"
|
||||
@@ -55,7 +72,7 @@
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/download"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textSecondary" />
|
||||
app:layout_constraintTop_toBottomOf="@id/textChangelog" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/manage" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -102,15 +102,15 @@
|
||||
<string name="notification_sound">Suara pemberitahuan</string>
|
||||
<string name="categories_">Kategori…</string>
|
||||
<string name="rename">Ubah Nama</string>
|
||||
<string name="category_delete_confirm">Hapus kategori \"%s\" dari favorit Anda\?
|
||||
\nSemua manga di situ akan hilang.</string>
|
||||
<string name="category_delete_confirm">"Hapus kategori \"%s\" dari favorit Anda\?
|
||||
\nSemua manga disana akan hilang."</string>
|
||||
<string name="text_empty_holder_primary">Sepi juga di sini…</string>
|
||||
<string name="text_categories_holder">Anda bisa menggunakan kategori untuk mengelola favorit Anda. Tekan «+» untuk membuat kategori</string>
|
||||
<string name="text_history_holder_primary">Apa yang Anda baca akan ditampilkan di sini</string>
|
||||
<string name="text_history_holder_secondary">Cari apa untuk di baca di bilah samping.</string>
|
||||
<string name="text_local_holder_primary">Simpan sesuatu dulu</string>
|
||||
<string name="manga_shelf">Rak</string>
|
||||
<string name="recent_manga">Baru-baru ini</string>
|
||||
<string name="recent_manga">Terbaru</string>
|
||||
<string name="pages_animation">Animasi halaman</string>
|
||||
<string name="manga_save_location">Folder untuk unduhan</string>
|
||||
<string name="not_available">Tidak tersedia</string>
|
||||
@@ -298,7 +298,7 @@
|
||||
<string name="status_completed">Selesai</string>
|
||||
<string name="canceled">Dibatalkan</string>
|
||||
<string name="sync_title">Sinkronisasi data Anda</string>
|
||||
<string name="enter_email_text">Masukkan surel Anda untuk melanjutkan</string>
|
||||
<string name="enter_email_text">Masukkan email Anda untuk melanjutkan</string>
|
||||
<string name="tracking">Pelacakan</string>
|
||||
<string name="logout">Keluar</string>
|
||||
<string name="sync">Sinkronisasi</string>
|
||||
@@ -329,7 +329,7 @@
|
||||
<string name="exit_confirmation_summary">Tekan Kembali dua kali untuk keluar dari aplikasi</string>
|
||||
<string name="exit_confirmation">Konfirmasi keluar</string>
|
||||
<string name="pages_cache">Tembolok halaman</string>
|
||||
<string name="other_cache">Tembolok lainnya</string>
|
||||
<string name="other_cache">Cache lainnya</string>
|
||||
<string name="storage_usage">Penggunaan penyimpanan</string>
|
||||
<string name="available">Tersedia</string>
|
||||
<string name="incognito_mode">Mode Incognito</string>
|
||||
@@ -363,9 +363,13 @@
|
||||
\n Semua manga di sana akan hilang dan ini tidak bisa diurungkan.</string>
|
||||
<string name="reorder">Atur Ulang</string>
|
||||
<string name="saved_manga">Manga tersimpan</string>
|
||||
<string name="confirm_exit">Tekan Kembali lagi untuk keluar</string>
|
||||
<string name="confirm_exit">Tekan lagi untuk keluar</string>
|
||||
<string name="no_chapters">Tidak ada bab</string>
|
||||
<string name="history_shortcuts">Tampilkan pintasan manga baru-baru ini</string>
|
||||
<string name="history_shortcuts_summary">Buat manga baru-baru ini tersedia dengan menekan panjang pada ikon aplikasi</string>
|
||||
<string name="select_range">Pilih jangkauan</string>
|
||||
<string name="disable_all">Matikan semua</string>
|
||||
<string name="gestures_only">Hanya gestur</string>
|
||||
<string name="dns_over_https">DNS melalui HTTPS</string>
|
||||
<string name="status_dropped">Istirahat</string>
|
||||
</resources>
|
||||
@@ -375,4 +375,15 @@
|
||||
<string name="downloading_manga">漫画をダウンロードする</string>
|
||||
<string name="feed">フィード</string>
|
||||
<string name="manga_error_description_pattern">エラーの詳細:<br><tt>%1$s</tt><br><br>1. <a href=%2$s>Webブラウザで漫画を開いてみて</a>、そのソースで利用できるか確認する<br>2. 利用できる場合は、開発者にエラーレポートを送信する</a>。</string>
|
||||
<string name="reader_control_ltr">人間工学に基づいたリーダーコントロール</string>
|
||||
<string name="history_shortcuts">最近のマンガのショートカットを表示</string>
|
||||
<string name="history_shortcuts_summary">アプリケーションアイコンを長押しして最近のマンガを利用できるようにする</string>
|
||||
<string name="reader_control_ltr_summary">右端をタップするか、右キーを押すと、常に次のページに切り替わります</string>
|
||||
<string name="color_correction">色補正</string>
|
||||
<string name="brightness">輝度</string>
|
||||
<string name="contrast">コントラスト</string>
|
||||
<string name="reset">リセット</string>
|
||||
<string name="text_unsaved_changes_prompt">未保存の変更がありますが、保存しますか、それとも破棄しますか?</string>
|
||||
<string name="color_correction_hint">選択した色の設定は、この漫画のために記憶されます</string>
|
||||
<string name="discard">破棄</string>
|
||||
</resources>
|
||||
@@ -43,7 +43,8 @@
|
||||
</style>
|
||||
|
||||
<style name="Theme.Kotatsu.AppWidgetContainer" parent="@android:style/Theme.DeviceDefault.DayNight">
|
||||
<item name="android:colorBackground">@android:color/system_accent1_900</item>
|
||||
<item name="android:panelColorBackground">@android:color/system_accent1_800</item>
|
||||
<item name="android:colorBackground">@color/m3_sys_color_dynamic_dark_secondary_container</item>
|
||||
<item name="android:panelColorBackground">@color/m3_sys_color_dynamic_dark_inverse_primary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<string name="dont_ask_again">Não pergunte novamente</string>
|
||||
<string name="cancelling_">Cancelando…</string>
|
||||
<string name="error">Erro</string>
|
||||
<string name="clear_thumbs_cache">Limpar cache de thumbnails</string>
|
||||
<string name="clear_thumbs_cache">Limpar cache de miniaturas</string>
|
||||
<string name="search_history_cleared">Limpo</string>
|
||||
<string name="gestures_only">Apenas gestos</string>
|
||||
<string name="internal_storage">Armazenamento interno</string>
|
||||
@@ -193,7 +193,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="category_delete_confirm">Remover a categoria «%s» dos seus favoritos\?
|
||||
<string name="category_delete_confirm">Remover a categoria \"%s\" dos seus favoritos\?
|
||||
\nTodos os mangás nela serão perdidos.</string>
|
||||
<string name="text_history_holder_secondary">Encontre o que ler no menu lateral.</string>
|
||||
<string name="text_local_holder_secondary">Salve-o de fontes online ou importe arquivos.</string>
|
||||
@@ -263,4 +263,6 @@
|
||||
<string name="content">Conteúdo</string>
|
||||
<string name="chapters_empty">Não há capítulos nesta manga</string>
|
||||
<string name="percent_string_pattern">%1$s%%</string>
|
||||
<string name="text_shelf_holder_primary">Seu mangá será exibido aqui</string>
|
||||
<string name="color_correction">Correção de cor</string>
|
||||
</resources>
|
||||
@@ -43,8 +43,8 @@
|
||||
</style>
|
||||
|
||||
<style name="Theme.Kotatsu.AppWidgetContainer" parent="@android:style/Theme.DeviceDefault.DayNight">
|
||||
<item name="android:colorBackground">@android:color/system_accent1_50</item>
|
||||
<item name="android:panelColorBackground">@android:color/system_accent1_100</item>
|
||||
<item name="android:colorBackground">@color/m3_sys_color_dynamic_light_secondary_container</item>
|
||||
<item name="android:panelColorBackground">@color/m3_sys_color_dynamic_light_inverse_primary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<item quantity="other">%1$d 个新章节</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="other">%1$d 章节</item>
|
||||
<item quantity="other">%1$d 个章节</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="other">来自 %2$d 中的 %1$d 个章节</item>
|
||||
|
||||
107
app/src/main/res/values-zh-rTW/strings.xml
Normal file
107
app/src/main/res/values-zh-rTW/strings.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="show_notification_new_chapters_off">你将不会收到通知,但新的章节将在列表中突出显示</string>
|
||||
<string name="computing_">计算…</string>
|
||||
<string name="try_again">再试一次</string>
|
||||
<string name="history_is_empty">尚无历史记录</string>
|
||||
<string name="operation_not_supported">不支持这种操作</string>
|
||||
<string name="application_update">检查应用程序的新版本</string>
|
||||
<string name="text_shelf_holder_secondary">在 \"探索 \"部分找到要读的内容</string>
|
||||
<string name="text_local_holder_secondary">从在线来源保存或导入文件。</string>
|
||||
<string name="text_history_holder_primary">你所读的内容将在这里显示</string>
|
||||
<string name="text_history_holder_secondary">在侧面菜单中找到要读的内容。</string>
|
||||
<string name="text_shelf_holder_primary">你的漫画将显示在这里</string>
|
||||
<string name="text_empty_holder_primary">这里有点空…</string>
|
||||
<string name="feed_will_update_soon">饲料更新将很快开始</string>
|
||||
<string name="reader_mode_hint">所选择的配置将因这部漫画而被记住</string>
|
||||
<string name="data_restored_with_errors">数据被恢复了,但有错误</string>
|
||||
<string name="black_dark_theme_summary">在AMOLED屏幕上使用更少的电力</string>
|
||||
<string name="auth_required">登录后可查看此内容</string>
|
||||
<string name="text_clear_search_history_prompt">永久地删除所有最近的搜索查询?</string>
|
||||
<string name="text_clear_updates_feed_prompt">永久地清除所有的更新历史?</string>
|
||||
<string name="text_clear_cookies_prompt">你将从所有来源中注销</string>
|
||||
<string name="exclude_nsfw_from_history">從歷史中排除NSFW漫畫</string>
|
||||
<string name="dynamic_theme_summary">应用根据你的墙纸的颜色方案创建的主题</string>
|
||||
<string name="text_suggestion_holder">开始阅读漫画,你会得到个性化的建议</string>
|
||||
<string name="suggestions_summary">根据你的喜好推荐漫画</string>
|
||||
<string name="error_empty_name">你必须输入一个名称</string>
|
||||
<string name="suggestions_excluded_genres_summary">指定您不希望在建议中看到的体裁</string>
|
||||
<string name="text_delete_local_manga_batch">从设备中永久删除选定的项目?</string>
|
||||
<string name="filter_load_error">无法加载流派列表</string>
|
||||
<string name="chapters_will_removed_background">章节将在后台被删除。这可能需要一些时间</string>
|
||||
<string name="check_new_chapters_title">检查新的章节并通知有关情况</string>
|
||||
<string name="download_slowdown_summary">有助于避免阻断你的IP地址</string>
|
||||
<string name="email_enter_hint">输入你的电子邮件以继续</string>
|
||||
<string name="new_sources_text">有了新的漫画来源</string>
|
||||
<string name="detect_reader_mode_summary">自动检测漫画是否为网络漫画</string>
|
||||
<string name="show_reading_indicators_summary">在历史和收藏夹中显示阅读百分比</string>
|
||||
<string name="confirm_exit">再次按 \"返回 \"键退出</string>
|
||||
<string name="local_storage">本地存储</string>
|
||||
<string name="reader_control_ltr_summary">轻敲右边缘或按右键总是切换到下一页</string>
|
||||
<string name="text_unsaved_changes_prompt">你有未保存的修改,你想保存还是丢弃它们?</string>
|
||||
<string name="import_completed_hint">您可以從儲存中刪除原始檔案以節省空間</string>
|
||||
<string name="close_menu">关闭菜单</string>
|
||||
<string name="favourites">最爱</string>
|
||||
<string name="chapters">章节</string>
|
||||
<string name="list">列表</string>
|
||||
<string name="grid">栅格</string>
|
||||
<string name="settings">设置</string>
|
||||
<string name="nothing_found">没有发现</string>
|
||||
<string name="onboard_text">选择你想看的漫画的语言。你可以在以后的设置中改变它。</string>
|
||||
<string name="clear_cookies_summary">可以在出现一些问题时提供帮助。所有授权将被视为无效</string>
|
||||
<string name="open_menu">打开菜单</string>
|
||||
<string name="history">历史</string>
|
||||
<string name="error_occurred">发生了一个错误</string>
|
||||
<string name="network_error">无法连接到互联网</string>
|
||||
<string name="details">详情</string>
|
||||
<string name="chapter_d_of_d">第%1$d 。%2$d</string>
|
||||
<string name="detailed_list">详细列表</string>
|
||||
<string name="list_mode">列表模式</string>
|
||||
<string name="remote_sources">远程资源</string>
|
||||
<string name="loading_">正在加载…</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="clear_history">清除历史</string>
|
||||
<string name="text_categories_holder">你可以使用类别来组织你的收藏夹。按 \"+\"来创建一个类别</string>
|
||||
<string name="suggestions_info">所有的数据都在这个设备上进行本地分析。您的个人数据不会被转移到任何服务机构。</string>
|
||||
<string name="exclude_nsfw_from_history_summary">标记为NSFW的漫画将永远不会被添加到历史中,你的进度也不会被保存</string>
|
||||
<string name="backup_information">你可以创建你的历史和收藏的备份并恢复它</string>
|
||||
<string name="crash_text">出了点问题。请向开发人员提交一份错误报告,以帮助我们修复它。</string>
|
||||
<string name="category_delete_confirm">从你的收藏夹中删除 \"%s\" 类别?
|
||||
\n
|
||||
\n里面的所有漫画都会丢失。</string>
|
||||
<string name="color_correction_hint">所选择的颜色设置将被铭记在这部漫画中</string>
|
||||
<string name="show_notification_app_update">如果有新版本,显示通知</string>
|
||||
<string name="text_file_not_supported">要么选择ZIP或CBZ文件。</string>
|
||||
<string name="no_bookmarks_summary">你可以在阅读漫画时创建书签</string>
|
||||
<string name="show_notification_new_chapters_on">你会收到你正在阅读的漫画的更新通知</string>
|
||||
<string name="tracker_warning">一些设备有不同的系统行为,这可能会破坏后台任务。</string>
|
||||
<string name="protect_application_subtitle">输入密码以启动应用程序</string>
|
||||
<string name="chapters_checking_progress">检查新的章节:%1$d of%2$d</string>
|
||||
<string name="chapter_is_missing_text">下载或在线阅读这缺失的章节。</string>
|
||||
<string name="protect_application_summary">在启动Kotatsu时要求输入密码</string>
|
||||
<string name="chapters_empty">这部漫画中没有章节</string>
|
||||
<string name="text_feed_holder">你正在阅读的新章节显示在这里</string>
|
||||
<string name="history_shortcuts_summary">通过长按应用程序图标来提供最近的漫画</string>
|
||||
<string name="large_manga_save_confirm">这部漫画有%s 。全部保存?</string>
|
||||
<string name="network_consumption_warning">这可能会传输大量的数据</string>
|
||||
<string name="app_update_available">新版本的应用程序已经推出</string>
|
||||
<string name="password_length_hint">密码必须是4个字符或以上</string>
|
||||
<string name="auth_not_supported_by">不支持在%s 上登录</string>
|
||||
<string name="_s_deleted_from_local_storage">\"%s\" 从本地存储中删除</string>
|
||||
<string name="exit_confirmation_summary">按两次 \"返回 \"键,退出应用程序</string>
|
||||
<string name="no_manga_sources_text">启用漫画来源,在线阅读漫画</string>
|
||||
<string name="text_clear_history_prompt">永久清除所有阅读历史?</string>
|
||||
<string name="wait_for_loading_finish">等待加载完成…</string>
|
||||
<string name="text_delete_local_manga">从设备中永久删除 \"%s\"?</string>
|
||||
<string name="text_search_holder_secondary">尝试重新表述查询。</string>
|
||||
<string name="disable_battery_optimization_summary">帮助进行背景更新检查</string>
|
||||
<string name="enter_email_text">输入你的电子邮件以继续</string>
|
||||
<string name="importing_progress">进口漫画:%1$d 的%2$d</string>
|
||||
<string name="exclude_nsfw_from_suggestions">请勿推荐NSFW漫画</string>
|
||||
<string name="update_check_failed">无法查找更新</string>
|
||||
<string name="add_new_category">新类别</string>
|
||||
<string name="read">阅读</string>
|
||||
<string name="you_have_not_favourites_yet">暂时没有喜欢的人</string>
|
||||
<string name="add_to_favourites">最喜欢这个</string>
|
||||
<string name="add">添加</string>
|
||||
</resources>
|
||||
@@ -388,4 +388,6 @@
|
||||
<string name="color_correction_hint">The chosen color settings will be remembered for this manga</string>
|
||||
<string name="text_unsaved_changes_prompt">You have unsaved changes, do you want to save or discard them?</string>
|
||||
<string name="discard">Discard</string>
|
||||
<string name="error_no_space_left">No space left on device</string>
|
||||
<string name="reader_slider">Show page switching slider</string>
|
||||
</resources>
|
||||
|
||||
@@ -45,6 +45,11 @@
|
||||
android:key="reader_bar"
|
||||
android:title="@string/reader_info_bar" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="reader_slider"
|
||||
android:title="@string/reader_slider" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="pages_numbers"
|
||||
|
||||
@@ -7,7 +7,7 @@ buildscript {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10'
|
||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.43.2'
|
||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
Reference in New Issue
Block a user