Show inconito mode indicator on Read button

pull/287/head
Koitharu 3 years ago
parent 6273a9decb
commit 689670b3ff
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -228,18 +228,18 @@ class DetailsActivity :
} }
} }
private fun onHistoryChanged(info: HistoryInfo?) { private fun onHistoryChanged(info: HistoryInfo) {
with(binding.buttonRead) { with(binding.buttonRead) {
if (info?.history != null) { if (info.history != null) {
setText(R.string._continue) setText(R.string._continue)
setIconResource(R.drawable.ic_play) setIconResource(if (info.isIncognitoMode) R.drawable.ic_incognito else R.drawable.ic_play)
} else { } else {
setText(R.string.read) setText(R.string.read)
setIconResource(R.drawable.ic_read) setIconResource(if (info.isIncognitoMode) R.drawable.ic_incognito else R.drawable.ic_play)
} }
} }
val text = when { val text = when {
info == null -> getString(R.string.loading_) !info.isValid -> getString(R.string.loading_)
info.currentChapter >= 0 -> getString(R.string.chapter_d_of_d, info.currentChapter + 1, info.totalChapters) info.currentChapter >= 0 -> getString(R.string.chapter_d_of_d, info.currentChapter + 1, info.totalChapters)
info.totalChapters == 0 -> getString(R.string.no_chapters) info.totalChapters == 0 -> getString(R.string.no_chapters)
else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters) else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters)

@ -18,7 +18,6 @@ import coil.request.ImageRequest
import coil.util.CoilUtils import coil.util.CoilUtils
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.BaseFragment
@ -27,9 +26,9 @@ import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.base.ui.widgets.ChipsView import org.koitharu.kotatsu.base.ui.widgets.ChipsView
import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.HistoryInfo
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration
import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
@ -45,8 +44,20 @@ import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingInfo
import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.search.ui.MangaListActivity
import org.koitharu.kotatsu.search.ui.SearchActivity import org.koitharu.kotatsu.search.ui.SearchActivity
import org.koitharu.kotatsu.utils.FileSize import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ext.* import org.koitharu.kotatsu.utils.ext.computeSize
import org.koitharu.kotatsu.utils.ext.crossfade
import org.koitharu.kotatsu.utils.ext.drawableTop
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.ifNullOrEmpty
import org.koitharu.kotatsu.utils.ext.measureHeight
import org.koitharu.kotatsu.utils.ext.referer
import org.koitharu.kotatsu.utils.ext.resolveDp
import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf
import org.koitharu.kotatsu.utils.ext.textAndVisible
import org.koitharu.kotatsu.utils.ext.toFileOrNull
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
import org.koitharu.kotatsu.utils.image.CoverSizeResolver import org.koitharu.kotatsu.utils.image.CoverSizeResolver
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class DetailsFragment : class DetailsFragment :
@ -75,7 +86,7 @@ class DetailsFragment :
binding.chipsTags.onChipClickListener = this binding.chipsTags.onChipClickListener = this
viewModel.manga.observe(viewLifecycleOwner, ::onMangaUpdated) viewModel.manga.observe(viewLifecycleOwner, ::onMangaUpdated)
viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged) viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged)
viewModel.readingHistory.observe(viewLifecycleOwner, ::onHistoryChanged) viewModel.historyInfo.observe(viewLifecycleOwner, ::onHistoryChanged)
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged) viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged) viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged) viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
@ -123,12 +134,14 @@ class DetailsFragment :
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_finished) drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_finished)
} }
} }
MangaState.ONGOING -> { MangaState.ONGOING -> {
infoLayout.textViewState.apply { infoLayout.textViewState.apply {
textAndVisible = resources.getString(R.string.state_ongoing) textAndVisible = resources.getString(R.string.state_ongoing)
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_ongoing) drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_ongoing)
} }
} }
else -> infoLayout.textViewState.isVisible = false else -> infoLayout.textViewState.isVisible = false
} }
if (manga.source == MangaSource.LOCAL) { if (manga.source == MangaSource.LOCAL) {
@ -178,8 +191,8 @@ class DetailsFragment :
} }
} }
private fun onHistoryChanged(history: MangaHistory?) { private fun onHistoryChanged(history: HistoryInfo) {
binding.progressView.setPercent(history?.percent ?: PROGRESS_NONE, animate = true) binding.progressView.setPercent(history.history?.percent ?: PROGRESS_NONE, animate = true)
} }
private fun onLoadingStateChanged(isLoading: Boolean) { private fun onLoadingStateChanged(isLoading: Boolean) {
@ -229,6 +242,7 @@ class DetailsFragment :
), ),
) )
} }
R.id.textView_source -> { R.id.textView_source -> {
startActivity( startActivity(
MangaListActivity.newIntent( MangaListActivity.newIntent(
@ -237,6 +251,7 @@ class DetailsFragment :
), ),
) )
} }
R.id.imageView_cover -> { R.id.imageView_cover -> {
startActivity( startActivity(
ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }), ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }),
@ -249,7 +264,7 @@ class DetailsFragment :
override fun onLongClick(v: View): Boolean { override fun onLongClick(v: View): Boolean {
when (v.id) { when (v.id) {
R.id.button_read -> { R.id.button_read -> {
if (viewModel.readingHistory.value == null) { if (viewModel.historyInfo.value?.history == null) {
return false return false
} }
val menu = PopupMenu(v.context, v) val menu = PopupMenu(v.context, v)
@ -271,12 +286,14 @@ class DetailsFragment :
) )
true true
} }
else -> false else -> false
} }
} }
menu.show() menu.show()
return true return true
} }
else -> return false else -> return false
} }
} }

