diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d8c9a30..54e7572 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ + diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/receive/ReceiveScreen.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/receive/ReceiveScreen.kt index cdb7ab5..9bffb5a 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/receive/ReceiveScreen.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/receive/ReceiveScreen.kt @@ -58,10 +58,14 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.bitcointxoko.gudariwallet.LocalAppStrings import com.bitcointxoko.gudariwallet.ui.ReceiveState +import com.bitcointxoko.gudariwallet.ui.SendState import com.bitcointxoko.gudariwallet.ui.WalletViewModel import com.bitcointxoko.gudariwallet.ui.components.UnitWheelPicker import com.bitcointxoko.gudariwallet.ui.nfc.NfcViewModel +import com.bitcointxoko.gudariwallet.util.buildUpTaps +import com.bitcointxoko.gudariwallet.util.doubleTap import com.bitcointxoko.gudariwallet.util.fiatLabel +import com.bitcointxoko.gudariwallet.util.heavyClick import kotlinx.coroutines.delay @Composable @@ -107,6 +111,14 @@ fun ReceiveScreen( } } + LaunchedEffect(receiveState) { + when (receiveState) { + is ReceiveState.PaymentReceived -> context.buildUpTaps() + // is SendState.Error -> context.errorBuzz() + else -> Unit + } + } + Box(modifier = Modifier.fillMaxSize()) { if (receiveState !is ReceiveState.InvoiceReady) { Column( diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendScreen.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendScreen.kt index 24a6a8b..8685d93 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendScreen.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/send/SendScreen.kt @@ -31,8 +31,10 @@ import com.bitcointxoko.gudariwallet.ui.WalletViewModel import com.bitcointxoko.gudariwallet.ui.nfc.NfcViewModel import com.bitcointxoko.gudariwallet.ui.receive.AmountDisplay import com.bitcointxoko.gudariwallet.util.SendInputDetector +import com.bitcointxoko.gudariwallet.util.doubleTap import com.bitcointxoko.gudariwallet.util.feePpm import com.bitcointxoko.gudariwallet.util.fiatLabel +import com.bitcointxoko.gudariwallet.util.heavyClick import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -82,6 +84,16 @@ fun SendScreen( } } + LaunchedEffect(sendState) { + when (sendState) { + is SendState.Bolt11Decoded, + is SendState.LnurlInvoiceReady -> context.doubleTap() + is SendState.PaymentSent -> context.heavyClick() + // is SendState.Error -> context.errorBuzz() + else -> Unit + } + } + Column( modifier = Modifier .fillMaxSize() diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/util/Haptics.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/util/Haptics.kt new file mode 100644 index 0000000..eccffb8 --- /dev/null +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/util/Haptics.kt @@ -0,0 +1,54 @@ +package com.bitcointxoko.gudariwallet.util + +import android.content.Context +import android.os.* + +fun Context.heavyClick() { + val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + (getSystemService(VibratorManager::class.java)).defaultVibrator + } else { + @Suppress("DEPRECATION") + getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK)) + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vibrator.vibrate(VibrationEffect.createOneShot(50L, VibrationEffect.DEFAULT_AMPLITUDE)) + } +} + +fun Context.doubleTap() { + val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + (getSystemService(VibratorManager::class.java)).defaultVibrator + } else { + @Suppress("DEPRECATION") + getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK)) + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vibrator.vibrate(VibrationEffect.createOneShot(50L, VibrationEffect.DEFAULT_AMPLITUDE)) + } +} + +fun Context.buildUpTaps() { + val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + (getSystemService(VibratorManager::class.java)).defaultVibrator + } else { + @Suppress("DEPRECATION") + getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Timings: off, on, off, on, off, on, off, FINAL ON + val timings = longArrayOf(0, 30, 80, 30, 50, 30, 20, 60) + val amplitudes = intArrayOf(0, 60, 0, 90, 0, 140, 0, 255) + // ^gap ^tap ^gap ^tap ^gap ^tap ^gap ^THUD + + vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, -1)) + } else { + // Legacy fallback — simple triple-tap + @Suppress("DEPRECATION") + vibrator.vibrate(longArrayOf(0, 40, 60, 40, 40, 80), -1) + } +} \ No newline at end of file