Skip to content

Commit f8cf404

Browse files
committed
Merge branch 'master' into fix/price-widget-layout
2 parents 17a15f8 + cf1c8bc commit f8cf404

11 files changed

Lines changed: 135 additions & 51 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches: [ "master" ]
66
pull_request:
7+
types: [ opened, synchronize, reopened, ready_for_review ]
78

89
workflow_dispatch:
910

.github/workflows/e2e.yml

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ on:
88
required: false
99
default: "default-feature-branch"
1010
pull_request:
11+
paths:
12+
- 'app/**'
13+
- 'gradle/**'
14+
- '*.gradle.kts'
15+
- 'gradle.properties'
16+
- '.github/workflows/e2e.yml'
1117

1218
env:
1319
TERM: xterm-256color
@@ -104,6 +110,28 @@ jobs:
104110
sudo udevadm control --reload-rules
105111
sudo udevadm trigger --name-match=kvm
106112
113+
- name: AVD cache
114+
uses: actions/cache@v4
115+
id: avd-cache
116+
with:
117+
path: |
118+
~/.android/avd/*
119+
~/.android/adb*
120+
key: avd-33-x86_64-pixel_6
121+
122+
- name: Create AVD and generate snapshot for caching
123+
if: steps.avd-cache.outputs.cache-hit != 'true'
124+
uses: reactivecircus/android-emulator-runner@v2
125+
with:
126+
profile: pixel_6
127+
api-level: 33
128+
arch: x86_64
129+
avd-name: Pixel_6
130+
force-avd-creation: false
131+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
132+
disable-animations: false
133+
script: echo "Generated AVD snapshot for caching."
134+
107135
- name: Download APK
108136
uses: actions/download-artifact@v4
109137
with:
@@ -156,7 +184,7 @@ jobs:
156184
arch: x86_64
157185
avd-name: Pixel_6
158186
force-avd-creation: false
159-
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none
187+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
160188
script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ matrix.shard.grep }}"
161189
env:
162190
RECORD_VIDEO: true
@@ -173,7 +201,7 @@ jobs:
173201
arch: x86_64
174202
avd-name: Pixel_6
175203
force-avd-creation: false
176-
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none
204+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
177205
script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ matrix.shard.grep }}"
178206
env:
179207
RECORD_VIDEO: true
@@ -189,7 +217,7 @@ jobs:
189217
arch: x86_64
190218
avd-name: Pixel_6
191219
force-avd-creation: false
192-
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none
220+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
193221
script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ matrix.shard.grep }}"
194222
env:
195223
RECORD_VIDEO: true

.github/workflows/e2e_migration.yml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,28 @@ jobs:
108108
sudo udevadm control --reload-rules
109109
sudo udevadm trigger --name-match=kvm
110110
111+
- name: AVD cache
112+
uses: actions/cache@v4
113+
id: avd-cache
114+
with:
115+
path: |
116+
~/.android/avd/*
117+
~/.android/adb*
118+
key: avd-33-x86_64-pixel_6
119+
120+
- name: Create AVD and generate snapshot for caching
121+
if: steps.avd-cache.outputs.cache-hit != 'true'
122+
uses: reactivecircus/android-emulator-runner@v2
123+
with:
124+
profile: pixel_6
125+
api-level: 33
126+
arch: x86_64
127+
avd-name: Pixel_6
128+
force-avd-creation: false
129+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
130+
disable-animations: false
131+
script: echo "Generated AVD snapshot for caching."
132+
111133
- name: Download APK
112134
uses: actions/download-artifact@v4
113135
with:
@@ -149,7 +171,7 @@ jobs:
149171
arch: x86_64
150172
avd-name: Pixel_6
151173
force-avd-creation: false
152-
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none
174+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
153175
script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ matrix.scenario.grep }}"
154176
env:
155177
BACKEND: regtest
@@ -167,7 +189,7 @@ jobs:
167189
arch: x86_64
168190
avd-name: Pixel_6
169191
force-avd-creation: false
170-
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none
192+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
171193
script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ matrix.scenario.grep }}"
172194
env:
173195
BACKEND: regtest
@@ -184,7 +206,7 @@ jobs:
184206
arch: x86_64
185207
avd-name: Pixel_6
186208
force-avd-creation: false
187-
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none
209+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-front none
188210
script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ matrix.scenario.grep }}"
189211
env:
190212
BACKEND: regtest

app/src/main/java/to/bitkit/repositories/LightningRepo.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import com.synonym.bitkitcore.PreActivityMetadata
88
import com.synonym.bitkitcore.Scanner
99
import com.synonym.bitkitcore.createChannelRequestUrl
1010
import com.synonym.bitkitcore.createWithdrawCallbackUrl
11-
import com.synonym.bitkitcore.decode
1211
import com.synonym.bitkitcore.lnurlAuth
1312
import kotlinx.coroutines.CoroutineDispatcher
1413
import kotlinx.coroutines.CoroutineScope
@@ -256,6 +255,12 @@ class LightningRepo @Inject constructor(
256255
scope.launch { registerForNotifications() }
257256
Unit
258257
}.onFailure { e ->
258+
val currentLifecycleState = _lightningState.value.nodeLifecycleState
259+
if (currentLifecycleState.isRunning()) {
260+
Logger.warn("Start error occurred but node is $currentLifecycleState, skipping retry", e, context = TAG)
261+
return@withContext Result.success(Unit)
262+
}
263+
259264
if (shouldRetry) {
260265
val retryDelay = 2.seconds
261266
Logger.warn("Start error, retrying after $retryDelay...", e, context = TAG)
@@ -546,7 +551,7 @@ class LightningRepo @Inject constructor(
546551
return runCatching {
547552
// TODO use bitkit-core getLnurlInvoice if it works with callbackUrl
548553
val bolt11 = lnurlService.fetchLnurlInvoice(callbackUrl, amountSats, comment).getOrThrow().pr
549-
val decoded = (decode(bolt11) as Scanner.Lightning).invoice
554+
val decoded = (coreService.decode(bolt11) as Scanner.Lightning).invoice
550555
return@runCatching decoded
551556
}.onFailure {
552557
Logger.error(

app/src/main/java/to/bitkit/repositories/WalletRepo.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package to.bitkit.repositories
33
import com.synonym.bitkitcore.AddressType
44
import com.synonym.bitkitcore.PreActivityMetadata
55
import com.synonym.bitkitcore.Scanner
6-
import com.synonym.bitkitcore.decode
76
import kotlinx.coroutines.CoroutineDispatcher
87
import kotlinx.coroutines.CoroutineScope
98
import kotlinx.coroutines.Job
@@ -137,7 +136,7 @@ class WalletRepo @Inject constructor(
137136
) {
138137
val onChainAddress = getOnchainAddress()
139138
val paymentHash = runCatching {
140-
when (val decoded = decode(bip21Url)) {
139+
when (val decoded = coreService.decode(bip21Url)) {
141140
is Scanner.Lightning -> decoded.invoice.paymentHash.toHex()
142141
is Scanner.OnChain -> decoded.extractLightningHash()
143142
else -> null
@@ -418,7 +417,7 @@ class WalletRepo @Inject constructor(
418417
if (bolt11.isEmpty()) return@withContext null
419418

420419
runCatching {
421-
when (val decoded = decode(bolt11)) {
420+
when (val decoded = coreService.decode(bolt11)) {
422421
is Scanner.Lightning -> decoded.invoice.paymentHash.toHex()
423422
else -> null
424423
}
@@ -544,7 +543,7 @@ class WalletRepo @Inject constructor(
544543
private suspend fun Scanner.OnChain.extractLightningHash(): String? {
545544
val lightningInvoice: String = this.invoice.params?.get("lightning") ?: return null
546545

547-
return when (val decoded = decode(lightningInvoice)) {
546+
return when (val decoded = coreService.decode(lightningInvoice)) {
548547
is Scanner.Lightning -> decoded.invoice.paymentHash.toHex()
549548
else -> null
550549
}

app/src/main/java/to/bitkit/services/CoreService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.synonym.bitkitcore.OnchainActivity
2020
import com.synonym.bitkitcore.PaymentState
2121
import com.synonym.bitkitcore.PaymentType
2222
import com.synonym.bitkitcore.PreActivityMetadata
23+
import com.synonym.bitkitcore.Scanner
2324
import com.synonym.bitkitcore.SortDirection
2425
import com.synonym.bitkitcore.WordCount
2526
import com.synonym.bitkitcore.addTags
@@ -185,6 +186,12 @@ class CoreService @Inject constructor(
185186
com.synonym.bitkitcore.isAddressUsed(address = address)
186187
}
187188

189+
@Suppress("ForbiddenComment")
190+
suspend fun decode(input: String): Scanner = ServiceQueue.CORE.background {
191+
// TODO: Remove lowercase workaround when https://github.com/synonymdev/bitkit-core/issues/66 is fixed
192+
com.synonym.bitkitcore.decode(input.lowercase())
193+
}
194+
188195
companion object {
189196
private const val TAG = "CoreService"
190197
}

app/src/main/java/to/bitkit/ui/onboarding/OnboardingSlidesScreen.kt

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import androidx.compose.foundation.layout.size
2020
import androidx.compose.foundation.pager.HorizontalPager
2121
import androidx.compose.foundation.pager.rememberPagerState
2222
import androidx.compose.foundation.shape.CircleShape
23-
import androidx.compose.material3.Text
24-
import androidx.compose.material3.TextButton
2523
import androidx.compose.runtime.Composable
2624
import androidx.compose.runtime.getValue
2725
import androidx.compose.runtime.rememberCoroutineScope
@@ -33,16 +31,16 @@ import androidx.compose.ui.layout.ContentScale
3331
import androidx.compose.ui.platform.testTag
3432
import androidx.compose.ui.res.painterResource
3533
import androidx.compose.ui.res.stringResource
36-
import androidx.compose.ui.text.font.FontWeight
3734
import androidx.compose.ui.tooling.preview.Preview
3835
import androidx.compose.ui.unit.IntOffset
3936
import androidx.compose.ui.unit.dp
40-
import androidx.compose.ui.unit.sp
4137
import kotlinx.coroutines.launch
4238
import to.bitkit.R
4339
import to.bitkit.ui.components.BodyM
40+
import to.bitkit.ui.components.ButtonSize
4441
import to.bitkit.ui.components.Display
4542
import to.bitkit.ui.components.Footnote
43+
import to.bitkit.ui.components.SecondaryButton
4644
import to.bitkit.ui.components.VerticalSpacer
4745
import to.bitkit.ui.scaffold.AppTopBar
4846
import to.bitkit.ui.shared.util.screen
@@ -153,31 +151,27 @@ fun OnboardingSlidesScreen(
153151
titleText = null,
154152
actions = {
155153
if (pagerState.currentPage == LAST_PAGE_INDEX) {
156-
TextButton(
154+
SecondaryButton(
155+
text = stringResource(R.string.onboarding__advanced_setup),
157156
onClick = onAdvancedSetupClick,
158-
modifier = Modifier.testTag("Passphrase")
159-
) {
160-
Text(
161-
text = stringResource(R.string.onboarding__advanced_setup),
162-
fontSize = 17.sp,
163-
color = Colors.White64,
164-
fontWeight = FontWeight.SemiBold,
165-
)
166-
}
157+
size = ButtonSize.Small,
158+
fullWidth = false,
159+
modifier = Modifier
160+
.testTag("Passphrase")
161+
.padding(horizontal = 16.dp)
162+
)
167163
} else {
168-
TextButton(
164+
SecondaryButton(
165+
text = stringResource(R.string.onboarding__skip),
169166
onClick = {
170167
scope.launch { pagerState.animateScrollToPage(pagerState.pageCount - 1) }
171168
},
172-
modifier = Modifier.testTag("SkipButton")
173-
) {
174-
Text(
175-
text = stringResource(R.string.onboarding__skip),
176-
fontSize = 17.sp,
177-
color = Colors.White64,
178-
fontWeight = FontWeight.SemiBold,
179-
)
180-
}
169+
size = ButtonSize.Small,
170+
fullWidth = false,
171+
modifier = Modifier
172+
.testTag("SkipButton")
173+
.padding(horizontal = 16.dp)
174+
)
181175
}
182176
}
183177
)

app/src/main/java/to/bitkit/ui/utils/AutoReadClipboardHandler.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import androidx.lifecycle.Lifecycle
1616
import androidx.lifecycle.LifecycleEventObserver
1717
import androidx.lifecycle.compose.LocalLifecycleOwner
1818
import androidx.lifecycle.compose.collectAsStateWithLifecycle
19-
import com.synonym.bitkitcore.decode
2019
import kotlinx.coroutines.delay
2120
import kotlinx.coroutines.launch
2221
import to.bitkit.R
2322
import to.bitkit.ext.getClipboardText
2423
import to.bitkit.ui.appViewModel
2524
import to.bitkit.ui.scaffold.AppAlertDialog
2625
import to.bitkit.ui.settingsViewModel
26+
import to.bitkit.viewmodels.AppViewModel
2727

2828
@Composable
2929
fun AutoReadClipboardHandler() {
@@ -43,7 +43,7 @@ fun AutoReadClipboardHandler() {
4343
// Check clipboard on app startup - only after authentication
4444
LaunchedEffect(isAuthenticated, isAutoReadClipboardEnabled) {
4545
if (isAuthenticated && isAutoReadClipboardEnabled && !hasCheckedOnStartup) {
46-
showClipboardDialog = context.hasScanDataInClipboard()
46+
showClipboardDialog = hasScanDataInClipboard(context, appViewModel)
4747
hasCheckedOnStartup = true
4848
}
4949
}
@@ -60,7 +60,7 @@ fun AutoReadClipboardHandler() {
6060
if (event == Lifecycle.Event.ON_RESUME && isAuthenticated) {
6161
if (hasCheckedOnStartup && isAutoReadClipboardEnabled) {
6262
scope.launch {
63-
showClipboardDialog = context.hasScanDataInClipboard()
63+
showClipboardDialog = hasScanDataInClipboard(context, appViewModel)
6464
}
6565
}
6666
}
@@ -86,12 +86,11 @@ fun AutoReadClipboardHandler() {
8686
}
8787
}
8888

89-
private suspend fun Context.hasScanDataInClipboard(): Boolean {
89+
private suspend fun hasScanDataInClipboard(context: Context, appViewModel: AppViewModel): Boolean {
9090
delay(1000) // delay needed for Android clipboard accessibility on start
9191

92-
val clipText = this.getClipboardText()
92+
val clipText = context.getClipboardText()
9393
if (clipText.isNullOrBlank()) return false
9494

95-
val scanResult = runCatching { decode(clipText) }
96-
return scanResult.isSuccess
95+
return appViewModel.canDecodeClipboard(clipText)
9796
}

0 commit comments

Comments
 (0)