Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
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
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
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
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
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
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
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
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
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
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
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
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
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
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
14 changes: 14 additions & 0 deletions .editorconfig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
root = true

# Apply to all files
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length=120
indent_style = space
indent_size = 4

[*.md]
max_line_length = off
trim_trailing_whitespace = false
8 changes: 7 additions & 1 deletion app/build.gradle.kts
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,9 +97,11 @@ dependencies {
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycleVersion")

// Compose Navigation
val composeNavigationVersion = "2.7.7"
implementation("androidx.navigation:navigation-compose:$composeNavigationVersion")
androidTestImplementation("androidx.navigation:navigation-testing:$composeNavigationVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Compose Tooling for Android Studio Preview
val composeToolingVersion = "1.6.8"
Expand All@@ -110,7 +112,11 @@ dependencies {
val hiltVersion = "2.51.1"
implementation("com.google.dagger:hilt-android:$hiltVersion")
ksp("com.google.dagger:hilt-android-compiler:$hiltVersion")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
ksp("androidx.hilt:hilt-compiler:1.2.0")

// WorkManager
implementation("androidx.hilt:hilt-work:1.2.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")

// Material Design
implementation("com.google.android.material:material:1.12.0")
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
Expand DownExpand Up@@ -39,6 +40,12 @@
</intent-filter>
</service>

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />

<!-- Set custom default icon.
This is used when no icon is set for incoming notification messages.
See https://goo.gl/l4GJaQ -->
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/to/bitkit/App.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,11 +4,22 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import kotlin.reflect.typeOf

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

override val workManagerConfiguration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()

override fun onCreate() {
super.onCreate()
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,4 +7,5 @@ internal const val _FCM = "_FCM"
internal const val _LDK = "_LDK"
internal const val _BDK = "_BDK"

internal val NETWORK = Network.REGTEST
internal val BDK_NETWORK = Network.REGTEST
internal val LDK_NETWORK get() = org.ldk.enums.Network.LDKNetwork_Regtest
5 changes: 4 additions & 1 deletion app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,12 +9,15 @@ import to.bitkit.ldk.Ldk
import to.bitkit.ldk.init
import to.bitkit.ldk.ldkDir
import to.bitkit.ui.MainActivity
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken
import java.io.File

class LauncherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
warmupNode(filesDir.absolutePath)
startActivity(Intent(this, MainActivity::class.java))
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/to/bitkit/bdk/Bdk.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ import org.ldk.structs.Result_NoneAPIErrorZ
import org.ldk.structs.Result_ThirtyTwoBytesAPIErrorZ
import org.ldk.structs.UserConfig
import org.ldk.util.UInt128
import to.bitkit.NETWORK
import to.bitkit.BDK_NETWORK
import to.bitkit._BDK
import to.bitkit._LDK
import to.bitkit.bdk.Bdk.wallet
Expand All@@ -43,12 +43,12 @@ object Bdk {

private fun initWallet() {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(NETWORK, Mnemonic.fromString(mnemonic), null)
val key = DescriptorSecretKey(BDK_NETWORK, Mnemonic.fromString(mnemonic), null)

wallet = Wallet(
Descriptor.newBip84(key, KeychainKind.INTERNAL, NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, NETWORK),
NETWORK,
Descriptor.newBip84(key, KeychainKind.INTERNAL, BDK_NETWORK),
Descriptor.newBip84(key, KeychainKind.EXTERNAL, BDK_NETWORK),
BDK_NETWORK,
DatabaseConfig.Memory,
)

Expand DownExpand Up@@ -86,7 +86,7 @@ object Bdk {
fun getLdkEntropy(): ByteArray {
val mnemonic = loadMnemonic()
val key = DescriptorSecretKey(
network = NETWORK,
network = BDK_NETWORK,
mnemonic = Mnemonic.fromString(mnemonic),
password = null,
)
Expand DownExpand Up@@ -141,9 +141,9 @@ object Bdk {
} catch (e: Throwable) {
// if mnemonic doesn't exist, generate one and save it
Log.d(_BDK, "No mnemonic backup, we'll create a new wallet")
val mnemonic = Mnemonic(WordCount.WORDS12)
mnemonicFile.writeText(mnemonic.asString())
mnemonic.asString()
val mnemonic = Mnemonic(WordCount.WORDS12).asString()
mnemonicFile.writeText(mnemonic)
mnemonic
}
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/to/bitkit/data/Syncer.kt
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,7 +82,6 @@ class LdkSyncer @Inject constructor(
}

}

}

// Add confirmed Tx from filtered Transaction Ids
Expand Down
77 changes: 69 additions & 8 deletions app/src/main/java/to/bitkit/fcm/MessagingService.kt
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
package to.bitkit.fcm

import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import to.bitkit._FCM
import to.bitkit.data.RestApi
import to.bitkit.data.Syncer
import to.bitkit.ui.payInvoice
import to.bitkit.warmupNode
import java.util.Date

internal class MessagingService : FirebaseMessagingService() {
Expand DownExpand Up@@ -33,7 +46,7 @@ internal class MessagingService : FirebaseMessagingService() {
Log.d(_FCM, "\n")

if (message.needsScheduling()) {
scheduleJob()
scheduleJob(message.data)
} else {
handleNow(message.data)
}
Expand All@@ -42,7 +55,7 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Handle message within 10 seconds.
* Handle message within 10 seconds.
*/
private fun handleNow(data: Map<String, String>) {
val bolt11 = data["bolt11"].orEmpty()
Expand All@@ -54,19 +67,67 @@ internal class MessagingService : FirebaseMessagingService() {
}

/**
* TODO Schedule async work using WorkManager for long-running tasks (10 seconds or more)
* Schedule async work using WorkManager for tasks of 10+ seconds.
*/
private fun scheduleJob() {
TODO("Not yet implemented: scheduleJob")
private fun scheduleJob(messageData: Map<String, String>) {
val work = OneTimeWorkRequest.Builder(PayWorker::class.java)
.setInputData(
Data.Builder()
.putString("bolt11", messageData["bolt11"].orEmpty())
.build()
)
.build()
WorkManager.getInstance(this)
.beginWith(work)
.enqueue()
}

private fun RemoteMessage.needsScheduling(): Boolean {
// return notification == null && data.isNotEmpty()
return false
return notification == null &&
data.containsKey("bolt11")
}

override fun onNewToken(token: String) {
this.token = token
Log.d(_FCM, "onNewToken: $token")
}
}
}

@HiltWorker
class PayWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@Assisted private val workerParams: WorkerParameters,
private val syncer: Syncer,
private val restApi: RestApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
Log.d(_FCM, "Node waking up from notification…")
warmupNode(appContext.filesDir.absolutePath)
.let { Log.d(_FCM, "Node wakeup result: $it…") }

Log.d(_FCM, "Syncing BDK & LDK from notification…")
syncer.sync()

restApi.connectPeer()
.let { Log.d(_FCM, "Connect peer from notification result: $it") }

workerParams.inputData.getString("bolt11")?.let { bolt11 ->
delay(1500) // sleep on bg queue
val isSuccess = payInvoice(bolt11)
if (isSuccess) {
return Result.success()
} else {
return Result.failure(
Data.Builder()
.putString("reason:", "payment error")
.build()
)
}
}
return Result.failure(
Data.Builder()
.putString("reason:", "bolt11 field missing")
.build()
)
}
}
Loading