Compare commits

..

7 Commits

Author SHA1 Message Date
1cbfe017ea Fix network state observer 2022-11-30 09:22:51 +02:00
f469369b14 Fix manga search suggestions #268 2022-11-30 09:08:40 +02:00
1ddcaed483 Update parsers 2022-11-30 08:29:36 +02:00
Cường Bá
7bb7736f18 Translated using Weblate (Vietnamese)
Currently translated at 87.5% (7 of 8 strings)

Co-authored-by: Cường Bá <cuongba956@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/vi/
Translation: Kotatsu/plurals
2022-11-30 08:11:39 +02:00
john d
d1e7e7a2a6 Translated using Weblate (Greek)
Currently translated at 20.8% (83 of 398 strings)

Translated using Weblate (Greek)

Currently translated at 2.2% (9 of 398 strings)

Translated using Weblate (Greek)

Currently translated at 87.5% (7 of 8 strings)

Added translation using Weblate (Greek)

Added translation using Weblate (Greek)

Co-authored-by: john d <rasengan1405@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/el/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/el/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2022-11-30 08:11:39 +02:00
0c4b7b0586 Replace ArrayMap with an AndroidX implementation 2022-11-28 19:59:32 +02:00
f320f22863 Improve network state observer 2022-11-28 19:40:01 +02:00
26 changed files with 282 additions and 141 deletions

View File

@@ -15,8 +15,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdkVersion 21
targetSdkVersion 33
versionCode 504
versionName '4.0.4'
versionCode 505
versionName '4.0.5'
generatedDensities = []
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -83,7 +83,7 @@ afterEvaluate {
}
}
dependencies {
implementation('com.github.KotatsuApp:kotatsu-parsers:1e49d4095b') {
implementation('com.github.KotatsuApp:kotatsu-parsers:2ab03cb668') {
exclude group: 'org.json', module: 'json'
}
@@ -118,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.44"
kapt "com.google.dagger:hilt-compiler:2.44"
implementation 'com.google.dagger:hilt-android:2.44.2'
kapt 'com.google.dagger:hilt-compiler:2.44.2'
implementation 'androidx.hilt:hilt-work:1.0.0'
kapt 'androidx.hilt:hilt-compiler:1.0.0'
@@ -128,10 +128,10 @@ dependencies {
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:f8a38b08fe'
implementation 'com.github.solkin:disk-lru-cache:1.4'
implementation 'ch.acra:acra-http:5.9.6'
implementation 'ch.acra:acra-dialog:5.9.6'
implementation 'ch.acra:acra-http:5.9.7'
implementation 'ch.acra:acra-dialog:5.9.7'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.json:json:20220924'
@@ -147,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.44'
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.44'
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.44.2'
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.44.2'
}

View File

@@ -2,20 +2,20 @@ package org.koitharu.kotatsu.base.ui.list
import android.app.Activity
import android.os.Bundle
import android.util.ArrayMap
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.collection.ArrayMap
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryOwner
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.Dispatchers
import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration
import kotlin.coroutines.EmptyCoroutineContext
private const val PROVIDER_NAME = "selection_decoration_sectioned"

View File

@@ -25,6 +25,7 @@ import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.base.ui.util.ActivityRecreationHandle
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.network.*
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.os.ShortcutsUpdater
import org.koitharu.kotatsu.core.parser.MangaLoaderContextImpl
import org.koitharu.kotatsu.core.parser.MangaRepository
@@ -39,6 +40,7 @@ import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
import org.koitharu.kotatsu.settings.backup.BackupObserver
import org.koitharu.kotatsu.sync.domain.SyncController
import org.koitharu.kotatsu.utils.IncognitoModeIndicator
import org.koitharu.kotatsu.utils.ext.connectivityManager
import org.koitharu.kotatsu.utils.ext.isLowRamDevice
import org.koitharu.kotatsu.utils.image.CoilImageGetter
import org.koitharu.kotatsu.widget.WidgetUpdater
@@ -81,6 +83,12 @@ interface AppModule {
}.build()
}
@Provides
@Singleton
fun provideNetworkState(
@ApplicationContext context: Context
) = NetworkState(context.connectivityManager)
@Provides
@Singleton
fun provideMangaDatabase(

View File

@@ -14,11 +14,11 @@ abstract class MangaDao {
abstract suspend fun find(id: Long): MangaWithTags?
@Transaction
@Query("SELECT * FROM manga WHERE title LIKE :query OR alt_title LIKE :query LIMIT :limit")
@Query("SELECT * FROM manga WHERE (title LIKE :query OR alt_title LIKE :query) AND manga_id IN (SELECT manga_id FROM favourites UNION SELECT manga_id FROM history) LIMIT :limit")
abstract suspend fun searchByTitle(query: String, limit: Int): List<MangaWithTags>
@Transaction
@Query("SELECT * FROM manga WHERE (title LIKE :query OR alt_title LIKE :query) AND source = :source LIMIT :limit")
@Query("SELECT * FROM manga WHERE (title LIKE :query OR alt_title LIKE :query) AND source = :source AND manga_id IN (SELECT manga_id FROM favourites UNION SELECT manga_id FROM history) LIMIT :limit")
abstract suspend fun searchByTitle(query: String, source: String, limit: Int): List<MangaWithTags>
@Insert(onConflict = OnConflictStrategy.IGNORE)
@@ -47,4 +47,4 @@ abstract class MangaDao {
}
}
}
}
}

