Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dfc8316
fix: rename withdraw screen title and hide config sections when toggl…
swedishfrenchpress Mar 21, 2026
57704c0
fix: disabled primary buttons nearly invisible in both light and dark…
swedishfrenchpress Mar 21, 2026
f2d4785
fix: withdraw screens background color inconsistent with rest of app
swedishfrenchpress Mar 21, 2026
6f4d75e
fix: withdraw screen section headers use wrong font weight and letter…
swedishfrenchpress Mar 21, 2026
1b5b2b7
fix: differentiate auto-withdraw and withdraw now icons
swedishfrenchpress Mar 21, 2026
dc99f41
fix: hero card gradient too strong, add decorative lightning bolt wat…
swedishfrenchpress Mar 21, 2026
645b472
fix: hero card lightning bolt positioning, rotation, fade, and opacity
swedishfrenchpress Mar 21, 2026
b98914d
fix: rename hero title to Auto-Withdraw and add lightning strike anim…
swedishfrenchpress Mar 21, 2026
d563d4f
fix: tone down lightning animation and remove address label icon
swedishfrenchpress Mar 21, 2026
b03a80f
fix: remove dark mode visual inconsistencies on withdraw screen
swedishfrenchpress Mar 21, 2026
d9bfa9f
fix: remove icon from mint selection bottom sheet
swedishfrenchpress Mar 21, 2026
8db4063
fix: polish withdraw now screen for consistency
swedishfrenchpress Mar 21, 2026
21c0c7a
fix: redesign withdraw now inputs and add fiat balance display
swedishfrenchpress Mar 21, 2026
6e624a3
fix: cashu section spacing, rename amount label, add scan icon
swedishfrenchpress Mar 21, 2026
4a404e7
fix: match withdraw toggle to payment request screen's segmented control
swedishfrenchpress Mar 21, 2026
0b380c6
fix: match withdraw history icons to activity screen and clean up sty…
swedishfrenchpress Mar 21, 2026
19658b3
fix: unify recent activity fonts with withdraw screen's own typography
swedishfrenchpress Mar 21, 2026
9647d8d
fix: match recent activity icon size to other withdraw screen icons
swedishfrenchpress Mar 21, 2026
d597f25
fix: title bar dividers, font sizes, and bottom sheet typography
swedishfrenchpress Mar 22, 2026
db404bd
fix: scan invoice button border and height consistency
swedishfrenchpress Mar 22, 2026
2a3aa10
fix: confirm withdrawal screen consistency with withdraw flow
swedishfrenchpress Mar 22, 2026
6b7ff29
fix: extend entrance animation to Recent Activity section
swedishfrenchpress Mar 22, 2026
50a8936
fix: hero card gradient reflects active/inactive state, orange slider
swedishfrenchpress Mar 22, 2026
34e7775
fix: auto-withdraw row icon reflects toggle state
swedishfrenchpress Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.electricdreams.numo.feature.autowithdraw

import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Intent
import android.os.Bundle
import android.text.Editable
Expand Down Expand Up @@ -33,6 +32,7 @@ import com.electricdreams.numo.core.cashu.CashuWalletManager
import com.electricdreams.numo.core.model.Amount
import com.electricdreams.numo.core.util.MintManager
import com.electricdreams.numo.feature.settings.WithdrawLightningActivity
import com.electricdreams.numo.ui.components.LightningStrikeView
import com.electricdreams.numo.ui.components.MintSelectionBottomSheet
import com.electricdreams.numo.ui.util.DialogHelper
import com.google.android.material.slider.Slider
Expand All @@ -56,12 +56,17 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
private lateinit var autoWithdrawManager: AutoWithdrawManager

// Hero section
private lateinit var heroIcon: ImageView
private lateinit var heroIconContainer: FrameLayout
private lateinit var heroBg: FrameLayout
private lateinit var heroBolt: ImageView
private lateinit var heroBoltFade: View
private lateinit var statusContainer: LinearLayout
private lateinit var statusDot: View
private lateinit var statusText: TextView

// Toggle icon
private lateinit var toggleIconContainer: FrameLayout
private lateinit var toggleIcon: ImageView

// Settings controls
private lateinit var enableSwitch: SwitchCompat
private lateinit var enableToggleRow: LinearLayout
Expand All @@ -76,14 +81,16 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
private lateinit var historyRecyclerView: RecyclerView
private lateinit var seeAllButton: TextView

// Auto-withdraw config container (Destination + Trigger Settings)
private lateinit var configContainer: LinearLayout

// Manual withdraw
private lateinit var manualWithdrawRow: LinearLayout

// Manager for mint info
private lateinit var mintManager: MintManager

private var isUpdatingUI = false
private var iconAnimator: ObjectAnimator? = null

// Current threshold value (in sats)
private var currentThreshold: Long = AutoWithdrawSettingsManager.DEFAULT_THRESHOLD_SATS
Expand Down Expand Up @@ -115,12 +122,17 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
}

// Hero section
heroIcon = findViewById(R.id.hero_icon)
heroIconContainer = findViewById(R.id.icon_container)
heroBg = findViewById(R.id.hero_bg)
heroBolt = findViewById(R.id.hero_bolt)
heroBoltFade = findViewById(R.id.hero_bolt_fade)
statusContainer = findViewById(R.id.status_container)
statusDot = findViewById(R.id.status_dot)
statusText = findViewById(R.id.status_text)

// Toggle icon
toggleIconContainer = findViewById(R.id.toggle_icon_container)
toggleIcon = findViewById(R.id.toggle_icon)

// Main toggle
enableSwitch = findViewById(R.id.enable_switch)
enableToggleRow = findViewById(R.id.enable_toggle_row)
Expand All @@ -137,6 +149,9 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
historyRecyclerView = findViewById(R.id.history_recycler_view)
seeAllButton = findViewById(R.id.see_all_button)

// Config container (Destination + Trigger Settings)
configContainer = findViewById(R.id.auto_withdraw_config_container)

// Manual withdraw
manualWithdrawRow = findViewById(R.id.manual_withdraw_row)

Expand All @@ -154,8 +169,12 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
if (!isUpdatingUI) {
settingsManager.setGloballyEnabled(isChecked)
updateStatusIndicator(isChecked)
updateConfigFieldsEnabled(isChecked)
updateHeroGradient(isChecked, animate = true)
animateConfigContainer(isChecked)
animateStatusChange(isChecked)
if (isChecked) {
playLightningStrike()
}
}
}

Expand Down Expand Up @@ -284,7 +303,8 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
val enabled = settingsManager.isGloballyEnabled()
enableSwitch.isChecked = enabled
updateStatusIndicator(enabled)
updateConfigFieldsEnabled(enabled)
updateHeroGradient(enabled, animate = false)
configContainer.visibility = if (enabled) View.VISIBLE else View.GONE

lightningAddressInput.setText(settingsManager.getDefaultLightningAddress())

Expand Down Expand Up @@ -312,6 +332,35 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
}
}

private fun updateHeroGradient(enabled: Boolean, animate: Boolean) {
val gradientRes = if (enabled) R.drawable.bg_hero_gradient_active else R.drawable.bg_hero_gradient
val fadeRes = if (enabled) R.drawable.bg_hero_bolt_fade_active else R.drawable.bg_hero_bolt_fade
val boltColor = if (enabled) R.color.color_success_green else R.color.color_bitcoin_orange

heroBg.setBackgroundResource(gradientRes)
heroBolt.setColorFilter(ContextCompat.getColor(this, boltColor))
heroBoltFade.setBackgroundResource(fadeRes)

// Toggle row icon
if (enabled) {
toggleIconContainer.backgroundTintList = android.content.res.ColorStateList.valueOf(android.graphics.Color.parseColor("#E8F5E9"))
toggleIcon.setColorFilter(ContextCompat.getColor(this, R.color.color_success_green))
} else {
toggleIconContainer.backgroundTintList = ContextCompat.getColorStateList(this, R.color.color_bg_light)
toggleIcon.setColorFilter(ContextCompat.getColor(this, R.color.color_text_primary))
}
}

