Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
4327451
fix: gate sheets and rotate address on restore
ovitrif Nov 7, 2025
ba1e164
fix: gate new transaction sheet on restore
ovitrif Nov 7, 2025
e146b1c
refactor: unify should backup check
ovitrif Nov 7, 2025
9823b7f
chore: Cleanup and add todo
ovitrif Nov 7, 2025
bc53574
feat: dismiss keyboard on valid words paste
ovitrif Nov 7, 2025
918f111
refactor: encapsulate wallet init logic
ovitrif Nov 7, 2025
4aaa73b
chore: update bitkit-core to 0.1.24
ovitrif Nov 7, 2025
3fb3b06
refactor: use core tag metadata model
ovitrif Nov 7, 2025
ab5bacf
feat: backup & restore activity tags
ovitrif Nov 7, 2025
0ea7ab9
refactor: migrate RestoreWalletScreen to MVVM
ovitrif Nov 7, 2025
970a6b3
feat: nav to previous input on backspace if empty
ovitrif Nov 8, 2025
ce10188
feat: bold text in focused input
ovitrif Nov 8, 2025
7e22f02
fix: avoid validation errors on focused input
ovitrif Nov 8, 2025
ac21d53
feat: use bitkit-core for bip39 & checksum
ovitrif Nov 8, 2025
48534ef
feat: wipe core db on wipe wallet
ovitrif Nov 8, 2025
6a40ee1
feat: reset logs on wipe wallet
ovitrif Nov 11, 2025
896109a
Merge branch 'fix/rotate-address' into feat/backup-polish
ovitrif Nov 11, 2025
1ac1eb9
feat: reset blocktank repo data on wipe
ovitrif Nov 11, 2025
4e48d95
fix: logger crash in unit tests
ovitrif Nov 12, 2025
bfc497c
refactor: add activity.txType extension
ovitrif Nov 13, 2025
ca35a69
feat: reset activity state and wipe fixes
ovitrif Nov 14, 2025
4ecd67b
feat: integrate bitkit-core 0.1.27 minimally
ovitrif Nov 14, 2025
3efa06f
Merge branch 'master' into feat/backup-polish
ovitrif Nov 14, 2025
6effdff
chore: lint
ovitrif Nov 14, 2025
42a48c7
feat: use payload models for settings and widgets
ovitrif Nov 17, 2025
e699c54
fix: preserve backup times & fix race condition
ovitrif Nov 17, 2025
df10340
chore: backup status docs & comments
ovitrif Nov 17, 2025
67cfcf9
chore: fix params compiler ambiguity
ovitrif Nov 17, 2025
3aa6ff8
fix: notify observers after activity restore
ovitrif Nov 17, 2025
b5d724c
fix: restore wallet input cursor & text style
ovitrif Nov 17, 2025
b634464
refactor: extract wipe wallet use case
ovitrif Nov 17, 2025
c359bad
test: wipe wallet use case
ovitrif Nov 17, 2025
d831803
refactor: split restore screen content
ovitrif Nov 17, 2025
1f97313
chore: lint
ovitrif Nov 17, 2025
9994130
refactor: extract bip39 service
ovitrif Nov 17, 2025
d1f8e8e
test: restore screen viewmodel
ovitrif Nov 17, 2025
3ebd9d5
feat: backup relative dates
ovitrif Nov 18, 2025
59d9b48
chore: lint
ovitrif Nov 18, 2025
daed5db
fix: backup relative dates
ovitrif Nov 18, 2025
342e144
test: fix syncActivities success flow test
ovitrif Nov 18, 2025
1a47b85
test: validate wipe order
ovitrif Nov 18, 2025
907a54d
fix: support tab and newline mnemonic separators
ovitrif Nov 18, 2025
e173ae6
fix: dependencies repositories ordering
ovitrif Nov 18, 2025
f0e8371
chore: enable dynamic agent loading explicitly
ovitrif Nov 18, 2025
c6ed583
fix: clear widgets data on wipe
ovitrif Nov 19, 2025
0573515
Merge branch 'master' into feat/backup-polish
ovitrif Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import io.gitlab.arturbosch.detekt.Detekt
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
Expand Down Expand Up @@ -34,6 +36,8 @@ val keystoreProperties by lazy {
keystoreProperties
}

val locales = listOf("en", "ar", "ca", "cs", "de", "el", "es", "fr", "it", "nl", "pl", "pt", "ru")

android {
namespace = "to.bitkit"
compileSdk = 35
Expand All @@ -49,6 +53,7 @@ android {
}
buildConfigField("boolean", "E2E", System.getenv("E2E")?.toBoolean()?.toString() ?: "false")
buildConfigField("boolean", "GEO", System.getenv("GEO")?.toBoolean()?.toString() ?: "true")
buildConfigField("String", "LOCALES", "\"${locales.joinToString(",")}\"")
}

