Handle offline mode in history list
This commit is contained in:
@@ -5,11 +5,15 @@ import coil.ImageLoader
|
||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
|
||||
class BookmarksAdapter(
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
clickListener: OnListItemClickListener<Bookmark>,
|
||||
) : BaseListAdapter<Bookmark>(
|
||||
bookmarkListAD(coil, lifecycleOwner, clickListener),
|
||||
)
|
||||
) : BaseListAdapter<Bookmark>() {
|
||||
|
||||
init {
|
||||
addDelegate(ListItemType.PAGE_THUMB, bookmarkListAD(coil, lifecycleOwner, clickListener))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.koitharu.kotatsu.core.os
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun NetworkManageIntent(): Intent {
|
||||
val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
Settings.Panel.ACTION_INTERNET_CONNECTIVITY
|
||||
} else {
|
||||
Settings.ACTION_WIRELESS_SETTINGS
|
||||
}
|
||||
return Intent(action)
|
||||
}
|
||||
@@ -13,13 +13,10 @@ import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
open class BaseListAdapter<T : ListModel>(
|
||||
vararg delegates: AdapterDelegate<List<T>>,
|
||||
) : AsyncListDifferDelegationAdapter<T>(
|
||||
open class BaseListAdapter<T : ListModel> : AsyncListDifferDelegationAdapter<T>(
|
||||
AsyncDifferConfig.Builder(ListModelDiffCallback<T>())
|
||||
.setBackgroundThreadExecutor(Dispatchers.Default.limitedParallelism(2).asExecutor())
|
||||
.build(),
|
||||
*delegates,
|
||||
), FlowCollector<List<T>?> {
|
||||
|
||||
override suspend fun emit(value: List<T>?) = suspendCoroutine { cont ->
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
|
||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.image.ui.ImageActivity
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
|
||||
@@ -227,14 +228,16 @@ class DetailsFragment :
|
||||
val rv = viewBinding?.recyclerViewRelated ?: return
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val adapter = (rv.adapter as? BaseListAdapter<ListModel>) ?: BaseListAdapter(
|
||||
mangaGridItemAD(
|
||||
coil, viewLifecycleOwner,
|
||||
StaticItemSizeResolver(resources.getDimensionPixelSize(R.dimen.smaller_grid_width)),
|
||||
) { item, view ->
|
||||
startActivity(DetailsActivity.newIntent(view.context, item))
|
||||
},
|
||||
).also { rv.adapter = it }
|
||||
val adapter = (rv.adapter as? BaseListAdapter<ListModel>) ?: BaseListAdapter<ListModel>()
|
||||
.addDelegate(
|
||||
ListItemType.MANGA_GRID,
|
||||
mangaGridItemAD(
|
||||
coil, viewLifecycleOwner,
|
||||
StaticItemSizeResolver(resources.getDimensionPixelSize(R.dimen.smaller_grid_width)),
|
||||
) { item, view ->
|
||||
startActivity(DetailsActivity.newIntent(view.context, item))
|
||||
},
|
||||
).also { rv.adapter = it }
|
||||
adapter.items = related
|
||||
requireViewBinding().groupRelated.isVisible = true
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.appcompat.view.ActionMode
|
||||
import androidx.fragment.app.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.os.NetworkManageIntent
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
@@ -32,6 +33,10 @@ class HistoryListFragment : MangaListFragment() {
|
||||
|
||||
override fun onScrolledToEnd() = Unit
|
||||
|
||||
override fun onEmptyActionClick() {
|
||||
startActivity(NetworkManageIntent())
|
||||
}
|
||||
|
||||
override fun onCreateActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean {
|
||||
mode.menuInflater.inflate(R.menu.mode_history, menu)
|
||||
return super.onCreateActionMode(controller, mode, menu)
|
||||
|
||||
@@ -13,6 +13,8 @@ import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.isLocal
|
||||
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
|
||||
@@ -28,6 +30,7 @@ import org.koitharu.kotatsu.history.domain.model.HistoryOrder
|
||||
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyHint
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
@@ -36,6 +39,7 @@ import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toGridModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toListDetailedModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toListModel
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
@@ -45,6 +49,8 @@ class HistoryListViewModel @Inject constructor(
|
||||
private val repository: HistoryRepository,
|
||||
private val settings: AppSettings,
|
||||
private val extraProvider: ListExtraProvider,
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
networkState: NetworkState,
|
||||
downloadScheduler: DownloadWorker.Scheduler,
|
||||
) : MangaListViewModel(settings, downloadScheduler) {
|
||||
|
||||
@@ -69,7 +75,8 @@ class HistoryListViewModel @Inject constructor(
|
||||
sortOrder.flatMapLatest { repository.observeAllWithHistory(it) },
|
||||
isGroupingEnabled,
|
||||
listMode,
|
||||
) { list, grouped, mode ->
|
||||
networkState,
|
||||
) { list, grouped, mode, online ->
|
||||
when {
|
||||
list.isEmpty() -> listOf(
|
||||
EmptyState(
|
||||
@@ -80,7 +87,7 @@ class HistoryListViewModel @Inject constructor(
|
||||
),
|
||||
)
|
||||
|
||||
else -> mapList(list, grouped, mode)
|
||||
else -> mapList(list, grouped, mode, online)
|
||||
}
|
||||
}.onStart {
|
||||
loadingCounter.increment()
|
||||
@@ -129,11 +136,25 @@ class HistoryListViewModel @Inject constructor(
|
||||
list: List<MangaWithHistory>,
|
||||
grouped: Boolean,
|
||||
mode: ListMode,
|
||||
isOnline: Boolean,
|
||||
): List<ListModel> {
|
||||
val result = ArrayList<ListModel>(if (grouped) (list.size * 1.4).toInt() else list.size + 1)
|
||||
val order = sortOrder.value
|
||||
var prevHeader: ListHeader? = null
|
||||
for ((manga, history) in list) {
|
||||
if (!isOnline) {
|
||||
result += EmptyHint(
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.network_unavailable,
|
||||
textSecondary = R.string.network_unavailable_hint,
|
||||
actionStringRes = R.string.manage,
|
||||
)
|
||||
}
|
||||
for ((m, history) in list) {
|
||||
val manga = if (!isOnline && !m.isLocal) {
|
||||
localMangaRepository.findSavedManga(m)?.manga ?: continue
|
||||
} else {
|
||||
m
|
||||
}
|
||||
if (grouped) {
|
||||
val header = history.header(order)
|
||||
if (header != prevHeader) {
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.koitharu.kotatsu.core.util.ext.measureHeight
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||
import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf
|
||||
import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
|
||||
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
@@ -200,7 +199,7 @@ abstract class MangaListFragment :
|
||||
coil = coil,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
listener = this,
|
||||
sizeResolver = DynamicItemSizeResolver(resources, settings, adjustWidth = false)
|
||||
sizeResolver = DynamicItemSizeResolver(resources, settings, adjustWidth = false),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ open class MangaListAdapter(
|
||||
addDelegate(ListItemType.STATE_ERROR, errorStateListAD(listener))
|
||||
addDelegate(ListItemType.FOOTER_ERROR, errorFooterAD(listener))
|
||||
addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, lifecycleOwner, listener))
|
||||
addDelegate(ListItemType.HINT_EMPTY, emptyHintAD(coil, lifecycleOwner, listener))
|
||||
addDelegate(ListItemType.HEADER, listHeaderAD(listener))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class TypedListSpacingDecoration(
|
||||
null -> outRect.set(0)
|
||||
|
||||
ListItemType.TIP -> outRect.set(0) // TODO
|
||||
ListItemType.HINT_EMPTY -> outRect.set(0) // TODO
|
||||
ListItemType.HINT_EMPTY -> outRect.set(spacingList)
|
||||
ListItemType.FEED -> outRect.set(spacingList, 0, spacingList, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ class LocalMangaRepository @Inject constructor(
|
||||
}
|
||||
|
||||
suspend fun findSavedManga(remoteManga: Manga): LocalManga? {
|
||||
// TODO fast path by name
|
||||
val files = getAllFiles()
|
||||
return channelFlow {
|
||||
for (file in files) {
|
||||
|
||||
@@ -11,6 +11,9 @@ class SourcesSelectAdapter(
|
||||
listener: SourceConfigListener,
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) : BaseListAdapter<SourceConfigItem>(
|
||||
sourceConfigItemCheckableDelegate(listener, coil, lifecycleOwner),
|
||||
)
|
||||
) : BaseListAdapter<SourceConfigItem>() {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(sourceConfigItemCheckableDelegate(listener, coil, lifecycleOwner))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,15 @@ class SourceConfigAdapter(
|
||||
listener: SourceConfigListener,
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) : BaseListAdapter<SourceConfigItem>(
|
||||
sourceConfigHeaderDelegate(),
|
||||
sourceConfigGroupDelegate(listener),
|
||||
sourceConfigItemDelegate2(listener, coil, lifecycleOwner),
|
||||
sourceConfigEmptySearchDelegate(),
|
||||
sourceConfigTipDelegate(listener),
|
||||
)
|
||||
) : BaseListAdapter<SourceConfigItem>() {
|
||||
|
||||
init {
|
||||
with(delegatesManager) {
|
||||
addDelegate(sourceConfigHeaderDelegate())
|
||||
addDelegate(sourceConfigGroupDelegate(listener))
|
||||
addDelegate(sourceConfigItemDelegate2(listener, coil, lifecycleOwner))
|
||||
addDelegate(sourceConfigEmptySearchDelegate())
|
||||
addDelegate(sourceConfigTipDelegate(listener))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
|
||||
class TrackerCategoriesConfigAdapter(
|
||||
listener: OnListItemClickListener<FavouriteCategory>,
|
||||
) : BaseListAdapter<FavouriteCategory>(
|
||||
trackerCategoryAD(listener),
|
||||
)
|
||||
) : BaseListAdapter<FavouriteCategory>() {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(trackerCategoryAD(listener))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
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"
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
style="@style/Widget.Kotatsu.CardView.Light"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:contentPadding="@dimen/margin_normal">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
Reference in New Issue
Block a user