mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
refactor: Replace LoadingButtonAndroid with compose
REF: https://gist.github.com/mmolosay/584ce5c47567cb66228b76ef98c3c4e4
This commit is contained in:
parent
61e43e047f
commit
6f94dd091b
9 changed files with 228 additions and 26 deletions
|
@ -235,7 +235,6 @@ dependencies {
|
||||||
implementation(libs.aboutlibraries)
|
implementation(libs.aboutlibraries)
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
implementation(libs.loading.button)
|
|
||||||
implementation(libs.fastadapter)
|
implementation(libs.fastadapter)
|
||||||
implementation(libs.fastadapter.extensions.binding)
|
implementation(libs.fastadapter.extensions.binding)
|
||||||
implementation(libs.flexible.adapter)
|
implementation(libs.flexible.adapter)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.widget.preference
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||||
import com.bluelinelabs.conductor.ControllerChangeType
|
import com.bluelinelabs.conductor.ControllerChangeType
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
@ -53,7 +52,7 @@ abstract class LoginDialogPreference(
|
||||||
binding.usernameInput.hint = view.context.getString(usernameLabelRes)
|
binding.usernameInput.hint = view.context.getString(usernameLabelRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.login.setOnClickListener {
|
binding.login.setOnClick {
|
||||||
checkLogin()
|
checkLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,7 @@ package eu.kanade.tachiyomi.widget.preference
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import br.com.simplepass.loadingbutton.animatedDrawables.ProgressType
|
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import yokai.i18n.MR
|
|
||||||
import yokai.util.lang.getString
|
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
@ -16,6 +10,8 @@ import eu.kanade.tachiyomi.util.system.withIOContext
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import yokai.i18n.MR
|
||||||
|
import yokai.util.lang.getString
|
||||||
|
|
||||||
class TrackLoginDialog(usernameLabelRes: StringResource? = null, bundle: Bundle? = null) :
|
class TrackLoginDialog(usernameLabelRes: StringResource? = null, bundle: Bundle? = null) :
|
||||||
LoginDialogPreference(usernameLabelRes, bundle) {
|
LoginDialogPreference(usernameLabelRes, bundle) {
|
||||||
|
@ -36,10 +32,7 @@ class TrackLoginDialog(usernameLabelRes: StringResource? = null, bundle: Bundle?
|
||||||
|
|
||||||
override fun checkLogin() {
|
override fun checkLogin() {
|
||||||
v?.apply {
|
v?.apply {
|
||||||
binding.login.apply {
|
binding.login.startAnimation()
|
||||||
progressType = ProgressType.INDETERMINATE
|
|
||||||
startAnimation()
|
|
||||||
}
|
|
||||||
if (binding.username.text.isNullOrBlank() || binding.password.text.isNullOrBlank()) {
|
if (binding.username.text.isNullOrBlank() || binding.password.text.isNullOrBlank()) {
|
||||||
errorResult()
|
errorResult()
|
||||||
context.toast(MR.strings.username_must_not_be_blank)
|
context.toast(MR.strings.username_must_not_be_blank)
|
||||||
|
|
154
app/src/main/java/yokai/presentation/component/LoadingButton.kt
Normal file
154
app/src/main/java/yokai/presentation/component/LoadingButton.kt
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
package yokai.presentation.component
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
|
import androidx.compose.animation.core.Transition
|
||||||
|
import androidx.compose.animation.core.VisibilityThreshold
|
||||||
|
import androidx.compose.animation.core.animateDp
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
|
import androidx.compose.animation.core.updateTransition
|
||||||
|
import androidx.compose.animation.expandHorizontally
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.shrinkHorizontally
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
// REF: https://gist.github.com/mmolosay/584ce5c47567cb66228b76ef98c3c4e4
|
||||||
|
|
||||||
|
private val SpringStiffness = Spring.StiffnessMediumLow
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoadingButton(
|
||||||
|
text: () -> String,
|
||||||
|
loading: () -> Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val transition = updateTransition(
|
||||||
|
targetState = loading(),
|
||||||
|
label = "master transition",
|
||||||
|
)
|
||||||
|
val horizontalContentPadding by transition.animateDp(
|
||||||
|
transitionSpec = {
|
||||||
|
spring(
|
||||||
|
stiffness = SpringStiffness,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
targetValueByState = { toLoading -> if (toLoading) 12.dp else 24.dp },
|
||||||
|
label = "button's content padding",
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier.defaultMinSize(minWidth = 1.dp),
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
horizontal = horizontalContentPadding,
|
||||||
|
vertical = 8.dp,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Box(contentAlignment = Alignment.Center) {
|
||||||
|
LoadingContent(
|
||||||
|
loadingStateTransition = transition,
|
||||||
|
)
|
||||||
|
PrimaryContent(
|
||||||
|
text = text(),
|
||||||
|
loadingStateTransition = transition,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LoadingContent(
|
||||||
|
loadingStateTransition: Transition<Boolean>,
|
||||||
|
) {
|
||||||
|
loadingStateTransition.AnimatedVisibility(
|
||||||
|
visible = { loading -> loading },
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(
|
||||||
|
animationSpec = spring(
|
||||||
|
stiffness = SpringStiffness,
|
||||||
|
visibilityThreshold = 0.10f,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.size(18.dp),
|
||||||
|
color = LocalContentColor.current,
|
||||||
|
strokeWidth = 1.5f.dp,
|
||||||
|
strokeCap = StrokeCap.Round,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PrimaryContent(
|
||||||
|
text: String,
|
||||||
|
loadingStateTransition: Transition<Boolean>,
|
||||||
|
) {
|
||||||
|
loadingStateTransition.AnimatedVisibility(
|
||||||
|
visible = { loading -> !loading },
|
||||||
|
enter = fadeIn() + expandHorizontally(
|
||||||
|
animationSpec = spring(
|
||||||
|
stiffness = SpringStiffness,
|
||||||
|
dampingRatio = Spring.DampingRatioMediumBouncy,
|
||||||
|
visibilityThreshold = IntSize.VisibilityThreshold,
|
||||||
|
),
|
||||||
|
expandFrom = Alignment.CenterHorizontally,
|
||||||
|
),
|
||||||
|
exit = fadeOut(
|
||||||
|
animationSpec = spring(
|
||||||
|
stiffness = SpringStiffness,
|
||||||
|
visibilityThreshold = 0.10f,
|
||||||
|
),
|
||||||
|
) + shrinkHorizontally(
|
||||||
|
animationSpec = spring(
|
||||||
|
stiffness = SpringStiffness,
|
||||||
|
// dampingRatio is not applicable here, size cannot become negative
|
||||||
|
visibilityThreshold = IntSize.VisibilityThreshold,
|
||||||
|
),
|
||||||
|
shrinkTowards = Alignment.CenterHorizontally,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
modifier = Modifier
|
||||||
|
// so that bouncing button's width doesn't cut first and last letters
|
||||||
|
.padding(horizontal = 4.dp),
|
||||||
|
fontSize = 16.sp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun LoadingButtonPreview() {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
LoadingButton(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
text = { "Test" },
|
||||||
|
loading = { false },
|
||||||
|
onClick = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package yokai.presentation.component
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.AbstractComposeView
|
||||||
|
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import yokai.presentation.theme.YokaiTheme
|
||||||
|
|
||||||
|
class LoadingButtonComposeView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0,
|
||||||
|
) : AbstractComposeView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
var text by mutableStateOf("placeholder")
|
||||||
|
private var onClick: () -> Unit = {}
|
||||||
|
private var isLoading by mutableStateOf(false)
|
||||||
|
|
||||||
|
init {
|
||||||
|
layoutParams = FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER)
|
||||||
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindowOrReleasedFromPool)
|
||||||
|
attrs?.let {
|
||||||
|
val arr = context.obtainStyledAttributes(it, R.styleable.LoadingButtonComposeView)
|
||||||
|
text = context.getString(arr.getResourceId(R.styleable.LoadingButtonComposeView_android_text, R.string.log_in))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnClick(onClick: () -> Unit) {
|
||||||
|
this.onClick = onClick
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startAnimation() {
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun revertAnimation(after: () -> Unit = {}) {
|
||||||
|
isLoading = false
|
||||||
|
after()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
YokaiTheme {
|
||||||
|
LoadingButton(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
text = { text },
|
||||||
|
loading = { isLoading },
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,21 +49,12 @@
|
||||||
android:inputType="textPassword" />
|
android:inputType="textPassword" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<yokai.presentation.component.LoadingButtonComposeView
|
||||||
<br.com.simplepass.loadingbutton.customViews.CircularProgressButton
|
|
||||||
android:id="@+id/login"
|
android:id="@+id/login"
|
||||||
style="@style/Widget.Tachiyomi.Button.Primary"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="55dp"
|
android:layout_height="55dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginTop="20dp"
|
android:layout_marginTop="20dp"
|
||||||
android:scaleType="fitCenter"
|
android:text="@string/log_in" />
|
||||||
android:text="@string/log_in"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:finalCornerAngle="50dp"
|
|
||||||
app:initialCornerAngle="2dp"
|
|
||||||
app:spinning_bar_color="@color/md_white_1000"
|
|
||||||
app:spinning_bar_padding="6dp"
|
|
||||||
app:spinning_bar_width="3dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -49,6 +49,10 @@
|
||||||
<attr name="endIcon" format="reference" />
|
<attr name="endIcon" format="reference" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="LoadingButtonComposeView">
|
||||||
|
<attr name="android:text" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
<bool name="isLightMode">true</bool>
|
<bool name="isLightMode">true</bool>
|
||||||
|
|
||||||
<fraction name="chartRatio">1</fraction>
|
<fraction name="chartRatio">1</fraction>
|
||||||
|
|
|
@ -100,6 +100,8 @@
|
||||||
<color name="pale_green">#99CC99</color>
|
<color name="pale_green">#99CC99</color>
|
||||||
<color name="strong_green">#106010</color>
|
<color name="strong_green">#106010</color>
|
||||||
|
|
||||||
|
<color name="black">#000</color>
|
||||||
|
|
||||||
<!-- Navigation overlay colors -->
|
<!-- Navigation overlay colors -->
|
||||||
<color name="navigation_menu">#CC95818D</color>
|
<color name="navigation_menu">#CC95818D</color>
|
||||||
<color name="navigation_next">#CCA6CFD5</color>
|
<color name="navigation_next">#CCA6CFD5</color>
|
||||||
|
|
|
@ -57,7 +57,6 @@ jsoup = { module = "org.jsoup:jsoup", version = "1.17.1" }
|
||||||
junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||||
junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
||||||
junit-android = { module = "androidx.test.ext:junit", version = "1.1.5" }
|
junit-android = { module = "androidx.test.ext:junit", version = "1.1.5" }
|
||||||
loading-button = { module = "com.github.leandroBorgesFerreira:LoadingButtonAndroid", version = "2.2.0" } # FIXME: Don't depends on this
|
|
||||||
mockk = { module = "io.mockk:mockk", version = "1.13.11" }
|
mockk = { module = "io.mockk:mockk", version = "1.13.11" }
|
||||||
|
|
||||||
moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko" }
|
moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko" }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue