Compare commits

..

7 Commits

Author SHA1 Message Date
Koitharu
009eb9fe44 Fix recursive sync 2023-06-17 18:34:08 +03:00
Koitharu
fc8a5ccd9f Fix Continue button in offline mode 2023-06-17 18:20:57 +03:00
Koitharu
91f46de547 Fix crashes 2023-06-17 18:11:09 +03:00
Koitharu
d548993e14 Move syncronization to main process 2023-06-17 17:36:12 +03:00
Koitharu
4f32664b33 Respect system PowerSave mode 2023-06-17 16:12:14 +03:00
Koitharu
71b14a3aa8 Refactor FilterOwner 2023-06-17 16:05:08 +03:00
Isira Seneviratne
183a61272e Use ParcelCompat methods. 2023-06-17 15:50:08 +03:00
26 changed files with 191 additions and 148 deletions

View File

@@ -15,8 +15,8 @@ android {
applicationId 'org.koitharu.kotatsu' applicationId 'org.koitharu.kotatsu'
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 33 targetSdkVersion 33
versionCode 554 versionCode 555
versionName '5.2.2' versionName '5.2.3'
generatedDensities = [] generatedDensities = []
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -188,8 +188,7 @@
<service <service
android:name="org.koitharu.kotatsu.sync.ui.favourites.FavouritesSyncService" android:name="org.koitharu.kotatsu.sync.ui.favourites.FavouritesSyncService"
android:exported="false" android:exported="false"
android:label="@string/favourites" android:label="@string/favourites">
android:process=":sync">
<intent-filter> <intent-filter>
<action android:name="android.content.SyncAdapter" /> <action android:name="android.content.SyncAdapter" />
</intent-filter> </intent-filter>
@@ -200,8 +199,7 @@
<service <service
android:name="org.koitharu.kotatsu.sync.ui.history.HistorySyncService" android:name="org.koitharu.kotatsu.sync.ui.history.HistorySyncService"
android:exported="false" android:exported="false"
android:label="@string/history" android:label="@string/history">
android:process=":sync">
<intent-filter> <intent-filter>
<action android:name="android.content.SyncAdapter" /> <action android:name="android.content.SyncAdapter" />
</intent-filter> </intent-filter>

View File

@@ -6,6 +6,7 @@ import android.app.ActivityManager.MemoryInfo
import android.app.ActivityOptions import android.app.ActivityOptions
import android.content.Context import android.content.Context
import android.content.Context.ACTIVITY_SERVICE import android.content.Context.ACTIVITY_SERVICE
import android.content.Context.POWER_SERVICE
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.OperationApplicationException import android.content.OperationApplicationException
import android.content.SharedPreferences import android.content.SharedPreferences
@@ -17,6 +18,7 @@ import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import android.view.View import android.view.View
import android.view.ViewPropertyAnimator import android.view.ViewPropertyAnimator
@@ -51,6 +53,9 @@ import kotlin.math.roundToLong
val Context.activityManager: ActivityManager? val Context.activityManager: ActivityManager?
get() = getSystemService(ACTIVITY_SERVICE) as? ActivityManager get() = getSystemService(ACTIVITY_SERVICE) as? ActivityManager
val Context.powerManager: PowerManager?
get() = getSystemService(POWER_SERVICE) as? PowerManager
fun String.toUriOrNull() = if (isEmpty()) null else Uri.parse(this) fun String.toUriOrNull() = if (isEmpty()) null else Uri.parse(this)
suspend fun CoroutineWorker.trySetForeground(): Boolean = runCatchingCancellable { suspend fun CoroutineWorker.trySetForeground(): Boolean = runCatchingCancellable {
@@ -141,6 +146,10 @@ fun Context.isLowRamDevice(): Boolean {
return activityManager?.isLowRamDevice ?: false return activityManager?.isLowRamDevice ?: false
} }
fun Context.isPowerSaveMode(): Boolean {
return powerManager?.isPowerSaveMode == true
}
val Context.ramAvailable: Long val Context.ramAvailable: Long
get() { get() {
val result = MemoryInfo() val result = MemoryInfo()

View File

@@ -9,8 +9,8 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
import androidx.core.os.BundleCompat import androidx.core.os.BundleCompat
import androidx.core.os.ParcelCompat
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaTags
import java.io.Serializable import java.io.Serializable
// https://issuetracker.google.com/issues/240585930 // https://issuetracker.google.com/issues/240585930
@@ -36,11 +36,11 @@ inline fun <reified T : Serializable> Bundle.getSerializableCompat(key: String):
} }
inline fun <reified T : Parcelable> Parcel.readParcelableCompat(): T? { inline fun <reified T : Parcelable> Parcel.readParcelableCompat(): T? {
return readParcelable(ParcelableMangaTags::class.java.classLoader) as T? return ParcelCompat.readParcelable(this, T::class.java.classLoader, T::class.java)
} }
inline fun <reified T : Serializable> Parcel.readSerializableCompat(): T? { inline fun <reified T : Serializable> Parcel.readSerializableCompat(): T? {
return readSerializable() as T? return ParcelCompat.readSerializable(this, T::class.java.classLoader, T::class.java)
} }
inline fun <reified T : Serializable> Bundle.requireSerializable(key: String): T { inline fun <reified T : Serializable> Bundle.requireSerializable(key: String): T {
@@ -49,12 +49,6 @@ inline fun <reified T : Serializable> Bundle.requireSerializable(key: String): T
} }
} }
inline fun <reified T : Parcelable> Bundle.requireParcelable(key: String): T {
return checkNotNull(getParcelableCompat(key)) {
"Parcelable of type \"${T::class.java.name}\" not found at \"$key\""
}
}
fun <T> SavedStateHandle.require(key: String): T { fun <T> SavedStateHandle.require(key: String): T {
return checkNotNull(get(key)) { return checkNotNull(get(key)) {
"Value $key not found in SavedStateHandle or has a wrong type" "Value $key not found in SavedStateHandle or has a wrong type"

View File

@@ -10,12 +10,13 @@ import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaChapters
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.ui.CoroutineIntentService import org.koitharu.kotatsu.core.ui.CoroutineIntentService
import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat
import org.koitharu.kotatsu.core.util.ext.isPowerSaveMode
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@@ -115,6 +116,9 @@ class MangaPrefetchService : CoroutineIntentService() {
if (source == MangaSource.LOCAL) { if (source == MangaSource.LOCAL) {
return false return false
} }
if (context.isPowerSaveMode()) {
return false
}
val entryPoint = EntryPointAccessors.fromApplication(context, PrefetchCompanionEntryPoint::class.java) val entryPoint = EntryPointAccessors.fromApplication(context, PrefetchCompanionEntryPoint::class.java)
return entryPoint.contentCache.isCachingEnabled && entryPoint.settings.isContentPrefetchEnabled return entryPoint.contentCache.isCachingEnabled && entryPoint.settings.isContentPrefetchEnabled
} }

View File

@@ -22,6 +22,7 @@ import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.util.ext.lifecycleScope import org.koitharu.kotatsu.core.util.ext.lifecycleScope
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
import org.koitharu.kotatsu.filter.ui.model.FilterItem import org.koitharu.kotatsu.filter.ui.model.FilterItem
@@ -35,7 +36,6 @@ import org.koitharu.kotatsu.parsers.util.SuspendLazy
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
import org.koitharu.kotatsu.search.domain.MangaSearchRepository import org.koitharu.kotatsu.search.domain.MangaSearchRepository
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import java.text.Collator import java.text.Collator
import java.util.LinkedList import java.util.LinkedList
import java.util.Locale import java.util.Locale
@@ -49,7 +49,7 @@ class FilterCoordinator @Inject constructor(
dataRepository: MangaDataRepository, dataRepository: MangaDataRepository,
private val searchRepository: MangaSearchRepository, private val searchRepository: MangaSearchRepository,
lifecycle: ViewModelLifecycle, lifecycle: ViewModelLifecycle,
) : FilterOwner { ) : MangaFilter {
private val coroutineScope = lifecycle.lifecycleScope private val coroutineScope = lifecycle.lifecycleScope
private val repository = mangaRepositoryFactory.create(savedStateHandle.require(RemoteListFragment.ARG_SOURCE)) private val repository = mangaRepositoryFactory.create(savedStateHandle.require(RemoteListFragment.ARG_SOURCE))

View File

@@ -19,9 +19,8 @@ import com.google.android.material.R as materialR
class FilterHeaderFragment : BaseFragment<FragmentFilterHeaderBinding>(), ChipsView.OnChipClickListener { class FilterHeaderFragment : BaseFragment<FragmentFilterHeaderBinding>(), ChipsView.OnChipClickListener {
private val owner by lazy(LazyThreadSafetyMode.NONE) { private val filter: MangaFilter
FilterOwner.from(requireActivity()) get() = (requireActivity() as FilterOwner).filter
}
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFilterHeaderBinding { override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFilterHeaderBinding {
return FragmentFilterHeaderBinding.inflate(inflater, container, false) return FragmentFilterHeaderBinding.inflate(inflater, container, false)
@@ -30,7 +29,7 @@ class FilterHeaderFragment : BaseFragment<FragmentFilterHeaderBinding>(), ChipsV
override fun onViewBindingCreated(binding: FragmentFilterHeaderBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentFilterHeaderBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
binding.chipsTags.onChipClickListener = this binding.chipsTags.onChipClickListener = this
owner.header.observe(viewLifecycleOwner, ::onDataChanged) filter.header.observe(viewLifecycleOwner, ::onDataChanged)
} }
override fun onWindowInsetsChanged(insets: Insets) = Unit override fun onWindowInsetsChanged(insets: Insets) = Unit
@@ -40,7 +39,7 @@ class FilterHeaderFragment : BaseFragment<FragmentFilterHeaderBinding>(), ChipsV
if (tag == null) { if (tag == null) {
FilterSheetFragment.show(parentFragmentManager) FilterSheetFragment.show(parentFragmentManager)
} else { } else {
owner.onTagItemClick(FilterItem.Tag(tag, !chip.isChecked)) filter.onTagItemClick(FilterItem.Tag(tag, !chip.isChecked))
} }
} }

View File

@@ -1,32 +1,6 @@
package org.koitharu.kotatsu.filter.ui package org.koitharu.kotatsu.filter.ui
import androidx.fragment.app.Fragment interface FilterOwner {
import androidx.fragment.app.FragmentActivity
import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.core.util.ext.values
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaTag
interface FilterOwner : OnFilterChangedListener { val filter: MangaFilter
val filterItems: StateFlow<List<ListModel>>
val header: StateFlow<FilterHeaderModel>
fun applyFilter(tags: Set<MangaTag>)
companion object {
fun from(activity: FragmentActivity): FilterOwner {
for (f in activity.supportFragmentManager.fragments) {
return find(f) ?: continue
}
error("Cannot find FilterOwner")
}
fun find(fragment: Fragment): FilterOwner? {
return fragment.viewModelStore.values.firstNotNullOfOrNull { it as? FilterOwner }
}
}
} }

View File

@@ -21,20 +21,17 @@ class FilterSheetFragment :
AdaptiveSheetCallback, AdaptiveSheetCallback,
AsyncListDiffer.ListListener<ListModel> { AsyncListDiffer.ListListener<ListModel> {
private val owner by lazy(LazyThreadSafetyMode.NONE) {
FilterOwner.from(requireActivity())
}
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding { override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding {
return SheetFilterBinding.inflate(inflater, container, false) return SheetFilterBinding.inflate(inflater, container, false)
} }
override fun onViewBindingCreated(binding: SheetFilterBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: SheetFilterBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
val filter = (requireActivity() as FilterOwner).filter
addSheetCallback(this) addSheetCallback(this)
val adapter = FilterAdapter(owner, this) val adapter = FilterAdapter(filter, this)
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
owner.filterItems.observe(viewLifecycleOwner, adapter::setItems) filter.filterItems.observe(viewLifecycleOwner, adapter::setItems)
if (dialog == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (dialog == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
binding.recyclerView.scrollIndicators = 0 binding.recyclerView.scrollIndicators = 0

View File

@@ -0,0 +1,15 @@
package org.koitharu.kotatsu.filter.ui
import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaTag
interface MangaFilter : OnFilterChangedListener {
val filterItems: StateFlow<List<ListModel>>
val header: StateFlow<FilterHeaderModel>
fun applyFilter(tags: Set<MangaTag>)
}

View File

@@ -45,6 +45,13 @@ class HistoryRepository @Inject constructor(
return entity.manga.toManga(entity.tags.toMangaTags()) return entity.manga.toManga(entity.tags.toMangaTags())
} }
fun observeLast(): Flow<Manga?> {
return db.historyDao.observeAll(1).map {
val first = it.firstOrNull()
first?.manga?.toManga(first.tags.toMangaTags())
}
}
fun observeAll(): Flow<List<Manga>> { fun observeAll(): Flow<List<Manga>> {
return db.historyDao.observeAll().mapItems { return db.historyDao.observeAll().mapItems {
it.manga.toManga(it.tags.toMangaTags()) it.manga.toManga(it.tags.toMangaTags())

View File

@@ -1,13 +1,15 @@
package org.koitharu.kotatsu.history.domain package org.koitharu.kotatsu.history.domain
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import javax.inject.Inject import javax.inject.Inject
class HistoryUpdateUseCase @Inject constructor( class HistoryUpdateUseCase @Inject constructor(
@@ -30,7 +32,9 @@ class HistoryUpdateUseCase @Inject constructor(
percent: Float percent: Float
) = processLifecycleScope.launch(Dispatchers.Default) { ) = processLifecycleScope.launch(Dispatchers.Default) {
runCatchingCancellable { runCatchingCancellable {
invoke(manga, readerState, percent) withContext(NonCancellable) {
invoke(manga, readerState, percent)
}
}.onFailure { }.onFailure {
it.printStackTraceDebug() it.printStackTraceDebug()
} }

View File

@@ -10,7 +10,6 @@ import androidx.core.net.toUri
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.ListSelectionController
import org.koitharu.kotatsu.core.util.ShareHelper import org.koitharu.kotatsu.core.util.ShareHelper
@@ -20,18 +19,18 @@ import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.filter.ui.FilterOwner import org.koitharu.kotatsu.filter.ui.FilterOwner
import org.koitharu.kotatsu.filter.ui.FilterSheetFragment import org.koitharu.kotatsu.filter.ui.FilterSheetFragment
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel import org.koitharu.kotatsu.filter.ui.MangaFilter
import org.koitharu.kotatsu.filter.ui.model.FilterItem
import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaSource 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.remotelist.ui.RemoteListFragment
class LocalListFragment : MangaListFragment(), FilterOwner { class LocalListFragment : MangaListFragment(), FilterOwner {
override val viewModel by viewModels<LocalListViewModel>() override val viewModel by viewModels<LocalListViewModel>()
override val filter: MangaFilter
get() = viewModel
override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
addMenuProvider(LocalListMenuProvider(this::onEmptyActionClick)) addMenuProvider(LocalListMenuProvider(this::onEmptyActionClick))
@@ -71,24 +70,6 @@ class LocalListFragment : MangaListFragment(), FilterOwner {
} }
} }
override val filterItems: StateFlow<List<ListModel>>
get() = viewModel.filterItems
override val header: StateFlow<FilterHeaderModel>
get() = viewModel.header
override fun applyFilter(tags: Set<MangaTag>) {
viewModel.applyFilter(tags)
}
override fun onSortItemClick(item: FilterItem.Sort) {
viewModel.onSortItemClick(item)
}
override fun onTagItemClick(item: FilterItem.Tag) {
viewModel.onTagItemClick(item)
}
private fun showDeletionConfirm(ids: Set<Long>, mode: ActionMode) { private fun showDeletionConfirm(ids: Set<Long>, mode: ActionMode) {
MaterialAlertDialogBuilder(context ?: return) MaterialAlertDialogBuilder(context ?: return)
.setTitle(R.string.delete_manga) .setTitle(R.string.delete_manga)

View File

@@ -0,0 +1,31 @@
package org.koitharu.kotatsu.main.domain
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsFlow
import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.parsers.model.MangaSource
import javax.inject.Inject
class ReadingResumeEnabledUseCase @Inject constructor(
private val networkState: NetworkState,
private val historyRepository: HistoryRepository,
private val settings: AppSettings,
) {
operator fun invoke(): Flow<Boolean> = settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) {
isIncognitoModeEnabled
}.flatMapLatest { incognito ->
if (incognito) {
flowOf(false)
} else {
combine(networkState, historyRepository.observeLast()) { isOnline, last ->
last != null && (isOnline || last.source == MangaSource.LOCAL)
}
}
}
}

View File

@@ -97,7 +97,7 @@ class MainNavigationDelegate(
} }
private fun onNavigationItemSelected(@IdRes itemId: Int): Boolean { private fun onNavigationItemSelected(@IdRes itemId: Int): Boolean {
setPrimaryFragment( return setPrimaryFragment(
when (itemId) { when (itemId) {
R.id.nav_shelf -> ShelfFragment.newInstance() R.id.nav_shelf -> ShelfFragment.newInstance()
R.id.nav_explore -> ExploreFragment.newInstance() R.id.nav_explore -> ExploreFragment.newInstance()
@@ -106,7 +106,6 @@ class MainNavigationDelegate(
else -> return false else -> return false
}, },
) )
return true
} }
private fun getItemId(fragment: Fragment) = when (fragment) { private fun getItemId(fragment: Fragment) = when (fragment) {
@@ -117,13 +116,17 @@ class MainNavigationDelegate(
else -> 0 else -> 0
} }
private fun setPrimaryFragment(fragment: Fragment) { private fun setPrimaryFragment(fragment: Fragment): Boolean {
if (fragmentManager.isStateSaved) {
return false
}
fragmentManager.beginTransaction() fragmentManager.beginTransaction()
.setReorderingAllowed(true) .setReorderingAllowed(true)
.replace(R.id.container, fragment, TAG_PRIMARY) .replace(R.id.container, fragment, TAG_PRIMARY)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit() .commit()
onFragmentChanged(fragment, fromUser = true) onFragmentChanged(fragment, fromUser = true)
return true
} }
private fun onFragmentChanged(fragment: Fragment, fromUser: Boolean) { private fun onFragmentChanged(fragment: Fragment, fromUser: Boolean) {

View File

@@ -13,12 +13,12 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.core.github.AppUpdateRepository import org.koitharu.kotatsu.core.github.AppUpdateRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsFlow
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.main.domain.ReadingResumeEnabledUseCase
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject import javax.inject.Inject
@@ -29,16 +29,12 @@ class MainViewModel @Inject constructor(
private val appUpdateRepository: AppUpdateRepository, private val appUpdateRepository: AppUpdateRepository,
trackingRepository: TrackingRepository, trackingRepository: TrackingRepository,
settings: AppSettings, settings: AppSettings,
readingResumeEnabledUseCase: ReadingResumeEnabledUseCase,
) : BaseViewModel() { ) : BaseViewModel() {
val onOpenReader = MutableEventFlow<Manga>() val onOpenReader = MutableEventFlow<Manga>()
val isResumeEnabled = combine( val isResumeEnabled = readingResumeEnabledUseCase().stateIn(
historyRepository.observeHasItems(),
settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
) { hasItems, incognito ->
hasItems && !incognito
}.stateIn(
scope = viewModelScope + Dispatchers.Default, scope = viewModelScope + Dispatchers.Default,
started = SharingStarted.WhileSubscribed(5000), started = SharingStarted.WhileSubscribed(5000),
initialValue = false, initialValue = false,

View File

@@ -34,6 +34,8 @@ import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope
import org.koitharu.kotatsu.core.util.ext.ensureSuccess import org.koitharu.kotatsu.core.util.ext.ensureSuccess
import org.koitharu.kotatsu.core.util.ext.isNotEmpty import org.koitharu.kotatsu.core.util.ext.isNotEmpty
import org.koitharu.kotatsu.core.util.ext.isPowerSaveMode
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.ramAvailable import org.koitharu.kotatsu.core.util.ext.ramAvailable
import org.koitharu.kotatsu.core.util.ext.withProgress import org.koitharu.kotatsu.core.util.ext.withProgress
import org.koitharu.kotatsu.core.util.progress.ProgressDeferred import org.koitharu.kotatsu.core.util.progress.ProgressDeferred
@@ -42,7 +44,6 @@ import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import java.io.File import java.io.File
import java.util.LinkedList import java.util.LinkedList
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
@@ -83,7 +84,10 @@ class PageLoader @Inject constructor(
} }
fun isPrefetchApplicable(): Boolean { fun isPrefetchApplicable(): Boolean {
return repository is RemoteMangaRepository && settings.isPagesPreloadEnabled && !isLowRam() return repository is RemoteMangaRepository
&& settings.isPagesPreloadEnabled
&& !context.isPowerSaveMode()
&& !isLowRam()
} }
@AnyThread @AnyThread

View File

@@ -10,7 +10,6 @@ import androidx.appcompat.widget.SearchView
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.ListSelectionController
import org.koitharu.kotatsu.core.util.ext.addMenuProvider import org.koitharu.kotatsu.core.util.ext.addMenuProvider
@@ -18,13 +17,10 @@ import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.filter.ui.FilterOwner import org.koitharu.kotatsu.filter.ui.FilterOwner
import org.koitharu.kotatsu.filter.ui.FilterSheetFragment import org.koitharu.kotatsu.filter.ui.FilterSheetFragment
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel import org.koitharu.kotatsu.filter.ui.MangaFilter
import org.koitharu.kotatsu.filter.ui.model.FilterItem
import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.search.ui.SearchActivity import org.koitharu.kotatsu.search.ui.SearchActivity
import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.SettingsActivity
@@ -33,6 +29,9 @@ class RemoteListFragment : MangaListFragment(), FilterOwner {
override val viewModel by viewModels<RemoteListViewModel>() override val viewModel by viewModels<RemoteListViewModel>()
override val filter: MangaFilter
get() = viewModel
override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
addMenuProvider(RemoteListMenuProvider()) addMenuProvider(RemoteListMenuProvider())
@@ -55,24 +54,6 @@ class RemoteListFragment : MangaListFragment(), FilterOwner {
viewModel.resetFilter() viewModel.resetFilter()
} }
override val filterItems: StateFlow<List<ListModel>>
get() = viewModel.filterItems
override val header: StateFlow<FilterHeaderModel>
get() = viewModel.header
override fun applyFilter(tags: Set<MangaTag>) {
viewModel.applyFilter(tags)
}
override fun onSortItemClick(item: FilterItem.Sort) {
viewModel.onSortItemClick(item)
}
override fun onTagItemClick(item: FilterItem.Tag) {
viewModel.onTagItemClick(item)
}
private inner class RemoteListMenuProvider : private inner class RemoteListMenuProvider :
MenuProvider, MenuProvider,
SearchView.OnQueryTextListener, SearchView.OnQueryTextListener,

View File

@@ -20,10 +20,11 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.filter.ui.FilterCoordinator import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.filter.ui.FilterOwner import org.koitharu.kotatsu.filter.ui.MangaFilter
import org.koitharu.kotatsu.filter.ui.model.FilterState import org.koitharu.kotatsu.filter.ui.model.FilterState
import org.koitharu.kotatsu.list.domain.ListExtraProvider import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.MangaListViewModel
@@ -36,7 +37,6 @@ import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import javax.inject.Inject import javax.inject.Inject
private const val FILTER_MIN_INTERVAL = 250L private const val FILTER_MIN_INTERVAL = 250L
@@ -49,7 +49,7 @@ open class RemoteListViewModel @Inject constructor(
settings: AppSettings, settings: AppSettings,
listExtraProvider: ListExtraProvider, listExtraProvider: ListExtraProvider,
downloadScheduler: DownloadWorker.Scheduler, downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler), FilterOwner by filter { ) : MangaListViewModel(settings, downloadScheduler), MangaFilter by filter {
val source = savedStateHandle.require<MangaSource>(RemoteListFragment.ARG_SOURCE) val source = savedStateHandle.require<MangaSource>(RemoteListFragment.ARG_SOURCE)
private val repository = mangaRepositoryFactory.create(source) private val repository = mangaRepositoryFactory.create(source)

View File

@@ -26,6 +26,7 @@ import org.koitharu.kotatsu.databinding.ActivityMangaListBinding
import org.koitharu.kotatsu.filter.ui.FilterHeaderFragment import org.koitharu.kotatsu.filter.ui.FilterHeaderFragment
import org.koitharu.kotatsu.filter.ui.FilterOwner import org.koitharu.kotatsu.filter.ui.FilterOwner
import org.koitharu.kotatsu.filter.ui.FilterSheetFragment import org.koitharu.kotatsu.filter.ui.FilterSheetFragment
import org.koitharu.kotatsu.filter.ui.MangaFilter
import org.koitharu.kotatsu.local.ui.LocalListFragment import org.koitharu.kotatsu.local.ui.LocalListFragment
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -35,11 +36,16 @@ import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
@AndroidEntryPoint @AndroidEntryPoint
class MangaListActivity : class MangaListActivity :
BaseActivity<ActivityMangaListBinding>(), BaseActivity<ActivityMangaListBinding>(),
AppBarOwner, View.OnClickListener { AppBarOwner, View.OnClickListener, FilterOwner {
override val appBar: AppBarLayout override val appBar: AppBarLayout
get() = viewBinding.appbar get() = viewBinding.appbar
override val filter: MangaFilter
get() = checkNotNull(findFilterOwner()) {
"Cannot find FilterOwner fragment in ${supportFragmentManager.fragments}"
}.filter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(ActivityMangaListBinding.inflate(layoutInflater)) setContentView(ActivityMangaListBinding.inflate(layoutInflater))
@@ -109,13 +115,14 @@ class MangaListActivity :
} }
} }
} }
val filter = filterOwner.filter
val chipSort = viewBinding.chipSort val chipSort = viewBinding.chipSort
if (chipSort != null) { if (chipSort != null) {
filterOwner.header.observe(this) { filter.header.observe(this) {
chipSort.setTextAndVisible(it.sortOrder?.titleRes ?: 0) chipSort.setTextAndVisible(it.sortOrder?.titleRes ?: 0)
} }
} else { } else {
filterOwner.header.map { filter.header.map {
it.textSummary it.textSummary
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.observe(this) { .observe(this) {
@@ -124,13 +131,17 @@ class MangaListActivity :
} }
} }
private fun findFilterOwner(): FilterOwner? {
return supportFragmentManager.findFragmentById(R.id.container) as? FilterOwner
}
private class ApplyFilterRunnable( private class ApplyFilterRunnable(
private val filterOwner: FilterOwner, private val filterOwner: FilterOwner,
private val tags: Set<MangaTag>, private val tags: Set<MangaTag>,
) : Runnable { ) : Runnable {
override fun run() { override fun run() {
filterOwner.applyFilter(tags) filterOwner.filter.applyFilter(tags)
} }
} }

View File

@@ -24,6 +24,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.powerManager
import org.koitharu.kotatsu.settings.tracker.categories.TrackerCategoriesConfigSheet import org.koitharu.kotatsu.settings.tracker.categories.TrackerCategoriesConfigSheet
import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
@@ -155,7 +156,7 @@ class TrackerSettingsFragment :
return return
} }
val packageName = context.packageName val packageName = context.packageName
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager val powerManager = context.powerManager ?: return
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) { if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
try { try {
val intent = Intent( val intent = Intent(

View File

@@ -43,10 +43,12 @@ class SyncController @Inject constructor(
private val defaultGcPeriod = TimeUnit.DAYS.toMillis(2) // gc period if sync disabled private val defaultGcPeriod = TimeUnit.DAYS.toMillis(2) // gc period if sync disabled
override fun onInvalidated(tables: Set<String>) { override fun onInvalidated(tables: Set<String>) {
requestSync( val favourites = (TABLE_FAVOURITES in tables || TABLE_FAVOURITE_CATEGORIES in tables)
favourites = TABLE_FAVOURITES in tables || TABLE_FAVOURITE_CATEGORIES in tables, && !isSyncActiveOrPending(authorityFavourites)
history = TABLE_HISTORY in tables, val history = TABLE_HISTORY in tables && !isSyncActiveOrPending(authorityHistory)
) if (favourites || history) {
requestSync(favourites, history)
}
} }
fun isEnabled(account: Account): Boolean { fun isEnabled(account: Account): Boolean {
@@ -126,6 +128,11 @@ class SyncController @Inject constructor(
} }
} }
private fun isSyncActiveOrPending(authority: String): Boolean {
val account = peekAccount() ?: return false
return ContentResolver.isSyncActive(account, authority) || ContentResolver.isSyncPending(account, authority)
}
companion object { companion object {
@JvmStatic @JvmStatic

View File

@@ -11,6 +11,10 @@ import android.database.Cursor
import android.net.Uri import android.net.Uri
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.core.content.contentValuesOf import androidx.core.content.contentValuesOf
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.qualifiers.ApplicationContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
@@ -23,9 +27,9 @@ import org.koitharu.kotatsu.core.db.TABLE_HISTORY
import org.koitharu.kotatsu.core.db.TABLE_MANGA import org.koitharu.kotatsu.core.db.TABLE_MANGA
import org.koitharu.kotatsu.core.db.TABLE_MANGA_TAGS import org.koitharu.kotatsu.core.db.TABLE_MANGA_TAGS
import org.koitharu.kotatsu.core.db.TABLE_TAGS import org.koitharu.kotatsu.core.db.TABLE_TAGS
import org.koitharu.kotatsu.core.logs.LoggersModule import org.koitharu.kotatsu.core.logs.FileLogger
import org.koitharu.kotatsu.core.network.GZipInterceptor import org.koitharu.kotatsu.core.logs.SyncLogger
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.network.BaseHttpClient
import org.koitharu.kotatsu.core.util.ext.parseJsonOrNull import org.koitharu.kotatsu.core.util.ext.parseJsonOrNull
import org.koitharu.kotatsu.core.util.ext.toContentValues import org.koitharu.kotatsu.core.util.ext.toContentValues
import org.koitharu.kotatsu.core.util.ext.toJson import org.koitharu.kotatsu.core.util.ext.toJson
@@ -39,23 +43,20 @@ import java.util.concurrent.TimeUnit
private const val FIELD_TIMESTAMP = "timestamp" private const val FIELD_TIMESTAMP = "timestamp"
/** class SyncHelper @AssistedInject constructor(
* Warning! This class may be used in another process @ApplicationContext context: Context,
*/ @BaseHttpClient baseHttpClient: OkHttpClient,
@WorkerThread @Assisted private val account: Account,
class SyncHelper( @Assisted private val provider: ContentProviderClient,
context: Context, private val settings: SyncSettings,
private val account: Account, @SyncLogger private val logger: FileLogger,
private val provider: ContentProviderClient,
) { ) {
private val authorityHistory = context.getString(R.string.sync_authority_history) private val authorityHistory = context.getString(R.string.sync_authority_history)
private val authorityFavourites = context.getString(R.string.sync_authority_favourites) private val authorityFavourites = context.getString(R.string.sync_authority_favourites)
private val settings = SyncSettings(context, account) private val httpClient = baseHttpClient.newBuilder()
private val httpClient = OkHttpClient.Builder()
.authenticator(SyncAuthenticator(context, account, settings, SyncAuthApi(OkHttpClient()))) .authenticator(SyncAuthenticator(context, account, settings, SyncAuthApi(OkHttpClient())))
.addInterceptor(SyncInterceptor(context, account)) .addInterceptor(SyncInterceptor(context, account))
.addInterceptor(GZipInterceptor())
.build() .build()
private val baseUrl: String by lazy { private val baseUrl: String by lazy {
val host = settings.host val host = settings.host
@@ -64,8 +65,8 @@ class SyncHelper(
} }
private val defaultGcPeriod: Long // gc period if sync enabled private val defaultGcPeriod: Long // gc period if sync enabled
get() = TimeUnit.DAYS.toMillis(4) get() = TimeUnit.DAYS.toMillis(4)
private val logger = LoggersModule.provideSyncLogger(context, AppSettings(context))
@WorkerThread
fun syncFavourites(syncResult: SyncResult) { fun syncFavourites(syncResult: SyncResult) {
val data = JSONObject() val data = JSONObject()
data.put(TABLE_FAVOURITE_CATEGORIES, getFavouriteCategories()) data.put(TABLE_FAVOURITE_CATEGORIES, getFavouriteCategories())
@@ -89,6 +90,7 @@ class SyncHelper(
gcFavourites() gcFavourites()
} }
@WorkerThread
fun syncHistory(syncResult: SyncResult) { fun syncHistory(syncResult: SyncResult) {
val data = JSONObject() val data = JSONObject()
data.put(TABLE_HISTORY, getHistory()) data.put(TABLE_HISTORY, getHistory())
@@ -321,4 +323,13 @@ class SyncHelper(
logger.log("$code ${request.url}") logger.log("$code ${request.url}")
} }
} }
@AssistedFactory
interface Factory {
fun create(
account: Account,
contentProviderClient: ContentProviderClient,
): SyncHelper
}
} }

View File

@@ -0,0 +1,12 @@
package org.koitharu.kotatsu.sync.ui
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.koitharu.kotatsu.sync.domain.SyncHelper
@EntryPoint
@InstallIn(SingletonComponent::class)
interface SyncAdapterEntryPoint {
val syncHelperFactory: SyncHelper.Factory
}

View File

@@ -6,11 +6,12 @@ import android.content.ContentProviderClient
import android.content.Context import android.content.Context
import android.content.SyncResult import android.content.SyncResult
import android.os.Bundle import android.os.Bundle
import dagger.hilt.android.EntryPointAccessors
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.onError import org.koitharu.kotatsu.core.util.ext.onError
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.sync.domain.SyncController import org.koitharu.kotatsu.sync.domain.SyncController
import org.koitharu.kotatsu.sync.domain.SyncHelper import org.koitharu.kotatsu.sync.ui.SyncAdapterEntryPoint
class FavouritesSyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context, true) { class FavouritesSyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context, true) {
@@ -24,7 +25,8 @@ class FavouritesSyncAdapter(context: Context) : AbstractThreadedSyncAdapter(cont
if (!context.resources.getBoolean(R.bool.is_sync_enabled)) { if (!context.resources.getBoolean(R.bool.is_sync_enabled)) {
return return
} }
val syncHelper = SyncHelper(context, account, provider) val entryPoint = EntryPointAccessors.fromApplication(context, SyncAdapterEntryPoint::class.java)
val syncHelper = entryPoint.syncHelperFactory.create(account, provider)
runCatchingCancellable { runCatchingCancellable {
syncHelper.syncFavourites(syncResult) syncHelper.syncFavourites(syncResult)
SyncController.setLastSync(context, account, authority, System.currentTimeMillis()) SyncController.setLastSync(context, account, authority, System.currentTimeMillis())

View File

@@ -6,11 +6,12 @@ import android.content.ContentProviderClient
import android.content.Context import android.content.Context
import android.content.SyncResult import android.content.SyncResult
import android.os.Bundle import android.os.Bundle
import dagger.hilt.android.EntryPointAccessors
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.onError import org.koitharu.kotatsu.core.util.ext.onError
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.sync.domain.SyncController import org.koitharu.kotatsu.sync.domain.SyncController
import org.koitharu.kotatsu.sync.domain.SyncHelper import org.koitharu.kotatsu.sync.ui.SyncAdapterEntryPoint
class HistorySyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context, true) { class HistorySyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context, true) {
@@ -24,7 +25,8 @@ class HistorySyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context
if (!context.resources.getBoolean(R.bool.is_sync_enabled)) { if (!context.resources.getBoolean(R.bool.is_sync_enabled)) {
return return
} }
val syncHelper = SyncHelper(context, account, provider) val entryPoint = EntryPointAccessors.fromApplication(context, SyncAdapterEntryPoint::class.java)
val syncHelper = entryPoint.syncHelperFactory.create(account, provider)
runCatchingCancellable { runCatchingCancellable {
syncHelper.syncHistory(syncResult) syncHelper.syncHistory(syncResult)
SyncController.setLastSync(context, account, authority, System.currentTimeMillis()) SyncController.setLastSync(context, account, authority, System.currentTimeMillis())