View File

@@ -1,9 +1,9 @@
package org.koitharu.kotatsu.core.exceptions.resolve
import android.util.ArrayMap
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
import androidx.collection.ArrayMap
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import kotlinx.coroutines.suspendCancellableCoroutine

View File

@@ -0,0 +1,46 @@
package org.koitharu.kotatsu.core.os
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import android.net.NetworkRequest
import kotlinx.coroutines.flow.first
import org.koitharu.kotatsu.utils.MediatorStateFlow
import org.koitharu.kotatsu.utils.ext.isNetworkAvailable
class NetworkState(
private val connectivityManager: ConnectivityManager,
) : MediatorStateFlow<Boolean>(connectivityManager.isNetworkAvailable) {
private val callback = NetworkCallbackImpl()
override fun onActive() {
invalidate()
val request = NetworkRequest.Builder().build()
connectivityManager.registerNetworkCallback(request, callback)
}
override fun onInactive() {
connectivityManager.unregisterNetworkCallback(callback)
}
suspend fun awaitForConnection() {
if (value) {
return
}
first { it }
}
private fun invalidate() {
publishValue(connectivityManager.isNetworkAvailable)
}
private inner class NetworkCallbackImpl : NetworkCallback() {
override fun onAvailable(network: Network) = invalidate()
override fun onLost(network: Network) = invalidate()
override fun onUnavailable() = invalidate()
}
}

View File

@@ -1,78 +0,0 @@
package org.koitharu.kotatsu.core.os
import android.content.Context
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import android.net.NetworkRequest
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.onSuccess
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.first
import org.koitharu.kotatsu.utils.ext.connectivityManager
import org.koitharu.kotatsu.utils.ext.isNetworkAvailable
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NetworkStateObserver @Inject constructor(
@ApplicationContext context: Context,
) : StateFlow<Boolean> {
private val connectivityManager = context.connectivityManager
override val replayCache: List<Boolean>
get() = listOf(value)
override val value: Boolean
get() = connectivityManager.isNetworkAvailable
override suspend fun collect(collector: FlowCollector<Boolean>): Nothing {
collector.emit(value)
while (true) {
observeImpl().collect(collector)
}
}
suspend fun awaitForConnection(): Unit {
if (value) {
return
}
first { it }
}
private fun observeImpl() = callbackFlow<Boolean> {
val request = NetworkRequest.Builder().build()
val callback = FlowNetworkCallback(this)
connectivityManager.registerNetworkCallback(request, callback)
awaitClose {
connectivityManager.unregisterNetworkCallback(callback)
}
}
private inner class FlowNetworkCallback(
private val producerScope: ProducerScope<Boolean>,
) : NetworkCallback() {
private var prevValue = value
override fun onAvailable(network: Network) = update()
override fun onLost(network: Network) = update()
override fun onUnavailable() = update()
private fun update() {
val newValue = connectivityManager.isNetworkAvailable
if (newValue != prevValue) {
producerScope.trySendBlocking(newValue).onSuccess {
prevValue = newValue
}
}
}
}
}

