diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/data/ReadingTime.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/data/ReadingTime.kt new file mode 100644 index 000000000..bdd02b4df --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/data/ReadingTime.kt @@ -0,0 +1,21 @@ +package org.koitharu.kotatsu.details.data + +import android.content.res.Resources +import org.koitharu.kotatsu.R + +data class ReadingTime( + val minutes: Int, + val hours: Int, + val isContinue: Boolean, +) { + + fun format(resources: Resources): String = when { + hours == 0 -> resources.getQuantityString(R.plurals.minutes, minutes, minutes) + minutes == 0 -> resources.getQuantityString(R.plurals.hours, hours, hours) + else -> resources.getString( + R.string.remaining_time_pattern, + resources.getQuantityString(R.plurals.hours, hours, hours), + resources.getQuantityString(R.plurals.minutes, minutes, minutes), + ) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/ReadingTimeUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/ReadingTimeUseCase.kt new file mode 100644 index 000000000..1df2035e8 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/ReadingTimeUseCase.kt @@ -0,0 +1,33 @@ +package org.koitharu.kotatsu.details.domain + +import org.koitharu.kotatsu.core.model.MangaHistory +import org.koitharu.kotatsu.core.model.findById +import org.koitharu.kotatsu.details.data.MangaDetails +import org.koitharu.kotatsu.details.data.ReadingTime +import javax.inject.Inject +import kotlin.math.roundToInt + +class ReadingTimeUseCase @Inject constructor() { + + fun invoke(manga: MangaDetails?, branch: String?, history: MangaHistory?): ReadingTime? { + // FIXME MAXIMUM HARDCODE!!! To do calculation with user's page read speed and his favourites/history mangas average pages in chapter + val chapters = manga?.chapters?.get(branch) + if (chapters.isNullOrEmpty()) { + return null + } + val isOnHistoryBranch = history != null && chapters.findById(history.chapterId) != null + // Impossible task, I guess. Good luck on this. + var averageTimeSec: Int = 20 * 10 * chapters.size // 20 pages, 10 seconds per page + if (isOnHistoryBranch) { + averageTimeSec = (averageTimeSec * (1f - checkNotNull(history).percent)).roundToInt() + } + if (averageTimeSec < 60) { + return null + } + return ReadingTime( + minutes = averageTimeSec / 60, + hours = averageTimeSec / 3600, + isContinue = isOnHistoryBranch, + ) + } +} 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 31b7e39ca..412d83856 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 @@ -48,6 +48,7 @@ import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.core.util.ext.showOrHide import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.FragmentDetailsBinding +import org.koitharu.kotatsu.details.data.ReadingTime import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.HistoryInfo import org.koitharu.kotatsu.details.ui.related.RelatedMangaActivity @@ -70,6 +71,8 @@ import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.search.ui.SearchActivity +import java.text.SimpleDateFormat +import java.util.Date import javax.inject.Inject @AndroidEntryPoint @@ -118,6 +121,7 @@ class DetailsFragment : viewModel.localSize.observe(viewLifecycleOwner, ::onLocalSizeChanged) viewModel.relatedManga.observe(viewLifecycleOwner, ::onRelatedMangaChanged) viewModel.chapters.observe(viewLifecycleOwner, ::onChaptersChanged) + viewModel.readingTime.observe(viewLifecycleOwner, ::onReadingTimeChanged) } override fun onItemClick(item: Bookmark, view: View) { @@ -201,32 +205,29 @@ class DetailsFragment : private fun onChaptersChanged(chapters: List?) { val infoLayout = requireViewBinding().infoLayout - val tv = requireViewBinding().approximateReadTime if (chapters.isNullOrEmpty()) { infoLayout.textViewChapters.isVisible = false - tv.text = "..." } else { val count = chapters.countChaptersByBranch() - - // FIXME MAXIMUM HARDCODE!!! To do calculation with user's page read speed and his favourites/history mangas average pages in chapter - // Impossible task, I guess. Good luck on this. - val averageTime: Int = 20 * 10 * (count) // 20 pages, 10 seconds per page - val hours = averageTime / 3600 - val minutes = averageTime % 3600 / 60 - - tv.text = buildString { - append(resources.getQuantityString(R.plurals.hour, hours, hours)) - append(" ") - append(resources.getQuantityString(R.plurals.minute, minutes, minutes)) - } - - // Info infoLayout.textViewChapters.isVisible = true val chaptersText = resources.getQuantityString(R.plurals.chapters, count, count) infoLayout.textViewChapters.text = chaptersText } } + private fun onReadingTimeChanged(time: ReadingTime?) { + val binding = viewBinding ?: return + if (time == null) { + binding.approximateReadTimeLayout.isVisible = false + return + } + binding.approximateReadTime.text = time.format(resources) + binding.approximateReadTimeTitle.setText( + if (time.isContinue) R.string.approximate_remaining_time else R.string.approximate_reading_time + ) + binding.approximateReadTimeLayout.isVisible = true + } + private fun onDescriptionChanged(description: CharSequence?) { val tv = requireViewBinding().textViewDescription if (description.isNullOrBlank()) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt index bf15a0ac4..fe40a4c21 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt @@ -42,6 +42,7 @@ import org.koitharu.kotatsu.details.domain.BranchComparator import org.koitharu.kotatsu.details.domain.DetailsInteractor import org.koitharu.kotatsu.details.domain.DetailsLoadUseCase import org.koitharu.kotatsu.details.domain.ProgressUpdateUseCase +import org.koitharu.kotatsu.details.domain.ReadingTimeUseCase import org.koitharu.kotatsu.details.domain.RelatedMangaUseCase import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.HistoryInfo @@ -76,6 +77,7 @@ class DetailsViewModel @Inject constructor( private val extraProvider: ListExtraProvider, private val detailsLoadUseCase: DetailsLoadUseCase, private val progressUpdateUseCase: ProgressUpdateUseCase, + private val readingTimeUseCase: ReadingTimeUseCase, ) : BaseViewModel() { private val intent = MangaIntent(savedStateHandle) @@ -200,6 +202,14 @@ class DetailsViewModel @Inject constructor( (if (reversed) list.asReversed() else list).filterSearch(query) }.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList()) + val readingTime = combine( + details, + selectedBranch, + history, + ) { m, b, h -> + readingTimeUseCase.invoke(m, b, h) + }.stateIn(viewModelScope, SharingStarted.Lazily, null) + val selectedBranchValue: String? get() = selectedBranch.value diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 0e8af1ad6..1d87ea806 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -147,9 +147,11 @@ android:gravity="center_vertical" android:orientation="horizontal" android:paddingHorizontal="16dp" + android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/chips_tags"> + app:layout_constraintTop_toBottomOf="@id/chips_tags" + tools:visibility="visible"> diff --git a/app/src/main/res/values/plurals.xml b/app/src/main/res/values/plurals.xml index 2e8254031..7952e3e5c 100644 --- a/app/src/main/res/values/plurals.xml +++ b/app/src/main/res/values/plurals.xml @@ -28,11 +28,11 @@ %1$d month ago %1$d months ago - + %1$d hour %1$d hours - + %1$d minute %1$d minutes diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7ed3f140..61b1b3443 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -560,4 +560,6 @@ Mark selected manga as completely read?\n\nWarning: current reading progress will be lost. This category was hidden from the main screen and is accessible through Menu → Manage categories Approximate reading time + Approximate remaining time + %1$s %2$s