flavorDimensions += "network"
Expand Down Expand Up @@ -131,7 +136,7 @@ android {
}
androidResources {
@Suppress("UnstableApiUsage")
localeFilters.addAll(listOf("en", "ar", "ca", "cs", "de", "el", "es", "fr", "it", "nl", "pl", "pt", "ru"))
localeFilters.addAll(locales)
@Suppress("UnstableApiUsage")
generateLocaleConfig = true
}
Expand All @@ -153,7 +158,7 @@ android {
applicationVariants.all {
val variant = this
outputs
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.map { it as BaseVariantOutputImpl }
.forEach { output ->
val apkName = "bitkit-android-${defaultConfig.versionCode}-${variant.name}.apk"
output.outputFileName = apkName
Expand All @@ -169,17 +174,6 @@ composeCompiler {
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}

tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
ignoreFailures = true
reports {
html.required.set(true)
sarif.required.set(true)
md.required.set(false)
txt.required.set(false)
xml.required.set(false)
}
}

dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(libs.jna) { artifact { type = "aar" } }
Expand Down Expand Up @@ -281,6 +275,19 @@ room {
schemaDirectory("$projectDir/schemas")
}

// region Tasks

tasks.withType<Detekt>().configureEach {
ignoreFailures = true
reports {
html.required.set(true)
sarif.required.set(true)
md.required.set(false)
txt.required.set(false)
xml.required.set(false)
}
}

tasks.withType<Test> {
testLogging {
events(
Expand All @@ -297,3 +304,12 @@ tasks.withType<Test> {
showStackTraces = true
}
}

// JDK 21+ prints warnings when ByteBuddy loads a dynamic Java agent during tests.
// Our test stack triggers this automatically.
// Explicitly enabling dynamic agent loading silences the warning without altering behavior.
tasks.withType<Test>().configureEach {
jvmArgs("-XX:+EnableDynamicAgentLoading")
}

// endregion
47 changes: 0 additions & 47 deletions app/detekt-baseline.xml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions app/src/main/java/to/bitkit/data/SettingsStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import to.bitkit.env.Env
import to.bitkit.models.BitcoinDisplayUnit
import to.bitkit.models.CoinSelectionPreference
import to.bitkit.models.PrimaryDisplay
import to.bitkit.models.SettingsBackupV1
import to.bitkit.models.Suggestion
import to.bitkit.models.TransactionSpeed
import to.bitkit.utils.Logger
Expand All @@ -30,6 +31,14 @@ class SettingsStore @Inject constructor(

val data: Flow<SettingsData> = store.data

suspend fun restoreFromBackup(payload: SettingsBackupV1) =
runCatching {
val data = payload.settings.resetPin()
store.updateData { data }
}.onSuccess {
Logger.debug("Restored settings", TAG)
}

suspend fun update(transform: (SettingsData) -> SettingsData) {
store.updateData(transform)
}
Expand Down Expand Up @@ -61,6 +70,7 @@ class SettingsStore @Inject constructor(
}

companion object {
private const val TAG = "SettingsStore"
private const val MAX_LAST_USED_TAGS = 10
}
}
Expand Down
23 changes: 16 additions & 7 deletions app/src/main/java/to/bitkit/data/WidgetsStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import to.bitkit.data.dto.price.PriceDTO
import to.bitkit.data.serializers.WidgetsSerializer
import to.bitkit.models.WidgetType
import to.bitkit.models.WidgetWithPosition
import to.bitkit.models.WidgetsBackupV1
import to.bitkit.models.widget.BlocksPreferences
import to.bitkit.models.widget.CalculatorValues
import to.bitkit.models.widget.FactsPreferences
Expand Down Expand Up @@ -43,9 +44,13 @@ class WidgetsStore @Inject constructor(
val weatherFlow: Flow<WeatherDTO?> = data.map { it.weather }
val priceFlow: Flow<PriceDTO?> = data.map { it.price }

suspend fun update(transform: (WidgetsData) -> WidgetsData) {
store.updateData(transform)
}
suspend fun restoreFromBackup(payload: WidgetsBackupV1) =
runCatching {
val data = payload.widgets
store.updateData { data }
}.onSuccess {
Logger.debug("Restored widgets", TAG)
}

suspend fun updateCalculatorValues(calculatorValues: CalculatorValues) {
store.updateData {
Expand Down Expand Up @@ -127,16 +132,16 @@ class WidgetsStore @Inject constructor(
suspend fun addWidget(type: WidgetType) {
if (store.data.first().widgets.map { it.type }.contains(type)) return

store.updateData {
it.copy(widgets = (it.widgets + WidgetWithPosition(type = type)).sortedBy { it.position })
store.updateData { data ->
data.copy(widgets = (data.widgets + WidgetWithPosition(type = type)).sortedBy { it.position })
}
}

suspend fun deleteWidget(type: WidgetType) {
if (!store.data.first().widgets.map { it.type }.contains(type)) return

store.updateData {
it.copy(widgets = it.widgets.filterNot { it.type == type })
store.updateData { data ->
data.copy(widgets = data.widgets.filterNot { it.type == type })
}
}

Expand All @@ -145,6 +150,10 @@ class WidgetsStore @Inject constructor(
it.copy(widgets = widgets)
}
}

companion object {
private const val TAG = "WidgetsStore"
}
}

@Serializable
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/java/to/bitkit/env/Env.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal object Env {
const val isE2eTest = BuildConfig.E2E
const val isGeoblockingEnabled = BuildConfig.GEO
val network = Network.valueOf(BuildConfig.NETWORK)
val locales = BuildConfig.LOCALES.split(",")
val walletSyncIntervalSecs = 10_uL // TODO review
val platform = "Android ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})"
const val version = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
Expand Down Expand Up @@ -116,10 +117,10 @@ internal object Env {
Logger.info("App storage path: $path")
}

val logDir: String
val logDir: File
get() {
require(::appStoragePath.isInitialized)
return File(appStoragePath).resolve("logs").ensureDir().path
return File(appStoragePath).resolve("logs").ensureDir()
}

fun ldkStoragePath(walletIndex: Int) = storagePathOf(walletIndex, network.name.lowercase(), "ldk")
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/to/bitkit/ext/Activities.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ fun Activity.rawId(): String = when (this) {
is Activity.Onchain -> v1.id
}

fun Activity.txType(): PaymentType = when (this) {
is Activity.Lightning -> v1.txType
is Activity.Onchain -> v1.txType
}

/**
* Calculates the total value of an activity based on its type.
*
Expand Down
Loading
Loading