Information toast in reader
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
11
app/src/main/res/drawable/bg_reader_indicator.xml
Normal file
11
app/src/main/res/drawable/bg_reader_indicator.xml
Normal 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>
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user