View File

@@ -5,7 +5,7 @@ import androidx.annotation.CallSuper
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.LayoutPageInfoBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -14,12 +14,12 @@ abstract class BasePageHolder<B : ViewBinding>(
protected val binding: B,
loader: PageLoader,
settings: ReaderSettings,
networkStateObserver: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) : RecyclerView.ViewHolder(binding.root), PageHolderDelegate.Callback {
@Suppress("LeakingThis")
protected val delegate = PageHolderDelegate(loader, settings, this, networkStateObserver, exceptionResolver)
protected val delegate = PageHolderDelegate(loader, settings, this, networkState, exceptionResolver)
protected val bindingInfo = LayoutPageInfoBinding.bind(binding.root)
val context: Context

View File

@@ -5,7 +5,7 @@ import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
import org.koitharu.kotatsu.utils.ext.resetTransformations
@@ -16,7 +16,7 @@ import kotlin.coroutines.suspendCoroutine
abstract class BaseReaderAdapter<H : BasePageHolder<*>>(
private val loader: PageLoader,
private val readerSettings: ReaderSettings,
private val networkState: NetworkStateObserver,
private val networkState: NetworkState,
private val exceptionResolver: ExceptionResolver,
) : RecyclerView.Adapter<H>() {
@@ -70,7 +70,7 @@ abstract class BaseReaderAdapter<H : BasePageHolder<*>>(
parent: ViewGroup,
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
): H

View File

@@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -28,7 +28,7 @@ class PageHolderDelegate(
private val loader: PageLoader,
private val readerSettings: ReaderSettings,
private val callback: Callback,
private val networkState: NetworkStateObserver,
private val networkState: NetworkState,
private val exceptionResolver: ExceptionResolver,
) : DefaultOnImageEventListener, Observer<ReaderSettings> {

View File

@@ -6,7 +6,7 @@ import android.widget.FrameLayout
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.ItemPageBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -16,7 +16,7 @@ class ReversedPageHolder(
binding: ItemPageBinding,
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) : PageHolder(binding, loader, settings, networkState, exceptionResolver) {

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.reader.ui.pager.reversed
import android.view.LayoutInflater
import android.view.ViewGroup
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.ItemPageBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -12,7 +12,7 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
class ReversedPagesAdapter(
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) : BaseReaderAdapter<ReversedPageHolder>(loader, settings, networkState, exceptionResolver) {
@@ -20,7 +20,7 @@ class ReversedPagesAdapter(
parent: ViewGroup,
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) = ReversedPageHolder(
binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false),

View File

@@ -8,7 +8,7 @@ import android.view.ViewGroup
import androidx.core.view.children
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
@@ -26,7 +26,7 @@ import kotlin.math.absoluteValue
class ReversedReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
@Inject
lateinit var networkStateObserver: NetworkStateObserver
lateinit var networkState: NetworkState
private var pagerAdapter: ReversedPagesAdapter? = null
@@ -41,7 +41,7 @@ class ReversedReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
pagerAdapter = ReversedPagesAdapter(
viewModel.pageLoader,
viewModel.readerSettings,
networkStateObserver,
networkState,
exceptionResolver,
)
with(binding.pager) {

View File

@@ -10,7 +10,7 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.ItemPageBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -22,7 +22,7 @@ open class PageHolder(
binding: ItemPageBinding,
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) : BasePageHolder<ItemPageBinding>(binding, loader, settings, networkState, exceptionResolver),
View.OnClickListener {

View File

@@ -8,7 +8,7 @@ import android.view.ViewGroup
import androidx.core.view.children
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
@@ -25,7 +25,7 @@ import kotlin.math.absoluteValue
class PagerReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
@Inject
lateinit var networkStateObserver: NetworkStateObserver
lateinit var networkState: NetworkState
private var pagesAdapter: PagesAdapter? = null
@@ -40,7 +40,7 @@ class PagerReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
pagesAdapter = PagesAdapter(
viewModel.pageLoader,
viewModel.readerSettings,
networkStateObserver,
networkState,
exceptionResolver,
)
with(binding.pager) {

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.reader.ui.pager.standard
import android.view.LayoutInflater
import android.view.ViewGroup
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.ItemPageBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -12,15 +12,15 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
class PagesAdapter(
loader: PageLoader,
settings: ReaderSettings,
networkStateObserver: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) : BaseReaderAdapter<PageHolder>(loader, settings, networkStateObserver, exceptionResolver) {
) : BaseReaderAdapter<PageHolder>(loader, settings, networkState, exceptionResolver) {
override fun onCreateViewHolder(
parent: ViewGroup,
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) = PageHolder(
binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false),

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.view.LayoutInflater
import android.view.ViewGroup
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -12,7 +12,7 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
class WebtoonAdapter(
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) : BaseReaderAdapter<WebtoonHolder>(loader, settings, networkState, exceptionResolver) {
@@ -20,7 +20,7 @@ class WebtoonAdapter(
parent: ViewGroup,
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) = WebtoonHolder(
binding = ItemPageWebtoonBinding.inflate(

View File

@@ -8,7 +8,7 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.davemorrissey.labs.subscaleview.decoder.SkiaPooledImageRegionDecoder
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
@@ -25,7 +25,7 @@ class WebtoonHolder(
binding: ItemPageWebtoonBinding,
loader: PageLoader,
settings: ReaderSettings,
networkState: NetworkStateObserver,
networkState: NetworkState,
exceptionResolver: ExceptionResolver,
) : BasePageHolder<ItemPageWebtoonBinding>(binding, loader, settings, networkState, exceptionResolver),
View.OnClickListener {

View File

@@ -7,7 +7,7 @@ import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.databinding.FragmentReaderWebtoonBinding
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
@@ -22,7 +22,7 @@ import javax.inject.Inject
class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
@Inject
lateinit var networkStateObserver: NetworkStateObserver
lateinit var networkState: NetworkState
private val scrollInterpolator = AccelerateDecelerateInterpolator()
private var webtoonAdapter: WebtoonAdapter? = null
@@ -37,7 +37,7 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
webtoonAdapter = WebtoonAdapter(
viewModel.pageLoader,
viewModel.readerSettings,
networkStateObserver,
networkState,
exceptionResolver,
)
with(binding.recyclerView) {

View File

@@ -12,7 +12,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.base.ui.util.ReversibleAction
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.os.NetworkStateObserver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.prefs.observeAsFlow
@@ -46,14 +46,14 @@ class ShelfViewModel @Inject constructor(
private val favouritesRepository: FavouritesRepository,
private val trackingRepository: TrackingRepository,
private val settings: AppSettings,
networkStateObserver: NetworkStateObserver,
networkState: NetworkState,
) : BaseViewModel(), ListExtraProvider {
val onActionDone = SingleLiveEvent<ReversibleAction>()
val content: LiveData<List<ListModel>> = combine(
settings.observeAsFlow(AppSettings.KEY_SHELF_SECTIONS) { shelfSections },
networkStateObserver,
networkState,
repository.observeShelfContent(),
) { sections, isConnected, content ->
mapList(content, sections, isConnected)

View File

@@ -5,16 +5,17 @@ import android.accounts.AccountManager
import android.content.ContentResolver
import android.content.Context
import android.os.Bundle
import android.util.ArrayMap
import androidx.collection.ArrayMap
import androidx.room.InvalidationTracker
import androidx.room.withTransaction
import dagger.hilt.android.qualifiers.ApplicationContext
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.db.MangaDatabase
@@ -22,6 +23,9 @@ import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
import org.koitharu.kotatsu.core.db.TABLE_HISTORY
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SyncController @Inject constructor(

View File

@@ -1,14 +1,14 @@
package org.koitharu.kotatsu.utils
import android.util.ArrayMap
import java.util.*
import kotlin.coroutines.coroutineContext
import kotlin.coroutines.resume
import androidx.collection.ArrayMap
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.isActive
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.LinkedList
import kotlin.coroutines.coroutineContext
import kotlin.coroutines.resume
class CompositeMutex<T : Any> : Set<T> {
@@ -27,7 +27,7 @@ class CompositeMutex<T : Any> : Set<T> {
}
override fun isEmpty(): Boolean {
return data.isEmpty()
return data.isEmpty
}
override fun iterator(): Iterator<T> {
@@ -59,7 +59,7 @@ class CompositeMutex<T : Any> : Set<T> {
private suspend fun waitForRemoval(element: T) {
val list = data[element] ?: return
suspendCancellableCoroutine<Unit> { continuation ->
suspendCancellableCoroutine { continuation ->
list.add(continuation)
continuation.invokeOnCancellation {
list.remove(continuation)

View File

@@ -0,0 +1,39 @@
package org.koitharu.kotatsu.utils
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.concurrent.atomic.AtomicInteger
abstract class MediatorStateFlow<T>(initialValue: T) : StateFlow<T> {
private val delegate = MutableStateFlow(initialValue)
private val collectors = AtomicInteger(0)
final override val replayCache: List<T>
get() = delegate.replayCache
final override val value: T
get() = delegate.value
final override suspend fun collect(collector: FlowCollector<T>): Nothing {
try {
if (collectors.getAndIncrement() == 0) {
onActive()
}
delegate.collect(collector)
} finally {
if (collectors.decrementAndGet() == 0) {
onInactive()
}
}
}
protected fun publishValue(v: T) {
delegate.value = v
}
abstract fun onActive()
abstract fun onInactive()
}

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="days_ago">
<item quantity="one">%1$d μέρα πριν</item>
<item quantity="other">%1$d μέρες πριν</item>
</plurals>
<plurals name="items">
<item quantity="one">%1$dστοιχείο</item>
<item quantity="other">%1$dστοιχεία</item>
</plurals>
<plurals name="new_chapters">
<item quantity="one">%1$dνέο κεφάλαιο</item>
<item quantity="other">%1$dνέα κεφάλαια</item>
</plurals>
<plurals name="chapters_from_x">
<item quantity="one">%1$dκεφάλαιο%2$d</item>
<item quantity="other">%1$dκεφάλαια%2$d</item>
</plurals>
<plurals name="hours_ago">
<item quantity="one">%1$dώρα πριν</item>
<item quantity="other">%1$dώρες πριν</item>
</plurals>
<plurals name="chapters">
<item quantity="one">%1$dκεφάλαιο</item>
<item quantity="other">%1$dκεφάλαια</item>
</plurals>
<plurals name="pages">
<item quantity="one">Σύνολο%1$dσελίδα</item>
<item quantity="other">Σύνολο%1$dσελίδες</item>
</plurals>
<plurals name="minutes_ago">
<item quantity="one">%1$dλεπτό πριν</item>
<item quantity="other">%1$d λεπτά πριν</item>
</plurals>
</resources>

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="local_storage">Εσωτερικός χώρος</string>
<string name="close_menu">Κλείσιμο μενού</string>
<string name="open_menu">Άνοιγμα μενού</string>
<string name="favourites">Αγαπημένα</string>
<string name="history">Ιστορικό</string>
<string name="error_occurred">Προέκυψε σφάλμα</string>
<string name="try_again">Επανάληψη</string>
<string name="grid">Πλέγμα</string>
<string name="list_mode">Εμφάνιση ως λίστα</string>
<string name="settings">Ρυθμίσεις</string>
<string name="remote_sources">Απομακρυσμένες πηγές</string>
<string name="computing_">Επεξεργασία…</string>
<string name="close">Κλείσιμο</string>
<string name="clear_history">Εκκαθάριση ιστορικού</string>
<string name="nothing_found">Δεν βρέθηκε τίποτα</string>
<string name="history_is_empty">Κενό ιστορικό</string>
<string name="read">Διάβασε</string>
<string name="add_to_favourites">Προσθήκη στα αγαπημένα</string>
<string name="add_new_category">Νέα κατηγορία</string>
<string name="save">Αποθήκευση</string>
<string name="share">Κοινοποιήση</string>
<string name="create_shortcut">Δημιουργία συντόμευσης…</string>
<string name="share_s">Κοινοποίηση %s</string>
<string name="search">Αναζήτηση</string>
<string name="search_manga">Αναζήτηση μάνγκα</string>
<string name="manga_downloading_">Λήψη…</string>
<string name="download_complete">Κατεβασμένο</string>
<string name="downloads">Λήψεις</string>
<string name="updated">Ενημερωμένο</string>
<string name="newest">Νεότερο</string>
<string name="by_rating">Βαθμολογία</string>
<string name="filter">Φίλτρο</string>
<string name="dark">Σκοτεινό</string>
<string name="automatic">Όπως στο σύστημα</string>
<string name="clear">Εκκαθάριση</string>
<string name="text_clear_history_prompt">Να διαγράψετε μόνιμα όλο το ιστορικό ανάγνωσης;</string>
<string name="remove">Διαγραφή</string>
<string name="save_page">Αποθήκευση σελίδας</string>
<string name="page_saved">Αποθηκευμένα</string>
<string name="share_image">Κοινή χρήση εικόνας</string>
<string name="_import">Εισαγωγή</string>
<string name="delete">Διαγραφή</string>
<string name="text_file_not_supported">Επιλέξτε ένα αρχείο ZIP ή CBZ.</string>
<string name="no_description">Χωρίς περιγραφή</string>
<string name="history_and_cache">Ιστορικό και μνήμη cache</string>
<string name="clear_pages_cache">Εκκαθάριση μνήμης cache της σελίδας</string>
<string name="cache">Προσωρινή Μνήμη</string>
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="standard">Τυπικό</string>
<string name="webtoon">Μάνχγουα</string>
<string name="search_on_s">Αναζήτηση στο %s</string>
<string name="delete_manga">Διαγραφή μάνγκα</string>
<string name="text_delete_local_manga">Μόνιμη διαγραφή του \"%s\" από τη συσκευή;</string>
<string name="reader_settings">Ρυθμίσεις λειτουργίας ανάγνωσης</string>
<string name="switch_pages">Αλλαγή σελίδων</string>
<string name="network_error">Αδυναμία σύνδεσης στο ίντερνετ</string>
<string name="chapters">Κεφάλαια</string>
<string name="details">Πληροφορίες</string>
<string name="list">Λίστα</string>
<string name="detailed_list">Λεπτομερής λίστα</string>
<string name="loading_">Φόρτωση…</string>
<string name="chapter_d_of_d">Κεφάλαιο%1$d από %2$d</string>
<string name="you_have_not_favourites_yet">Δεν υπάρχουν αγαπημένα</string>
<string name="add">Προσθήκη</string>
<string name="enter_category_name">Εισαγωγή ονόματος κατηγορίας</string>
<string name="processing_">Επεξεργασία…</string>
<string name="by_name">Όνομα</string>
<string name="popular">Δημοφιλή</string>
<string name="sort_order">Τρόπος Ταξινόμησης</string>
<string name="_s_removed_from_history">Το \"%s\" αφαιρέθηκε από το ιστορικό</string>
<string name="theme">Θέμα</string>
<string name="light">Φωτεινό</string>
<string name="pages">Σελίδες</string>
<string name="wait_for_loading_finish">Περιμένετε να ολοκληρωθεί η φόρτωση…</string>
<string name="_s_deleted_from_local_storage">Το \"%s\" διαγράφηκε από τον τοπικό χώρο αποθήκευσης</string>
<string name="operation_not_supported">Αυτή η λειτουργία δεν υποστηρίζεται</string>
<string name="read_mode">Λειτουργία ανάγνωσης</string>
<string name="grid_size">Μέγεθος πλέγματος</string>
</resources>

View File

@@ -15,4 +15,10 @@
<plurals name="days_ago">
<item quantity="other">%1$d ngày trước</item>
</plurals>
<plurals name="chapters_from_x">
<item quantity="other">%1$d chương từ %2$d</item>
</plurals>
<plurals name="pages">
<item quantity="other">Tổng %1$d trang</item>
</plurals>
</resources>