diff --git a/app/src/main/java/to/bitkit/androidServices/LightningNodeService.kt b/app/src/main/java/to/bitkit/androidServices/LightningNodeService.kt index ad77c1f50..2bfe8257f 100644 --- a/app/src/main/java/to/bitkit/androidServices/LightningNodeService.kt +++ b/app/src/main/java/to/bitkit/androidServices/LightningNodeService.kt @@ -100,7 +100,7 @@ class LightningNodeService : Service() { } private fun createNotification( - contentText: String = getString(R.string.notification_running_in_background), + contentText: String = getString(R.string.notification__service__body), ): Notification { val notificationIntent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP @@ -120,7 +120,7 @@ class LightningNodeService : Service() { .setContentIntent(pendingIntent) .addAction( R.drawable.ic_x, - getString(R.string.notification_stop_app), + getString(R.string.notification__service__stop), stopPendingIntent ) .build() diff --git a/app/src/main/java/to/bitkit/async/ServiceQueue.kt b/app/src/main/java/to/bitkit/async/ServiceQueue.kt index 5d3cf2e55..759957601 100644 --- a/app/src/main/java/to/bitkit/async/ServiceQueue.kt +++ b/app/src/main/java/to/bitkit/async/ServiceQueue.kt @@ -26,7 +26,7 @@ enum class ServiceQueue { ): T { return runBlocking(coroutineContext) { try { - measured(functionName) { + measured(label = functionName, context = TAG) { block() } } catch (e: Exception) { @@ -43,7 +43,7 @@ enum class ServiceQueue { ): T { return withContext(coroutineContext) { try { - measured(functionName) { + measured(label = functionName, context = TAG) { block() } } catch (e: Exception) { @@ -52,6 +52,10 @@ enum class ServiceQueue { } } } + + companion object { + private const val TAG = "ServiceQueue" + } } fun newSingleThreadDispatcher(id: String): ExecutorCoroutineDispatcher { diff --git a/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt b/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt index d3058a9dd..fb17d1adf 100644 --- a/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt +++ b/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt @@ -96,11 +96,11 @@ class NotifyPaymentReceivedHandler @Inject constructor( private suspend fun buildNotificationContent(sats: Long): NotificationDetails { val settings = settingsStore.data.first() - val title = context.getString(R.string.notification_received_title) + val title = context.getString(R.string.notification__received__title) val body = if (settings.showNotificationDetails) { formatNotificationAmount(sats, settings) } else { - context.getString(R.string.notification_received_body_hidden) + context.getString(R.string.notification__received__body_hidden) } return NotificationDetails(title, body) } @@ -117,7 +117,7 @@ class NotifyPaymentReceivedHandler @Inject constructor( } } ?: "$BITCOIN_SYMBOL ${sats.formatToModernDisplay()}" - return context.getString(R.string.notification_received_body_amount, amountText) + return context.getString(R.string.notification__received__body_amount, amountText) } companion object { diff --git a/app/src/main/java/to/bitkit/ext/DateTime.kt b/app/src/main/java/to/bitkit/ext/DateTime.kt index bea254c2b..0b877161d 100644 --- a/app/src/main/java/to/bitkit/ext/DateTime.kt +++ b/app/src/main/java/to/bitkit/ext/DateTime.kt @@ -60,11 +60,11 @@ fun Long.toDateUTC(): String { return dateTime.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) } -fun Long.toLocalizedTimestamp(): String { - val uLocale = ULocale.forLocale(Locale.US) - val formatter = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT, uLocale) - ?: return SimpleDateFormat("MMMM d, yyyy 'at' h:mm a", Locale.US).format(Date(this)) - return formatter.format(Date(this)) +fun Long.toLocalizedTimestamp(locale: Locale = Locale.US): String { + val date = Date(this) + val formatter = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT, ULocale.forLocale(locale)) + ?: return SimpleDateFormat("MMMM d, yyyy 'at' h:mm a", locale).format(date) + return formatter.format(date) } @Suppress("LongMethod") @@ -72,6 +72,8 @@ fun Long.toLocalizedTimestamp(): String { fun Long.toRelativeTimeString( locale: Locale = Locale.getDefault(), clock: Clock = Clock.System, + style: RelativeDateTimeFormatter.Style = RelativeDateTimeFormatter.Style.LONG, + capitalizationContext: DisplayContext = DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, ): String { val now = nowMillis(clock) val diffMillis = now - this @@ -82,9 +84,9 @@ fun Long.toRelativeTimeString( val formatter = RelativeDateTimeFormatter.getInstance( uLocale, numberFormat, - RelativeDateTimeFormatter.Style.LONG, - DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, - ) ?: return toLocalizedTimestamp() + style, + capitalizationContext, + ) ?: return toLocalizedTimestamp(locale) val seconds = diffMillis / Factor.MILLIS_TO_SECONDS val minutes = seconds / Factor.SECONDS_TO_MINUTES diff --git a/app/src/main/java/to/bitkit/fcm/WakeNodeWorker.kt b/app/src/main/java/to/bitkit/fcm/WakeNodeWorker.kt index 77aedd670..a3a12243b 100644 --- a/app/src/main/java/to/bitkit/fcm/WakeNodeWorker.kt +++ b/app/src/main/java/to/bitkit/fcm/WakeNodeWorker.kt @@ -76,8 +76,8 @@ class WakeNodeWorker @AssistedInject constructor( Logger.warn("Notification type is null, proceeding with node wake", context = TAG) } - try { - measured(TAG) { + return runCatching { + measured(label = "doWork", context = TAG) { lightningRepo.start( walletIndex = 0, timeout = timeout, @@ -92,31 +92,35 @@ class WakeNodeWorker @AssistedInject constructor( Logger.error("Missing orderId", context = TAG) } else { Logger.info("Open channel request for order $orderId", context = TAG) - blocktankRepo.openChannel(orderId).onFailure { e -> - Logger.error("Failed to open channel", e, context = TAG) + blocktankRepo.openChannel(orderId).onFailure { + Logger.error("Failed to open channel", it, context = TAG) bestAttemptContent = NotificationDetails( - title = appContext.getString(R.string.notification_channel_open_failed_title), - body = e.message ?: appContext.getString(R.string.notification_unknown_error), + title = appContext.getString(R.string.notification__channel_open_failed_title), + body = it.message ?: appContext.getString(R.string.common__error_body), ) deliver() } } } } - withTimeout(timeout) { deliverSignal.await() } // Stops node on timeout & avoids notification replay by OS - return Result.success() - } catch (e: Exception) { - val reason = e.message ?: appContext.getString(R.string.notification_unknown_error) + // Stops node on timeout & avoids notification replay by OS + withTimeout(timeout) { deliverSignal.await() } + } + .fold( + onSuccess = { Result.success() }, + onFailure = { e -> + val reason = e.message ?: appContext.getString(R.string.common__error_body) - bestAttemptContent = NotificationDetails( - title = appContext.getString(R.string.notification_lightning_error_title), - body = reason, - ) - Logger.error("Lightning error", e, context = TAG) - deliver() + bestAttemptContent = NotificationDetails( + title = appContext.getString(R.string.notification__lightning_error_title), + body = reason, + ) + Logger.error("Lightning error", e, context = TAG) + deliver() - return Result.failure(workDataOf("Reason" to reason)) - } + Result.failure(workDataOf("Reason" to reason)) + } + ) } /** @@ -125,14 +129,14 @@ class WakeNodeWorker @AssistedInject constructor( */ private suspend fun handleLdkEvent(event: Event) { val showDetails = settingsStore.data.first().showNotificationDetails - val hiddenBody = appContext.getString(R.string.notification_received_body_hidden) + val hiddenBody = appContext.getString(R.string.notification__received__body_hidden) when (event) { is Event.PaymentReceived -> onPaymentReceived(event, showDetails, hiddenBody) is Event.ChannelPending -> { bestAttemptContent = NotificationDetails( - title = appContext.getString(R.string.notification_channel_opened_title), - body = appContext.getString(R.string.notification_channel_pending_body), + title = appContext.getString(R.string.notification__channel_opened_title), + body = appContext.getString(R.string.notification__channel_pending_body), ) // Don't deliver, give a chance for channelReady event to update the content if it's a turbo channel } @@ -142,7 +146,7 @@ class WakeNodeWorker @AssistedInject constructor( is Event.PaymentFailed -> { bestAttemptContent = NotificationDetails( - title = appContext.getString(R.string.notification_payment_failed_title), + title = appContext.getString(R.string.notification__payment_failed_title), body = "⚡ ${event.reason}", ) @@ -158,18 +162,18 @@ class WakeNodeWorker @AssistedInject constructor( private suspend fun onChannelClosed(event: Event.ChannelClosed) { bestAttemptContent = when (notificationType) { mutualClose -> NotificationDetails( - title = appContext.getString(R.string.notification_channel_closed_title), - body = appContext.getString(R.string.notification_channel_closed_mutual_body), + title = appContext.getString(R.string.notification__channel_closed__title), + body = appContext.getString(R.string.notification__channel_closed__mutual_body), ) orderPaymentConfirmed -> NotificationDetails( - title = appContext.getString(R.string.notification_channel_open_bg_failed_title), - body = appContext.getString(R.string.notification_please_try_again_body), + title = appContext.getString(R.string.notification__channel_open_bg_failed_title), + body = appContext.getString(R.string.notification__please_try_again_body), ) else -> NotificationDetails( - title = appContext.getString(R.string.notification_channel_closed_title), - body = appContext.getString(R.string.notification_channel_closed_reason_body, event.reason), + title = appContext.getString(R.string.notification__channel_closed__title), + body = appContext.getString(R.string.notification__channel_closed__reason_body, event.reason), ) } @@ -193,7 +197,7 @@ class WakeNodeWorker @AssistedInject constructor( ) val content = if (showDetails) "$BITCOIN_SYMBOL $sats" else hiddenBody bestAttemptContent = NotificationDetails( - title = appContext.getString(R.string.notification_received_title), + title = appContext.getString(R.string.notification__received__title), body = content, ) if (notificationType == incomingHtlc) { @@ -206,10 +210,10 @@ class WakeNodeWorker @AssistedInject constructor( showDetails: Boolean, hiddenBody: String, ) { - val viaNewChannel = appContext.getString(R.string.notification_via_new_channel_body) + val viaNewChannel = appContext.getString(R.string.notification__received__body_channel) if (notificationType == cjitPaymentArrived) { bestAttemptContent = NotificationDetails( - title = appContext.getString(R.string.notification_received_title), + title = appContext.getString(R.string.notification__received__title), body = viaNewChannel, ) @@ -235,8 +239,8 @@ class WakeNodeWorker @AssistedInject constructor( } } else if (notificationType == orderPaymentConfirmed) { bestAttemptContent = NotificationDetails( - title = appContext.getString(R.string.notification_channel_opened_title), - body = appContext.getString(R.string.notification_channel_ready_body), + title = appContext.getString(R.string.notification__channel_opened_title), + body = appContext.getString(R.string.notification__channel_ready_body), ) } deliver() diff --git a/app/src/main/java/to/bitkit/models/ActivityBannerType.kt b/app/src/main/java/to/bitkit/models/ActivityBannerType.kt index d13d6df9c..cd366c294 100644 --- a/app/src/main/java/to/bitkit/models/ActivityBannerType.kt +++ b/app/src/main/java/to/bitkit/models/ActivityBannerType.kt @@ -14,11 +14,11 @@ enum class ActivityBannerType( SPENDING( color = Colors.Purple, icon = R.drawable.ic_transfer, - title = R.string.activity_banner__transfer_in_progress + title = R.string.lightning__transfer_in_progress ), SAVINGS( color = Colors.Brand, icon = R.drawable.ic_transfer, - title = R.string.activity_banner__transfer_in_progress + title = R.string.lightning__transfer_in_progress ) } diff --git a/app/src/main/java/to/bitkit/models/NodeLifecycleState.kt b/app/src/main/java/to/bitkit/models/NodeLifecycleState.kt index ab8cf7694..1c5e92a16 100644 --- a/app/src/main/java/to/bitkit/models/NodeLifecycleState.kt +++ b/app/src/main/java/to/bitkit/models/NodeLifecycleState.kt @@ -1,5 +1,8 @@ package to.bitkit.models +import android.content.Context +import to.bitkit.R + sealed class NodeLifecycleState { data object Stopped : NodeLifecycleState() data object Starting : NodeLifecycleState() @@ -14,16 +17,14 @@ sealed class NodeLifecycleState { fun isRunning() = this is Running fun canRun() = this.isRunningOrStarting() || this is Initializing - // TODO add missing localized texts - val uiText: String - get() = when (this) { - is Stopped -> "Stopped" - is Starting -> "Starting" - is Running -> "Running" - is Stopping -> "Stopping" - is ErrorStarting -> "Error starting: ${cause.message}" - is Initializing -> "Setting up wallet..." - } + fun uiText(context: Context): String = when (this) { + is Stopped -> context.getString(R.string.other__node_stopped) + is Starting -> context.getString(R.string.other__node_starting) + is Running -> context.getString(R.string.other__node_running) + is Stopping -> context.getString(R.string.other__node_stopping) + is ErrorStarting -> context.getString(R.string.other__node_error_starting, cause.message ?: "") + is Initializing -> context.getString(R.string.other__node_initializing) + } fun asHealth() = when (this) { Running -> HealthState.READY diff --git a/app/src/main/java/to/bitkit/models/widget/ArticleModel.kt b/app/src/main/java/to/bitkit/models/widget/ArticleModel.kt index 16dd06cc1..4db972e6a 100644 --- a/app/src/main/java/to/bitkit/models/widget/ArticleModel.kt +++ b/app/src/main/java/to/bitkit/models/widget/ArticleModel.kt @@ -2,71 +2,53 @@ package to.bitkit.models.widget import kotlinx.serialization.Serializable import to.bitkit.data.dto.ArticleDTO +import to.bitkit.ext.toRelativeTimeString import to.bitkit.utils.Logger import java.time.OffsetDateTime import java.time.format.DateTimeFormatter import java.time.format.DateTimeParseException -import java.time.temporal.ChronoUnit import java.util.Locale +import kotlin.time.ExperimentalTime @Serializable data class ArticleModel( val title: String, val timeAgo: String, val link: String, - val publisher: String + val publisher: String, ) fun ArticleDTO.toArticleModel() = ArticleModel( title = this.title, timeAgo = timeAgo(this.publishedDate), link = this.link, - publisher = this.publisher.title + publisher = this.publisher.title, ) -/** - * Converts a date string to a human-readable time ago format - * @param dateString Date string in format "EEE, dd MMM yyyy HH:mm:ss Z" - * @return Human-readable time difference (e.g. "5 hours ago") - */ +private const val TAG = "ArticleModel" + +@OptIn(ExperimentalTime::class) private fun timeAgo(dateString: String): String { - return try { + return runCatching { val formatters = listOf( - DateTimeFormatter.RFC_1123_DATE_TIME, // Handles "EEE, dd MMM yyyy HH:mm:ss zzz" (like GMT) - DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH) // Handles "+0000" + DateTimeFormatter.RFC_1123_DATE_TIME, + DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH) ) var parsedDateTime: OffsetDateTime? = null for (formatter in formatters) { try { parsedDateTime = OffsetDateTime.parse(dateString, formatter) - break // Successfully parsed, stop trying other formatters - } catch (e: DateTimeParseException) { - // Continue to the next formatter if this one fails + break + } catch (_: DateTimeParseException) { + // Continue to the next formatter } } - if (parsedDateTime == null) { - Logger.debug("Failed to parse date: Unparseable date: $dateString") - return "" - } - - val now = OffsetDateTime.now() + requireNotNull(parsedDateTime) { "Unparseable date: '$dateString'" } - val diffMinutes = ChronoUnit.MINUTES.between(parsedDateTime, now) - val diffHours = ChronoUnit.HOURS.between(parsedDateTime, now) - val diffDays = ChronoUnit.DAYS.between(parsedDateTime, now) - val diffMonths = ChronoUnit.MONTHS.between(parsedDateTime, now) - - return when { - diffMinutes < 1 -> "just now" - diffMinutes < 60 -> "$diffMinutes minutes ago" - diffHours < 24 -> "$diffHours hours ago" - diffDays < 30 -> "$diffDays days ago" // Approximate for months - else -> "$diffMonths months ago" - } - } catch (e: Exception) { - Logger.warn("An unexpected error occurred while parsing date: ${e.message}") - "" - } + parsedDateTime.toInstant().toEpochMilli().toRelativeTimeString() + }.onFailure { + Logger.warn("Failed to parse date: ${it.message}", it, context = TAG) + }.getOrDefault("") } diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index b361e67e9..2873d7696 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -174,7 +174,7 @@ class WalletRepo @Inject constructor( val startHeight = lightningRepo.lightningState.value.block()?.height Logger.debug("Sync $sourceLabel started at block height=$startHeight", context = TAG) - val result = measured("Sync $sourceLabel") { + val result = measured(label = "Sync $sourceLabel", context = TAG) { syncBalances() lightningRepo.sync().onSuccess { syncBalances() diff --git a/app/src/main/java/to/bitkit/services/RNBackupClient.kt b/app/src/main/java/to/bitkit/services/RNBackupClient.kt index 61160c3ab..8e2a8462f 100644 --- a/app/src/main/java/to/bitkit/services/RNBackupClient.kt +++ b/app/src/main/java/to/bitkit/services/RNBackupClient.kt @@ -27,7 +27,6 @@ import to.bitkit.utils.AppError import to.bitkit.utils.Crypto import to.bitkit.utils.Logger import javax.crypto.Cipher -import javax.crypto.Mac import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.SecretKeySpec import javax.inject.Inject @@ -48,8 +47,6 @@ class RNBackupClient @Inject constructor( private const val SIGNED_MESSAGE_PREFIX = "react-native-ldk backup server auth:" private const val GCM_IV_LENGTH = 12 private const val GCM_TAG_LENGTH = 16 - private const val PBKDF2_ITERATIONS = 2048 - private const val PBKDF2_KEY_LENGTH_BITS = 512 } @Volatile diff --git a/app/src/main/java/to/bitkit/ui/MainActivity.kt b/app/src/main/java/to/bitkit/ui/MainActivity.kt index 77723f287..f9fea9f7b 100644 --- a/app/src/main/java/to/bitkit/ui/MainActivity.kt +++ b/app/src/main/java/to/bitkit/ui/MainActivity.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.serialization.Serializable +import to.bitkit.R import to.bitkit.androidServices.LightningNodeService import to.bitkit.androidServices.LightningNodeService.Companion.CHANNEL_ID_NODE import to.bitkit.models.NewTransactionSheetDetails @@ -76,10 +77,9 @@ class MainActivity : FragmentActivity() { initNotificationChannel() initNotificationChannel( - // TODO Transifex id = CHANNEL_ID_NODE, - name = "Lightning node notification", - desc = "Channel for LightningNodeService", + name = getString(R.string.notification__channel_node__name), + desc = getString(R.string.notification__channel_node__body), importance = NotificationManager.IMPORTANCE_LOW ) appViewModel.handleDeeplinkIntent(intent) diff --git a/app/src/main/java/to/bitkit/ui/NodeInfoScreen.kt b/app/src/main/java/to/bitkit/ui/NodeInfoScreen.kt index 320165885..795426274 100644 --- a/app/src/main/java/to/bitkit/ui/NodeInfoScreen.kt +++ b/app/src/main/java/to/bitkit/ui/NodeInfoScreen.kt @@ -191,16 +191,17 @@ private fun NodeStateSection( nodeLifecycleState: NodeLifecycleState, nodeStatus: NodeStatus?, ) { + val context = LocalContext.current Column(modifier = Modifier.fillMaxWidth()) { SectionHeader("Node State") SettingsTextButtonRow( title = stringResource(R.string.lightning__status), - value = nodeLifecycleState.uiText, + value = nodeLifecycleState.uiText(context), ) nodeStatus?.let { status -> SettingsTextButtonRow( - title = stringResource(R.string.common_ready), + title = stringResource(R.string.common__ready), value = if (status.isRunning) "✅" else "⏳", ) SettingsTextButtonRow( @@ -344,7 +345,7 @@ private fun ChannelsSection( VerticalSpacer(8.dp) ChannelDetailRow( - title = stringResource(R.string.common_ready), + title = stringResource(R.string.common__ready), value = if (channel.isChannelReady) "✅" else "❌", ) ChannelDetailRow( @@ -376,7 +377,7 @@ private fun ChannelsSection( value = "₿ ${(channel.nextOutboundHtlcMinimumMsat / 1000u).formatToModernDisplay()}", ) ChannelDetailRow( - title = stringResource(R.string.common_confirmations), + title = stringResource(R.string.common__confirmations), value = "${channel.confirmations ?: 0}/${channel.confirmationsRequired ?: 0}", ) diff --git a/app/src/main/java/to/bitkit/ui/components/ActivityBanner.kt b/app/src/main/java/to/bitkit/ui/components/ActivityBanner.kt index 00d35fbee..5d03ad156 100644 --- a/app/src/main/java/to/bitkit/ui/components/ActivityBanner.kt +++ b/app/src/main/java/to/bitkit/ui/components/ActivityBanner.kt @@ -183,7 +183,7 @@ private fun Preview() { items(items = ActivityBannerType.entries) { item -> ActivityBanner( gradientColor = item.color, - title = stringResource(R.string.activity_banner__transfer_in_progress), + title = stringResource(R.string.lightning__transfer_in_progress), icon = item.icon, onClick = {}, modifier = Modifier.fillMaxWidth() diff --git a/app/src/main/java/to/bitkit/ui/components/NotificationPreview.kt b/app/src/main/java/to/bitkit/ui/components/NotificationPreview.kt index f36ac8bce..2169aa50b 100644 --- a/app/src/main/java/to/bitkit/ui/components/NotificationPreview.kt +++ b/app/src/main/java/to/bitkit/ui/components/NotificationPreview.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import to.bitkit.R @@ -50,7 +51,10 @@ fun NotificationPreview( verticalArrangement = Arrangement.SpaceBetween ) { BodySSB(text = title, color = Colors.Black) - val textDescription = if (showDetails) description else "Open Bitkit to see details" // TODO Transifex + val textDescription = when (showDetails) { + true -> description + else -> stringResource(R.string.notification__received__body_hidden) + } AnimatedContent(targetState = textDescription) { text -> Footnote(text = text, color = Colors.Gray3) } diff --git a/app/src/main/java/to/bitkit/ui/screens/profile/CreateProfileScreen.kt b/app/src/main/java/to/bitkit/ui/screens/profile/CreateProfileScreen.kt index 0c5071c03..8d8e11c83 100644 --- a/app/src/main/java/to/bitkit/ui/screens/profile/CreateProfileScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/profile/CreateProfileScreen.kt @@ -33,7 +33,7 @@ fun CreateProfileScreen( Spacer(Modifier.weight(1f)) Display( - text = "Comming soon", + text = stringResource(R.string.other__coming_soon), color = Colors.White ) Spacer(Modifier.weight(1f)) diff --git a/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryMnemonicViewModel.kt b/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryMnemonicViewModel.kt index d32aba3c1..fd53d23a9 100644 --- a/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryMnemonicViewModel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryMnemonicViewModel.kt @@ -1,13 +1,16 @@ package to.bitkit.ui.screens.recovery +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import to.bitkit.R import to.bitkit.data.keychain.Keychain import to.bitkit.models.Toast import to.bitkit.ui.shared.toast.ToastEventBus @@ -16,6 +19,7 @@ import javax.inject.Inject @HiltViewModel class RecoveryMnemonicViewModel @Inject constructor( + @ApplicationContext private val context: Context, private val keychain: Keychain, ) : ViewModel() { @@ -40,8 +44,8 @@ class RecoveryMnemonicViewModel @Inject constructor( } ToastEventBus.send( type = Toast.ToastType.ERROR, - title = "Failed to load mnemonic", - description = "Failed to load mnemonic", + title = context.getString(R.string.security__mnemonic_load_error), + description = context.getString(R.string.security__mnemonic_load_error), ) return@launch } diff --git a/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryViewModel.kt b/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryViewModel.kt index 1b72b16d4..c0f19f378 100644 --- a/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryViewModel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/recovery/RecoveryViewModel.kt @@ -75,8 +75,8 @@ class RecoveryViewModel @Inject constructor( } ToastEventBus.send( type = Toast.ToastType.ERROR, - title = "Error", - description = "Failed to create log zip file", + title = context.getString(R.string.common__error), + description = context.getString(R.string.other__logs_export_error), ) } ) @@ -100,8 +100,8 @@ class RecoveryViewModel @Inject constructor( viewModelScope.launch { ToastEventBus.send( type = Toast.ToastType.ERROR, - title = "Error", - description = "Failed to open support links", + title = context.getString(R.string.common__error), + description = context.getString(R.string.settings__support__link_error), ) } } diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/SpendingConfirmScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/SpendingConfirmScreen.kt index dd2c4cc60..7a5912fea 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/SpendingConfirmScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/SpendingConfirmScreen.kt @@ -99,14 +99,12 @@ fun SpendingConfirmScreen( onLearnMoreClick = onLearnMoreClick, onAdvancedClick = onAdvancedClick, onConfirm = onConfirm, - onUseDefaultLspBalanceClick = { viewModel.onUseDefaultLspBalanceClick() }, - onTransferToSpendingConfirm = { order -> viewModel.onTransferToSpendingConfirm(order) }, + onUseDefaultLspBalanceClick = viewModel::onUseDefaultLspBalanceClick, + onTransferToSpendingConfirm = viewModel::onTransferToSpendingConfirm, order = order, hasNotificationPermission = notificationsGranted, - onSwitchClick = { - NotificationUtils.openNotificationSettings(context) - }, - isAdvanced = isAdvanced + onSwitchClick = { NotificationUtils.openNotificationSettings(context) }, + isAdvanced = isAdvanced, ) } @@ -140,7 +138,7 @@ private fun Content( modifier = Modifier .fillMaxWidth() .padding(horizontal = 60.dp) - .align(alignment = Alignment.BottomCenter) + .align(Alignment.BottomCenter) .padding(bottom = 76.dp) ) } @@ -158,10 +156,7 @@ private fun Content( val lspBalance = order.lspBalanceSat VerticalSpacer(32.dp) - Display( - text = stringResource(R.string.lightning__transfer__confirm) - .withAccent(accentColor = Colors.Purple) - ) + Display(stringResource(R.string.lightning__transfer__confirm).withAccent(accentColor = Colors.Purple)) VerticalSpacer(8.dp) Row( @@ -206,7 +201,7 @@ private fun Content( } SettingsSwitchRow( - title = "Set up in background", + title = stringResource(R.string.settings__bg__setup), isChecked = hasNotificationPermission, colors = AppSwitchDefaults.colorsPurple, onClick = onSwitchClick, diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConfirmScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConfirmScreen.kt index fbaae5a19..9fd0426b5 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConfirmScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalConfirmScreen.kt @@ -66,7 +66,7 @@ fun ExternalConfirmScreen( Content( uiState = uiState, - onConfirm = { viewModel.onConfirm() }, + onConfirm = viewModel::onConfirm, onNetworkFeeClick = onNetworkFeeClick, onBackClick = onBackClick, ) @@ -96,9 +96,7 @@ private fun Content( val totalFee = uiState.amount.sats + networkFee Spacer(modifier = Modifier.height(16.dp)) - Display( - text = stringResource(R.string.lightning__transfer__confirm).withAccent(accentColor = Colors.Purple) - ) + Display(stringResource(R.string.lightning__transfer__confirm).withAccent(accentColor = Colors.Purple)) Spacer(modifier = Modifier.height(8.dp)) Row( diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/SavingsWalletScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/SavingsWalletScreen.kt index d071dfe6d..6f02b7dd2 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/SavingsWalletScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/SavingsWalletScreen.kt @@ -112,7 +112,7 @@ fun SavingsWalletScreen( if (canTransfer) { SecondaryButton( onClick = onTransferToSpendingClick, - text = "Transfer To Spending", // TODO add missing localized text + text = stringResource(R.string.wallet__transfer_to_spending), icon = { Icon( painter = painterResource(R.drawable.ic_transfer), diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/SpendingWalletScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/SpendingWalletScreen.kt index 82460dca1..71be8ee9f 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/SpendingWalletScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/SpendingWalletScreen.kt @@ -114,7 +114,7 @@ fun SpendingWalletScreen( if (canTransfer) { SecondaryButton( onClick = onTransferToSavingsClick, - text = "Transfer To Savings", // TODO add missing localized text + text = stringResource(R.string.wallet__transfer_to_savings), icon = { Icon( painter = painterResource(R.drawable.ic_transfer), diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt index 0e2a543f7..63886cd13 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt @@ -436,7 +436,7 @@ private fun ActivityDetailContent( text = when { isTransferToSpending -> stringResource(R.string.wallet__activity_transfer_to_spending) isTransferFromSpending -> stringResource(R.string.wallet__activity_transfer_to_savings) - isSelfSend -> "Sent to myself" // TODO add missing localized text + isSelfSend -> stringResource(R.string.wallet__activity_sent_self) else -> stringResource(R.string.wallet__activity_payment) }, color = Colors.White64, @@ -740,7 +740,6 @@ private fun StatusSection( if (item.v1.isTransfer) { val duration = FeeRate.getFeeDescription(item.v1.feeRate, feeRates) - .removeEstimationSymbol() statusText = stringResource(R.string.wallet__activity_transfer_pending) .replace("{duration}", duration) statusTestTag = "StatusTransfer" @@ -953,6 +952,3 @@ private fun isBoostCompleted( return activity.boostTxIds.any { boostTxDoesExist[it] == true } } } - -// TODO remove this method after transifex update -private fun String.removeEstimationSymbol() = this.replace("±", "") diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/DateRangeSelectorSheet.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/DateRangeSelectorSheet.kt index 6c47947fa..07580448a 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/DateRangeSelectorSheet.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/DateRangeSelectorSheet.kt @@ -255,7 +255,7 @@ private fun Content( ) { Icon( imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft, - contentDescription = "Previous month", + contentDescription = stringResource(R.string.wallet__activity_previous_month), tint = Colors.Brand ) } @@ -268,7 +268,7 @@ private fun Content( ) { Icon( imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, - contentDescription = "Next month", + contentDescription = stringResource(R.string.wallet__activity_next_month), tint = Colors.Brand ) } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt index b7c9d7638..81962392a 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt @@ -130,7 +130,7 @@ fun ActivityRow( } else { val duration = FeeRate.getFeeDescription(item.v1.feeRate, feeRates) stringResource(R.string.wallet__activity_transfer_spending_pending) - .replace("{duration}", duration.removeEstimationSymbol()) + .replace("{duration}", duration) } isTransfer && !isSent -> if (item.v1.confirmed) { @@ -138,17 +138,15 @@ fun ActivityRow( } else { val duration = FeeRate.getFeeDescription(item.v1.feeRate, feeRates) stringResource(R.string.wallet__activity_transfer_savings_pending) - .replace("{duration}", duration.removeEstimationSymbol()) + .replace("{duration}", duration) } confirmed == true -> formattedTime(timestamp) else -> { val feeDescription = FeeRate.getFeeDescription(item.v1.feeRate, feeRates) - stringResource(R.string.wallet__activity_confirms_in).replace( - "{feeRateDescription}", - feeDescription - ) + stringResource(R.string.wallet__activity_confirms_in) + .replace("{feeRateDescription}", feeDescription) } } } @@ -323,9 +321,6 @@ private fun formattedTime(timestamp: ULong): String { } } -// TODO remove this method after transifex update -private fun String.removeEstimationSymbol() = this.replace("±", "") - private class ActivityItemsPreviewProvider : PreviewParameterProvider { override val values: Sequence get() = previewActivityItems.asSequence() } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt index 611224d24..8b3465186 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt @@ -171,7 +171,7 @@ private fun Content( FillHeight() SettingsSwitchRow( - title = "Set up in background", + title = stringResource(R.string.settings__bg__setup), isChecked = hasNotificationPermission, colors = AppSwitchDefaults.colorsPurple, onClick = onSystemSettingsClick, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveLiquidityScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveLiquidityScreen.kt index 5f3b9c801..4cfc6b879 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveLiquidityScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveLiquidityScreen.kt @@ -131,14 +131,14 @@ private fun Content( FillHeight() BodyM( - text = "Enable background setup to safely exit Bitkit while your balance is being configured.", + text = stringResource(R.string.wallet__receive_liquidity__bg_setup_desc), color = Colors.White64 ) VerticalSpacer(15.dp) SettingsSwitchRow( - title = "Set up in background", + title = stringResource(R.string.wallet__receive_liquidity__bg_setup_switch), isChecked = hasNotificationPermission, colors = AppSwitchDefaults.colorsPurple, onClick = onSwitchClick, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt index 640e47aa8..ea965d6a6 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendRecipientScreen.kt @@ -77,11 +77,14 @@ import to.bitkit.ui.theme.Colors import to.bitkit.ui.theme.Shapes import to.bitkit.ui.theme.TRANSITION_SCREEN_MS import to.bitkit.ui.utils.withAccent +import to.bitkit.utils.AppError import to.bitkit.utils.Logger import to.bitkit.viewmodels.SendEvent import java.util.concurrent.Executors import androidx.camera.core.Preview as CameraPreview +private const val TAG = "SendRecipientScreen" + @OptIn(ExperimentalPermissionsApi::class) @Composable fun SendRecipientScreen( @@ -98,7 +101,11 @@ fun SendRecipientScreen( // Camera state var isFlashlightOn by remember { mutableStateOf(false) } var isCameraInitialized by remember { mutableStateOf(false) } - val previewView = remember { PreviewView(context) } + val previewView = remember { + PreviewView(context).apply { + setLayerType(LAYER_TYPE_HARDWARE, null) + } + } val preview = remember { CameraPreview.Builder().build() } var camera by remember { mutableStateOf(null) } val executor = remember { Executors.newSingleThreadExecutor() } @@ -127,11 +134,11 @@ fun SendRecipientScreen( QrCodeAnalyzer { result -> if (result.isSuccess) { val qrCode = result.getOrThrow() - Logger.debug("QR scanned: $qrCode") + Logger.debug("QR scanned: '$qrCode'", context = TAG) onEvent(SendEvent.AddressContinue(qrCode)) } else { val error = requireNotNull(result.exceptionOrNull()) - Logger.error("Scan failed", error) + Logger.error("Scan failed", error, context = TAG) app?.toast( type = Toast.ToastType.ERROR, title = context.getString(R.string.other__qr_error_header), @@ -164,12 +171,13 @@ fun SendRecipientScreen( ) preview.surfaceProvider = previewView.surfaceProvider isCameraInitialized = true - }.onFailure { e -> - Logger.error("Camera initialization failed", e) + }.onFailure { + Logger.error("Camera initialization failed", it, context = TAG) app?.toast( type = Toast.ToastType.ERROR, title = context.getString(R.string.other__qr_error_header), - description = "Failed to initialize camera: ${e.message}" + description = context.getString(R.string.other__camera_init_error) + .replace("{message}", it.message.orEmpty()) ) isCameraInitialized = false } @@ -182,8 +190,8 @@ fun SendRecipientScreen( camera?.let { runCatching { ProcessCameraProvider.getInstance(context).get().unbindAll() - }.onFailure { e -> - Logger.error("Camera cleanup failed", e) + }.onFailure { + Logger.error("Camera cleanup failed", it, context = TAG) } } // Reset state - camera will reinit if needed on next composition @@ -193,21 +201,12 @@ fun SendRecipientScreen( } // Gallery picker launchers - val handleGalleryScanSuccess = remember(onEvent) { - { - qrCode: String -> - Logger.debug("QR from gallery: $qrCode") - onEvent(SendEvent.AddressContinue(qrCode)) - } + val handleGalleryScanSuccess = { qrCode: String -> + Logger.debug("QR from gallery: $qrCode", context = TAG) + onEvent(SendEvent.AddressContinue(qrCode)) } - val handleGalleryError = remember(app) { - { - e: Exception -> - app?.toast(e) - Unit - } - } + val handleGalleryError: (Throwable) -> Unit = { app?.toast(it) } val galleryLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetContent(), @@ -217,7 +216,7 @@ fun SendRecipientScreen( context = context, uri = it, onScanSuccess = handleGalleryScanSuccess, - onError = handleGalleryError + onError = handleGalleryError, ) } } @@ -231,7 +230,7 @@ fun SendRecipientScreen( context = context, uri = it, onScanSuccess = handleGalleryScanSuccess, - onError = handleGalleryError + onError = handleGalleryError, ) } } @@ -243,8 +242,8 @@ fun SendRecipientScreen( isFlashlightOn = !isFlashlightOn runCatching { control.enableTorch(isFlashlightOn) - }.onFailure { e -> - Logger.error("Torch control failed", e) + }.onFailure { + Logger.error("Torch control failed", it, context = TAG) // Revert state isFlashlightOn = !isFlashlightOn } @@ -260,13 +259,13 @@ fun SendRecipientScreen( } }, onClickContact = { - app?.toast(Exception("Coming soon: Contact")) + app?.toast(AppError("Coming soon: Contact")) }, onClickPaste = { onEvent(SendEvent.Paste) }, onClickManual = { onEvent(SendEvent.EnterManually) }, cameraPermissionGranted = cameraPermissionState.status.isGranted, onRequestPermission = { context.startActivityAppSettings() }, - modifier = modifier + modifier = modifier, ) } @@ -363,11 +362,7 @@ private fun CameraPreviewWithControls( modifier = Modifier .fillMaxSize() .clipToBounds(), - factory = { - previewView.apply { - setLayerType(LAYER_TYPE_HARDWARE, null) - } - } + factory = { previewView } ) // Gallery button (top-left) @@ -388,7 +383,7 @@ private fun CameraPreviewWithControls( } BodyMSB( - "Scan QR", + stringResource(R.string.other__camera_scan_qr), color = Colors.White, modifier = Modifier .padding(top = 31.dp) @@ -425,12 +420,16 @@ private fun PermissionDenied( .background(Colors.Black) .padding(32.dp) ) { - Display("SCAN\nQR CODE".withAccent(accentColor = Colors.Brand), color = Colors.White) + Display( + stringResource(R.string.other__camera_permission_title) + .withAccent(accentColor = Colors.Brand), + color = Colors.White + ) VerticalSpacer(8.dp) BodyM( - "Allow camera access to scan bitcoin invoices and pay more quickly.", + stringResource(R.string.other__camera_permission_description), color = Colors.White64, modifier = Modifier.fillMaxWidth() ) @@ -438,7 +437,7 @@ private fun PermissionDenied( VerticalSpacer(32.dp) PrimaryButton( - text = "Enable camera", + text = stringResource(R.string.other__camera_permission_button), icon = { Icon(painter = painterResource(R.drawable.ic_camera), contentDescription = null) }, @@ -451,7 +450,7 @@ private fun processImageFromGallery( context: Context, uri: Uri, onScanSuccess: (String) -> Unit, - onError: (Exception) -> Unit, + onError: (Throwable) -> Unit, ) { try { val image = InputImage.fromFilePath(context, uri) @@ -465,19 +464,19 @@ private fun processImageFromGallery( for (barcode in barcodes) { barcode.rawValue?.let { qrCode -> onScanSuccess(qrCode) - Logger.info("QR from gallery: $qrCode") + Logger.info("QR from gallery: $qrCode", context = TAG) return@addOnSuccessListener } } - Logger.error("No QR code in image") - onError(Exception(context.getString(R.string.other__qr_error_text))) + Logger.error("No QR code in image", context = TAG) + onError(AppError(context.getString(R.string.other__qr_error_text))) } - .addOnFailureListener { e -> - Logger.error("Gallery scan failed", e) - onError(e) + .addOnFailureListener { + Logger.error("Gallery scan failed", it, context = TAG) + onError(it) } } catch (e: IllegalArgumentException) { - Logger.error("Gallery processing failed", e) + Logger.error("Gallery processing failed", e, context = TAG) onError(e) } } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/withdraw/WithdrawErrorScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/withdraw/WithdrawErrorScreen.kt index bae5d34bf..f2f7b6f65 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/withdraw/WithdrawErrorScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/withdraw/WithdrawErrorScreen.kt @@ -56,8 +56,7 @@ fun WithdrawErrorScreen( VerticalSpacer(46.dp) BodyM( - // TODO add missing localized text - text = "Your withdrawal was unsuccessful. Please scan the QR code again or contact support.", + text = stringResource(R.string.wallet__withdraw_error), color = Colors.White64, ) diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt index 2c4a086d5..7c6cffb50 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/blocks/BlocksEditScreen.kt @@ -114,7 +114,7 @@ fun BlocksEditContent( // Block number toggle BlockEditOptionRow( - label = "Block", + label = stringResource(R.string.widgets__blocks__field__block), value = block.height, isEnabled = blocksPreferences.showBlock, onClick = onClickShowBlock, @@ -123,7 +123,7 @@ fun BlocksEditContent( // Time toggle BlockEditOptionRow( - label = "Time", + label = stringResource(R.string.widgets__blocks__field__time), value = block.time, isEnabled = blocksPreferences.showTime, onClick = onClickShowTime, @@ -132,7 +132,7 @@ fun BlocksEditContent( // Date toggle BlockEditOptionRow( - label = "Date", + label = stringResource(R.string.widgets__blocks__field__date), value = block.date, isEnabled = blocksPreferences.showDate, onClick = onClickShowDate, @@ -141,7 +141,7 @@ fun BlocksEditContent( // Transactions toggle BlockEditOptionRow( - label = "Transactions", + label = stringResource(R.string.widgets__blocks__field__transactions), value = block.transactionCount, isEnabled = blocksPreferences.showTransactions, onClick = onClickShowTransactions, @@ -150,7 +150,7 @@ fun BlocksEditContent( // Size toggle BlockEditOptionRow( - label = "Size", + label = stringResource(R.string.widgets__blocks__field__size), value = block.size, isEnabled = blocksPreferences.showSize, onClick = onClickShowSize, diff --git a/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt index 558d127f2..329dc6b1b 100644 --- a/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/BackupSettingsScreen.kt @@ -124,7 +124,7 @@ private fun BackupSettingsScreenContent( if (Env.isE2eTest && allSynced) { Icon( painter = painterResource(R.drawable.ic_check_circle), - contentDescription = "All Synced", + contentDescription = null, tint = Colors.Green, modifier = Modifier .padding(end = 4.dp) @@ -243,7 +243,7 @@ private fun BackupRetryButton(onClick: () -> Unit) { } } -@Preview +@Preview(showSystemUi = true) @Composable private fun Preview() { val categories = BackupCategory.entries diff --git a/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt index 3b21f3a2d..9c0276207 100644 --- a/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/LanguageSettingsScreen.kt @@ -8,10 +8,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import to.bitkit.R import to.bitkit.models.Language import to.bitkit.ui.components.Text13Up import to.bitkit.ui.components.settings.SettingsButtonRow @@ -53,7 +55,7 @@ private fun Content( modifier = modifier.screen() ) { AppTopBar( - titleText = "Language", // TODO Transifex + titleText = stringResource(R.string.settings__language_title), onBackClick = onBackClick, actions = { DrawerNavIcon() } ) diff --git a/app/src/main/java/to/bitkit/ui/settings/advanced/CoinSelectPreferenceScreen.kt b/app/src/main/java/to/bitkit/ui/settings/advanced/CoinSelectPreferenceScreen.kt index 9555246b5..dbe193a4c 100644 --- a/app/src/main/java/to/bitkit/ui/settings/advanced/CoinSelectPreferenceScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/advanced/CoinSelectPreferenceScreen.kt @@ -143,8 +143,8 @@ private fun Content( // ) SettingsButtonRow( - title = "Branch and Bound", // TODO add missing localized text - description = "Finds exact amount matches to minimize change", // TODO add missing localized text + title = stringResource(R.string.settings__cs__bnb_title), + description = stringResource(R.string.settings__cs__bnb_desc), value = SettingsButtonValue.BooleanValue( uiState.coinSelectionPreference == CoinSelectionPreference.BranchAndBound ), @@ -153,8 +153,8 @@ private fun Content( ) SettingsButtonRow( - title = "Single Random Draw", // TODO add missing localized text - description = "Random selection for privacy", // TODO add missing localized text + title = stringResource(R.string.settings__cs__srd_title), + description = stringResource(R.string.settings__cs__srd_desc), value = SettingsButtonValue.BooleanValue( uiState.coinSelectionPreference == CoinSelectionPreference.SingleRandomDraw ), diff --git a/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusScreen.kt b/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusScreen.kt index 9fe6a8799..3f1e8c174 100644 --- a/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusScreen.kt @@ -31,7 +31,6 @@ import to.bitkit.R import to.bitkit.ext.startActivityAppSettings import to.bitkit.ext.toLocalizedTimestamp import to.bitkit.models.HealthState -import to.bitkit.models.NodeLifecycleState import to.bitkit.repositories.AppHealthState import to.bitkit.ui.Routes import to.bitkit.ui.components.BodyMSB @@ -262,7 +261,7 @@ private fun Preview() { backups = HealthState.READY, ), backupSubtitle = now().minus(3.minutes).toEpochMilliseconds().toLocalizedTimestamp(), - nodeSubtitle = NodeLifecycleState.Running.uiText, + nodeSubtitle = "Running", ), ) } diff --git a/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusViewModel.kt b/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusViewModel.kt index 44199deca..5c9ec0d3f 100644 --- a/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusViewModel.kt +++ b/app/src/main/java/to/bitkit/ui/settings/appStatus/AppStatusViewModel.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import to.bitkit.R import to.bitkit.data.CacheStore @@ -36,45 +37,40 @@ class AppStatusViewModel @Inject constructor( collectState() } - private fun collectState() { - viewModelScope.launch { - combine( - healthRepo.healthState, - cacheStore.backupStatuses, - lightningRepo.lightningState, - ) { healthState, backupStatuses, lightningState -> - AppStatusUiState( - health = healthState, - backupSubtitle = computeBackupSubtitle(healthState.backups, backupStatuses), - nodeSubtitle = when (healthState.node) { - HealthState.ERROR -> context.getString(R.string.settings__status__lightning_node__error) - else -> lightningState.nodeLifecycleState.uiText - }, - ) - }.collect { newState -> - _uiState.value = newState - } + private fun collectState() = viewModelScope.launch { + combine( + healthRepo.healthState, + cacheStore.backupStatuses, + lightningRepo.lightningState, + ) { healthState, backupStatuses, lightningState -> + AppStatusUiState( + health = healthState, + backupSubtitle = computeBackupSubtitle(healthState.backups, backupStatuses), + nodeSubtitle = when (healthState.node) { + HealthState.ERROR -> context.getString(R.string.settings__status__lightning_node__error) + else -> lightningState.nodeLifecycleState.uiText(context) + }, + ) + }.collect { newState -> + _uiState.update { newState } } } private fun computeBackupSubtitle( backupHealthState: HealthState, backupStatuses: Map, - ): String { - return when (backupHealthState) { - HealthState.ERROR -> context.getString(R.string.settings__status__backup__error) - else -> { - val syncTimes = BackupCategory.entries - .filter { it != BackupCategory.LIGHTNING_CONNECTIONS } - .map { category -> backupStatuses[category]?.synced ?: 0L } + ) = when (backupHealthState) { + HealthState.ERROR -> context.getString(R.string.settings__status__backup__error) + else -> { + val syncTimes = BackupCategory.entries + .filter { it != BackupCategory.LIGHTNING_CONNECTIONS } + .map { category -> backupStatuses[category]?.synced ?: 0L } - when (val maxSyncTime = syncTimes.max()) { - 0L -> context.getString(R.string.settings__status__backup__ready) - else -> runCatching { maxSyncTime.toLocalizedTimestamp() } - .getOrDefault( - context.getString(R.string.settings__status__backup__ready) - ) - } + when (val maxSyncTime = syncTimes.max()) { + 0L -> context.getString(R.string.settings__status__backup__ready) + else -> runCatching { maxSyncTime.toLocalizedTimestamp() }.getOrDefault( + context.getString(R.string.settings__status__backup__ready) + ) } } } diff --git a/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsIntroScreen.kt b/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsIntroScreen.kt index 08204ea77..73c8a8457 100644 --- a/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsIntroScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsIntroScreen.kt @@ -36,7 +36,7 @@ fun BackgroundPaymentsIntroScreen( modifier = modifier.screen() ) { AppTopBar( - titleText = "Background Payments", // Todo Transifex + titleText = stringResource(R.string.settings__bg__title), onBackClick = onBack, actions = { DrawerNavIcon() }, ) @@ -66,11 +66,11 @@ fun BackgroundPaymentsIntroContent( ) Display( - text = "GET PAID\nPASSIVELY".withAccent(accentColor = Colors.Blue), - color = Colors.White + text = stringResource(R.string.settings__bg__intro_title).withAccent(accentColor = Colors.Blue), + color = Colors.White, ) VerticalSpacer(8.dp) - BodyM(text = "Turn on notifications to get paid, even when your Bitkit app is closed.", color = Colors.White64) + BodyM(text = stringResource(R.string.settings__bg__intro_desc), color = Colors.White64) VerticalSpacer(32.dp) PrimaryButton( text = stringResource(R.string.common__continue), diff --git a/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsSettings.kt b/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsSettings.kt index 562bbfe62..573312612 100644 --- a/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsSettings.kt +++ b/app/src/main/java/to/bitkit/ui/settings/backgroundPayments/BackgroundPaymentsSettings.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -35,44 +36,40 @@ import to.bitkit.viewmodels.SettingsViewModel @Composable fun BackgroundPaymentsSettings( - onBack: () -> Unit, settingsViewModel: SettingsViewModel = hiltViewModel(), + onBack: () -> Unit, ) { val context = LocalContext.current val notificationsGranted by settingsViewModel.notificationsGranted.collectAsStateWithLifecycle() val showNotificationDetails by settingsViewModel.showNotificationDetails.collectAsStateWithLifecycle() RequestNotificationPermissions( - onPermissionChange = { granted -> - settingsViewModel.setNotificationPreference(granted) - }, - showPermissionDialog = false + onPermissionChange = settingsViewModel::setNotificationPreference, + showPermissionDialog = false, ) Content( - onBack = onBack, - onSystemSettingsClick = { - NotificationUtils.openNotificationSettings(context) - }, hasPermission = notificationsGranted, showDetails = showNotificationDetails, + onBack = onBack, + onSystemSettingsClick = { NotificationUtils.openNotificationSettings(context) }, toggleNotificationDetails = settingsViewModel::toggleNotificationDetails, ) } @Composable private fun Content( + hasPermission: Boolean, + showDetails: Boolean, onBack: () -> Unit, onSystemSettingsClick: () -> Unit, toggleNotificationDetails: () -> Unit, - hasPermission: Boolean, - showDetails: Boolean, ) { Column( modifier = Modifier.screen() ) { AppTopBar( - titleText = "Background Payments", + titleText = stringResource(R.string.settings__bg__title), onBackClick = onBack, actions = { DrawerNavIcon() }, ) @@ -85,15 +82,14 @@ private fun Content( VerticalSpacer(16.dp) SettingsSwitchRow( - title = "Get paid when Bitkit is closed", + title = stringResource(R.string.settings__bg__switch_title), isChecked = hasPermission, - onClick = onSystemSettingsClick + onClick = onSystemSettingsClick, ) if (hasPermission) { - @Suppress("MaxLineLength") // TODO transifex BodyM( - text = "Background payments are enabled. You can receive funds even when the app is closed (if your device is connected to the internet).", + text = stringResource(R.string.settings__bg__enabled), color = Colors.White64, modifier = Modifier.padding(vertical = 16.dp), ) @@ -104,14 +100,14 @@ private fun Content( modifier = Modifier.padding(vertical = 16.dp) ) { BodyMB( - text = "Background payments are disabled, because you have denied notifications.", + text = stringResource(R.string.settings__bg__disabled), color = Colors.Red, ) } NotificationPreview( enabled = hasPermission, - title = "Payment Received", + title = stringResource(R.string.notification__received__title), description = "₿ 21 000", showDetails = showDetails, modifier = Modifier.fillMaxWidth() @@ -120,12 +116,12 @@ private fun Content( VerticalSpacer(32.dp) Text13Up( - text = "Privacy", + text = stringResource(R.string.settings__bg__privacy_header), color = Colors.White64 ) SettingsButtonRow( - "Include amount in notifications", + stringResource(R.string.settings__bg__include_amount), value = SettingsButtonValue.BooleanValue(showDetails), onClick = toggleNotificationDetails, ) @@ -133,18 +129,16 @@ private fun Content( VerticalSpacer(32.dp) Text13Up( - text = "Notifications", + text = stringResource(R.string.settings__bg__notifications_header), color = Colors.White64 ) VerticalSpacer(16.dp) SecondaryButton( - "Customize in Android Bitkit Settings", - icon = { - Image(painter = painterResource(R.drawable.ic_bell), contentDescription = null) - }, - onClick = onSystemSettingsClick + stringResource(R.string.settings__bg__customize), + icon = { Image(painter = painterResource(R.drawable.ic_bell), contentDescription = null) }, + onClick = onSystemSettingsClick, ) } } @@ -155,11 +149,11 @@ private fun Content( private fun Preview1() { AppThemeSurface { Content( + hasPermission = true, + showDetails = true, onBack = {}, onSystemSettingsClick = {}, toggleNotificationDetails = {}, - hasPermission = true, - showDetails = true, ) } } @@ -169,11 +163,11 @@ private fun Preview1() { private fun Preview2() { AppThemeSurface { Content( + hasPermission = false, + showDetails = false, onBack = {}, onSystemSettingsClick = {}, toggleNotificationDetails = {}, - hasPermission = false, - showDetails = false, ) } } diff --git a/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt b/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt index cc63bfc56..1e188b51c 100644 --- a/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt +++ b/app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt @@ -109,7 +109,7 @@ private fun GeneralSettingsContent( .verticalScroll(rememberScrollState()) ) { SettingsButtonRow( - title = "Language", + title = stringResource(R.string.settings__language_title), value = SettingsButtonValue.StringValue(selectedLanguage), onClick = onLanguageSettingsClick, modifier = Modifier.testTag("LanguageSettings") @@ -155,9 +155,11 @@ private fun GeneralSettingsContent( modifier = Modifier.testTag("QuickpaySettings") ) SettingsButtonRow( - title = "Background Payments", // TODO Transifex + title = stringResource(R.string.settings__bg__title), onClick = onBgPaymentsClick, - value = SettingsButtonValue.StringValue(if (notificationsGranted) "On" else "Off"), + value = SettingsButtonValue.StringValue( + stringResource(if (notificationsGranted) R.string.settings__bg__on else R.string.settings__bg__off) + ), modifier = Modifier.testTag("BackgroundPaymentSettings") ) } diff --git a/app/src/main/java/to/bitkit/ui/sheets/BackgroundPaymentsIntroSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/BackgroundPaymentsIntroSheet.kt index feae729ea..7daaf880d 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/BackgroundPaymentsIntroSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/BackgroundPaymentsIntroSheet.kt @@ -6,7 +6,9 @@ import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import to.bitkit.R import to.bitkit.ui.components.BottomSheetPreview import to.bitkit.ui.scaffold.SheetTopBar import to.bitkit.ui.settings.backgroundPayments.BackgroundPaymentsIntroContent @@ -27,9 +29,7 @@ fun BackgroundPaymentsIntroSheet( .navigationBarsPadding() .testTag("background_payments_intro_sheet") ) { - SheetTopBar( - titleText = "Background Payments", // Todo Transifex - ) + SheetTopBar(titleText = stringResource(R.string.settings__bg__title)) BackgroundPaymentsIntroContent(onContinue = onContinue) } } diff --git a/app/src/main/java/to/bitkit/ui/sheets/BoostTransactionSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/BoostTransactionSheet.kt index f9d38456a..2ba8313cb 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/BoostTransactionSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/BoostTransactionSheet.kt @@ -302,7 +302,7 @@ private fun CustomModeContent( backgroundColor = Colors.Red16, enabled = uiState.decreaseEnabled, onClick = { onChangeAmount(false) }, - contentDescription = "Reduce fee", + contentDescription = stringResource(R.string.wallet__boost_decrease_fee), modifier = Modifier.testTag(BoostTransactionTestTags.DECREASE_FEE_BUTTON) ) @@ -349,7 +349,7 @@ private fun CustomModeContent( backgroundColor = Colors.Green16, enabled = uiState.increaseEnabled, onClick = { onChangeAmount(true) }, - contentDescription = "Increase fee", + contentDescription = stringResource(R.string.wallet__boost_increase_fee), modifier = Modifier.testTag(BoostTransactionTestTags.INCREASE_FEE_BUTTON) ) } diff --git a/app/src/main/java/to/bitkit/ui/sheets/LnurlAuthSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/LnurlAuthSheet.kt index 36c951e96..711b13ebd 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/LnurlAuthSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/LnurlAuthSheet.kt @@ -62,13 +62,11 @@ private fun Content( .navigationBarsPadding() .padding(horizontal = 16.dp) ) { - // TODO add missing localized text - SheetTopBar(titleText = "Log In") + SheetTopBar(titleText = stringResource(R.string.other__lnurl_auth_login_title)) VerticalSpacer(16.dp) BodyM( - // TODO add missing localized text - text = "Log in to {domain}?".replace("{domain}", domain), + text = stringResource(R.string.other__lnurl_auth_login_prompt).replace("{domain}", domain), color = Colors.White64, ) @@ -93,9 +91,8 @@ private fun Content( .weight(1f) .testTag("LnurlAuthCancel") ) - // TODO add missing localized text PrimaryButton( - text = "Log In", + text = stringResource(R.string.other__lnurl_auth_login_button), onClick = onContinue, fullWidth = false, modifier = Modifier diff --git a/app/src/main/java/to/bitkit/ui/utils/NotificationUtils.kt b/app/src/main/java/to/bitkit/ui/utils/NotificationUtils.kt index 61445ae23..e74efc74e 100644 --- a/app/src/main/java/to/bitkit/ui/utils/NotificationUtils.kt +++ b/app/src/main/java/to/bitkit/ui/utils/NotificationUtils.kt @@ -1,6 +1,6 @@ package to.bitkit.ui.utils -import android.Manifest +import android.Manifest.permission.POST_NOTIFICATIONS import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -17,17 +17,16 @@ object NotificationUtils { * On older versions, opens the general app settings. */ fun openNotificationSettings(context: Context) { - val intent = - Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { - putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) - } + val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { + putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) + } intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) runCatching { context.startActivity(intent) - }.onFailure { e -> - Logger.error("Failed to open notification settings", e = e, context = "NotificationUtils") + }.onFailure { + Logger.error("Failed to open notification settings", e = it, context = "NotificationUtils") } } @@ -38,10 +37,7 @@ object NotificationUtils { */ fun areNotificationsEnabled(context: Context): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - ContextCompat.checkSelfPermission( - context, - Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED + ContextCompat.checkSelfPermission(context, POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED } else { NotificationManagerCompat.from(context).areNotificationsEnabled() } diff --git a/app/src/main/java/to/bitkit/utils/Perf.kt b/app/src/main/java/to/bitkit/utils/Perf.kt index d1a120f37..b5ef217aa 100644 --- a/app/src/main/java/to/bitkit/utils/Perf.kt +++ b/app/src/main/java/to/bitkit/utils/Perf.kt @@ -15,12 +15,13 @@ fun Duration.formatted(): String = toComponents { hours, minutes, seconds, nanos internal inline fun measured( label: String, + context: String, block: () -> T, ): T { var result: T val elapsed = measureTime { result = block() } - Logger.perf("$label took ${elapsed.formatted()}") + Logger.perf("$label took ${elapsed.formatted()}", context = context) return result } diff --git a/app/src/main/java/to/bitkit/utils/timedsheets/sheets/AppUpdateTimedSheet.kt b/app/src/main/java/to/bitkit/utils/timedsheets/sheets/AppUpdateTimedSheet.kt index 919dbcb50..873e2a2ff 100644 --- a/app/src/main/java/to/bitkit/utils/timedsheets/sheets/AppUpdateTimedSheet.kt +++ b/app/src/main/java/to/bitkit/utils/timedsheets/sheets/AppUpdateTimedSheet.kt @@ -17,6 +17,7 @@ class AppUpdateTimedSheet @Inject constructor( override val type = TimedSheetType.APP_UPDATE override val priority = 5 + @Suppress("TooGenericExceptionCaught") override suspend fun shouldShow(): Boolean = withContext(bgDispatcher) { try { val androidReleaseInfo = appUpdaterService.getReleaseInfo().platforms.android diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index 31186901d..afab19013 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -881,6 +881,7 @@ class AppViewModel @Inject constructor( } } + @Suppress("LongMethod") private suspend fun onScanOnchain(invoice: OnChainInvoice, scanResult: String) { val lnInvoice: LightningInvoice? = invoice.params?.get("lightning")?.let { bolt11 -> runCatching { decode(bolt11) }.getOrNull() @@ -971,8 +972,8 @@ class AppViewModel @Inject constructor( if (!lightningRepo.canSend(invoice.amountSatoshis)) { toast( type = Toast.ToastType.ERROR, - title = "Insufficient Funds", - description = "You do not have enough funds to send this payment." + title = context.getString(R.string.wallet__error_insufficient_funds_title), + description = context.getString(R.string.wallet__error_insufficient_funds_msg) ) return } @@ -1231,6 +1232,7 @@ class AppViewModel @Inject constructor( } } + @Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount") private suspend fun handleSanityChecks(amountSats: ULong) { if (_sendUiState.value.showSanityWarningDialog != null) return @@ -1317,7 +1319,7 @@ class AppViewModel @Inject constructor( it.copy(decodedInvoice = invoice) } }.onFailure { - toast(Exception("Error fetching lnurl invoice")) + toast(Exception(context.getString(R.string.wallet__error_lnurl_invoice_fetch))) hideSheet() return } @@ -1330,7 +1332,7 @@ class AppViewModel @Inject constructor( val validatedAddress = runCatching { validateBitcoinAddress(address) } .getOrElse { e -> Logger.error("Invalid bitcoin send address: '$address'", e, context = TAG) - toast(Exception("Invalid bitcoin send address")) + toast(Exception(context.getString(R.string.wallet__error_invalid_bitcoin_address))) hideSheet() return } @@ -1354,8 +1356,8 @@ class AppViewModel @Inject constructor( Logger.error(msg = "Error sending onchain payment", e = e, context = TAG) toast( type = Toast.ToastType.ERROR, - title = "Error Sending", - description = e.message ?: "Unknown error" + title = context.getString(R.string.wallet__error_sending_title), + description = e.message ?: context.getString(R.string.common__error_body) ) hideSheet() } @@ -1810,7 +1812,11 @@ class AppViewModel @Inject constructor( } fun toast(error: Throwable) { - toast(type = Toast.ToastType.ERROR, title = "Error", description = error.message ?: "Unknown error") + toast( + type = Toast.ToastType.ERROR, + title = context.getString(R.string.common__error), + description = error.message ?: context.getString(R.string.common__error_body) + ) } fun toast(toast: Toast) { diff --git a/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt index 7895faf4b..5893416fe 100644 --- a/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt @@ -1,11 +1,13 @@ package to.bitkit.viewmodels +import android.content.Context import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job import kotlinx.coroutines.async @@ -23,6 +25,7 @@ import org.lightningdevkit.ldknode.ChannelDataMigration import org.lightningdevkit.ldknode.ChannelDetails import org.lightningdevkit.ldknode.NodeStatus import org.lightningdevkit.ldknode.PeerDetails +import to.bitkit.R import to.bitkit.data.SettingsStore import to.bitkit.di.BgDispatcher import to.bitkit.models.NodeLifecycleState @@ -43,8 +46,10 @@ import kotlin.coroutines.cancellation.CancellationException import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds +@Suppress("LongParameterList") @HiltViewModel class WalletViewModel @Inject constructor( + @ApplicationContext private val context: Context, @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val walletRepo: WalletRepo, private val lightningRepo: LightningRepo, @@ -334,15 +339,15 @@ class WalletViewModel @Inject constructor( .onSuccess { ToastEventBus.send( type = Toast.ToastType.INFO, - title = "Success", - description = "Peer disconnected." + title = context.getString(R.string.common__success), + description = context.getString(R.string.wallet__peer_disconnected), ) } .onFailure { error -> ToastEventBus.send( type = Toast.ToastType.ERROR, - title = "Error", - description = error.message ?: "Unknown error" + title = context.getString(R.string.common__error), + description = error.message ?: context.getString(R.string.common__error_body) ) } } @@ -355,8 +360,8 @@ class WalletViewModel @Inject constructor( walletRepo.updateBip21Invoice(amountSats).onFailure { error -> ToastEventBus.send( type = Toast.ToastType.ERROR, - title = "Error updating invoice", - description = error.message ?: "Unknown error" + title = context.getString(R.string.wallet__error_invoice_update), + description = error.message ?: context.getString(R.string.common__error_body) ) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bd0d18aeb..ea8614b1f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,1235 +1,1296 @@ - Back up Store your bitcoin - Discount + Back up + Please try again + Failed + Buy some bitcoin + Buy Spending Balance - QuickPay - Scan and pay - Invite + Discount Share Bitkit - Spend - Instant payments - Transfer - In progress... - Connecting + Invite Ready in ±10m - Ready + Connecting Connected! - Transfer - Ready in ±{duration} - Initiating - Keep app open - Secure + Ready + In progress... + Transfer + Instant payments + Spend + When Bitkit is closed + Get paid Set up a PIN code - Shop + Secure + Scan and pay + QuickPay Shop with Bitcoin - Profile + Shop Add your details - Support - Get assistance - Buy - Buy some bitcoin - Failed - Please try again - Get paid - When Bitkit is closed + Profile Suggestions - TRANSFER IN PROGRESS + Get assistance + Support + Keep app open + Initiating + Ready in ±{duration} + Transfer Advanced - Continue + Announced + Are You Sure? + Back Cancel Close - Are You Sure? - Yes, Proceed - Try Again - No, Cancel - ₿ / vbyte - ₿/vbyte - Edit + Confirmations + Connect + Continue + Copied To Clipboard Copy - Share - Search - Discard - Save - Done + Default Delete Yes, Delete + No, Cancel + Discard + Done + Edit + Empty + Error + Unknown error + Later + Learn More + Max + Min + Never + No OK Awesome!\nNice!\nCool!\nGreat!\nFantastic!\nSweet!\nExcellent!\nTerrific! + Preview + Ready Reset Retry - Later + ₿ / vbyte + ₿/vbyte + Save + Search + Share Skip - Copied To Clipboard - Yes - No - Back - Learn More - Never + Success + Try Again Understood - Connect - Min - Max - Default - Preview - Instant - ±2-10 seconds - 2-10s - ±2s - Fast + Usable + Yes + Yes, Proceed + Depends on the fee + Depends on the fee + Depends on the fee + Custom ±10-20 minutes - 10-20m ±10m - Normal + 10-20m + Fast + ±2-10 seconds + ±2s + 2-10s + Instant + +2 hours + +2h + +2h + Minimum ±20-60 minutes - 20-60m ±20m - Slow + 20-60m + Normal ±1-2 hours - 1-2h ±1h - Minimum - +2 hours - +2h - +2h - Custom - Depends on the fee - Depends on the fee - Depends on the fee - Spending\n<accent>Balance</accent> - Fund your spending balance to enjoy instant and cheap transactions with friends, family, and merchants. - Get Started + 1-2h + Slow + Funds transfer to savings is usually instant, but settlement may take up to <accent>14 days</accent> under certain network conditions. + Funds\n<accent>availability</accent> + Balance + Spending base fee + Block Height + Channel ID + Peer ID + You can now pay anyone, anywhere, instantly. + Spending Balance Ready + Channel point + Close + Close Connection + Transfer Failed + Unable to transfer your funds back to savings. Please try again. + Your funds are being transferred back to your savings. + Transfer Initiated + The fee to close this Lightning Connection and transfer your funds back to your savings depends on network conditions.\n\nFunds transfer to savings is usually instant, but settlement may take up to <accent>14 days</accent> under certain network conditions. + Closed on + Closure reason + Add Connection + Export Logs + Closed connections + Hide Closed & Failed + Show Closed & Failed + Failed connections + Open connections + Pending connections + Connection + The funds on your spending balance have been transferred to your savings. + Connection Closed + Lightning Connections + Created on + Bitkit could not add the Lightning peer. + Unable To Add Lightning Peer + Bitkit cannot add Tor nodes. + The URI appears to be invalid. + Instant Payments Setup Failed + Receiving amount needs to be greater than ${usdValue} + An error occurred when setting up your instant balance. {raw} + Unable To Decode Invoice + Failed to Create Invoice + Log Export Failed + Bitkit was not able to export the LDK logs. + Unable to save lightning peer + Unable To Save Lightning Peer + Export Lightning Logs + Lightning Connection + Spending\n<accent>balance</accent> + Host + Node ID + Paste Node URI + Port + Scan QR + You can use an external node to manually open a Lightning connection. Enter the node details to continue. + <accent>Manual setup</accent> + Lightning connection initiated. You will be able to use your spending balance in <accent>±30 minutes</accent> (depends on node configuration). + Connection\n<accent>initiated</accent> + Fee rate + Fee Rate Cache Update Time + Fees + Force Transfer + Some connections could not be closed. + Unable to transfer your funds back to savings. Please try again. + Force Transfer Failed + Your funds will be accessible in ±14 days. + Force Transfer Initiated + Force Transfer + Could not initiate transfer. Do you want to force this transfer? You won’t be able to use these funds for ±14 days (!) + Force\n<accent>Transfer</accent> + Transfer from Savings + Use Other Wallet + Advanced Spending Balance - Fund your <accent>spending balance</accent> You can use your Bitkit savings or send bitcoin from a different wallet. Bitkit does not currently provide Lightning services in your country, but you can still connect to other nodes directly. Bitkit does not currently provide Lightning services in your country, but you can still connect to other nodes directly. - Transfer from Savings - Use Other Wallet - Advanced - Spending Balance - Advanced <accent>setup</accent> - Scan a QR to claim your LNURL Channel from another LSP, or choose manual setup. + Fund your <accent>spending balance</accent> LNURL Channel QR Manual Setup - No Available Funds + Spending Balance + Scan a QR to claim your LNURL Channel from another LSP, or choose manual setup. + Advanced <accent>setup</accent> + Inbound Capacity + Inbound HTLC Max + Inbound HTLC Min + Copied Invoice to Clipboard + Is Usable + Lightning Balances + Spending Balance Liquidity + Your Spending Balance uses the Lightning Network to make your payments cheaper, faster, and more private.\n\nThis works like internet access, but you pay for liquidity & routing instead of bandwidth.\n\nThis setup includes some one-time costs. + Liquidity\n<accent>& routing</accent> + Channel Monitor Archival Height + Next Outbound HTLC Limit + Next Outbound HTLC Min Before you can transfer funds from your savings balance, you need to send bitcoin to your Bitkit wallet. Fund wallet - Transfer Funds - Please\n<accent>confirm</accent> - Custom <accent>fee</accent> - Swipe To Transfer - Transfer\n<accent>to spending</accent> - Transfer funds to your spending balance to enjoy instant and cheap transactions with friends, family, and merchants. - Get Started - Transfer Failure - Transfer\n<accent>failed</accent> - Sorry, Bitkit could not set up your spending balance or complete your transfer. Please try again. - Inspect Error - Try Again - Transfer\n<accent>to spending</accent> - 25% - Savings Balance Minimum - A minimum of ₿ {amount} is needed to set up your spending balance. - Spending Balance Maximum - The amount you can transfer to your spending balance is currently limited to ₿ {amount}. - Your transfer to the spending balance is limited due to liquidity policy. For details, visit the Help Center. - Network fees - Service fees - To spending - Total - Use Defaults - Receiving\n<accent>capacity</accent> - Liquidity fee - Liquidity\n<accent>& routing</accent> - Your Spending Balance uses the Lightning Network to make your payments cheaper, faster, and more private.\n\nThis works like internet access, but you pay for liquidity & routing instead of bandwidth.\n\nThis setup includes some one-time costs. - Spending Balance Liquidity - Transfer\n<accent>to savings</accent> - Transfer your spending balance to your savings to store your Bitcoin long-term and secure your future. - Get Started - Funds\n<accent>availability</accent> - Funds transfer to savings is usually instant, but settlement may take up to <accent>14 days</accent> under certain network conditions. - Transfer to savings - Transfer all - Select funds\n<accent>to transfer</accent> + No Available Funds + disconnected + Bitkit failed to initialize the Lightning node. + LDK Node ID + Lightning Node + Lightning Wallet Sync Time + Onchain Wallet Sync Time + Opened on + Order ID + Order Details + The setup process expired. Please try again. + Instant Payments Setup Failed + Order Expiry + Order fee + The setup process expired. Please try again. + Instant Payments Setup Failed + Processing payment + Connection closed + Connection closing + Order expired + Given up + Connection inactive + Connection open + Opening connection + Payment successful + Payment canceled + Queued for opening + Refund available + Payment refunded + Other + The Lightning peer was successfully added and saved. + Receiving capacity + Reserve balance (not spendable) + Rapid Gossip Sync Snapshot Time + Savings You can transfer part of your spending balance to savings, because you have multiple active Lightning Connections. + Select funds\n<accent>to transfer</accent> Total selected - Funds\n<accent>in transfer</accent> - Please wait, your funds transfer is in progress. This should take <accent>±10 seconds.</accent> + Transfer to savings + Transfer all Transfer\n<accent>interrupted</accent> - Keep Bitkit\n<accent>open</accent> Funds were not transferred yet. Bitkit will try to initiate the transfer during the next <accent>30 minutes</accent>. Please keep your app open. - Transfer Successful - Funds moved\n<accent>to spending</accent> - Your funds have been transferred and your spending balance is ready to use. - Funds moved\n<accent>to savings</accent> - Your funds have been transferred. You are able to use these funds immediately. - Lightning Connection - <accent>Manual setup</accent> - You can use an external node to manually open a Lightning connection. Enter the node details to continue. - Node ID - Host - Port - Paste Node URI - Scan QR - Spending\n<accent>balance</accent> - Connection\n<accent>initiated</accent> - Lightning connection initiated. You will be able to use your spending balance in <accent>±30 minutes</accent> (depends on node configuration). - Instant Payments Setup Failed - An error occurred when setting up your instant balance. {raw} - Receiving amount needs to be greater than ${usdValue} - Spending - Savings - Spending balance - Receiving capacity + Keep Bitkit\n<accent>open</accent> + Get Started + Transfer your spending balance to your savings to store your Bitcoin long-term and secure your future. + Transfer\n<accent>to savings</accent> + Please wait, your funds transfer is in progress. This should take <accent>±10 seconds.</accent> + Funds\n<accent>in transfer</accent> + Pathfinding Scores Sync Time + Continue Using Bitkit In transfer - Please wait, your funds transfer is in progress. This should take <accent>±10 minutes.</accent> Processing Payment Payment Successful Queued For Opening Opening Connection - Continue Using Bitkit - Lightning Node - LDK Node ID - disconnected - Bitkit failed to initialize the Lightning node. - Lightning Connections - Pending connections - Open connections - Closed connections - Failed connections - Show Closed & Failed - Hide Closed & Failed - Export Logs - Add Connection - Export Lightning Logs - Log Export Failed - Bitkit was not able to export the LDK logs. - Failed to Create Invoice - Unable To Add Lightning Peer - Bitkit could not add the Lightning peer. - The URI appears to be invalid. - Bitkit cannot add Tor nodes. - Unable To Save Lightning Peer - Unable to save lightning peer - Unable To Decode Invoice - The Lightning peer was successfully added and saved. - Copied Invoice to Clipboard - Connection + Please wait, your funds transfer is in progress. This should take <accent>±10 minutes.</accent> + Spendable Onchain + Spending + Liquidity fee + Receiving\n<accent>capacity</accent> + The amount you can transfer to your spending balance is currently limited to ₿ {amount}. + Your transfer to the spending balance is limited due to liquidity policy. For details, visit the Help Center. + Spending Balance Maximum + A minimum of ₿ {amount} is needed to set up your spending balance. + Savings Balance Minimum + 25% + Transfer\n<accent>to spending</accent> + To spending + Use Defaults + Service fees + Network fees + Total + Get Started + Transfer funds to your spending balance to enjoy instant and cheap transactions with friends, family, and merchants. + Transfer\n<accent>to spending</accent> + Spending balance Status - Order Details - Order ID - Created on - Order Expiry - Transaction - Order fee - Balance - Reserve balance (not spendable) - Total channel size - Fees - Spending base fee - Fee rate - Other - Is Usable - Opened on - Closed on - Peer ID - Channel ID - Channel point - Closure reason Support - Connection Closed - The funds on your spending balance have been transferred to your savings. - Close Connection - Transfer Failed - Unable to transfer your funds back to savings. Please try again. - Transfer Initiated - Your funds are being transferred back to your savings. - The fee to close this Lightning Connection and transfer your funds back to your savings depends on network conditions.\n\nFunds transfer to savings is usually instant, but settlement may take up to <accent>14 days</accent> under certain network conditions. - Close - Force Transfer - Force\n<accent>Transfer</accent> - Could not initiate transfer. Do you want to force this transfer? You won’t be able to use these funds for ±14 days (!) - Force Transfer - Force Transfer Initiated - Your funds will be accessible in ±14 days. - Some connections could not be closed. - Force Transfer Failed - Unable to transfer your funds back to savings. Please try again. - Spending Balance Ready - You can now pay anyone, anywhere, instantly. - Instant Payments Setup Failed - The setup process expired. Please try again. - Instant Payments Setup Failed - The setup process expired. Please try again. - Processing payment - Payment canceled - Payment successful - Refund available - Payment refunded - Queued for opening - Opening connection - Connection closing - Given up - Order expired - Connection closed - Connection open - Connection inactive - Please wait for Bitkit to connect to the payment network (±10 seconds). + Total Anchor Channels Reserve + Total Lightning + Total Onchain + Total channel size + Transaction + Please\n<accent>confirm</accent> + Custom <accent>fee</accent> + Transfer Funds + Swipe To Transfer + Inspect Error + Try Again + Transfer Failure + Sorry, Bitkit could not set up your spending balance or complete your transfer. Please try again. + Transfer\n<accent>failed</accent> + TRANSFER IN PROGRESS + Get Started + Fund your spending balance to enjoy instant and cheap transactions with friends, family, and merchants. + Spending\n<accent>Balance</accent> + Transfer Successful + Your funds have been transferred. You are able to use these funds immediately. + Your funds have been transferred and your spending balance is ready to use. + Funds moved\n<accent>to savings</accent> + Funds moved\n<accent>to spending</accent> Connecting & Syncing... - Bitkit\n<accent>terms of use</accent> - Terms of use - I declare that I have read and accept the terms of use. - Privacy Policy - I declare that I have read and accept the <accent>privacy policy.</accent> - You can ₿ \n<accent>the change</accent> - Use Bitkit to pay anyone, anywhere, any time, and spend your bitcoin on the things you value in life. - Get Started - Skip Intro - Skip - Freedom in\n<accent>your pocket</accent> - Bitkit hands you the keys to manage your money. Spend now or save for later. The choice is yours. - Instant\n<accent>payments</accent> - Spend bitcoin faster than ever. Enjoy instant and cheap payments with friends, family, and merchants. - *Bitkit does not currently provide Lightning services in your country, but you can still connect to other nodes. - Bitcoiners,\n<accent>borderless</accent> - Take charge of your digital life with portable profiles and payable contacts. - Privacy is\n<accent>not a crime</accent> - Swipe to hide your balance, enjoy more private payments, and protect your wallet by enabling security features. - Your keys,\n<accent>your coins</accent> - Let’s create your wallet. Please be aware that Bitkit is mobile software. <accent>Don’t store all your money in Bitkit.</accent> - New Wallet - Restore - Restore Wallet - Advanced Setup + Please wait for Bitkit to connect to the payment network (±10 seconds). + Wallet Balances + Please wait while your old wallet data migrates to this new Bitkit version... + Wallet Migration + MIGRATING\n<accent>WALLET</accent> + Balance moved from spending to savings + Reason: %s + Channel closed + Notification channel for Lightning node foreground service + Lightning node notification + Channel failed to open in the background + Channel open failed + Channel opened + Pending + Ready to send + Lightning error + Payment failed + Please try again + Received %s + Via new channel + Open Bitkit to see details + Payment Received + Bitkit is running in background so you can receive Lightning payments + Stop App Advanced - Secure with <accent>Passphrase</accent> - You can add a secret passphrase to the 12-word recovery phrase. If you do, make sure you don’t forget. - Passphrase + Advanced Setup Create New Wallet - <accent>Restore</accent>\nyour wallet - Please type in your recovery phrase from any (paper) backup. - If a word is shown in <accent>red</accent>, it means that it was not found in the recovery phrase dictionary. Check for spelling errors. - The checksum for the recovery phrase appears to be incorrect. Please double check your recovery phrase. - SUGGESTIONS - Passphrase* - *Optional, enter only if you’ve set up one. - Wallet Restore Failed - Bitkit could not restore your wallet from backup or recovery phrase. + To get\nstarted\n<accent>send\nBitcoin</accent>\nto your\nwallet + Wallet Creation Failed + Get Started Setting up\n<accent>your wallet</accent> - Wallet <accent>restored</accent> - You have successfully restored your wallet from backup. Enjoy Bitkit! + <accent>Caution:</accent>\nmultiple devices + Don\'t install your Bitkit recovery phrase into multiple phones simultaneously, as this can corrupt your data. + New Wallet + Passphrase + Secure with <accent>Passphrase</accent> + You can add a secret passphrase to the 12-word recovery phrase. If you do, make sure you don’t forget. + Privacy Policy + I declare that I have read and accept the <accent>privacy policy.</accent> + Restore + Bitkit could not restore your wallet from backup or recovery phrase. + Wallet Restore Failed Spending balance <accent>error</accent> Bitkit restored your savings, but failed to restore your current spending balance (Lightning state) and wallet data. + <accent>Restore</accent>\nyour wallet + The checksum for the recovery phrase appears to be incorrect. Please double check your recovery phrase. Proceed Without Backup If you previously had a lightning backup it will be overwritten and lost. This could result in a loss of funds. - To get\nstarted\n<accent>send\nBitcoin</accent>\nto your\nwallet - Wallet Creation Failed - <accent>Caution:</accent>\nmultiple devices - Don\'t install your Bitkit recovery phrase into multiple phones simultaneously, as this can corrupt your data. - Scan QR Code - Unable To Read QR - Bitkit is not able to read this QR code. - Unable to Read Data - Bitkit could not read the provided data. - Incorrect Network - Bitkit is currently set to {selectedNetwork} but data is for {dataNetwork}. - Paste QR Code + *Optional, enter only if you’ve set up one. + Passphrase* + Please type in your recovery phrase from any (paper) backup. + If a word is shown in <accent>red</accent>, it means that it was not found in the recovery phrase dictionary. Check for spelling errors. + Wallet <accent>restored</accent> + You have successfully restored your wallet from backup. Enjoy Bitkit! + SUGGESTIONS + Restore Wallet + Skip + Skip Intro + Freedom in\n<accent>your pocket</accent> + Bitkit hands you the keys to manage your money. Spend now or save for later. The choice is yours. + Instant\n<accent>payments</accent> + *Bitkit does not currently provide Lightning services in your country, but you can still connect to other nodes. + Spend bitcoin faster than ever. Enjoy instant and cheap payments with friends, family, and merchants. + Bitcoiners,\n<accent>borderless</accent> + Take charge of your digital life with portable profiles and payable contacts. + Privacy is\n<accent>not a crime</accent> + Swipe to hide your balance, enjoy more private payments, and protect your wallet by enabling security features. + Your keys,\n<accent>your coins</accent> + Let’s create your wallet. Please be aware that Bitkit is mobile software. <accent>Don’t store all your money in Bitkit.</accent> + Terms of use + I declare that I have read and accept the terms of use. + Bitkit\n<accent>terms of use</accent> + Use Bitkit to pay anyone, anywhere, any time, and spend your bitcoin on the things you value in life. + You can ₿ \n<accent>the change</accent> + You need {delta} more to complete this transaction. + Unable to add LSP node as a peer at this time. + Transfer Failed + An error occurred when moving funds. {raw} + Choose Exchange Buy some\n<accent>Bitcoin</accent> Don’t have any Bitcoin or need more? - Choose Exchange - Data Connection Issue - Could not load primary key from keychain. - Electrum Connection Restored - Bitkit successfully reconnected to Electrum. + Bitkit needs permission to use your camera + Permission to use camera + Failed to initialize camera: {message} + <bold>It appears Bitkit does not have permission to access your camera.</bold>\n\nTo utilize this feature in the future you will need to enable camera permissions for this app from your phone\'s settings. + Enable camera + Allow camera access to scan bitcoin invoices and pay more quickly. + SCAN\n<accent>QR CODE</accent> + Scan QR + Do you want to be redirected to the relevant screen? + Clipboard Data Detected + Coming soon + Bitkit successfully reconnected to the Internet. + Internet Connection Restored Internet Connectivity Issues It appears you’re disconnected, trying to reconnect... - Reconnecting To Electrum Lost connection to Electrum, trying to reconnect... - Internet Connection Restored - Bitkit successfully reconnected to the Internet. + Reconnecting To Electrum + Bitkit successfully reconnected to Electrum. + Electrum Connection Restored + EARLIER + Data Connection Issue + Could not load primary key from keychain. + Claiming your Bitkit gift code... + Claiming Gift + OK + Bitkit couldn\'t claim the funds. Please try again later or contact us. + Gift Code Error + OK + This Bitkit gift code has already been used, and the funds have been paid out. + Used Code + Sorry, you\'re too late! All gifts for this code have already been claimed. + Out of Gifts + Learn More + Understood High Balance - High\n<accent>Balance</accent> <accent>Your wallet balance exceeds $500.</accent>\nFor your security, consider moving some of your savings to an offline wallet. - Understood - Learn More - Critical Update - Update\n<accent>Bitkit now</accent> - There is a critical update for Bitkit. You must update to continue using Bitkit. - Update Bitkit - Permission to use camera - Bitkit needs permission to use your camera - <bold>It appears Bitkit does not have permission to access your camera.</bold>\n\nTo utilize this feature in the future you will need to enable camera permissions for this app from your phone\'s settings. - Clipboard Data Detected - Do you want to be redirected to the relevant screen? - Insufficient Savings - Insufficient Spending Balance - More ₿ needed to pay this Bitcoin invoice. - ₿ {amount} more needed to pay this Bitcoin invoice. - ₿ {amount} more needed to pay this Lightning invoice. - Swipe To Confirm - Decoding Error - Unable To Interpret Provided Data - This QR code does not appear to contain payment data. - Bitcoin Price Update Failed - Bitkit could not update the current Bitcoin exchange rate. Using price from {date} - Bitkit could not update the current Bitcoin exchange rate. Please try again later. - Unable To Pay (LNURL) - Could not start local Lightning node. Please try again or restart Bitkit. - Not enough outbound/sending capacity to complete lnurl-pay request. + High\n<accent>Balance</accent> + Sign In Failed (LNURL) + An error occurred when you attempted to sign in. {raw} + Log In + Log in to {domain}? + Log In + You successfully signed in to {domain}. + You successfully signed in. + Signed In + Bitkit could not connect to Blocktank. Unable To Open Channel (LNURL) An error occured when you tried paying: {raw} Lightning Connection - New\nlightning\n<accent>connection</accent> - Do you want open a Lightning connection with this Lightning Service Provider? + Host Lightning service provider + Do you want open a Lightning connection with this Lightning Service Provider? Node ID - Host Port - Bitkit could not connect to Blocktank. - Channel Requested - Successfully requested channel from: {peer} Successfully requested channel. - Sign In Failed (LNURL) - An error occurred when you attempted to sign in. {raw} - Signed In - You successfully signed in to {domain}. - You successfully signed in. + Successfully requested channel from: {peer} + Channel Requested + New\nlightning\n<accent>connection</accent> + Could not start local Lightning node. Please try again or restart Bitkit. + Unable To Pay (LNURL) + Not enough outbound/sending capacity to complete lnurl-pay request. Withdraw Failed (LNURL) - Sorry, an error occurred. - Not enough receiving capacity to complete withdraw request. Could not create invoice for withdraw. + Sorry, an error occurred. Incorrect LNURL withdraw params, min/max not set correctly. - Withdraw Requested + Not enough receiving capacity to complete withdraw request. Your withdraw was successfully requested. Waiting for payment. + Withdraw Requested + Failed to create log zip file + Error starting: %1$s + Setting up wallet… + Running + Starting + Stopped + Stopping + Insufficient Savings + ₿ {amount} more needed to pay this Bitcoin invoice. + More ₿ needed to pay this Bitcoin invoice. + Insufficient Spending Balance + ₿ {amount} more needed to pay this Lightning invoice. Open Phone Settings - Transfer Failed - Unable to add LSP node as a peer at this time. - An error occurred when moving funds. {raw} - You need {delta} more to complete this transaction. - EARLIER - Update Available - Update\n<accent>Bitkit</accent> - Please update Bitkit to the latest version for new features and bug fixes! - Update - Please try again. - Important: Bitkit Transfer - Open Bitkit to complete your transfer - Bitkit is unable to read the provided data. + Unable To Read QR + Incorrect Network + Bitkit is currently set to {selectedNetwork} but data is for {dataNetwork}. + Unable to Read Data + Bitkit could not read the provided data. + Bitkit is not able to read this QR code. + Paste QR Code + Scan QR Code + Bitkit could not update the current Bitcoin exchange rate. Using price from {date} + Bitkit could not update the current Bitcoin exchange rate. Please try again later. + Bitcoin Price Update Failed This Lightning invoice has expired. - Claiming Gift - Claiming your Bitkit gift code... - Gift Code Error - Bitkit couldn\'t claim the funds. Please try again later or contact us. - OK - Used Code - This Bitkit gift code has already been used, and the funds have been paid out. - OK - Out of Gifts - Sorry, you\'re too late! All gifts for this code have already been claimed. - Shop - Get your life on the Bitcoin standard. Spend your Bitcoin on digital gift cards, eSIMs, phone refills, and more. - Get Started - Shop - Shop - Map - Gift card categories - Gift Cards - Shop with Bitcoin - ESims + Bitkit is unable to read the provided data. + Decoding Error + Unable To Interpret Provided Data + This QR code does not appear to contain payment data. Go borderless - Phone Refill + ESims + Shop with Bitcoin + Gift Cards + Gift card categories + Shop Top up your phone - Travel + Phone Refill + Map + Shop Book your ₿ holiday + Travel + Get Started + Get your life on the Bitcoin standard. Spend your Bitcoin on digital gift cards, eSIMs, phone refills, and more. + Shop Shop - Wallet Backup - <accent>Safely store</accent> your Bitcoin + Swipe To Confirm + Open Bitkit to complete your transfer + Important: Bitkit Transfer + Please try again. + Update + Update Bitkit + Critical Update + There is a critical update for Bitkit. You must update to continue using Bitkit. + Update\n<accent>Bitkit now</accent> + Update Available + Please update Bitkit to the latest version for new features and bug fixes! + Update\n<accent>Bitkit</accent> + Authorize + Authorizing... + This service claims to be + Deny + Make sure you trust this service before granting permission to manage your data. + Unable to auth with Pubky service + Pubky Auth Error + Unable to retrieve Pubky key + Pubky Error + Requested Permissions + Success + Authorization + Back Up Now that you have some funds in your wallet, it is time to back up your money! There are no funds in your wallet yet, but you can create a backup if you wish. - Back Up - passphrase - Your Passphrase - You added a passphrase to your recovery phrase during wallet setup. - <accent>Never share</accent> your passphrase with anyone as this may result in the loss of funds. - Confirm Passphrase - Enter the passphrase you added while setting up and creating your wallet. - <accent>Passphrase:</accent> {passphrase} - Mnemonic Phrase - Your Recovery Phrase - Wallet Failure - Bitkit could not read your recovery phrase. - Write down these {length} words in the right order and store them in a safe place. - Use the 12 words below to recover your money at a later date. - Tap To Reveal - <accent>Never share</accent> your recovery phrase with anyone as this may result in the loss of funds. + <accent>Safely store</accent> your Bitcoin + Wallet Backup + Biometrics + PIN code set. Would you like to use {biometricsName} instead of your PIN code? + Authenticate with {biometricsName} + Confirm {biometricsName} + Bitkit could not set up {type} for your device. + Biometrics Setup Failed + Face ID + Loading... + It appears that your device does not support Biometric security. + Looks like you haven’t set up biometric security yet (or it is not supported). Try to enable it in your phone settings. + Phone Settings + Touch ID + Use {biometricsName} + Close Bitkit + Contact Support + You have successfully changed your PIN to a new 4-digit combination. + PIN changed + Please retype your 4-digit PIN to complete the setup process. + Retype New PIN + Please use a PIN you will remember. If you forget your PIN you can reset it, but that will require restoring your wallet. + Set New PIN + You can change your PIN code to a new\n4-digit combination. Please enter your current PIN code first. + Change PIN + Try again, this is not the same PIN. + Show Seed Phrase + Export + Bitkit could not create the backup file. + Bitkit could not export the backup file to your phone. + Backup Failed + Password + Share backup file + Bitkit successfully exported the backup file to your phone. + Backup Exported + You can export a copy of your wallet data as a .ZIP file. This file is encrypted with the password you set below. + Export To Phone Confirm Recovery Phrase Tap the 12 words in the correct order. Mnemonic copied to clipboard - Successful - Make sure you store your recovery phrase in a <accent>secure place</accent>, as this is the <accent>only way to recover</accent> your money! - Keep It Safe - Remember, <accent>never share your recovery phrase</accent> with anyone! If you do, they can steal your money, profile and other data. Wallet Data Your profile, contacts, accounts, tags, and activity will be backed up automatically to our free cloud service. + Wallet Failure + Bitkit could not read your recovery phrase. + Keep It Safe + Remember, <accent>never share your recovery phrase</accent> with anyone! If you do, they can steal your money, profile and other data. + <bold>Latest data backup:</bold> {time} + Failed to load mnemonic Multiple Devices Don\'t use your Bitkit recovery phrase on multiple phones simultaneously, as this can corrupt your data. - <bold>Latest data backup:</bold> {time} - Increase Security - <accent>Protect</accent>\nyour bitcoin - To increase wallet security, you can set up a PIN code and Face ID. - Secure Wallet + <accent>Never share</accent> your recovery phrase with anyone as this may result in the loss of funds. + Mnemonic Phrase + Successful + Make sure you store your recovery phrase in a <accent>secure place</accent>, as this is the <accent>only way to recover</accent> your money! + Tap To Reveal + Use the 12 words below to recover your money at a later date. + Write down these {length} words in the right order and store them in a safe place. + Your Recovery Phrase + passphrase + Confirm Passphrase + Enter the passphrase you added while setting up and creating your wallet. + <accent>Never share</accent> your passphrase with anyone as this may result in the loss of funds. + <accent>Passphrase:</accent> {passphrase} + You added a passphrase to your recovery phrase during wallet setup. + Your Passphrase + {attemptsRemaining} attempts remaining. Forgot your PIN? Choose 4-Digit PIN Please use a PIN you will remember. If you forget your PIN you can reset it, but that will require restoring your wallet. - Retype 4-Digit PIN - Please retype your 4-digit PIN to complete the setup process. - Try again, this is not the same PIN. - Disable PIN - PIN code is currently enabled. If you want to disable your PIN, you need to enter your current PIN code first. Disable PIN + PIN code is currently enabled. If you want to disable your PIN, you need to enter your current PIN code first. + Disable PIN Please enter your PIN code - Last attempt. Entering the wrong PIN again will reset your wallet. - {attemptsRemaining} attempts remaining. Forgot your PIN? - Forgot PIN? - Forgot your PIN? Reset and recover your Bitkit wallet with your recovery phrase. Set a new PIN after completing recovery. Reset (Requires Recovery Phrase) + Forgot your PIN? Reset and recover your Bitkit wallet with your recovery phrase. Set a new PIN after completing recovery. + Forgot PIN? + Last attempt. Entering the wrong PIN again will reset your wallet. + Try again, this is not the same PIN. + Retype 4-Digit PIN + Please retype your 4-digit PIN to complete the setup process. + Secure Wallet + Increase Security + To increase wallet security, you can set up a PIN code and Face ID. + <accent>Protect</accent>\nyour bitcoin Please enter your PIN code to confirm and send out this payment. Enter PIN Code Use {biometricsName} - Biometrics - Authenticate with {biometricsName} - It appears that your device does not support Biometric security. - Face ID - Touch ID - Confirm {biometricsName} - Biometrics Setup Failed - Bitkit could not set up {type} for your device. - Loading... - Looks like you haven’t set up biometric security yet (or it is not supported). Try to enable it in your phone settings. - PIN code set. Would you like to use {biometricsName} instead of your PIN code? - Use {biometricsName} - Phone Settings - Wallet Secured - You have successfully set up a PIN code and {biometricsName} to improve wallet security. - You have successfully set up a PIN code to improve your wallet security. - Also require for payments - Reset And Restore - Back up your wallet first to avoid loss of your funds and wallet data. Resetting will overwrite your current Bitkit setup. + Recovery + You\'ve entered Bitkit\'s recovery mode. Here are some actions to perform when running into issues that prevent the app from fully functioning. Restart the app for a normal startup. Back Up First Reset Wallet - Reset Bitkit? - Are you sure you want to reset your Bitkit Wallet? Do you have a backup of your recovery phrase and wallet data? Yes, Reset - Recovery - You\'ve entered Bitkit\'s recovery mode. Here are some actions to perform when running into issues that prevent the app from fully functioning. Restart the app for a normal startup. - Show Seed Phrase - Contact Support - Wipe App - Close Bitkit - Export To Phone - You can export a copy of your wallet data as a .ZIP file. This file is encrypted with the password you set below. - Password - Export - Share backup file - Backup Exported - Bitkit successfully exported the backup file to your phone. - Backup Failed - Bitkit could not export the backup file to your phone. - Bitkit could not create the backup file. - Change PIN - You can change your PIN code to a new\n4-digit combination. Please enter your current PIN code first. - Retype New PIN - Please retype your 4-digit PIN to complete the setup process. - Set New PIN - Please use a PIN you will remember. If you forget your PIN you can reset it, but that will require restoring your wallet. - Try again, this is not the same PIN. - PIN changed - You have successfully changed your PIN to a new 4-digit combination. + Are you sure you want to reset your Bitkit Wallet? Do you have a backup of your recovery phrase and wallet data? + Reset Bitkit? + Back up your wallet first to avoid loss of your funds and wallet data. Resetting will overwrite your current Bitkit setup. + Reset And Restore + You have successfully set up a PIN code and {biometricsName} to improve wallet security. + You have successfully set up a PIN code to improve your wallet security. + Also require for payments + Wallet Secured Use PIN code - Wallet Data Deleted + Wipe App Bitkit has been reset and all wallet data has been deleted. - Authorization - Make sure you trust this service before granting permission to manage your data. - Authorize - Deny - Authorizing... - Success - This service claims to be - Requested Permissions - Pubky Error - Unable to retrieve Pubky key - Pubky Auth Error - Unable to auth with Pubky service - Settings - Dev Options Enabled - Developer options are now enabled throughout the app. - Dev Options Disabled - Developer options are now disabled throughout the app. - General - Security and Privacy - Back up or Restore - Advanced - About - Support - About Bitkit - Thank you for being a responsible Bitcoiner.\nChange your wallet, change the world.\n\nBitkit hands you the keys to your money, profile, contacts, and web accounts.\n\nBitkit was crafted by Synonym Software Ltd. + Wallet Data Deleted Legal Share - Version Change your wallet, change the world. Download Bitkit for iPhone {appStoreUrl} or Android {playStoreUrl} - Dev Settings - Local currency - Local Currency - Prices powered by Bitfinex & CoinGecko. - Most Used - Other Currencies - Default unit - Default Unit - Display amounts in - Bitcoin - Tip: Quickly toggle between Bitcoin and {currency} by tapping on your wallet balance. - Bitcoin denomination - Modern (₿ 10 000) - Classic (₿ 0.00010000) - Transaction speed - Transaction Speed - Default Transaction Speed - Set Custom Fee - ₿ {feeSats} for the average transaction - ₿ {feeSats} for the average transaction ({fiatSymbol}{fiatFormatted}) - Tags - Previously used tags - Widgets - Widgets - Show Widget Titles - QuickPay - <accent>Frictionless</accent>\npayments - Bitkit QuickPay makes checking out faster by automatically paying QR codes when scanned. - Enable QuickPay - If enabled, scanned invoices below ${amount} will be paid automatically without requiring your confirmation or PIN*. - Quickpay threshold - * Bitkit QuickPay exclusively supports payments from your Spending Balance. - Security And Privacy - Swipe balance to hide - Hide balance on open - Read clipboard for ease of use - Warn when sending over $100 - PIN Code - Change PIN Code - Require PIN on launch - Require PIN when idle - Require PIN for payments - Enabled - Disabled - Use {biometryTypeName} instead - When enabled, you can use {biometryTypeName} instead of your PIN code to unlock your wallet or send payments. - Back Up Or Restore - Back up your wallet - Export wallet data to phone - Reset and restore wallet - Data Backup Failure - Bitkit failed to back up wallet data. Retrying in {interval, plural, one {# minute} other {# minutes}}. - latest data backups - Running - Failed Backup: {time} - Latest Backup: {time} - Connections - Connection Receipts - Transaction Log - Boosts & Transfers - Settings - Widgets - Tags - Profile - Contacts - Support - Need help? Report your issue from within Bitkit, visit the help center, check the status, or reach out to us on our social channels. - Report Issue - Help Center - App Status - Please describe the issue you are experiencing or ask a general question. - Email address - Issue or question - satoshi@satoshi.com - Describe the issue or ask a question - Send - Sent Successfully - Thank you for contacting us! We will try to get back to you as soon as possible. - OK - Failed To Send - Something went wrong while trying to send your issue or question. Please try again. - Try Again - App Status - Internet - Connected - Reconnecting... - Disconnected - Bitcoin Node - Connected - Connecting... - Could not connect to Electrum - Lightning Node - Synced - Syncing... - Could not initiate - Lightning Connection - Open - Opening... - No open connections - Latest Full Data Backup - Backed up - Backing up... - Failed to complete a full backup - Payments - Networks - Other + Thank you for being a responsible Bitcoiner.\nChange your wallet, change the world.\n\nBitkit hands you the keys to your money, profile, contacts, and web accounts.\n\nBitkit was crafted by Synonym Software Ltd. + About Bitkit + Version + About + Change Addresses + Receiving Addresses + Check Balances + Copied to clipboard + Generate 20 More + Index: {index} + Loading Addresses... + No Addresses To Display + No addresses found when searching for \"{searchTxt}\" + No addresses with funds found when searching for \"{searchTxt}\" + No funds found under the {addressType} address type, change addresses up to index {index}. + No funds found under the {addressType} address type, receiving addresses up to index {index}. + Path: {path} + Hide Private Key + Private Key: {privateKey} + View Private Key + Rescan Failed + Bitkit was not able to check the address balances. + ₿ {totalBalance} found + {count, plural, one {Spend All Funds From # address} other {Spend All Funds From # addresses}} + {count, plural, one {Spend ₿ {fundsToSpend} From # address} other {Spend ₿ {fundsToSpend} From # addresses}} Bitcoin Address Type - Monitored Address Types - Monitored Address Types Updated - Changes will take full effect after app restarts. - Address Gap Limit + Address Viewer + Bitcoin Network Coin Selection - Coin Selection Method - Manual Autopilot Autopilot Mode - Smallest First - Sort by and use smallest UTXO first. Potentially higher fee, but hides your largest UTXOs. - Largest First - Sort by and use largest UTXO first. Potentially lower fee, but reveals your largest UTXOs. Consolidate Use all available UTXOs regardless of the amount being sent. Use this method when fees are low in order to reduce fees in future transactions. First-In First-Out Use older UTXOs first (by block height). Last-In Last-Out Use newer UTXOs first (by block height). + Manual + Smallest First + Sort by and use smallest UTXO first. Potentially higher fee, but hides your largest UTXOs. + Coin Selection Method + Largest First + Sort by and use largest UTXO first. Potentially lower fee, but reveals your largest UTXOs. + Electrum Server + Address Gap Limit + Lightning Connections + Lightning Node + Monitored Address Types + Changes will take full effect after app restarts. + Monitored Address Types Updated Payment Preference - Choose how you prefer to receive money when users send funds to your profile key. - * This requires sharing payment data. - Payment preference (drag to reorder) Pay to/from contacts Enable payments with contacts* - Address Viewer + Payment preference (drag to reorder) + * This requires sharing payment data. + Choose how you prefer to receive money when users send funds to your profile key. Rescan Addresses - Reset Suggestions - Reset Suggestions? - Are you sure you want to reset the suggestions? They will reappear in case you have removed them from your Bitkit wallet overview. Yes, Reset - Lightning Connections - Lightning Node - Electrum Server + Are you sure you want to reset the suggestions? They will reappear in case you have removed them from your Bitkit wallet overview. + Reset Suggestions? Rapid-Gossip-Sync + Networks + Other + Payments + Reset Suggestions Slashtags Web Relay - Bitcoin Network - Fast (more expensive) - Fast - ± 10-20 minutes - Normal - Normal - ± 20-60 minutes - Slow (cheaper) - Slow - ± 1-2 hours - Custom - Custom - Depends on fee - No Addresses To Display - Loading Addresses... - No funds found under the {addressType} address type, receiving addresses up to index {index}. - No funds found under the {addressType} address type, change addresses up to index {index}. - No addresses with funds found when searching for \"{searchTxt}\" - No addresses found when searching for \"{searchTxt}\" - Rescan Failed - Bitkit was not able to check the address balances. - {count, plural, one {Spend ₿ {fundsToSpend} From # address} other {Spend ₿ {fundsToSpend} From # addresses}} - {count, plural, one {Spend All Funds From # address} other {Spend All Funds From # addresses}} - Index: {index} - Path: {path} - Hide Private Key - View Private Key - Private Key: {privateKey} - Change Addresses - Receiving Addresses - ₿ {totalBalance} found - Generate 20 More - Check Balances - Copied to clipboard - Please specify a host and port to connect to. + Advanced + Connection Receipts + Connections + Contacts + Profile + Settings + Tags + Transaction Log + Boosts & Transfers + Widgets + Export wallet data to phone + Bitkit failed to back up wallet data. Retrying in {interval, plural, one {# minute} other {# minutes}}. + Data Backup Failure + latest data backups + Reset and restore wallet + Failed Backup: {time} + Running + Latest Backup: {time} + Back Up Or Restore + Back up your wallet + Back up or Restore + Customize in Android Bitkit Settings + Background payments are disabled, because you have denied notifications. + Background payments are enabled. You can receive funds even when the app is closed (if your device is connected to the internet). + Include amount in notifications + Turn on notifications to get paid, even when your Bitkit app is closed. + GET PAID\n<accent>PASSIVELY</accent> + Notifications + Off + On + Privacy + Set up in background + Get paid when Bitkit is closed + Background Payments + Finds exact amount matches to minimize change + Branch and Bound + Random selection for privacy + Single Random Draw + Developer options are now disabled throughout the app. + Dev Options Disabled + Developer options are now enabled throughout the app. + Dev Options Enabled + Dev Settings + Connect To Host + Reset To Default + Currently connected to + disconnected Please specify a host to connect to. - Please specify a port to connect to. - Please specify a valid port. + Please specify a host and port to connect to. Please specify a valid url. Electrum Error - Electrum Server Updated - Successfully connected to {host}:{port} - Electrum Connection Failed - Bitkit could not establish a connection to Electrum. - Currently connected to - disconnected + Please specify a port to connect to. + Please specify a valid port. Host Port Protocol - Reset To Default - Connect To Host - Save - Reset - Address Gap Limit Updated + Electrum Connection Failed + Bitkit could not establish a connection to Electrum. + Successfully connected to {host}:{port} + Electrum Server Updated + Depends on fee + Custom + Custom + ± 10-20 minutes + Fast (more expensive) + Fast + ± 20-60 minutes + Normal + Normal + ± 1-2 hours + Slow (cheaper) + Slow Changes will take full effect after app restarts. - Look Behind + Address Gap Limit Updated Look Ahead - Look Behind Change Look Ahead Change - Rapid-Gossip-Sync Server URL + Look Behind + Look Behind Change + Reset + Save + Prices powered by Bitfinex & CoinGecko. + Local currency + Local Currency + Most Used + Other Currencies + Classic (₿ 0.00010000) + Bitcoin denomination + Modern (₿ 10 000) + Transaction speed + Default Transaction Speed + Set Custom Fee + ₿ {feeSats} for the average transaction + ₿ {feeSats} for the average transaction ({fiatSymbol}{fiatFormatted}) + Transaction Speed + Tags + Previously used tags + Default unit + Bitcoin + Display amounts in + Tip: Quickly toggle between Bitcoin and {currency} by tapping on your wallet balance. + Default Unit + General + Language + Bitkit QuickPay makes checking out faster by automatically paying QR codes when scanned. + <accent>Frictionless</accent>\npayments + QuickPay + Quickpay threshold + * Bitkit QuickPay exclusively supports payments from your Spending Balance. + If enabled, scanned invoices below ${amount} will be paid automatically without requiring your confirmation or PIN*. + Enable QuickPay Connect - Rapid-Gossip-Sync Server Updated + Rapid-Gossip-Sync Server URL You may need to restart the app once or twice for this change to take effect. - Web Relay Error - Please specify a URL to connect to. - Not a valid HTTPS url. - Healthcheck Failed - Web Relay Updated - Successfully connected to {url} - Your name - Your Name - Contact Name - Contact - Contacts - Add contact + Rapid-Gossip-Sync Server Updated + Read clipboard for ease of use + When enabled, you can use {biometryTypeName} instead of your PIN code to unlock your wallet or send payments. + Hide balance on open + PIN Code + Change PIN Code + Disabled + Enabled + Require PIN when idle + Require PIN on launch + Require PIN for payments + Swipe balance to hide + Security And Privacy + Use {biometryTypeName} instead + Warn when sending over $100 + Security and Privacy + Settings + Failed to complete a full backup + Backing up... + Backed up + Latest Full Data Backup + Could not connect to Electrum + Connecting... + Connected + Bitcoin Node + Disconnected + Reconnecting... + Connected + Internet + No open connections + Opening... + Open + Lightning Connection + Could not initiate + Syncing... + Synced + Lightning Node + App Status + Help Center + Email address + Issue or question + Failed to open support links + satoshi@satoshi.com + Describe the issue or ask a question + Report Issue + Please describe the issue you are experiencing or ask a general question. + App Status + Need help? Report your issue from within Bitkit, visit the help center, check the status, or reach out to us on our social channels. + Send + Thank you for contacting us! We will try to get back to you as soon as possible. + OK + Something went wrong while trying to send your issue or question. Please try again. + Try Again + Support + Sent Successfully + Failed To Send + Support + Widgets + Show Widget Titles + Widgets + Healthcheck Failed + Not a valid HTTPS url. + Please specify a URL to connect to. + Web Relay Error + Successfully connected to {url} + Web Relay Updated + Contact + Add contact + Add Add Contact Add a new contact by scanning their QR or by pasting their key below. - Add - Paste a key - You cannot add yourself as a contact. - This is not a valid key. + Assign Contact + Profile Key Copied To Clipboard + Are you sure you want to delete {name} from your contacts? + Delete {name}? + Yes, Delete Edit Contact - Contact\'s name - Your public\nprofile name - Retrieving\ncontact info... + This is not a valid key. + You cannot add yourself as a contact. + Paste a key + Lightning is not ready yet + Contact Name No links added yet... Unable To Pay Contact - Lightning is not ready yet + Retrieving\ncontact info... + Select Contact Share Profile Key this contact - Profile Key Copied To Clipboard - Delete {name}? - Are you sure you want to delete {name} from your contacts? - Yes, Delete - Select Contact - Assign Contact + Your public\nprofile name + Contacts + Contact\'s name No Contacts found Slashtags disabled - Dynamic\n<accent>contacts</accent> - Get automatic updates from your Bitkit contacts, pay them, and follow their public profiles. + Unable To Delete Profile + The contact you’re trying to send to hasn’t enabled payments. + Unable To Pay Contact + Unable To Save Contact + Unable To Save Profile + My profile + Enable payments with contacts* + * This requires sharing payment data. Add First Contact + Dynamic\n<accent>contacts</accent> Own your\n<accent>profile</accent> Set up your public profile and links, so your Bitkit contacts can reach you or pay you anytime, anywhere. Pay <accent>Bitkit\ncontacts</accent> You and your contacts can use Bitkit to send payments directly, without banks, anytime, anywhere. - My profile + Get automatic updates from your Bitkit contacts, pay them, and follow their public profiles. Profile - Save - Pay Your Contacts - Create Profile - Please note that all your profile information will be publicly available and visible. - Short bio. Say a bit about yourself. Add Link + Short bio. Say a bit about yourself. + Create Profile + Delete + Are you sure you want to delete all of your Bitkit profile information? + Delete Profile Information? + Yes, Delete + Your Bitkit profile information has been deleted. + Profile Deleted + Edit Profile Label For example \'Website\' - Link or text Note: Any link you add will be publicly visible. Suggestions Suggestions To Add + Link or text + Pay Your Contacts + Please note that all your profile information will be publicly available and visible. + Save Scan to add {name} - Edit Profile - Delete - Delete Profile Information? - Are you sure you want to delete all of your Bitkit profile information? - Yes, Delete - Profile Deleted - Your Bitkit profile information has been deleted. - Enable payments with contacts* - * This requires sharing payment data. - Unable To Save Contact - Unable To Save Profile - Unable To Delete Profile - Unable To Pay Contact - The contact you’re trying to send to hasn’t enabled payments. - Deprecated - Slashauth is deprecated. Please use Bitkit Beta. - Wallet - Activity - Contacts - Profile - Widgets - Shop - Settings - App Status - Send - Receive + Your name + Your Name + %1$s sats + Please wait while Bitkit looks for funds in unsupported addresses (Legacy, Nested SegWit, and Taproot). + LOOKING FOR FUNDS... + Looking For Funds + BROADCASTING… + Confirm Sweep + PREPARING… + Retry + Swipe to confirm + To Address + Custom Fee + for this transaction + Error + Network Fee + Bitkit found funds in unsupported addresses (Legacy, Nested SegWit, and Taproot). + FUNDS FOUND + Found Funds + Legacy (P2PKH) + Sweep Funds + Bitkit checked unsupported address types and found no funds to sweep. + No Funds To Sweep + Bitkit found funds on unsupported Bitcoin address types. Sweep to move the funds to your new wallet balance. + Sweep + SWEEP OLD\n<accent>BITKIT FUNDS</accent> + SegWit (P2SH) + Your funds have been swept and will be added to your wallet balance. + Sweep Complete + Wallet Overview + Taproot (P2TR) + Sweep To Wallet + Total + %1$s, %2$d UTXO + %1$s, %2$d UTXOs + Activity + Address + All Activity + Assign + Received Bitcoin + Sent Bitcoin + Boost + Boost Fee + Boosted incoming transaction + Already Boosted + BOOSTED TRANSACTION {num} (CPFP) + BOOSTED TRANSACTION {num} (RBF) + Boosting + Confirmed + Confirming + Confirms in {feeRateDescription} + Boosting. Confirms in {feeRateDescription} + Contact + Date + Detach + Transaction Retrieval Failed + Bitkit was not able to fetch the transaction data. + Failed to load activity + Activity not found + The transaction was not found. + Explore + Open Block Explorer + Failed + Fee + Fee (prepaid) + {count, plural, one {INPUT} other {INPUTS (#)}} + Invoice + Comment + Invoice note + Fee potentially too low + Next month + No activity yet + Receive some funds to get started + {count, plural, one {OUTPUT} other {OUTPUTS (#)}} + Payment + Payment hash + Pending + Preimage + Previous month + Received + Removed from Mempool + Please check your activity list. The {count} impacted transaction(s) will be highlighted in red. + Transactions Removed From Mempool + Sent + Sent to myself + Show All Activity + Status + Successful + All + Other + Received + Sent + Tag + Time + Transfer + Transfer ({duration}) + From Spending + From Spending ({duration}) + From Savings + From Savings ({duration}) + To Savings + To Spending + Transaction ID + Swipe your wallet balance to reveal it again. + Wallet Balance Hidden <text> (</text><pending/><text> pending)</text> - Please reopen the app and try again. - Wallet \"{walletName}\" already exists. - Invalid recovery phrase. - Please double-check if your recovery phrase is accurate. - Send Bitcoin - To + Tap your wallet balance to switch it back to {unit}. + Switched to {unit} + Boost + Reduce fee + Bitkit was unable to boost the transaction. + Unable to increase the fee any further. Otherwise, it will exceed half the current input balance + Boost Failed + Your transaction may settle faster if you include an additional network fee. Set your custom fee below. + Your transaction may settle faster if you include an additional network fee. Here is a recommendation: + Increase fee + Use Suggested Fee + The transaction was successfully boosted. + Boosted! + Swipe To Boost + Boost Transaction + Please reopen the app and try again. + Wallet \"{walletName}\" already exists. + Invalid recovery phrase. + Please double-check if your recovery phrase is accurate. + Savings + Spending + Incoming Transfer: + Activity + Contacts + Profile + Settings + Shop + App Status + Wallet + Widgets + Transaction Broadcast Failed + Please check your connection and try again.\n{message} + An error occurred when broadcasting your transaction. {raw} + Transaction Creation Failed + An error occurred. Please try again {raw} + You do not have enough funds to send this payment. + Insufficient Funds + Invalid bitcoin send address + Error updating invoice + Error fetching lnurl invoice + Please increase your fee and try again. + Minimum relay fee not met + No lightning invoice found. + Please check your transaction info and try again. + No transaction is available to broadcast. + Error Sending + Apply + Clear + Select Range + Received Instant Bitcoin + Lightning Startup Error + Lightning Sync Error + Maximum amount + Pay Bitcoin + The minimum amount for this invoice is ₿ {amount}. + Amount Too Low + Comment + Optional comment to receiver + Withdraw + AvailablE TO WITHDRAW + The funds you withdraw will be deposited into your Bitkit spending balance. + Withdraw Bitcoin + Fee Exceeds Maximum Limit + Lower the custom fee and try again. + Fee Below Minimum Limit + Increase the custom fee and try again. + MINIMUM + Note + Received Bitcoin + Peer disconnected. + Receive + Receive Lightning funds + Receive Bitcoin + Bitcoin invoice + Transaction Failed + Failed to send funds to your spending account. + To receive more instant Bitcoin, Bitkit has to increase your liquidity. A <accent>{networkFee}</accent> network fee and <accent>{serviceFee}</accent> service provider fee will be deducted from the amount you specified. + To set up your spending balance, a <accent>{networkFee}</accent> network fee and <accent>{serviceFee}</accent> service provider fee will be deducted. + Invoice copied to clipboard + Payments to your spending balance might fail if you switch between apps. + Keep Bitkit In Foreground + Bitkit does not provide Lightning services in your country, but you can still connect to other nodes. + Instant Payments Unavailable + Insufficient receiving capacity to receive this amount over Lightning. + Insufficient receiving balance. + Spending Balance Initializing... + Lightning invoice + Enable background setup to safely exit Bitkit while your balance is being configured. + Set up in background + Spending Balance Liquidity + Additional Spending Balance Liquidity + Spending Balance Setup + Increase Receiving Capacity + Your Spending Balance uses the Lightning Network to make your payments cheaper, faster, and more private.\n\nThis works like internet access, but you pay for liquidity & routing instead of bandwidth.\n\nThis setup includes some one-time costs. + Your Spending Balance uses the Lightning Network to make your payments cheaper, faster, and more private.\n\nThis works like internet access, but you pay for liquidity & routing instead of bandwidth.\n\nBitkit needs to increase the receiving capacity of your spending balance to process this payment. + Optional note to payer + Enjoy instant and cheap\ntransactions with friends, family,\nand merchants. + Receive on <accent>spending balance</accent> + Generating QR ... + Share receiving address + Show Details + Show QR Code + Edit Invoice + Receive on Spending Balance + Auto + Savings + Spending + Want to receive <accent>Lightning</accent> funds? + You will receive Contact Paste Invoice Enter Manually Scan QR - Invoice + An error occurred and couldn\'t refresh the wallet. Please try again. + Electrum server connection is throttled. Please wait several minutes before trying again. + Connection Throttled + Refresh Failed + Blockchain Reorg Detected + {count, plural, one {# of your transactions is no longer confirmed.} other {# of your transactions are no longer confirmed.}} Please wait. + Please check your activity list for affected transactions. + sats/vbyte + <accent>Send\nbitcoin</accent>\nto your\nsavings balance + Savings + Auto + Coin Selection + Total required + Total selected + Send Enter an invoice, address, or profile key - Clipboard Empty - Please copy an address or an invoice. Bitcoin Amount - MAX - DONE + The amount is greater than your current balance. + The amount indicated does not allow a fee to be included. + Amount Invalid Available - Available (spending) Available (savings) - Reserve Balance - The maximum spendable amount is a bit lower due to a required reserve balance. - Review & Send + Available (spending) + Send Bitcoin + Please copy an address or an invoice. + Clipboard Empty + Please remove UTXOs or increase your send amount to proceed. Confirming in - Invoice expiration - Swipe To Pay - Yes, Send + Details It appears you are sending over $100. Do you want to continue? It appears you are sending over 50% of your total balance. Do you want to continue? The transaction fee appears to be over 50% of the amount you are sending. Do you want to continue? The transaction fee appears to be over $10. Do you want to continue? - Fee is potentially too low Current network conditions require that your fee should be greater than {minimumFee} ₿/vbyte. This transaction may fail, take a while to confirm, or get trimmed from the mempool. Do you wish to proceed? - Bitcoin Sent - Payment Pending - QuickPay - Paying\n<accent>invoice...</accent> - This payment is taking a bit longer than expected. You can continue using Bitkit. - Instant Payment Failed - Regular Payment + Fee is potentially too low + DONE + Unable to broadcast the transaction. Please try again. Unfortunately contact could not be paid instantly. You can try a regular payment (more expensive, slower). Transaction Failed - Unable to broadcast the transaction. Please try again. - Removing Tag Failed - Bitkit was unable to find the transaction data. - No lightning invoice found. - No transaction is available to broadcast. - Please check your transaction info and try again. - Minimum relay fee not met - Please increase your fee and try again. - Amount Invalid - The amount is greater than your current balance. - The amount indicated does not allow a fee to be included. - Details Speed and fee - Speed + Set Custom Fee Fee Invalid - Fee Exceeds Maximum Limit - Lower the custom fee and try again. - Fee Below Minimum Limit - Increase the custom fee and try again. - sats/vbyte - Send Amount Too Small - Please increase your send amount to proceed. - Please remove UTXOs or increase your send amount to proceed. - Unable to decrease the fee any further. Unable to increase the fee any further. Otherwise, it will exceed half the current input balance. - Set Custom Fee + Unable to decrease the fee any further. + Speed ₿ {feeSats} for this transaction ₿ {feeSats} for this transaction ({fiatSymbol}{fiatFormatted}) - Note - Comment - Optional comment to receiver + Instant Payment Failed + Invoice + Invoice expiration + MAX + The maximum spendable amount is a bit lower due to a required reserve balance. + Reserve Balance + Please increase your send amount to proceed. + Send Amount Too Small + Payment Pending + This payment is taking a bit longer than expected. You can continue using Bitkit. + QuickPay + Paying\n<accent>invoice...</accent> + Regular Payment + Review & Send + Bitcoin Sent + Swipe To Pay + To + Yes, Send + <accent>Send\nbitcoin</accent>\nto your\nspending balance + Spending + Bitkit was unable to find the transaction data. + Removing Tag Failed Tags Add Tag Add - Adding Tag Failed Bitkit was unable to find the transaction data. - New tag - Enter a new tag - Previously used tags + Adding Tag Failed Filter activity using tags Select Tag + New tag + Enter a new tag No tags available yet - Payment Sent - Your instant payment was sent successfully. - Payment Failed + Previously used tags Your instant payment failed. Please try again. - Received Transaction Replaced + Payment Failed + Your instant payment was sent successfully. + Payment Sent Your received transaction was replaced by a fee bump - Transaction Removed + Received Transaction Replaced Transaction was removed from mempool - Transaction Replaced + Transaction Removed Your transaction was replaced by a fee bump - Transaction Unconfirmed + Transaction Replaced Transaction became unconfirmed due to blockchain reorganization - Coin Selection - Auto - Total required - Total selected - Receive Bitcoin - Edit Invoice - Invoice copied to clipboard - Share receiving address - Bitcoin invoice - Lightning invoice - Optional note to payer - Receive Lightning funds - Show QR Code - Show Details - Savings - Auto - Spending - Want to receive <accent>Lightning</accent> funds? - Receive on Spending Balance - To set up your spending balance, a <accent>{networkFee}</accent> network fee and <accent>{serviceFee}</accent> service provider fee will be deducted. - To receive more instant Bitcoin, Bitkit has to increase your liquidity. A <accent>{networkFee}</accent> network fee and <accent>{serviceFee}</accent> service provider fee will be deducted from the amount you specified. - Spending Balance Setup - Increase Receiving Capacity - Your Spending Balance uses the Lightning Network to make your payments cheaper, faster, and more private.\n\nThis works like internet access, but you pay for liquidity & routing instead of bandwidth.\n\nThis setup includes some one-time costs. - Your Spending Balance uses the Lightning Network to make your payments cheaper, faster, and more private.\n\nThis works like internet access, but you pay for liquidity & routing instead of bandwidth.\n\nBitkit needs to increase the receiving capacity of your spending balance to process this payment. - Spending Balance Liquidity - Additional Spending Balance Liquidity - Transaction Failed - Failed to send funds to your spending account. - You will receive - Spending Balance Initializing... - Receive on <accent>spending balance</accent> - Enjoy instant and cheap\ntransactions with friends, family,\nand merchants. - Generating QR ... - Instant Payments Unavailable - Bitkit does not provide Lightning services in your country, but you can still connect to other nodes. - MINIMUM - Activity - Show All Activity - No activity yet - Receive some funds to get started - Sent - Received - Pending - Failed - Boost Fee - Boosted incoming transaction - Transfer - From Spending (±{duration}) - From Spending - From Savings (±{duration}) - From Savings - To Spending - To Savings - Transfer (±{duration}) - Confirms in {feeRateDescription} - Boosting. Confirms in {feeRateDescription} - Fee potentially too low - Sent Bitcoin - Received Bitcoin - Transaction Retrieval Failed - Bitkit was not able to fetch the transaction data. - The transaction was not found. - Activity not found - Failed to load activity - Confirming - Confirmed - Removed from Mempool - Transactions Removed From Mempool - Please check your activity list. The {count} impacted transaction(s) will be highlighted in red. - Boosting - Fee - Fee (prepaid) - Payment - Status - Date - Time - Contact - Assign - Detach - Tag - Boost - Already Boosted - Explore - Transaction ID - Preimage - Payment hash - Address - {count, plural, one {INPUT} other {INPUTS (#)}} - {count, plural, one {OUTPUT} other {OUTPUTS (#)}} - BOOSTED TRANSACTION {num} (CPFP) - BOOSTED TRANSACTION {num} (RBF) - Open Block Explorer - Successful - Invoice note - Comment - Invoice - All Activity - All - Sent - Received - Other - Savings - Spending - Savings - <accent>Send\nbitcoin</accent>\nto your\nsavings balance - Spending - <accent>Send\nbitcoin</accent>\nto your\nspending balance - Incoming Transfer: + Transaction Unconfirmed + Transfer To Savings + Transfer To Spending Transaction Invalid - Boost - Boost Transaction - Boosted! - The transaction was successfully boosted. - Boost Failed - Bitkit was unable to boost the transaction. - Unable to increase the fee any further. Otherwise, it will exceed half the current input balance - Your transaction may settle faster if you include an additional network fee. Set your custom fee below. - Your transaction may settle faster if you include an additional network fee. Here is a recommendation: - Use Suggested Fee - Swipe To Boost - Received Bitcoin - Received Instant Bitcoin - Transaction Creation Failed - An error occurred. Please try again {raw} - Transaction Broadcast Failed - An error occurred when broadcasting your transaction. {raw} - Please check your connection and try again.\n{message} - Select Range - Clear - Apply - Blockchain Reorg Detected - {count, plural, one {# of your transactions is no longer confirmed.} other {# of your transactions are no longer confirmed.}} Please wait. - Please check your activity list for affected transactions. - Withdraw Bitcoin - AvailablE TO WITHDRAW - The funds you withdraw will be deposited into your Bitkit spending balance. - Withdraw - Pay Bitcoin - Amount Too Low - The minimum amount for this invoice is ₿ {amount}. - Maximum amount - Wallet Balance Hidden - Swipe your wallet balance to reveal it again. - Switched to {unit} - Tap your wallet balance to switch it back to {unit}. - Refresh Failed - An error occurred and couldn\'t refresh the wallet. Please try again. - Connection Throttled - Electrum server connection is throttled. Please wait several minutes before trying again. - Lightning Sync Error - Lightning Startup Error - Insufficient receiving balance. - Insufficient receiving capacity to receive this amount over Lightning. - Keep Bitkit In Foreground - Payments to your spending balance might fail if you switch between apps. - Widgets - Hello,\n<accent>Widgets</accent> - Enjoy decentralized feeds from your favorite web services, by adding fun and useful widgets to your Bitkit wallet. - Widget - Widget Feed - Default - Custom - Please select which fields you want to display in the {name} widget. - Source + Your withdrawal was unsuccessful. Please scan the QR code again or contact support. Add Widget - Delete Widget? - Are you sure you want to delete ‘{name}‘ from your widgets? - Bitcoin Price - Check the latest Bitcoin exchange rates for a variety of fiat currencies. - Couldn\'t get price data - Bitcoin Headlines - Read the latest & greatest Bitcoin headlines from various news sites. - Couldn\'t get the latest news - Bitcoin Blocks Examine various statistics on newly mined Bitcoin Blocks. Couldn\'t get blocks data - Bitcoin Facts - Discover fun facts about Bitcoin, every time you open your wallet. - Bitcoin Calculator + Block + Date + Size + Time + Transactions + Bitcoin Blocks Convert ₿ amounts to {fiatSymbol} or vice versa. - Bitcoin Weather - Find out when it’s a good time to transact on the Bitcoin blockchain. - Favorable Conditions - All clear. Now would be a good time to transact on the blockchain. - Average Conditions + Bitcoin Calculator + Are you sure you want to delete ‘{name}‘ from your widgets? + Delete Widget? + Discover fun facts about Bitcoin, every time you open your wallet. + Bitcoin Facts + Read the latest & greatest Bitcoin headlines from various news sites. + Couldn\'t get the latest news + Bitcoin Headlines + Enjoy decentralized feeds from your favorite web services, by adding fun and useful widgets to your Bitkit wallet. + Hello,\n<accent>Widgets</accent> + Check the latest Bitcoin exchange rates for a variety of fiat currencies. + Couldn\'t get price data + Bitcoin Price The next block rate is close to the monthly averages. - Poor Conditions + Average Conditions + All clear. Now would be a good time to transact on the blockchain. + Favorable Conditions If you are not in a hurry to transact, it may be better to wait a bit. + Poor Conditions Current average fee - Next block inclusion + Find out when it’s a good time to transact on the Bitcoin blockchain. Couldn\'t get current fee weather - Balance moved from spending to savings - Reason: %s - Channel closed - Channel failed to open in the background - Channel open failed - Channel opened - Pending - Ready to send - Lightning error - Payment failed - Please try again - Received %s - Open Bitkit to see details - Payment Received - Bitkit is running in background so you can receive Lightning payments - Stop App - Unknown error - Via new channel - Lightning Wallet Sync Time - Ready - Block Height - Onchain Wallet Sync Time - Rapid Gossip Sync Snapshot Time - Fee Rate Cache Update Time - Channel Monitor Archival Height - Pathfinding Scores Sync Time - Wallet Balances - Total Onchain - Spendable Onchain - Total Anchor Channels Reserve - Total Lightning - Lightning Balances - Usable - Announced - Inbound Capacity - Inbound HTLC Max - Inbound HTLC Min - Next Outbound HTLC Limit - Next Outbound HTLC Min - Confirmations - Please wait while your old wallet data migrates to this new Bitkit version... - Wallet Migration - MIGRATING\n<accent>WALLET</accent> - %1$s sats - Please wait while Bitkit looks for funds in unsupported addresses (Legacy, Nested SegWit, and Taproot). - LOOKING FOR FUNDS... - Looking For Funds - BROADCASTING… - Confirm Sweep - PREPARING… - Retry - Swipe to confirm - To Address - Custom Fee - for this transaction - Error - Network Fee - Bitkit found funds in unsupported addresses (Legacy, Nested SegWit, and Taproot). - FUNDS FOUND - Found Funds - Legacy (P2PKH) - Sweep Funds - Bitkit checked unsupported address types and found no funds to sweep. - No Funds To Sweep - Bitkit found funds on unsupported Bitcoin address types. Sweep to move the funds to your new wallet balance. - Sweep - SWEEP OLD\n<accent>BITKIT FUNDS</accent> - SegWit (P2SH) - Your funds have been swept and will be added to your wallet balance. - Sweep Complete - Wallet Overview - Taproot (P2TR) - Sweep To Wallet - Total - %1$s, %2$d UTXO - %1$s, %2$d UTXOs + Bitcoin Weather + Next block inclusion + Widget Feed + Custom + Default + Please select which fields you want to display in the {name} widget. + Widget + Source + Widgets diff --git a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt index 4e9e7fb58..200705b97 100644 --- a/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt +++ b/app/src/test/java/to/bitkit/androidServices/LightningNodeServiceTest.kt @@ -105,7 +105,7 @@ class LightningNodeServiceTest : BaseUnitTest() { sats = 100L, ) val notification = NotificationDetails( - title = context.getString(R.string.notification_received_title), + title = context.getString(R.string.notification__received__title), body = "Received ₿ 100 ($0.10)", ) whenever(notifyPaymentReceivedHandler.invoke(any())) @@ -147,7 +147,7 @@ class LightningNodeServiceTest : BaseUnitTest() { val shadows = Shadows.shadowOf(notificationManager) val paymentNotification = shadows.allNotifications.find { - it.extras.getString(Notification.EXTRA_TITLE) == context.getString(R.string.notification_received_title) + it.extras.getString(Notification.EXTRA_TITLE) == context.getString(R.string.notification__received__title) } assertNotNull("Payment notification should be present", paymentNotification) @@ -184,7 +184,7 @@ class LightningNodeServiceTest : BaseUnitTest() { val shadows = Shadows.shadowOf(notificationManager) val paymentNotification = shadows.allNotifications.find { - it.extras.getString(Notification.EXTRA_TITLE) == context.getString(R.string.notification_received_title) + it.extras.getString(Notification.EXTRA_TITLE) == context.getString(R.string.notification__received__title) } assertNull("Payment notification should NOT be present in foreground", paymentNotification) @@ -212,7 +212,7 @@ class LightningNodeServiceTest : BaseUnitTest() { val shadows = Shadows.shadowOf(notificationManager) val paymentNotification = shadows.allNotifications.find { - it.extras.getString(Notification.EXTRA_TITLE) == context.getString(R.string.notification_received_title) + it.extras.getString(Notification.EXTRA_TITLE) == context.getString(R.string.notification__received__title) } assertNotNull("Payment notification should be present", paymentNotification) diff --git a/app/src/test/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandlerTest.kt b/app/src/test/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandlerTest.kt index 3862b7f20..a1ed26584 100644 --- a/app/src/test/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandlerTest.kt +++ b/app/src/test/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandlerTest.kt @@ -39,7 +39,7 @@ class NotifyPaymentReceivedHandlerTest : BaseUnitTest() { @Before fun setUp() { - whenever(context.getString(R.string.notification_received_title)).thenReturn("Payment Received") + whenever(context.getString(R.string.notification__received__title)).thenReturn("Payment Received") whenever(context.getString(any(), any())).thenReturn("Received amount") whenever(settingsStore.data).thenReturn(flowOf(SettingsData(showNotificationDetails = true))) whenever(currencyRepo.convertSatsToFiat(any(), anyOrNull())).thenReturn( diff --git a/app/src/test/java/to/bitkit/ext/DateTimeExtTest.kt b/app/src/test/java/to/bitkit/ext/DateTimeExtTest.kt index f748a3ee7..710d1e408 100644 --- a/app/src/test/java/to/bitkit/ext/DateTimeExtTest.kt +++ b/app/src/test/java/to/bitkit/ext/DateTimeExtTest.kt @@ -135,7 +135,7 @@ class DateTimeExtTest : BaseUnitTest() { fun `toRelativeTimeString preserves backward compatibility with default locale`() { val twoDaysAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2) val resultWithoutParam = twoDaysAgo.toRelativeTimeString() - val resultWithDefaultParam = twoDaysAgo.toRelativeTimeString(Locale.getDefault()) + val resultWithDefaultParam = twoDaysAgo.toRelativeTimeString() assertEquals(resultWithDefaultParam, resultWithoutParam) } diff --git a/app/src/test/java/to/bitkit/ui/WalletViewModelTest.kt b/app/src/test/java/to/bitkit/ui/WalletViewModelTest.kt index 8f55f55a1..27928f2a9 100644 --- a/app/src/test/java/to/bitkit/ui/WalletViewModelTest.kt +++ b/app/src/test/java/to/bitkit/ui/WalletViewModelTest.kt @@ -1,5 +1,6 @@ package to.bitkit.ui +import android.content.Context import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.runBlocking @@ -33,6 +34,7 @@ import to.bitkit.viewmodels.WalletViewModel class WalletViewModelTest : BaseUnitTest() { private lateinit var sut: WalletViewModel + private val context = mock() private val walletRepo = mock() private val lightningRepo = mock() private val settingsStore = mock() @@ -47,11 +49,13 @@ class WalletViewModelTest : BaseUnitTest() { @Before fun setUp() = runBlocking { + whenever(context.getString(any())).thenReturn("") whenever(walletRepo.walletState).thenReturn(walletState) whenever(lightningRepo.lightningState).thenReturn(lightningState) whenever(migrationService.isMigrationChecked()).thenReturn(true) sut = WalletViewModel( + context = context, bgDispatcher = testDispatcher, walletRepo = walletRepo, lightningRepo = lightningRepo, @@ -235,6 +239,7 @@ class WalletViewModelTest : BaseUnitTest() { .thenReturn(Result.success(Unit)) val testSut = WalletViewModel( + context = context, bgDispatcher = testDispatcher, walletRepo = testWalletRepo, lightningRepo = testLightningRepo, @@ -274,6 +279,7 @@ class WalletViewModelTest : BaseUnitTest() { .thenReturn(Result.success(Unit)) val testSut = WalletViewModel( + context = context, bgDispatcher = testDispatcher, walletRepo = testWalletRepo, lightningRepo = testLightningRepo, diff --git a/app/src/test/java/to/bitkit/ui/settings/appStatus/AppStatusViewModelTest.kt b/app/src/test/java/to/bitkit/ui/settings/appStatus/AppStatusViewModelTest.kt index 8cb5bf270..dc5ff93a1 100644 --- a/app/src/test/java/to/bitkit/ui/settings/appStatus/AppStatusViewModelTest.kt +++ b/app/src/test/java/to/bitkit/ui/settings/appStatus/AppStatusViewModelTest.kt @@ -36,6 +36,11 @@ class AppStatusViewModelTest : BaseUnitTest() { fun setUp() { whenever(context.getString(R.string.settings__status__backup__error)).thenReturn(failedBackupSubtitle) whenever(context.getString(R.string.settings__status__backup__ready)).thenReturn(readyBackupSubtitle) + whenever(context.getString(R.string.other__node_stopped)).thenReturn("Stopped") + whenever(context.getString(R.string.other__node_starting)).thenReturn("Starting") + whenever(context.getString(R.string.other__node_running)).thenReturn("Running") + whenever(context.getString(R.string.other__node_stopping)).thenReturn("Stopping") + whenever(context.getString(R.string.other__node_initializing)).thenReturn("Setting up wallet…") whenever(healthRepo.healthState).thenReturn(MutableStateFlow(AppHealthState())) whenever(lightningRepo.lightningState).thenReturn(MutableStateFlow(LightningState())) whenever(cacheStore.backupStatuses).thenReturn(flowOf(emptyMap())) diff --git a/docs/strings.md b/docs/strings.md new file mode 100644 index 000000000..f411b80f7 --- /dev/null +++ b/docs/strings.md @@ -0,0 +1,118 @@ +# Untranslated Strings Tracker + +This document tracks hardcoded strings in the codebase. Strings in dev-only screens do not need translation. + +## Dev-Only Strings (No Translation Needed) + +These screens are only accessible in development builds and contain hardcoded strings that don't need localization. + +### DevSettingsScreen.kt +| Line | String | +|------|--------| +| 52 | Fee Settings | +| 53 | Channel Orders | +| 54 | LDK Debug | +| 56 | LOGS | +| 57 | Logs | +| 59 | Export Logs | +| 66 | REGTEST | +| 68 | Blocktank Regtest | +| 71 | APP CACHE | +| 74 | Reset Settings State | +| 77 | Settings state reset | +| 81 | Reset All Activities | +| 84 | Activities removed | +| 88 | Reset Backup State | +| 91 | Backup state reset | +| 95 | Reset Widgets State | +| 98 | Widgets state reset | +| 102 | Refresh Currency Rates | +| 105 | Currency rates refreshed | +| 109 | Reset App Database | +| 112 | Database state reset | +| 116 | Reset Blocktank State | +| 119 | Blocktank state reset | +| 123 | Reset Cache Store | +| 126 | Cache store reset | +| 130 | Wipe App | +| 133 | Wallet wiped | +| 137 | DEBUG | +| 140 | Generate Test Activities | +| 144 | Generated $count test activities | +| 148 | Fake New BG Receive | +| 151 | Restart app to see the payment received sheet | +| 155 | Open Channel To Trusted Peer | +| 161 | NOTIFICATIONS | +| 164 | Register For LSP Notifications | +| 170 | Test LSP Notification | + +### LdkDebugScreen.kt +| Line | String | +|------|--------| +| 96 | LDK Debug | +| 105 | ADD PEER | +| 109 | pubkey@host:port | +| 120 | Add Peer | +| 127 | Paste & Add | +| 135 | NETWORK GRAPH | +| 137 | Log Graph Info | +| 149 | Export to File | +| 160 | VSS | +| 162 | List Keys | +| 164 | found | +| 191 | Delete key | +| 205 | Delete All | +| 211 | NODE | +| 213 | Restart | +| 225 | Delete All VSS Keys? | +| 226 | This will permanently delete all... | +| 227 | Delete All | + +### BlocktankRegtestScreen.kt +| Line | String | +|------|--------| +| 57 | Blocktank Regtest | +| 81 | These actions are executed on the staging Blocktank server node. | +| 84 | DEPOSIT | +| 97 | Amount (sats) | +| 104 | Depositing... / Make Deposit | +| 116 | Success | +| 117 | Deposit successful. TxID: ... | +| 123 | Failed to deposit | +| 136 | MINING | +| 146 | Block Count | +| 180 | Mining... / Mine Blocks | +| 162 | Success | +| 163 | Successfully mined $count blocks | +| 169 | Failed to mine | +| 185 | LIGHTNING PAYMENT | +| 189 | Invoice | +| 197 | Amount (optional, sats) | +| 204 | Pay Invoice | +| 215 | Success | +| 216 | Payment successful. ID: ... | +| 222 | Failed to pay invoice from LND | +| 232 | CHANNEL CLOSE | +| 236 | Funding TxID | +| 244 | Vout | +| 253 | Force Close After (seconds) | +| 260 | Close Channel | +| 279 | Success | +| 280 | Channel closed. Closing TxID: ... | + +### LogsScreen.kt +- All strings are technical log display (no localization needed) + +### ChannelOrdersScreen.kt +- All strings are technical channel order data (no localization needed) + +## Preview Functions + +Hardcoded strings in `@Preview` functions throughout the codebase do not need translation as they are only visible in Android Studio previews, not to end users. + +## Borderline Cases + +| File | Line | String | Notes | +|------|------|--------|-------| +| NotificationPreview.kt | 63 | 3m ago | Placeholder in notification mockup | +| BackgroundPaymentsSettings.kt | 111 | ₿ 21 000 | Example amount in preview |