Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7cba039
refactor: Cleanup debug Manifest
ovitrif Aug 22, 2024
62f36cc
wip: Scaffold UI state
ovitrif Sep 2, 2024
6794a27
refactor: Rename to WalletViewModel
ovitrif Sep 2, 2024
2f3f3b2
feat: Add LCE (Loading Content Error) UiState flows and UI
ovitrif Sep 2, 2024
83205a6
chore: Update dependencies
ovitrif Sep 11, 2024
daf6d61
chore: Migrate to Kotlin 2 and Use Compose Compiler
ovitrif Sep 11, 2024
8f0c65b
fix: ellipsisVisualTransformation on Kotlin 2 Compose
ovitrif Sep 11, 2024
fdbc206
chore: Remove NDK version spec
ovitrif Sep 11, 2024
a0e5b1c
feat: Declare error types
ovitrif Sep 11, 2024
4453d8c
refactor: Move Constants to env package
ovitrif Sep 11, 2024
e27610d
chore: Print encrypted keychain value in ui test
ovitrif Sep 12, 2024
6ed233c
feat: Use Keychain typed errors
ovitrif Sep 12, 2024
9fc95f9
feat: Use typed errors in Lightning service
ovitrif Sep 15, 2024
46c0474
feat: Use typed errors in Migration service
ovitrif Sep 15, 2024
0cfed89
feat: Use typed errors in OnChain service
ovitrif Sep 15, 2024
5f05445
feat: Use LdkError
ovitrif Sep 15, 2024
1d9e9f7
feat: Generate keypair for BT push notifications
ovitrif Sep 16, 2024
ee323a9
feat: Use typed errors in Blocktank service
ovitrif Sep 16, 2024
599d681
refactor: Env & const's reorg, cleanup, alignment with iOS
ovitrif Sep 16, 2024
a17842b
feat: Unit testing dependencies
ovitrif Sep 16, 2024
9cf90ac
feat: Viewmodel unit test samples
ovitrif Sep 16, 2024
1bda1da
refactor: Remove EsploraApi
ovitrif Sep 16, 2024
1781c08
feat: Keychain en/decrypt to bytes
ovitrif Sep 16, 2024
15b7767
refactor: Cleanup test
ovitrif Sep 16, 2024
63b4247
refactor: Extract Tags
ovitrif Sep 17, 2024
2ab7c8a
refactor: Rename BlocktankApi to BlocktankClient
ovitrif Sep 18, 2024
47425d8
refactor: Cleanup LnPeer
ovitrif Sep 18, 2024
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
52 changes: 30 additions & 22 deletions app/build.gradle.kts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand All@@ -10,7 +13,6 @@ plugins {
android {
namespace = "to.bitkit"
compileSdk = 34
ndkVersion = "26.1.10909125" // probably required by LDK bindings? - safer to keep it for now.
defaultConfig {
applicationId = "to.bitkit"
minSdk = 28
Expand DownExpand Up@@ -54,10 +56,6 @@ android {
buildConfig = true
compose = true
}
composeOptions {
// https://developer.android.com/jetpack/androidx/releases/compose-kotlin#pre-release_kotlin_compatibility
kotlinCompilerExtensionVersion = "1.5.14"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
Expand All@@ -66,11 +64,18 @@ android {
@Suppress("UnstableApiUsage")
testOptions {
unitTests {
// isReturnDefaultValues = true // mockito
// isIncludeAndroidResources = true // robolectric
isReturnDefaultValues = true // mockito
isIncludeAndroidResources = true // robolectric
}
}
}
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
)
reportsDestination = layout.buildDirectory.dir("compose_compiler")
}
dependencies {
implementation(fileTree("libs") { include("*.aar") })
implementation(platform(libs.kotlin.bom))
Expand All@@ -81,6 +86,7 @@ dependencies {
implementation(libs.datastore.preferences)
// BDK + LDK
implementation(libs.bdk.android)
implementation(libs.bitcoinj.core)
implementation(libs.ldk.node.android)
// Firebase
implementation(platform(libs.firebase.bom))
Expand All@@ -96,12 +102,12 @@ dependencies {
// Compose
implementation(platform(libs.compose.bom))
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.material3)
implementation(libs.material.icons.extended)
implementation(libs.ui.tooling.preview)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
androidTestImplementation(libs.ui.test.junit4)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
androidTestImplementation(libs.compose.ui.test.junit4)
// Compose Navigation
implementation(libs.navigation.compose)
androidTestImplementation(libs.navigation.testing)
Expand All@@ -127,17 +133,19 @@ dependencies {
ksp(libs.room.compiler)
testImplementation(libs.room.testing)
// Test + Debug
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.junit.ext)
androidTestImplementation(kotlin("test"))
androidTestImplementation(libs.test.core)
androidTestImplementation(libs.test.coroutines)
androidTestImplementation(libs.test.espresso.core)
androidTestImplementation(libs.test.junit.ext)
testImplementation(kotlin("test"))
testImplementation(libs.junit.junit)
// testImplementation("androidx.test:core:1.6.1")
// testImplementation("org.mockito:mockito-core:5.12.0")
// testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// testImplementation("org.robolectric:robolectric:4.13")
// Other
implementation(libs.guava) // for ByteArray.toHex()+
testImplementation(libs.test.core)
testImplementation(libs.test.coroutines)
testImplementation(libs.test.junit)
testImplementation(libs.test.junit.ext)
testImplementation(libs.test.mockito.kotlin)
testImplementation(libs.test.robolectric)
testImplementation(libs.test.turbine)
}
ksp {
// cool but strict: https://developer.android.com/jetpack/androidx/releases/room#2.6.0
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,6 @@ import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
Expand All@@ -14,20 +13,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import to.bitkit.data.AppDb
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.test.BaseTest
import to.bitkit.shared.KeychainError
import to.bitkit.test.BaseAndroidTest
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class KeychainStoreTest : BaseTest() {
class KeychainTest : BaseAndroidTest() {

private val appContext by lazy { ApplicationProvider.getApplicationContext<Context>() }
private lateinit var db: AppDb

private lateinit var sut: KeychainStore
private lateinit var sut: Keychain

@Before
fun setUp() {
Expand All@@ -42,7 +41,7 @@ class KeychainStoreTest : BaseTest() {
}
}

sut = KeychainStore(
sut = Keychain(
db,
appContext,
testDispatcher,
Expand DownExpand Up@@ -70,7 +69,7 @@ class KeychainStoreTest : BaseTest() {
val key = "key"
sut.saveString(key, "value1")

assertFailsWith<IllegalArgumentException> { sut.saveString(key, "value2") }
assertFailsWith<KeychainError.FailedToSaveAlreadyExists> { sut.saveString(key, "value2") }
}

@Test
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,7 +30,7 @@ class LdkMigrationTest {
runBlocking { start() }

assertTrue { nodeId == "02cd08b7b375e4263849121f9f0ffb2732a0b88d0fb74487575ac539b374f45a55" }
assertTrue { channels.isNotEmpty() }
assertTrue { channels?.isNotEmpty() == true }

runBlocking { stop() }
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/androidTest/java/to/bitkit/test/BaseAndroidTest.kt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
package to.bitkit.test

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Rule

@OptIn(ExperimentalCoroutinesApi::class)
abstract class BaseAndroidTest(
testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) {
@get:Rule
val coroutinesTestRule = MainDispatcherRule(testDispatcher)

protected val testDispatcher get() = coroutinesTestRule.testDispatcher

protected fun test(block: suspend TestScope.() -> Unit) = runTest(testDispatcher) { block() }
}
12 changes: 4 additions & 8 deletions app/src/debug/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App"></application>
</manifest>
tools:ignore="MissingApplicationIcon" />
</manifest>
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,11 +7,12 @@ import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import to.bitkit.env.Env
import javax.inject.Inject
import kotlin.reflect.typeOf

@HiltAndroidApp
internal class App : Application(), Configuration.Provider {
internal open class App : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

Expand Down
113 changes: 0 additions & 113 deletions app/src/main/java/to/bitkit/Constants.kt

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import to.bitkit.Tag.APP
import to.bitkit.env.Tag.APP
import to.bitkit.ext.callerName
import to.bitkit.shared.measured
import java.util.concurrent.Executors
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/data/AppDb.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ import androidx.work.WorkerParameters
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import to.bitkit.BuildConfig
import to.bitkit.Env
import to.bitkit.data.entities.ConfigEntity
import to.bitkit.env.Env

@Database(
entities = [
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,28 +5,32 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import to.bitkit.shared.BlocktankError
import javax.inject.Inject
import javax.inject.Singleton

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
@Singleton
class BlocktankClient @Inject constructor(
private val client: HttpClient,
) : LspApi {
) {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
private suspend inline fun <reified T> post(url: String, payload: T): HttpResponse {
val response = client.post(url) { setBody(payload) }
return when (val statusCode = response.status.value) {
!in 200..299 -> throw BlocktankError.InvalidResponse(statusCode)
else -> response
}
}
}

@Serializable
Expand Down
Loading