private fun playLightningStrike() {
val root = findViewById<ViewGroup>(R.id.root_layout)
val strike = LightningStrikeView(this)
root.addView(strike, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
))
strike.strike()
}

private fun animateStatusChange(enabled: Boolean) {
// Pulse animation on status container
val scaleX = ObjectAnimator.ofFloat(statusContainer, "scaleX", 1f, 1.1f, 1f)
Expand All @@ -324,45 +373,32 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
start()
}

// Icon pulse
if (enabled) {
startIconPulseAnimation()
} else {
stopIconPulseAnimation()
}
}

private fun startIconPulseAnimation() {
iconAnimator?.cancel()

iconAnimator = ObjectAnimator.ofFloat(heroIconContainer, "alpha", 1f, 0.6f, 1f).apply {
duration = 1500
repeatCount = ValueAnimator.INFINITE
interpolator = AccelerateDecelerateInterpolator()
start()
private fun animateConfigContainer(show: Boolean) {
if (show) {
configContainer.visibility = View.VISIBLE
configContainer.alpha = 0f
configContainer.translationY = -20f
configContainer.animate()
.alpha(1f)
.translationY(0f)
.setDuration(250)
.setInterpolator(AccelerateDecelerateInterpolator())
.start()
} else {
configContainer.animate()
.alpha(0f)
.translationY(-20f)
.setDuration(200)
.setInterpolator(AccelerateDecelerateInterpolator())
.withEndAction {
configContainer.visibility = View.GONE
}
.start()
}
}

private fun stopIconPulseAnimation() {
iconAnimator?.cancel()
heroIconContainer.alpha = 1f
}

private fun updateConfigFieldsEnabled(enabled: Boolean) {
val alpha = if (enabled) 1f else 0.5f

// Animate alpha change
lightningAddressInput.animate().alpha(alpha).setDuration(200).start()
thresholdDisplay.animate().alpha(alpha).setDuration(200).start()
percentageSlider.animate().alpha(alpha).setDuration(200).start()
percentageBadge.animate().alpha(alpha).setDuration(200).start()

lightningAddressInput.isEnabled = enabled
thresholdDisplay.isEnabled = enabled
thresholdDisplay.isClickable = enabled
percentageSlider.isEnabled = enabled
}

private fun loadHistory() {
val history = autoWithdrawManager.getHistory()

Expand Down Expand Up @@ -393,17 +429,6 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
.setInterpolator(AccelerateDecelerateInterpolator())
.start()

// Icon bounce
heroIconContainer.scaleX = 0f
heroIconContainer.scaleY = 0f
heroIconContainer.animate()
.scaleX(1f)
.scaleY(1f)
.setStartDelay(200)
.setDuration(500)
.setInterpolator(OvershootInterpolator(2f))
.start()

// Status pill fade
statusContainer.alpha = 0f
statusContainer.animate()
Expand All @@ -415,14 +440,19 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
// Cards stagger in
val toggleCard: CardView = findViewById(R.id.toggle_card)
animateCardEntrance(toggleCard, 100)

val manualWithdrawCard: CardView = findViewById(R.id.manual_withdraw_card)
animateCardEntrance(manualWithdrawCard, 200)

// If auto-withdraw is enabled, start icon animation

// Animate config container entrance only if enabled
if (settingsManager.isGloballyEnabled()) {
heroIconContainer.postDelayed({ startIconPulseAnimation() }, 800)
animateCardEntrance(configContainer, 150)
}

val manualWithdrawCard: CardView = findViewById(R.id.manual_withdraw_card)
animateCardEntrance(manualWithdrawCard, 200)

// History section
val historySectionHeader: View = findViewById(R.id.history_section_header)
animateCardEntrance(historySectionHeader, 250)
animateCardEntrance(historyCard, 300)
}

private fun animateCardEntrance(card: View, delay: Long) {
Expand All @@ -445,7 +475,6 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {

override fun onDestroy() {
super.onDestroy()
iconAnimator?.cancel()
}

/**
Expand All @@ -461,6 +490,7 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val iconContainer: FrameLayout = view.findViewById(R.id.icon_container)
val statusIcon: ImageView = view.findViewById(R.id.status_icon)
val statusBadgeIcon: FrameLayout = view.findViewById(R.id.status_badge_icon_container)
val amountText: TextView = view.findViewById(R.id.amount_text)
val addressText: TextView = view.findViewById(R.id.address_text)
val mintText: TextView = view.findViewById(R.id.mint_text)
Expand Down Expand Up @@ -504,19 +534,18 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
// Status styling
when (entry.status) {
WithdrawHistoryEntry.STATUS_COMPLETED -> {
holder.statusIcon.setImageResource(R.drawable.ic_check)
holder.statusIcon.setColorFilter(ContextCompat.getColor(this@AutoWithdrawSettingsActivity, R.color.color_success_green))
holder.iconContainer.backgroundTintList = ContextCompat.getColorStateList(this@AutoWithdrawSettingsActivity, R.color.color_bg_secondary)
holder.statusBadge.text = getString(R.string.auto_withdraw_status_completed)
holder.statusBadge.setTextColor(ContextCompat.getColor(this@AutoWithdrawSettingsActivity, R.color.color_success_green))
holder.statusBadge.background = ContextCompat.getDrawable(this@AutoWithdrawSettingsActivity, R.drawable.bg_status_pill_success)
holder.statusIcon.setImageResource(R.drawable.ic_arrow_up_send)
holder.statusIcon.setColorFilter(ContextCompat.getColor(this@AutoWithdrawSettingsActivity, R.color.color_text_primary))
holder.statusBadgeIcon.visibility = View.VISIBLE
holder.statusBadge.visibility = View.GONE
holder.expandIndicator.visibility = View.GONE
holder.errorContainer.visibility = View.GONE
}
WithdrawHistoryEntry.STATUS_PENDING -> {
holder.statusIcon.setImageResource(R.drawable.ic_pending)
holder.statusIcon.setColorFilter(ContextCompat.getColor(this@AutoWithdrawSettingsActivity, R.color.color_warning))
holder.iconContainer.backgroundTintList = ContextCompat.getColorStateList(this@AutoWithdrawSettingsActivity, R.color.color_bg_secondary)
holder.statusBadgeIcon.visibility = View.GONE
holder.statusBadge.visibility = View.VISIBLE
holder.statusBadge.text = getString(R.string.auto_withdraw_status_pending)
holder.statusBadge.setTextColor(ContextCompat.getColor(this@AutoWithdrawSettingsActivity, R.color.color_warning))
holder.statusBadge.background = ContextCompat.getDrawable(this@AutoWithdrawSettingsActivity, R.drawable.bg_status_pill_pending)
Expand All @@ -526,7 +555,8 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() {
WithdrawHistoryEntry.STATUS_FAILED -> {
holder.statusIcon.setImageResource(R.drawable.ic_close)
holder.statusIcon.setColorFilter(ContextCompat.getColor(this@AutoWithdrawSettingsActivity, R.color.color_error))
holder.iconContainer.backgroundTintList = ContextCompat.getColorStateList(this@AutoWithdrawSettingsActivity, R.color.color_bg_secondary)
holder.statusBadgeIcon.visibility = View.GONE
holder.statusBadge.visibility = View.VISIBLE
holder.statusBadge.text = getString(R.string.auto_withdraw_status_failed)
holder.statusBadge.setTextColor(ContextCompat.getColor(this@AutoWithdrawSettingsActivity, R.color.color_error))
holder.statusBadge.background = ContextCompat.getDrawable(this@AutoWithdrawSettingsActivity, R.drawable.bg_status_pill_error)
Expand Down
Loading
Loading