diff --git a/app/src/main/java/com/electricdreams/numo/feature/autowithdraw/AutoWithdrawSettingsActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/autowithdraw/AutoWithdrawSettingsActivity.kt index 49d6edae..94129719 100644 --- a/app/src/main/java/com/electricdreams/numo/feature/autowithdraw/AutoWithdrawSettingsActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/feature/autowithdraw/AutoWithdrawSettingsActivity.kt @@ -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 @@ -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 @@ -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 @@ -76,6 +81,9 @@ 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 @@ -83,7 +91,6 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() { 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 @@ -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) @@ -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) @@ -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() + } } } @@ -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()) @@ -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(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) @@ -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() @@ -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() @@ -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) { @@ -445,7 +475,6 @@ class AutoWithdrawSettingsActivity : AppCompatActivity() { override fun onDestroy() { super.onDestroy() - iconAnimator?.cancel() } /** @@ -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) @@ -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) @@ -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) diff --git a/app/src/main/java/com/electricdreams/numo/feature/settings/WithdrawLightningActivity.kt b/app/src/main/java/com/electricdreams/numo/feature/settings/WithdrawLightningActivity.kt index ebd9afc5..52f522a5 100644 --- a/app/src/main/java/com/electricdreams/numo/feature/settings/WithdrawLightningActivity.kt +++ b/app/src/main/java/com/electricdreams/numo/feature/settings/WithdrawLightningActivity.kt @@ -28,6 +28,7 @@ import androidx.lifecycle.lifecycleScope import com.electricdreams.numo.R import com.electricdreams.numo.core.cashu.CashuWalletManager import com.electricdreams.numo.core.model.Amount +import com.electricdreams.numo.core.worker.BitcoinPriceWorker import com.electricdreams.numo.core.util.BalanceRefreshBroadcast import com.electricdreams.numo.core.util.LightningAddressManager import com.electricdreams.numo.core.util.MintManager @@ -73,6 +74,7 @@ class WithdrawLightningActivity : AppCompatActivity() { private lateinit var balanceCard: MaterialCardView private lateinit var mintNameText: TextView private lateinit var balanceText: TextView + private lateinit var fiatBalanceText: TextView private lateinit var invoiceCard: WithdrawInvoiceCard private lateinit var addressCard: WithdrawAddressCard private lateinit var loadingOverlay: FrameLayout @@ -144,6 +146,7 @@ class WithdrawLightningActivity : AppCompatActivity() { balanceCard = findViewById(R.id.balance_card) mintNameText = findViewById(R.id.mint_name_text) balanceText = findViewById(R.id.balance_text) + fiatBalanceText = findViewById(R.id.fiat_balance_text) invoiceCard = findViewById(R.id.invoice_card) addressCard = findViewById(R.id.address_card) loadingOverlay = findViewById(R.id.loading_overlay) @@ -217,7 +220,6 @@ class WithdrawLightningActivity : AppCompatActivity() { override fun afterTextChanged(s: android.text.Editable?) { val amount = s?.toString()?.toLongOrNull() ?: 0L createTokenButton.isEnabled = amount > 0 - createTokenButton.alpha = if (amount > 0) 1f else 0.5f } }) @@ -234,21 +236,21 @@ class WithdrawLightningActivity : AppCompatActivity() { private fun switchTab(isLightning: Boolean) { if (isLightning) { - tabLightning.setBackgroundResource(R.drawable.bg_segment_tab_selected) - tabLightning.setTextColor(getColor(R.color.color_text_primary)) - - tabCashu.background = null - tabCashu.setTextColor(getColor(R.color.color_text_tertiary)) - + tabLightning.setBackgroundResource(R.drawable.bg_button_primary_green) + tabLightning.setTextColor(getColor(R.color.color_bg_white)) + + tabCashu.setBackgroundResource(android.R.color.transparent) + tabCashu.setTextColor(getColor(R.color.color_text_secondary)) + lightningOptionsContainer.visibility = View.VISIBLE cashuTokenOptionsContainer.visibility = View.GONE } else { - tabCashu.setBackgroundResource(R.drawable.bg_segment_tab_selected) - tabCashu.setTextColor(getColor(R.color.color_text_primary)) - - tabLightning.background = null - tabLightning.setTextColor(getColor(R.color.color_text_tertiary)) - + tabCashu.setBackgroundResource(R.drawable.bg_button_primary_green) + tabCashu.setTextColor(getColor(R.color.color_bg_white)) + + tabLightning.setBackgroundResource(android.R.color.transparent) + tabLightning.setTextColor(getColor(R.color.color_text_secondary)) + lightningOptionsContainer.visibility = View.GONE cashuTokenOptionsContainer.visibility = View.VISIBLE } @@ -365,6 +367,18 @@ class WithdrawLightningActivity : AppCompatActivity() { val balanceAmount = Amount(balance, Amount.Currency.BTC) balanceText.text = balanceAmount.toString() + updateFiatDisplay(balance) + } + + private fun updateFiatDisplay(sats: Long) { + val priceWorker = BitcoinPriceWorker.getInstance(this) + val fiatAmount = priceWorker.satoshisToFiat(sats) + if (fiatAmount > 0) { + fiatBalanceText.text = priceWorker.formatFiatAmount(fiatAmount) + fiatBalanceText.visibility = android.view.View.VISIBLE + } else { + fiatBalanceText.visibility = android.view.View.GONE + } } private fun prefillFields() { @@ -621,7 +635,8 @@ class WithdrawLightningActivity : AppCompatActivity() { balance = newBalance val balanceAmount = Amount(balance, Amount.Currency.BTC) balanceText.text = balanceAmount.toString() - + updateFiatDisplay(balance) + // Update suggested amount in address card val suggestedAmount = (balance * (1 - FEE_BUFFER_PERCENT)).toLong() if (suggestedAmount > 0) { diff --git a/app/src/main/java/com/electricdreams/numo/ui/components/LightningStrikeView.kt b/app/src/main/java/com/electricdreams/numo/ui/components/LightningStrikeView.kt new file mode 100644 index 00000000..fef51aab --- /dev/null +++ b/app/src/main/java/com/electricdreams/numo/ui/components/LightningStrikeView.kt @@ -0,0 +1,169 @@ +package com.electricdreams.numo.ui.components + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.BlurMaskFilter +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import kotlin.math.cos +import kotlin.math.sin +import kotlin.random.Random + +/** + * Full-screen lightning strike overlay animation with realistic branching bolts. + * Add to the root layout, call [strike], and it auto-removes on completion. + */ +class LightningStrikeView(context: Context) : View(context) { + + private var boltAlpha = 0f + private val branches = mutableListOf() + + private data class BoltBranch(val path: Path, val depth: Int) + + private val corePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.parseColor("#F7931A") + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + strokeJoin = Paint.Join.ROUND + } + + private val innerGlowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.parseColor("#FFBB5C") + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + strokeJoin = Paint.Join.ROUND + } + + private val outerGlowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.parseColor("#FFCC80") + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + strokeJoin = Paint.Join.ROUND + } + + init { + setLayerType(LAYER_TYPE_SOFTWARE, null) + } + + fun strike() { + post { generateBoltTree() } + + // Fade in (0–120ms) → hold (120–250ms) → fade out (250–550ms) + ValueAnimator.ofFloat(0f, 1f, 1f, 0f).apply { + duration = 550 + addUpdateListener { + boltAlpha = it.animatedValue as Float + invalidate() + } + start() + } + + postDelayed({ + (parent as? ViewGroup)?.removeView(this) + }, 600) + } + + private fun generateBoltTree() { + branches.clear() + val w = width.toFloat() + val h = height.toFloat() + + // Single main bolt near center + val startX = w * (0.35f + Random.nextFloat() * 0.3f) + generateTrunk(startX, h * -0.02f, h * 1.02f, w, 0) + } + + /** + * Generate a trunk bolt that descends from startY to endY with guaranteed + * vertical coverage — each segment advances downward by a fixed Y step, + * only the X position zig-zags. + */ + private fun generateTrunk(startX: Float, startY: Float, endY: Float, screenW: Float, depth: Int) { + if (depth > 2) return + + val path = Path() + path.moveTo(startX, startY) + + val totalHeight = endY - startY + val segmentCount = when (depth) { + 0 -> Random.nextInt(16, 22) + 1 -> Random.nextInt(8, 14) + else -> Random.nextInt(5, 9) + } + val stepY = totalHeight / segmentCount + val maxJitterX = screenW * when (depth) { + 0 -> 0.08f + 1 -> 0.06f + else -> 0.04f + } + + var x = startX + var y = startY + + for (i in 0 until segmentCount) { + // Guaranteed downward progress with random Y variance + y += stepY * (0.8f + Random.nextFloat() * 0.4f) + + // Sharp zig-zag on X: alternate direction with randomness + val direction = if (i % 2 == 0) 1f else -1f + x += direction * maxJitterX * (0.5f + Random.nextFloat()) + + // Keep bolt on screen + x = x.coerceIn(screenW * 0.05f, screenW * 0.95f) + + path.lineTo(x, y) + + // Spawn sub-branches at sharp angles + if (depth < 2 && i > 0 && i < segmentCount - 1 && Random.nextFloat() < branchChance(depth)) { + val branchHeight = totalHeight * (0.15f + Random.nextFloat() * 0.15f) + val branchStartX = x + (if (Random.nextBoolean()) 1f else -1f) * maxJitterX * 2f + generateTrunk(x, y, y + branchHeight, screenW, depth + 1) + } + } + + branches.add(BoltBranch(path, depth)) + } + + private fun branchChance(depth: Int): Float = when (depth) { + 0 -> 0.20f + 1 -> 0.15f + else -> 0f + } + + override fun onDraw(canvas: Canvas) { + if (boltAlpha <= 0f) return + val alpha = (boltAlpha * 255 * 0.7f).toInt() + + for (branch in branches) { + val d = branch.depth + + // Outer aura + outerGlowPaint.strokeWidth = when (d) { 0 -> 16f; 1 -> 10f; else -> 6f } + outerGlowPaint.maskFilter = BlurMaskFilter( + when (d) { 0 -> 24f; 1 -> 14f; else -> 8f }, + BlurMaskFilter.Blur.NORMAL + ) + outerGlowPaint.alpha = (alpha * 0.25f).toInt() + canvas.drawPath(branch.path, outerGlowPaint) + + // Inner glow + innerGlowPaint.strokeWidth = when (d) { 0 -> 8f; 1 -> 5f; else -> 3f } + innerGlowPaint.maskFilter = BlurMaskFilter( + when (d) { 0 -> 8f; 1 -> 5f; else -> 3f }, + BlurMaskFilter.Blur.NORMAL + ) + innerGlowPaint.alpha = (alpha * 0.5f).toInt() + canvas.drawPath(branch.path, innerGlowPaint) + + // Core bolt + corePaint.strokeWidth = when (d) { 0 -> 3.5f; 1 -> 1.8f; else -> 0.8f } + corePaint.alpha = alpha + canvas.drawPath(branch.path, corePaint) + } + } +} diff --git a/app/src/main/java/com/electricdreams/numo/ui/components/MintSelectionBottomSheet.kt b/app/src/main/java/com/electricdreams/numo/ui/components/MintSelectionBottomSheet.kt index fbad8420..26e3c910 100644 --- a/app/src/main/java/com/electricdreams/numo/ui/components/MintSelectionBottomSheet.kt +++ b/app/src/main/java/com/electricdreams/numo/ui/components/MintSelectionBottomSheet.kt @@ -6,9 +6,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.AccelerateDecelerateInterpolator -import android.view.animation.OvershootInterpolator import android.widget.FrameLayout -import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager @@ -127,12 +125,9 @@ class MintSelectionBottomSheet : BottomSheetDialogFragment() { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val container: View = view.findViewById(R.id.mint_item_container) - val iconContainer: FrameLayout = view.findViewById(R.id.icon_container) - val mintIcon: ImageView = view.findViewById(R.id.mint_icon) val mintName: TextView = view.findViewById(R.id.mint_name) val mintUrl: TextView = view.findViewById(R.id.mint_url) val balanceText: TextView = view.findViewById(R.id.balance_text) - val chevron: ImageView = view.findViewById(R.id.chevron) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -153,11 +148,6 @@ class MintSelectionBottomSheet : BottomSheetDialogFragment() { val amount = Amount(balance, Amount.Currency.BTC) holder.balanceText.text = amount.toString() - // Icon styling - holder.mintIcon.setColorFilter( - ContextCompat.getColor(requireContext(), R.color.color_primary) - ) - // Click handler with ripple feedback holder.container.setOnClickListener { // Scale animation on press @@ -189,17 +179,6 @@ class MintSelectionBottomSheet : BottomSheetDialogFragment() { .setDuration(300) .setInterpolator(AccelerateDecelerateInterpolator()) .start() - - // Icon bounce animation - holder.iconContainer.scaleX = 0f - holder.iconContainer.scaleY = 0f - holder.iconContainer.animate() - .scaleX(1f) - .scaleY(1f) - .setStartDelay((position * 60 + 100).toLong()) - .setDuration(350) - .setInterpolator(OvershootInterpolator(2f)) - .start() } override fun getItemCount() = mintList.size diff --git a/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawAddressCard.kt b/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawAddressCard.kt index 29e1c3cd..c3ee6e74 100644 --- a/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawAddressCard.kt +++ b/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawAddressCard.kt @@ -6,12 +6,9 @@ import android.text.TextWatcher import android.util.AttributeSet import android.view.LayoutInflater import android.view.animation.AccelerateDecelerateInterpolator -import android.view.animation.OvershootInterpolator import android.widget.Button import android.widget.EditText -import android.widget.FrameLayout -import android.widget.ImageView -import android.widget.LinearLayout + import android.widget.TextView import com.electricdreams.numo.R import com.google.android.material.card.MaterialCardView @@ -39,14 +36,9 @@ class WithdrawAddressCard @JvmOverloads constructor( private var listener: OnContinueListener? = null - private val iconContainer: FrameLayout - private val addressIcon: ImageView - private val titleText: TextView - private val subtitleText: TextView private val addressInput: EditText private val amountInput: EditText private val continueButton: Button - private val inputContainer: LinearLayout init { LayoutInflater.from(context).inflate(R.layout.component_withdraw_address_card, this, true) @@ -54,17 +46,12 @@ class WithdrawAddressCard @JvmOverloads constructor( // Setup card styling radius = resources.getDimension(R.dimen.card_corner_radius) cardElevation = 0f - setCardBackgroundColor(context.getColor(R.color.color_bg_card)) + setCardBackgroundColor(android.graphics.Color.TRANSPARENT) // Find views - iconContainer = findViewById(R.id.icon_container) - addressIcon = findViewById(R.id.address_icon) - titleText = findViewById(R.id.title_text) - subtitleText = findViewById(R.id.subtitle_text) addressInput = findViewById(R.id.address_input) amountInput = findViewById(R.id.amount_input) continueButton = findViewById(R.id.continue_button) - inputContainer = findViewById(R.id.input_container) setupListeners() } @@ -117,10 +104,8 @@ class WithdrawAddressCard @JvmOverloads constructor( private fun updateButtonState() { val hasAddress = !addressInput.text.isNullOrBlank() val hasValidAmount = amountInput.text.toString().toLongOrNull()?.let { it > 0 } ?: false - val enabled = hasAddress && hasValidAmount - - continueButton.isEnabled = enabled - continueButton.alpha = if (enabled) 1f else 0.5f + + continueButton.isEnabled = hasAddress && hasValidAmount } /** @@ -191,16 +176,5 @@ class WithdrawAddressCard @JvmOverloads constructor( .setDuration(350) .setInterpolator(AccelerateDecelerateInterpolator()) .start() - - // Icon bounce - iconContainer.scaleX = 0f - iconContainer.scaleY = 0f - iconContainer.animate() - .scaleX(1f) - .scaleY(1f) - .setStartDelay(delay + 150) - .setDuration(400) - .setInterpolator(OvershootInterpolator(2f)) - .start() } } diff --git a/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawInvoiceCard.kt b/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawInvoiceCard.kt index d1b78c9d..423b3b1c 100644 --- a/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawInvoiceCard.kt +++ b/app/src/main/java/com/electricdreams/numo/ui/components/WithdrawInvoiceCard.kt @@ -6,12 +6,9 @@ import android.text.TextWatcher import android.util.AttributeSet import android.view.LayoutInflater import android.view.animation.AccelerateDecelerateInterpolator -import android.view.animation.OvershootInterpolator import android.widget.Button import android.widget.EditText -import android.widget.FrameLayout -import android.widget.ImageView -import android.widget.LinearLayout + import android.widget.TextView import com.electricdreams.numo.R import com.google.android.material.card.MaterialCardView @@ -43,14 +40,9 @@ class WithdrawInvoiceCard @JvmOverloads constructor( private var listener: OnContinueListener? = null private var scanListener: OnScanListener? = null - private val iconContainer: FrameLayout - private val invoiceIcon: ImageView - private val titleText: TextView - private val subtitleText: TextView private val invoiceInput: EditText private val continueButton: Button - private val scanButton: android.widget.ImageButton - private val inputContainer: LinearLayout + private val scanButton: Button init { LayoutInflater.from(context).inflate(R.layout.component_withdraw_invoice_card, this, true) @@ -58,17 +50,12 @@ class WithdrawInvoiceCard @JvmOverloads constructor( // Setup card styling radius = resources.getDimension(R.dimen.card_corner_radius) cardElevation = 0f - setCardBackgroundColor(context.getColor(R.color.color_bg_card)) + setCardBackgroundColor(android.graphics.Color.TRANSPARENT) // Find views - iconContainer = findViewById(R.id.icon_container) - invoiceIcon = findViewById(R.id.invoice_icon) - titleText = findViewById(R.id.title_text) - subtitleText = findViewById(R.id.subtitle_text) invoiceInput = findViewById(R.id.invoice_input) continueButton = findViewById(R.id.continue_button) scanButton = findViewById(R.id.scan_button) - inputContainer = findViewById(R.id.input_container) setupListeners() } @@ -115,7 +102,6 @@ class WithdrawInvoiceCard @JvmOverloads constructor( private fun updateButtonState(enabled: Boolean) { continueButton.isEnabled = enabled - continueButton.alpha = if (enabled) 1f else 0.5f } /** @@ -174,16 +160,5 @@ class WithdrawInvoiceCard @JvmOverloads constructor( .setDuration(350) .setInterpolator(AccelerateDecelerateInterpolator()) .start() - - // Icon bounce - iconContainer.scaleX = 0f - iconContainer.scaleY = 0f - iconContainer.animate() - .scaleX(1f) - .scaleY(1f) - .setStartDelay(delay + 150) - .setDuration(400) - .setInterpolator(OvershootInterpolator(2f)) - .start() } } diff --git a/app/src/main/res/color/color_button_primary_green_text.xml b/app/src/main/res/color/color_button_primary_green_text.xml new file mode 100644 index 00000000..607cb2c6 --- /dev/null +++ b/app/src/main/res/color/color_button_primary_green_text.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable-night/bg_hero_bolt_fade.xml b/app/src/main/res/drawable-night/bg_hero_bolt_fade.xml new file mode 100644 index 00000000..b6e3819b --- /dev/null +++ b/app/src/main/res/drawable-night/bg_hero_bolt_fade.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/drawable-night/bg_hero_bolt_fade_active.xml b/app/src/main/res/drawable-night/bg_hero_bolt_fade_active.xml new file mode 100644 index 00000000..b6e3819b --- /dev/null +++ b/app/src/main/res/drawable-night/bg_hero_bolt_fade_active.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/drawable-night/bg_hero_gradient.xml b/app/src/main/res/drawable-night/bg_hero_gradient.xml new file mode 100644 index 00000000..17829323 --- /dev/null +++ b/app/src/main/res/drawable-night/bg_hero_gradient.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable-night/bg_hero_gradient_active.xml b/app/src/main/res/drawable-night/bg_hero_gradient_active.xml new file mode 100644 index 00000000..a2d5a6ac --- /dev/null +++ b/app/src/main/res/drawable-night/bg_hero_gradient_active.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/bg_button_white.xml b/app/src/main/res/drawable/bg_button_white.xml index 27fd7562..bbf6b3e2 100644 --- a/app/src/main/res/drawable/bg_button_white.xml +++ b/app/src/main/res/drawable/bg_button_white.xml @@ -1,10 +1,10 @@ - + - + diff --git a/app/src/main/res/drawable/bg_hero_bolt_fade.xml b/app/src/main/res/drawable/bg_hero_bolt_fade.xml new file mode 100644 index 00000000..2f9105ea --- /dev/null +++ b/app/src/main/res/drawable/bg_hero_bolt_fade.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/drawable/bg_hero_bolt_fade_active.xml b/app/src/main/res/drawable/bg_hero_bolt_fade_active.xml new file mode 100644 index 00000000..2f9105ea --- /dev/null +++ b/app/src/main/res/drawable/bg_hero_bolt_fade_active.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/drawable/bg_hero_gradient.xml b/app/src/main/res/drawable/bg_hero_gradient.xml index a6d5fb70..4a3326fa 100644 --- a/app/src/main/res/drawable/bg_hero_gradient.xml +++ b/app/src/main/res/drawable/bg_hero_gradient.xml @@ -3,8 +3,8 @@ android:shape="rectangle"> diff --git a/app/src/main/res/drawable/bg_hero_gradient_active.xml b/app/src/main/res/drawable/bg_hero_gradient_active.xml new file mode 100644 index 00000000..7938fc53 --- /dev/null +++ b/app/src/main/res/drawable/bg_hero_gradient_active.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_autoplay.xml b/app/src/main/res/drawable/ic_autoplay.xml new file mode 100644 index 00000000..fac1568e --- /dev/null +++ b/app/src/main/res/drawable/ic_autoplay.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_auto_withdraw_settings.xml b/app/src/main/res/layout/activity_auto_withdraw_settings.xml index 8093be3f..d11452d0 100644 --- a/app/src/main/res/layout/activity_auto_withdraw_settings.xml +++ b/app/src/main/res/layout/activity_auto_withdraw_settings.xml @@ -2,6 +2,7 @@ @@ -14,7 +15,6 @@ android:orientation="horizontal" android:gravity="center_vertical" android:paddingHorizontal="8dp" - android:elevation="1dp" android:background="@color/color_bg_white" app:layout_constraintTop_toTopOf="parent"> @@ -32,9 +32,7 @@ android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/auto_withdraw_settings_title" - android:textSize="20sp" - android:textColor="@color/color_text_primary" - android:fontFamily="sans-serif-medium" + android:textAppearance="@style/Text.Title" android:textAlignment="center" /> @@ -48,7 +46,6 @@ android:layout_width="match_parent" android:layout_height="0dp" android:fillViewport="true" - android:background="@color/color_bg_surface" app:layout_constraintTop_toBottomOf="@id/top_bar" app:layout_constraintBottom_toBottomOf="parent"> @@ -76,46 +73,56 @@ app:cardCornerRadius="24dp" app:cardElevation="0dp"> - + android:clipChildren="false"> - + + android:layout_width="140dp" + android:layout_height="140dp" + android:layout_gravity="bottom|end" + android:layout_marginEnd="-30dp" + android:layout_marginBottom="-30dp"> + app:tint="@color/color_bitcoin_orange" /> + + + + + app:layout_constraintTop_toTopOf="parent" /> + @@ -188,7 +195,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" - app:cardBackgroundColor="@color/color_bg_card" + app:cardBackgroundColor="@android:color/transparent" app:cardCornerRadius="16dp" app:cardElevation="0dp"> @@ -205,18 +212,20 @@ + android:backgroundTint="@color/color_bg_light"> + android:src="@drawable/ic_autoplay" + app:tint="@color/color_text_primary" /> @@ -254,21 +263,27 @@ + + + + + + android:text="@string/auto_withdraw_section_destination" /> + android:text="@string/auto_withdraw_section_trigger" /> @@ -511,27 +519,25 @@ + + + android:text="@string/manual_withdraw_section_title" /> @@ -552,14 +558,14 @@ android:layout_height="44dp" android:layout_marginEnd="16dp" android:background="@drawable/bg_circle" - android:backgroundTint="#FFF3E0"> + android:backgroundTint="@color/color_bg_light"> + android:src="@drawable/ic_arrow_down_receive" + app:tint="@color/color_text_primary" /> @@ -602,6 +608,7 @@ + android:text="@string/auto_withdraw_section_history" /> @@ -642,7 +645,7 @@ android:id="@+id/history_card" android:layout_width="match_parent" android:layout_height="wrap_content" - app:cardBackgroundColor="@color/color_bg_card" + app:cardBackgroundColor="@android:color/transparent" app:cardCornerRadius="16dp" app:cardElevation="0dp"> @@ -656,13 +659,6 @@ android:paddingVertical="48dp" android:visibility="visible"> - - @@ -71,7 +66,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="20dp" android:layout_marginTop="20dp" - app:cardBackgroundColor="@color/color_bg_card" + app:cardBackgroundColor="@android:color/transparent" app:cardCornerRadius="16dp" app:cardElevation="0dp" app:strokeWidth="0dp"> @@ -79,6 +74,7 @@ @@ -104,6 +100,14 @@ android:textSize="40sp" android:textStyle="bold" /> + + + android:textSize="13sp" /> @@ -124,37 +125,39 @@ + android:textSize="18sp" + android:textStyle="bold" + android:textColor="@color/color_bg_white" + android:background="@drawable/bg_button_primary_green" /> + android:textSize="18sp" + android:textStyle="bold" + android:textColor="@color/color_text_secondary" + android:background="@android:color/transparent" /> @@ -167,17 +170,13 @@ + android:text="@string/auto_withdraw_section_destination" /> + android:text="@string/withdraw_tab_cashu" /> @@ -269,63 +263,15 @@ android:orientation="vertical" android:padding="20dp"> - - - - - - - - - - - - - - - - - + + android:textColor="@color/color_text_primary" + android:textSize="15sp" /> + android:enabled="false" /> @@ -367,7 +311,7 @@ android:layout_marginHorizontal="20dp" android:layout_marginTop="20dp" android:layout_marginBottom="24dp" - app:cardBackgroundColor="@color/color_bg_card" + app:cardBackgroundColor="@android:color/transparent" app:cardCornerRadius="16dp" app:cardElevation="0dp" app:strokeWidth="0dp" diff --git a/app/src/main/res/layout/activity_withdraw_melt_quote.xml b/app/src/main/res/layout/activity_withdraw_melt_quote.xml index 4df25a43..53d5d58e 100644 --- a/app/src/main/res/layout/activity_withdraw_melt_quote.xml +++ b/app/src/main/res/layout/activity_withdraw_melt_quote.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/color_bg_surface"> + android:background="@color/color_bg_white"> @@ -78,7 +73,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="20dp" android:layout_marginTop="24dp" - app:cardBackgroundColor="@color/color_bg_card" + app:cardBackgroundColor="@android:color/transparent" app:cardCornerRadius="16dp" app:cardElevation="0dp" app:strokeWidth="0dp"> @@ -90,14 +85,10 @@ android:padding="20dp"> + android:text="@string/withdraw_melt_destination_label" /> @@ -242,7 +233,7 @@ style="@style/Widget.Button.Primary.Green" android:layout_width="match_parent" android:layout_height="52dp" - android:layout_marginHorizontal="20dp" + android:layout_marginHorizontal="40dp" android:layout_marginBottom="24dp" android:text="@string/withdraw_melt_title" android:textAllCaps="false" @@ -387,7 +378,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="20dp" android:layout_marginTop="16dp" - app:cardBackgroundColor="@color/color_bg_card" + app:cardBackgroundColor="@android:color/transparent" app:cardCornerRadius="16dp" app:cardElevation="0dp" app:strokeWidth="0dp"> diff --git a/app/src/main/res/layout/component_withdraw_address_card.xml b/app/src/main/res/layout/component_withdraw_address_card.xml index 0ae7253e..c25e10e2 100644 --- a/app/src/main/res/layout/component_withdraw_address_card.xml +++ b/app/src/main/res/layout/component_withdraw_address_card.xml @@ -6,139 +6,65 @@ android:orientation="vertical" android:padding="20dp"> - - + - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - -