feat: add database indices and testing
This commit is contained in:
Generated
+44
@@ -13,6 +13,50 @@
|
|||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
<DialogSelection />
|
<DialogSelection />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="getAll_baseline()">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2026-05-31T17:55:52.851171Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=48090DLAQ00119" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="PaymentDaoBenchmark">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2026-05-31T17:55:52.851171Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=48090DLAQ00119" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="concurrentReadWrite()">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2026-05-31T17:55:52.851171Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=48090DLAQ00119" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="checkJournalMode()">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2026-05-31T17:55:52.851171Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=48090DLAQ00119" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+25
-1
@@ -29,9 +29,13 @@ android {
|
|||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
// testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
|
||||||
|
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "ACTIVITY-MISSING"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testBuildType = "benchmark"
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
@@ -40,6 +44,13 @@ android {
|
|||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
create("benchmark") {
|
||||||
|
initWith(buildTypes.getByName("release"))
|
||||||
|
signingConfig = signingConfigs.getByName("debug") // sign with debug key for easy deployment
|
||||||
|
installation {
|
||||||
|
enableBaselineProfile = false // ← disables profile embedding for this build type only
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
@@ -61,6 +72,16 @@ android {
|
|||||||
getByName("release") {
|
getByName("release") {
|
||||||
java.srcDirs("build/generated/ksp/release/kotlin")
|
java.srcDirs("build/generated/ksp/release/kotlin")
|
||||||
}
|
}
|
||||||
|
getByName("benchmark") {
|
||||||
|
java.srcDirs("build/generated/ksp/benchmark/kotlin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
androidComponents {
|
||||||
|
onVariants(selector().withBuildType("benchmark")) { variant ->
|
||||||
|
variant.packaging.resources.excludes.add("baseline-prof.txt")
|
||||||
|
variant.packaging.resources.excludes.add("baseline.profm")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +100,9 @@ dependencies {
|
|||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
|
implementation(libs.androidx.benchmark.junit4)
|
||||||
|
androidTestImplementation(libs.androidx.runner)
|
||||||
|
androidTestImplementation(libs.androidx.test.ext.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
</manifest>
|
||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
package com.bitcointxoko.gudariwallet.benchmark
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.benchmark.junit4.BenchmarkRule
|
||||||
|
import androidx.benchmark.junit4.measureRepeated
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.bitcointxoko.gudariwallet.data.db.AppDatabase
|
||||||
|
import com.bitcointxoko.gudariwallet.data.db.PaymentDao
|
||||||
|
import com.bitcointxoko.gudariwallet.data.db.PaymentRecordEntity
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class PaymentDaoBenchmark {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val benchmarkRule = BenchmarkRule()
|
||||||
|
|
||||||
|
private lateinit var db: AppDatabase
|
||||||
|
private lateinit var dao: PaymentDao
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
|
||||||
|
.allowMainThreadQueries()
|
||||||
|
.build()
|
||||||
|
dao = db.paymentDao()
|
||||||
|
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val fakePayments = (1..5_000).map { i ->
|
||||||
|
PaymentRecordEntity(
|
||||||
|
checkingId = "checking_$i",
|
||||||
|
paymentHash = "hash_$i",
|
||||||
|
amountMsat = if (i % 2 == 0) (i * 1000L) else -(i * 1000L),
|
||||||
|
feeMsat = 1000L,
|
||||||
|
memo = if (i % 3 == 0) "Coffee payment $i" else null,
|
||||||
|
time = (now - i * 60_000L).toString(),
|
||||||
|
createdAt = (now / 1000) - (i * 60L),
|
||||||
|
status = when (i % 4) {
|
||||||
|
0 -> "complete"
|
||||||
|
1 -> "paid"
|
||||||
|
2 -> "pending"
|
||||||
|
else -> "failed"
|
||||||
|
},
|
||||||
|
bolt11 = "lnbc${i}n1...",
|
||||||
|
pending = i % 4 == 2,
|
||||||
|
preimage = "preimage_$i",
|
||||||
|
extra = null,
|
||||||
|
savedAt = now - (i * 60_000L),
|
||||||
|
pubkey = if (i % 5 == 0) "pubkey_$i" else null,
|
||||||
|
alias = if (i % 5 == 0) "alias_$i" else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
runBlocking { dao.insertAll(fakePayments) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun teardown() {
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAll_baseline() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking { dao.getAll() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getPage_30rows() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking { dao.getPage(limit = 30, offset = 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getPage_30rows_deepOffset() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking { dao.getPage(limit = 30, offset = 2_000) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun queryAll_noFilters() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking {
|
||||||
|
dao.queryAll(null, null, null, null, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun queryAll_withSearchQuery() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking {
|
||||||
|
dao.queryAll("Coffee", null, null, null, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun queryAll_statusFilter() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking {
|
||||||
|
dao.queryAll(null, "complete,paid", null, null, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun queryAll_combinedFilters() = benchmarkRule.measureRepeated {
|
||||||
|
val now = System.currentTimeMillis() / 1000
|
||||||
|
runBlocking {
|
||||||
|
dao.queryAll(
|
||||||
|
searchQuery = "Coffee",
|
||||||
|
statusPattern = "complete,paid",
|
||||||
|
minAmountMsat = 10_000L,
|
||||||
|
maxAmountMsat = 5_000_000L,
|
||||||
|
minCreatedAt = now - 86_400 * 30,
|
||||||
|
maxCreatedAt = now
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getByCheckingId_lookup() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking { dao.getByCheckingId("checking_2500") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun count_rows() = benchmarkRule.measureRepeated {
|
||||||
|
runBlocking { dao.count() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun concurrentReadWrite_walBenefit() = benchmarkRule.measureRepeated {
|
||||||
|
// Simulate a write happening concurrently with a read
|
||||||
|
// (WAL allows these to proceed in parallel; default journal blocks)
|
||||||
|
val writeJob = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
dao.insert(
|
||||||
|
PaymentRecordEntity(
|
||||||
|
checkingId = "concurrent_${System.nanoTime()}",
|
||||||
|
paymentHash = "hash_concurrent",
|
||||||
|
amountMsat = 1000L,
|
||||||
|
feeMsat = 10L,
|
||||||
|
memo = "concurrent write",
|
||||||
|
time = System.currentTimeMillis().toString(),
|
||||||
|
createdAt = System.currentTimeMillis() / 1000,
|
||||||
|
status = "complete",
|
||||||
|
bolt11 = "lnbc...",
|
||||||
|
pending = false,
|
||||||
|
preimage = "preimage",
|
||||||
|
extra = null,
|
||||||
|
savedAt = System.currentTimeMillis(),
|
||||||
|
pubkey = null,
|
||||||
|
alias = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Read while write is in-flight
|
||||||
|
runBlocking {
|
||||||
|
dao.getPage(limit = 30, offset = 0)
|
||||||
|
writeJob.join()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.nfc" android:required="false" />
|
<uses-feature android:name="android.hardware.nfc" android:required="false" />
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
|
|||||||
LnurlCacheEntity::class,
|
LnurlCacheEntity::class,
|
||||||
NodeAliasCacheEntity::class,
|
NodeAliasCacheEntity::class,
|
||||||
],
|
],
|
||||||
version = 5, // ← bumped
|
version = 6,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
@TypeConverters(PaymentConverters::class)
|
@TypeConverters(PaymentConverters::class)
|
||||||
@@ -70,13 +70,22 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val MIGRATION_4_5 = object : Migration(4, 5) {
|
private val MIGRATION_4_5 = object : Migration(4, 5) {
|
||||||
override fun migrate(db: SupportSQLiteDatabase) {
|
override fun migrate(db: SupportSQLiteDatabase) {
|
||||||
db.execSQL("ALTER TABLE payment_records ADD COLUMN pubkey TEXT")
|
db.execSQL("ALTER TABLE payment_records ADD COLUMN pubkey TEXT")
|
||||||
db.execSQL("ALTER TABLE payment_records ADD COLUMN alias TEXT")
|
db.execSQL("ALTER TABLE payment_records ADD COLUMN alias TEXT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val MIGRATION_5_6 = object : Migration(5, 6) {
|
||||||
|
override fun migrate(db: SupportSQLiteDatabase) {
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_payment_record_status` ON `payment_records` (`status`)")
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_payment_record_created_at` ON `payment_records` (`createdAt`)")
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_payment_record_amount_msat` ON `payment_records` (`amountMsat`)")
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_payment_record_status_created_at` ON `payment_records` (`status`, `createdAt`)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getInstance(context: Context): AppDatabase =
|
fun getInstance(context: Context): AppDatabase =
|
||||||
INSTANCE ?: synchronized(this) {
|
INSTANCE ?: synchronized(this) {
|
||||||
INSTANCE ?: Room.databaseBuilder(
|
INSTANCE ?: Room.databaseBuilder(
|
||||||
@@ -84,7 +93,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
AppDatabase::class.java,
|
AppDatabase::class.java,
|
||||||
"gudari_wallet.db"
|
"gudari_wallet.db"
|
||||||
)
|
)
|
||||||
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) // ← added
|
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
|
||||||
.build()
|
.build()
|
||||||
.also { INSTANCE = it }
|
.also { INSTANCE = it }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,19 @@ package com.bitcointxoko.gudariwallet.data.db
|
|||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
|
|
||||||
@Entity(tableName = "payment_records")
|
@Entity(
|
||||||
|
tableName = "payment_records",
|
||||||
|
indices = [
|
||||||
|
Index(value = ["status"]), // WHERE status LIKE ?
|
||||||
|
Index(value = ["createdAt"]), // WHERE created_at BETWEEN ? AND ?
|
||||||
|
Index(value = ["amountMsat"]), // WHERE amount_msat BETWEEN ? AND ?
|
||||||
|
Index(value = ["status", "createdAt"]) // composite: common combined filter
|
||||||
|
]
|
||||||
|
)
|
||||||
@TypeConverters(PaymentConverters::class)
|
@TypeConverters(PaymentConverters::class)
|
||||||
data class PaymentRecordEntity(
|
data class PaymentRecordEntity(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
|||||||
@@ -3,4 +3,6 @@ plugins {
|
|||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.kotlin.compose) apply false
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
alias(libs.plugins.ksp) apply false
|
alias(libs.plugins.ksp) apply false
|
||||||
|
alias(libs.plugins.android.library) apply false
|
||||||
|
alias(libs.plugins.android.test) apply false
|
||||||
}
|
}
|
||||||
@@ -22,3 +22,16 @@ android.disallowKotlinSourceSets=false
|
|||||||
ksp.useKsp2=true
|
ksp.useKsp2=true
|
||||||
ksp.incremental=true
|
ksp.incremental=true
|
||||||
ksp.incremental.log=false
|
ksp.incremental.log=false
|
||||||
|
|
||||||
|
# Enable configuration cache
|
||||||
|
org.gradle.configuration-cache=true
|
||||||
|
|
||||||
|
# Parallel project execution (useful if you add more modules later)
|
||||||
|
org.gradle.parallel=true
|
||||||
|
|
||||||
|
# Increase heap if you see OOM during KSP/compilation
|
||||||
|
# org.gradle.jvmargs=-Xmx4g -XX:+UseG1GC
|
||||||
|
|
||||||
|
# Enable build caching (caches task outputs across clean builds)
|
||||||
|
org.gradle.caching=true
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ secp256k1-kmp = "0.23.0"
|
|||||||
lyricist = "1.8.0"
|
lyricist = "1.8.0"
|
||||||
material3 = "1.4.0"
|
material3 = "1.4.0"
|
||||||
work = "2.9.1"
|
work = "2.9.1"
|
||||||
|
benchmark = "1.4.1"
|
||||||
|
runner = "1.5.2"
|
||||||
|
androidx-test-ext = "1.2.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometric" }
|
androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometric" }
|
||||||
@@ -75,6 +78,9 @@ lyricist = { module = "cafe.adriel.lyricist:lyricist", version.ref = "lyricist"
|
|||||||
lyricist-processor = { module = "cafe.adriel.lyricist:lyricist-processor", version.ref = "lyricist" }
|
lyricist-processor = { module = "cafe.adriel.lyricist:lyricist-processor", version.ref = "lyricist" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||||
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
|
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
|
||||||
|
androidx-benchmark-junit4 = { group = "androidx.benchmark", name = "benchmark-junit4", version.ref = "benchmark" }
|
||||||
|
androidx-runner = { group = "androidx.test", name = "runner", version.ref = "runner" }
|
||||||
|
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
@@ -82,3 +88,6 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "ko
|
|||||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" }
|
protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" }
|
||||||
|
androidx-benchmark = { id = "androidx.benchmark", version.ref = "benchmark" }
|
||||||
|
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||||
|
android-test = { id = "com.android.test", version.ref = "agp" }
|
||||||
|
|||||||
+1
-2
@@ -23,5 +23,4 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "Gudari Wallet"
|
rootProject.name = "Gudari Wallet"
|
||||||
include(":app")
|
include(":app")
|
||||||
|
|
||||||
Reference in New Issue
Block a user