Skip to content

Commit

Permalink
refactor: Replace LoadingButtonAndroid with compose
Browse files Browse the repository at this point in the history
  • Loading branch information
null2264 committed Aug 24, 2024
1 parent 61e43e0 commit 6f94dd0
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 26 deletions.
1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,6 @@ dependencies {
implementation(libs.aboutlibraries)

// UI
implementation(libs.loading.button)
implementation(libs.fastadapter)
implementation(libs.fastadapter.extensions.binding)
implementation(libs.flexible.adapter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.widget.preference
import android.app.Dialog
import android.os.Bundle
import android.view.View
import androidx.annotation.StringRes
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import dev.icerock.moko.resources.StringResource
Expand Down Expand Up @@ -53,7 +52,7 @@ abstract class LoginDialogPreference(
binding.usernameInput.hint = view.context.getString(usernameLabelRes)
}

binding.login.setOnClickListener {
binding.login.setOnClick {
checkLogin()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@ package eu.kanade.tachiyomi.widget.preference

import android.os.Bundle
import android.view.View
import androidx.annotation.StringRes
import br.com.simplepass.loadingbutton.animatedDrawables.ProgressType
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.TrackService
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.withIOContext
import kotlinx.coroutines.launch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.i18n.MR
import yokai.util.lang.getString

class TrackLoginDialog(usernameLabelRes: StringResource? = null, bundle: Bundle? = null) :
LoginDialogPreference(usernameLabelRes, bundle) {
Expand All @@ -36,10 +32,7 @@ class TrackLoginDialog(usernameLabelRes: StringResource? = null, bundle: Bundle?

override fun checkLogin() {
v?.apply {
binding.login.apply {
progressType = ProgressType.INDETERMINATE
startAnimation()
}
binding.login.startAnimation()
if (binding.username.text.isNullOrBlank() || binding.password.text.isNullOrBlank()) {
errorResult()
context.toast(MR.strings.username_must_not_be_blank)
Expand Down
154 changes: 154 additions & 0 deletions app/src/main/java/yokai/presentation/component/LoadingButton.kt
Original file line number Diff line number Diff line change
@@ -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 = {},
)
}
}
Original file line number Diff line number Diff line change
@@ -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,
)
}
}
}
15 changes: 3 additions & 12 deletions app/src/main/res/layout/pref_account_login.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,12 @@
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>


<br.com.simplepass.loadingbutton.customViews.CircularProgressButton
<yokai.presentation.component.LoadingButtonComposeView
android:id="@+id/login"
style="@style/Widget.Tachiyomi.Button.Primary"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:scaleType="fitCenter"
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" />
android:text="@string/log_in" />

</LinearLayout>
</LinearLayout>
4 changes: 4 additions & 0 deletions app/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
<attr name="endIcon" format="reference" />
</declare-styleable>

<declare-styleable name="LoadingButtonComposeView">
<attr name="android:text" />
</declare-styleable>

<bool name="isLightMode">true</bool>

<fraction name="chartRatio">1</fraction>
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
<color name="pale_green">#99CC99</color>
<color name="strong_green">#106010</color>

<color name="black">#000</color>

<!-- Navigation overlay colors -->
<color name="navigation_menu">#CC95818D</color>
<color name="navigation_next">#CCA6CFD5</color>
Expand Down
1 change: 0 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
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" }

moko-resources = { module = "dev.icerock.moko:resources", version.ref = "moko" }
Expand Down

0 comments on commit 6f94dd0

Please sign in to comment.