@ -13,10 +13,18 @@ import androidx.lifecycle.viewModelScope
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import java.io.IOException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
@ -46,6 +54,7 @@ import org.koitharu.kotatsu.utils.asFlowLiveData
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
import java.io.IOException
class DetailsViewModel @AssistedInject constructor( class DetailsViewModel @AssistedInject constructor(
@Assisted intent: MangaIntent, @Assisted intent: MangaIntent,
@ -91,17 +100,18 @@ class DetailsViewModel @AssistedInject constructor(
val manga = delegate.manga.filterNotNull().asLiveData(viewModelScope.coroutineContext) val manga = delegate.manga.filterNotNull().asLiveData(viewModelScope.coroutineContext)
val favouriteCategories = favourite.asLiveData(viewModelScope.coroutineContext) val favouriteCategories = favourite.asLiveData(viewModelScope.coroutineContext)
val newChaptersCount = newChapters.asLiveData(viewModelScope.coroutineContext) val newChaptersCount = newChapters.asLiveData(viewModelScope.coroutineContext)
@Deprecated("")
val readingHistory = history.asLiveData(viewModelScope.coroutineContext)
val isChaptersReversed = chaptersReversed.asLiveData(viewModelScope.coroutineContext) val isChaptersReversed = chaptersReversed.asLiveData(viewModelScope.coroutineContext)
val historyInfo = combine( val historyInfo: LiveData<HistoryInfo> = combine(
delegate.manga, delegate.manga,
history, history,
) { m, h -> settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
HistoryInfo(m, h) ) { m, h, im ->
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, null) HistoryInfo(m, h, im)
}.asFlowLiveData(
context = viewModelScope.coroutineContext + Dispatchers.Default,
defaultValue = HistoryInfo(null, null, false),
)
val bookmarks = delegate.manga.flatMapLatest { val bookmarks = delegate.manga.flatMapLatest {
if (it != null) bookmarksRepository.observeBookmarks(it) else flowOf(emptyList()) if (it != null) bookmarksRepository.observeBookmarks(it) else flowOf(emptyList())

@ -7,8 +7,12 @@ class HistoryInfo(
val totalChapters: Int, val totalChapters: Int,
val currentChapter: Int, val currentChapter: Int,
val history: MangaHistory?, val history: MangaHistory?,
val isIncognitoMode: Boolean,
) { ) {
val isValid: Boolean
get() = totalChapters >= 0
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false
@ -18,6 +22,7 @@ class HistoryInfo(
if (totalChapters != other.totalChapters) return false if (totalChapters != other.totalChapters) return false
if (currentChapter != other.currentChapter) return false if (currentChapter != other.currentChapter) return false
if (history != other.history) return false if (history != other.history) return false
if (isIncognitoMode != other.isIncognitoMode) return false
return true return true
} }
@ -26,20 +31,21 @@ class HistoryInfo(
var result = totalChapters var result = totalChapters
result = 31 * result + currentChapter result = 31 * result + currentChapter
result = 31 * result + (history?.hashCode() ?: 0) result = 31 * result + (history?.hashCode() ?: 0)
result = 31 * result + isIncognitoMode.hashCode()
return result return result
} }
} }
@Suppress("FunctionName") fun HistoryInfo(manga: Manga?, history: MangaHistory?, isIncognitoMode: Boolean): HistoryInfo {
fun HistoryInfo(manga: Manga?, history: MangaHistory?): HistoryInfo? { val chapters = manga?.chapters
val chapters = manga?.chapters ?: return null
return HistoryInfo( return HistoryInfo(
totalChapters = chapters.size, totalChapters = chapters?.size ?: -1,
currentChapter = if (history != null) { currentChapter = if (history != null && !chapters.isNullOrEmpty()) {
chapters.indexOfFirst { it.id == history.chapterId } chapters.indexOfFirst { it.id == history.chapterId }
} else { } else {
-1 -1
}, },
history = history, history = history,
isIncognitoMode = isIncognitoMode,
) )
} }

@ -12,6 +12,7 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
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.observeAsLiveData import org.koitharu.kotatsu.core.prefs.observeAsLiveData
import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.history.domain.HistoryRepository
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
@ -34,9 +35,12 @@ class MainViewModel @Inject constructor(
val onOpenReader = SingleLiveEvent<Manga>() val onOpenReader = SingleLiveEvent<Manga>()
val isResumeEnabled = historyRepository val isResumeEnabled = combine(
.observeHasItems() historyRepository.observeHasItems(),
.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, false) settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
) { hasItems, incognito ->
hasItems && !incognito
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, false)
val isFeedAvailable = settings.observeAsLiveData( val isFeedAvailable = settings.observeAsLiveData(
context = viewModelScope.coroutineContext + Dispatchers.Default, context = viewModelScope.coroutineContext + Dispatchers.Default,

Loading…
Cancel
Save