From ed70ca4e1845dbcb160481433eba7dd86a0fd481 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 11 Feb 2021 19:29:17 +0200 Subject: [PATCH] Information toast in reader --- .../kotatsu/reader/ui/ReaderActivity.kt | 16 +++++- .../kotatsu/reader/ui/ReaderToastView.kt | 56 +++++++++++++++++++ .../org/koitharu/kotatsu/utils/anim/Motion.kt | 23 ++++---- .../koitharu/kotatsu/utils/ext/TextViewExt.kt | 20 +++++++ .../org/koitharu/kotatsu/utils/ext/ViewExt.kt | 7 --- .../main/res/drawable/bg_reader_indicator.xml | 11 ++++ app/src/main/res/layout/activity_reader.xml | 21 ++++--- 7 files changed, 125 insertions(+), 29 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderToastView.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/utils/ext/TextViewExt.kt create mode 100644 app/src/main/res/drawable/bg_reader_indicator.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index 8f13146a9..b8c0aafe1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -86,7 +86,7 @@ class ReaderActivity : BaseFullscreenActivity(), viewModel.onError.observe(this, this::onError) viewModel.readerMode.observe(this, this::onInitReader) viewModel.onPageSaved.observe(this, this::onPageSaved) - viewModel.uiState.observe(this, this::onUiStateChanged) + viewModel.uiState.observeWithPrevious(this, this::onUiStateChanged) viewModel.isLoading.observe(this, this::onLoadingStateChanged) viewModel.content.observe(this) { onLoadingStateChanged(viewModel.isLoading.value == true) @@ -204,7 +204,11 @@ class ReaderActivity : BaseFullscreenActivity(), private fun onLoadingStateChanged(isLoading: Boolean) { val hasPages = !viewModel.content.value?.pages.isNullOrEmpty() binding.layoutLoading.isVisible = isLoading && !hasPages - binding.progressBarBottom.isVisible = isLoading && hasPages + if (isLoading && hasPages) { + binding.toastView.show(R.string.loading_, true) + } else { + binding.toastView.hide() + } } private fun onError(e: Throwable) { @@ -336,19 +340,25 @@ class ReaderActivity : BaseFullscreenActivity(), setUiIsVisible(!binding.appbarTop.isVisible) } - private fun onUiStateChanged(uiState: ReaderUiState) { + private fun onUiStateChanged(uiState: ReaderUiState, previous: ReaderUiState?) { title = uiState.chapterName ?: uiState.mangaName ?: getString(R.string.loading_) supportActionBar?.subtitle = if (uiState.chapterNumber in 1..uiState.chaptersTotal) { getString(R.string.chapter_d_of_d, uiState.chapterNumber, uiState.chaptersTotal) } else { null } + if (previous?.chapterName != null && uiState.chapterName != previous.chapterName) { + if (!uiState.chapterName.isNullOrEmpty()) { + binding.toastView.showTemporary(uiState.chapterName, TOAST_DURATION) + } + } } companion object { const val ACTION_MANGA_READ = "${BuildConfig.APPLICATION_ID}.action.READ_MANGA" private const val EXTRA_STATE = "state" + private const val TOAST_DURATION = 1500L fun newIntent(context: Context, manga: Manga, state: ReaderState?): Intent { return Intent(context, ReaderActivity::class.java) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderToastView.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderToastView.kt new file mode 100644 index 000000000..4fb719855 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderToastView.kt @@ -0,0 +1,56 @@ +package org.koitharu.kotatsu.reader.ui + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import androidx.annotation.StringRes +import androidx.swiperefreshlayout.widget.CircularProgressDrawable +import com.google.android.material.textview.MaterialTextView +import org.koitharu.kotatsu.utils.anim.Duration +import org.koitharu.kotatsu.utils.anim.Motion +import org.koitharu.kotatsu.utils.ext.hideAnimated +import org.koitharu.kotatsu.utils.ext.showAnimated + +class ReaderToastView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : MaterialTextView(context, attrs, defStyleAttr) { + + private var hideRunnable = Runnable { + hide() + } + + fun show(message: CharSequence, isLoading: Boolean) { + removeCallbacks(hideRunnable) + text = message + this.showAnimated(Motion.Toast, Duration.SHORT) + } + + fun show(@StringRes messageId: Int, isLoading: Boolean) { + show(context.getString(messageId), isLoading) + } + + fun showTemporary(message: CharSequence, duration: Long) { + show(message, false) + postDelayed(hideRunnable, duration) + } + + fun hide() { + removeCallbacks(hideRunnable) + this.hideAnimated(Motion.Toast, Duration.SHORT) + } + + override fun onDetachedFromWindow() { + removeCallbacks(hideRunnable) + super.onDetachedFromWindow() + } + + // FIXME use it as compound drawable + private fun createProgressDrawable(): CircularProgressDrawable { + val drawable = CircularProgressDrawable(context) + drawable.setStyle(CircularProgressDrawable.DEFAULT) + drawable.arrowEnabled = false + drawable.setColorSchemeColors(Color.WHITE) + drawable.centerRadius = lineHeight / 3f + return drawable + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/anim/Motion.kt b/app/src/main/java/org/koitharu/kotatsu/utils/anim/Motion.kt index 411c561a3..ba9896400 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/anim/Motion.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/anim/Motion.kt @@ -2,7 +2,8 @@ package org.koitharu.kotatsu.utils.anim import android.view.View import android.view.ViewPropertyAnimator -import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator import org.koitharu.kotatsu.utils.ext.measureHeight sealed class Motion { @@ -69,27 +70,25 @@ sealed class Motion { } } - object CheckEffect : Motion() { + object Toast : Motion() { override fun reset(v: View) { - v.scaleX = 1f - v.scaleY = 1f + v.alpha = 1f } override fun hideView(v: View) { - v.scaleX = 0f - v.scaleY = 0f + v.alpha = 0f } override fun hide(v: View, anim: ViewPropertyAnimator) { - anim.scaleX(0f) - anim.scaleY(0f) - anim.interpolator = AccelerateDecelerateInterpolator() + anim.alpha(0f) + anim.translationY(v.measureHeight().toFloat()) + anim.interpolator = AccelerateInterpolator() } override fun show(v: View, anim: ViewPropertyAnimator) { - anim.scaleY(1f) - anim.scaleX(1f) - anim.interpolator = AccelerateDecelerateInterpolator() + anim.alpha(1f) + anim.translationY(0f) + anim.interpolator = DecelerateInterpolator() } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/TextViewExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/TextViewExt.kt new file mode 100644 index 000000000..cf3ee97bd --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/TextViewExt.kt @@ -0,0 +1,20 @@ +package org.koitharu.kotatsu.utils.ext + +import android.graphics.drawable.Drawable +import android.view.View +import android.widget.TextView +import androidx.core.view.isGone + +var TextView.textAndVisible: CharSequence? + inline get() = text?.takeIf { visibility == View.VISIBLE } + set(value) { + text = value + isGone = value.isNullOrEmpty() + } + +var TextView.drawableStart: Drawable? + inline get() = compoundDrawablesRelative[0] + set(value) { + val dr = compoundDrawablesRelative + setCompoundDrawablesRelativeWithIntrinsicBounds(value, dr[1], dr[2], dr[3]) + } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt index c1338fa1c..4a13722ec 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt @@ -40,13 +40,6 @@ inline fun ViewGroup.inflate(@LayoutRes resId: Int) = val RecyclerView.hasItems: Boolean get() = (adapter?.itemCount ?: 0) > 0 -var TextView.textAndVisible: CharSequence? - get() = text?.takeIf { visibility == View.VISIBLE } - set(value) { - text = value - isGone = value.isNullOrEmpty() - } - inline fun ChipGroup.addChips(data: Iterable, action: ChipsFactory.(T) -> Chip) { val factory = ChipsFactory(context) data.forEach { diff --git a/app/src/main/res/drawable/bg_reader_indicator.xml b/app/src/main/res/drawable/bg_reader_indicator.xml new file mode 100644 index 000000000..e312df0bf --- /dev/null +++ b/app/src/main/res/drawable/bg_reader_indicator.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index e12b2e329..cc5ade39d 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -2,6 +2,7 @@ @@ -11,14 +12,19 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - + android:background="@drawable/bg_reader_indicator" + android:singleLine="true" + android:drawablePadding="6dp" + android:textAppearance="?android:textAppearanceSmall" + android:textColor="?android:textColorPrimary" + android:theme="@style/ThemeOverlay.MaterialComponents.Dark" + tools:text="@string/loading_" /> + app:elevation="0dp" + tools:visibility="gone">