|
| 1 | +package to.bitkit.dev |
| 2 | + |
| 3 | +import android.content.ContentProvider |
| 4 | +import android.content.ContentValues |
| 5 | +import android.net.Uri |
| 6 | +import android.os.Bundle |
| 7 | +import androidx.core.os.bundleOf |
| 8 | +import dagger.hilt.EntryPoint |
| 9 | +import dagger.hilt.InstallIn |
| 10 | +import dagger.hilt.android.EntryPointAccessors |
| 11 | +import dagger.hilt.components.SingletonComponent |
| 12 | +import kotlinx.serialization.Serializable |
| 13 | +import kotlinx.serialization.json.Json |
| 14 | +import to.bitkit.async.ServiceQueue |
| 15 | +import to.bitkit.repositories.LightningRepo |
| 16 | + |
| 17 | +class DevToolsProvider : ContentProvider() { |
| 18 | + |
| 19 | + @EntryPoint |
| 20 | + @InstallIn(SingletonComponent::class) |
| 21 | + interface Dependencies { |
| 22 | + fun lightningRepo(): LightningRepo |
| 23 | + } |
| 24 | + |
| 25 | + private val deps: Dependencies by lazy { |
| 26 | + EntryPointAccessors.fromApplication(requireNotNull(context), Dependencies::class.java) |
| 27 | + } |
| 28 | + |
| 29 | + override fun call(method: String, arg: String?, extras: Bundle?): Bundle = runCatching { |
| 30 | + val command = requireNotNull(DevCommand.parse(method, arg)) { "Unknown command: '$method'" } |
| 31 | + ServiceQueue.LDK.blocking { command.execute(deps) } |
| 32 | + }.getOrElse { |
| 33 | + DevResult.Error(it.message) |
| 34 | + }.toBundle() |
| 35 | + |
| 36 | + override fun onCreate() = true |
| 37 | + override fun getType(uri: Uri): String? = null |
| 38 | + override fun insert(uri: Uri, values: ContentValues?): Uri? = null |
| 39 | + override fun delete(uri: Uri, sel: String?, args: Array<String>?) = 0 |
| 40 | + override fun update(uri: Uri, values: ContentValues?, sel: String?, args: Array<String>?) = 0 |
| 41 | + override fun query(uri: Uri, proj: Array<String>?, sel: String?, args: Array<String>?, sort: String?) = null |
| 42 | +} |
| 43 | + |
| 44 | +private sealed interface DevCommand { |
| 45 | + |
| 46 | + companion object { |
| 47 | + fun parse(method: String, arg: String?): DevCommand? = when (method) { |
| 48 | + CreateInvoice.METHOD -> CreateInvoice.parse(arg) |
| 49 | + else -> null |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + suspend fun execute(deps: DevToolsProvider.Dependencies): DevResult |
| 54 | + |
| 55 | + data class CreateInvoice(val args: Args) : DevCommand { |
| 56 | + companion object { |
| 57 | + const val METHOD = "createInvoice" |
| 58 | + fun parse(arg: String?) = CreateInvoice(arg.deserialize<Args>()) |
| 59 | + } |
| 60 | + |
| 61 | + @Serializable |
| 62 | + data class Args(val amount: ULong? = null, val description: String = "dev-invoice") |
| 63 | + |
| 64 | + override suspend fun execute(deps: DevToolsProvider.Dependencies) = |
| 65 | + deps.lightningRepo().createInvoice(args.amount, args.description).fold( |
| 66 | + onSuccess = { DevResult.Invoice(it) }, |
| 67 | + onFailure = { DevResult.Error(it.message) }, |
| 68 | + ) |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +@Serializable |
| 73 | +private sealed interface DevResult { |
| 74 | + |
| 75 | + companion object { |
| 76 | + private const val KEY_RESULT = "result" |
| 77 | + } |
| 78 | + |
| 79 | + @Serializable data class Invoice(val bolt11: String) : DevResult |
| 80 | + |
| 81 | + @Serializable data class Error(val message: String? = null) : DevResult |
| 82 | + |
| 83 | + fun toBundle() = bundleOf(KEY_RESULT to Json.encodeToString(this)) |
| 84 | +} |
| 85 | + |
| 86 | +private inline fun <reified T> String?.deserialize(): T = |
| 87 | + if (isNullOrBlank()) Json.decodeFromString("{}") else Json.decodeFromString(this) |
0 commit comments