diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt index c484af1f3..9bec3962d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt @@ -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, -) : BaseListAdapter( - bookmarkListAD(coil, lifecycleOwner, clickListener), -) +) : BaseListAdapter() { + + init { + addDelegate(ListItemType.PAGE_THUMB, bookmarkListAD(coil, lifecycleOwner, clickListener)) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/NetworkManageIntent.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/NetworkManageIntent.kt new file mode 100644 index 000000000..50f0e2fdf --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/NetworkManageIntent.kt @@ -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) +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseListAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseListAdapter.kt index 378e638b1..8b8cec8a4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseListAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BaseListAdapter.kt @@ -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( - vararg delegates: AdapterDelegate>, -) : AsyncListDifferDelegationAdapter( +open class BaseListAdapter : AsyncListDifferDelegationAdapter( AsyncDifferConfig.Builder(ListModelDiffCallback()) .setBackgroundThreadExecutor(Dispatchers.Default.limitedParallelism(2).asExecutor()) .build(), - *delegates, ), FlowCollector?> { override suspend fun emit(value: List?) = suspendCoroutine { cont -> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 056675880..32ffa04ef 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -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) ?: 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) ?: BaseListAdapter() + .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 } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt index 825115da3..14edb0b09 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt @@ -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) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index a7c68d9d5..779d84c4a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -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, grouped: Boolean, mode: ListMode, + isOnline: Boolean, ): List { val result = ArrayList(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) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index cec2d1b95..cb2d98121 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -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), ) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt index c53e556d7..91edfdaf2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt @@ -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)) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt index a540d43d8..b1005b123 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/TypedListSpacingDecoration.kt @@ -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) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt index a5eb18cb8..620901296 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt @@ -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) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/SourcesSelectAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/SourcesSelectAdapter.kt index 6f9039bac..30fc3e448 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/SourcesSelectAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/SourcesSelectAdapter.kt @@ -11,6 +11,9 @@ class SourcesSelectAdapter( listener: SourceConfigListener, coil: ImageLoader, lifecycleOwner: LifecycleOwner, -) : BaseListAdapter( - sourceConfigItemCheckableDelegate(listener, coil, lifecycleOwner), -) +) : BaseListAdapter() { + + init { + delegatesManager.addDelegate(sourceConfigItemCheckableDelegate(listener, coil, lifecycleOwner)) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt index ef10593e4..da45d8fe3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt @@ -9,10 +9,15 @@ class SourceConfigAdapter( listener: SourceConfigListener, coil: ImageLoader, lifecycleOwner: LifecycleOwner, -) : BaseListAdapter( - sourceConfigHeaderDelegate(), - sourceConfigGroupDelegate(listener), - sourceConfigItemDelegate2(listener, coil, lifecycleOwner), - sourceConfigEmptySearchDelegate(), - sourceConfigTipDelegate(listener), -) +) : BaseListAdapter() { + + init { + with(delegatesManager) { + addDelegate(sourceConfigHeaderDelegate()) + addDelegate(sourceConfigGroupDelegate(listener)) + addDelegate(sourceConfigItemDelegate2(listener, coil, lifecycleOwner)) + addDelegate(sourceConfigEmptySearchDelegate()) + addDelegate(sourceConfigTipDelegate(listener)) + } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigAdapter.kt index 8b811c710..6d7d43cd0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/tracker/categories/TrackerCategoriesConfigAdapter.kt @@ -6,6 +6,9 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener class TrackerCategoriesConfigAdapter( listener: OnListItemClickListener, -) : BaseListAdapter( - trackerCategoryAD(listener), -) +) : BaseListAdapter() { + + init { + delegatesManager.addDelegate(trackerCategoryAD(listener)) + } +} diff --git a/app/src/main/res/layout/item_empty_card.xml b/app/src/main/res/layout/item_empty_card.xml index 51255ae82..872dcc7d2 100644 --- a/app/src/main/res/layout/item_empty_card.xml +++ b/app/src/main/res/layout/item_empty_card.xml @@ -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">