setup project

This commit is contained in:
Eric Ampire
2025-10-08 11:13:18 -04:00
commit 743eb3dddb
18 changed files with 870 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
alias(libs.plugins.composeHotReload)
}
kotlin {
jvm()
sourceSets {
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.materialIconsExtended)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.lifecycle.viewmodelCompose)
implementation(libs.androidx.lifecycle.runtimeCompose)
// Koin DI
implementation(libs.koin.core)
implementation(libs.koin.compose)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
jvmMain.dependencies {
implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutinesSwing)
}
}
}
compose.desktop {
application {
mainClass = "com.brp.tools.adbkey.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "com.brp.tools.adbkey"
packageVersion = "1.0.0"
}
}
}
@@ -0,0 +1,44 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="450dp"
android:height="450dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="M56.25,18V46L32,60 7.75,46V18L32,4Z"
android:fillColor="#6075f2"/>
<path
android:pathData="m41.5,26.5v11L32,43V60L56.25,46V18Z"
android:fillColor="#6b57ff"/>
<path
android:pathData="m32,43 l-9.5,-5.5v-11L7.75,18V46L32,60Z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="23.131"
android:centerY="18.441"
android:gradientRadius="42.132"
android:type="radial">
<item android:offset="0" android:color="#FF5383EC"/>
<item android:offset="0.867" android:color="#FF7F52FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M22.5,26.5 L32,21 41.5,26.5 56.25,18 32,4 7.75,18Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="44.172"
android:startY="4.377"
android:endX="17.973"
android:endY="34.035"
android:type="linear">
<item android:offset="0" android:color="#FF33C3FF"/>
<item android:offset="0.878" android:color="#FF5383EC"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="m32,21 l9.526,5.5v11L32,43 22.474,37.5v-11z"
android:fillColor="#000000"/>
</vector>
@@ -0,0 +1,162 @@
package com.brp.tools.adbkey
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import org.jetbrains.compose.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
data class AdbCommand(
val title: String,
val cmd: String,
val description: String = "",
)
val commands = listOf(
AdbCommand(
title = "Android Setting",
cmd = "adb -s {device} shell am start -a android.settings.SETTINGS"
),
AdbCommand(
title = "App List",
cmd = "adb -s {device} shell am start -a android.settings.APPLICATION_SETTINGS"
),
AdbCommand(
title = "BGE Setting",
cmd = "adb -s {device} shell am start -a android.settings.APPLICATION_DETAILS_SETTINGS -e \"android.provider.extra.APP_PACKAGE\" \"com.brp.goembedded\""
),
AdbCommand(
title = "Location Setting",
cmd = "adb -s {device} shell am start -a android.settings.LOCATION_SOURCE_SETTINGS"
),
AdbCommand(
title = "Launch BGE",
cmd = "adb -s {device} shell monkey -p com.brp.goembedded -c android.intent.category.LAUNCHER 1"
),
AdbCommand(
title = "Key Home",
cmd = "adb -s {device} shell input keyevent KEYCODE_HOME "
),
AdbCommand(
title = "Force Stop Setting",
cmd = "adb -s {device} shell am force-stop com.android.car.settings"
),
AdbCommand(
title = "Uninstall BGE",
cmd = "adb -s {device} uninstall com.brp.goembedded"
),
AdbCommand(
title = "Stop BGE",
cmd = "adb -s {device} shell am force-stop com.brp.goembedded"
),
AdbCommand(
title = "Clea Cache For All ",
cmd = "adb -s {device} shell pm trim-caches 9999999999"
)
)
@Composable
fun CommandItemView(
modifier: Modifier = Modifier,
adbCommand: AdbCommand,
executeCommand: (AdbCommand) -> Unit = {}
) {
Button(
modifier = modifier.defaultMinSize(minWidth = 300.dp),
onClick = {
executeCommand(adbCommand)
},
content = {
Text(text = adbCommand.title)
},
)
}
data class AppState(
val message: String = "",
val commands: List<AdbCommand> = emptyList(),
)
class AppViewModel {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val mutableStateFlow = MutableStateFlow(AppState())
val state: StateFlow<AppState> = mutableStateFlow
init {
mutableStateFlow.update {
it.copy(commands = commands)
}
}
fun executeCommand(command: AdbCommand, selectedDevice: AdbDevice?) {
scope.launch {
try {
if (selectedDevice != null) {
val newCommand = command.cmd.replace("{device}", selectedDevice.id)
println("New command: $newCommand")
val process = Runtime.getRuntime().exec(newCommand)
process.inputStream.bufferedReader().forEachLine { line ->
println("Process line: $line")
mutableStateFlow.update {
it.copy(message = line)
}
}
}
} catch (e: Exception) {
e.printStackTrace()
mutableStateFlow.update {
it.copy(message = e.localizedMessage)
}
}
}
}
}
@Composable
@Preview
fun App(viewModel: AppViewModel = AppViewModel()) {
val state by viewModel.state.collectAsState()
var selectedDevice by remember { mutableStateOf<AdbDevice?>(null) }
MaterialTheme {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
content = {
item {
Text("Selected Device : ${selectedDevice?.model}")
}
item {
DeviceSelector {
selectedDevice = it
}
}
items(items = state.commands) { command ->
CommandItemView(
adbCommand = command,
executeCommand = {
viewModel.executeCommand(it, selectedDevice)
}
)
}
item {
Text(text = "Message : ${state.message}")
}
}
)
}
}
@@ -0,0 +1,80 @@
package com.brp.tools.adbkey
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
data class AdbDevice(
val id: String,
val model: String,
)
@Composable
fun DeviceSelector(
onDeviceSelected: (AdbDevice) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
var devices by remember { mutableStateOf<List<AdbDevice>>(emptyList()) }
LaunchedEffect(expanded) {
if (expanded) {
val process = Runtime.getRuntime().exec("adb devices -l")
val output = process.inputStream.bufferedReader().readText()
devices = output.split("\n").drop(1).filter { it.isNotBlank() }.map {
val deviceId = it.substringBefore("device").trim()
val deviceName = it.substringAfter("model:").substringBefore("device:").trim()
AdbDevice(id = deviceId, model = deviceName)
}
println("Devices found: $devices")
}
}
DisposableEffect(Unit) {
onDispose {
expanded = false
devices = emptyList()
}
}
Box(
modifier = Modifier.padding(16.dp),
content = {
IconButton(onClick = { expanded = !expanded }) {
Icon(Icons.Default.MoreVert, contentDescription = "More options")
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
content = {
devices.forEach { device ->
DropdownMenuItem(
text = {
Text("${device.id} - ${device.model}")
},
onClick = {
onDeviceSelected(device)
expanded = false
}
)
}
}
)
}
)
}
@@ -0,0 +1,7 @@
package com.brp.tools.adbkey
class JVMPlatform {
val name: String = "Java ${System.getProperty("java.version")}"
}
fun getPlatform() = JVMPlatform()
@@ -0,0 +1,43 @@
package com.brp.tools.adbkey.kointest
import com.brp.tools.adbkey.kointest.module
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.context.GlobalContext.startKoin
import org.koin.core.parameter.parametersOf
import org.koin.dsl.module
data class BtDevice(val id: String)
class LinkConnector(val btDevice: BtDevice) {
init {
println("Link connector ${btDevice.id}")
}
}
class Client() : KoinComponent {
fun connect(btDevice: BtDevice) {
val link = get<LinkConnector> { parametersOf(btDevice) }
println("Connected to ${link.btDevice}, Link $link")
}
}
val module = module {
single { Client() }
factory { (device: BtDevice) -> LinkConnector(device) }
}
fun main() {
val koin = startKoin {
modules(listOf(module))
}
val bt1 = BtDevice("abc")
val bt2 = BtDevice("efg")
val client = koin.koin.get<Client>()
client.connect(bt1)
client.connect(bt2)
}
@@ -0,0 +1,15 @@
package com.brp.tools.adbkey
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
fun main() = application {
// Initialize Koin once at app start
Window(
onCloseRequest = ::exitApplication,
title = "adbkey",
content = {
App()
}
)
}
@@ -0,0 +1,12 @@
package com.brp.tools.adbkey
import kotlin.test.Test
import kotlin.test.assertEquals
class ComposeAppDesktopTest {
@Test
fun example() {
assertEquals(3, 1 + 2)
}
}