Information toast in reader

This commit is contained in:
Koitharu
2021-02-11 19:29:17 +02:00
parent a371bb6514
commit ed70ca4e18
7 changed files with 125 additions and 29 deletions

View File

@@ -86,7 +86,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
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<ActivityReaderBinding>(),
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<ActivityReaderBinding>(),
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)

View File

@@ -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
}
}

View File

@@ -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()
}
}
}

View File

@@ -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])
}

View File

@@ -40,13 +40,6 @@ inline fun <reified T : View> 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 <T> ChipGroup.addChips(data: Iterable<T>, action: ChipsFactory.(T) -> Chip) {
val factory = ChipsFactory(context)
data.forEach {

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="16dp" />
<solid android:color="@color/dim" />
<padding
android:bottom="8dp"
android:left="12dp"
android:right="12dp"
android:top="8dp" />
</shape>

View File

@@ -2,6 +2,7 @@
<FrameLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
@@ -11,14 +12,19 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressBar_bottom"
android:layout_width="match_parent"
<org.koitharu.kotatsu.reader.ui.ReaderToastView
android:id="@+id/toastView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:layout_gravity="bottom"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="20dp"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
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_" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_top"
@@ -45,7 +51,8 @@
android:background="@color/dim"
android:elevation="0dp"
android:theme="@style/AppToolbarTheme"
app:elevation="0dp">
app:elevation="0dp"
tools:visibility="gone">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_bottom"