feat: [DEVICES] being able to target one spceific device with adb (#110)

Co-authored-by: Florent Champigny <florent@bere.al>
This commit is contained in:
Florent CHAMPIGNY 2025-08-15 22:45:41 +02:00 committed by GitHub
parent 0321da0c4a
commit e9cb50dbd8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 68 additions and 21 deletions

View file

@ -2,7 +2,11 @@ package io.github.openflocon.flocondesktop.common
import io.github.openflocon.domain.common.Either
actual fun localExecuteAdbCommand(adbPath: String, command: String): Either<Throwable, String> {
actual fun localExecuteAdbCommand(
adbPath: String,
command: String,
deviceSerial: String?,
): Either<Throwable, String> {
TODO("Not yet implemented")
}

View file

@ -1,5 +1,6 @@
package io.github.openflocon.flocondesktop.adb
import io.github.openflocon.domain.adb.AdbCommandTargetDomainModel
import io.github.openflocon.domain.adb.repository.AdbRepository
import io.github.openflocon.domain.common.Either
import io.github.openflocon.flocondesktop.common.askSerialToAllDevices
@ -24,8 +25,18 @@ class AdbRepositoryImpl : AdbRepository {
// TODO be able to pass a serial
override fun executeAdbCommand(
adbPath: String,
target: AdbCommandTargetDomainModel,
command: String,
): Either<Throwable, String> = localExecuteAdbCommand(adbPath = adbPath, command = command)
): Either<Throwable, String> {
return localExecuteAdbCommand(
adbPath = adbPath,
command = command,
deviceSerial = when(target) {
is AdbCommandTargetDomainModel.Device -> getAdbSerial(target.deviceId)
is AdbCommandTargetDomainModel.AllDevices -> null
},
)
}
override fun executeAdbAskSerialToAllDevices(
adbPath: String,

View file

@ -4,6 +4,10 @@ import io.github.openflocon.domain.common.Either
expect fun localFindAdbPath(): String?
expect fun localExecuteAdbCommand(adbPath: String, command: String): Either<Throwable, String>
expect fun localExecuteAdbCommand(
adbPath: String,
command: String,
deviceSerial: String?,
): Either<Throwable, String>
expect fun askSerialToAllDevices(adbPath: String, command: String, serialVariableName: String): Either<Throwable, String>

View file

@ -55,20 +55,28 @@ actual fun localFindAdbPath(): String? {
return null
}
actual fun localExecuteAdbCommand(adbPath: String, command: String): Either<Throwable, String> = try {
val devices = listConnectedDevices(adbPath)
if (devices.isEmpty() || devices.size == 1) {
singleDeviceExecuteSystemCommand(adbPath = adbPath, command = command)
actual fun localExecuteAdbCommand(
adbPath: String,
command: String,
deviceSerial: String?,
): Either<Throwable, String> = try {
if(deviceSerial != null) {
singleDeviceExecuteSystemCommand(adbPath = "$adbPath -s $deviceSerial", command = command)
} else {
devices.map { serial ->
singleDeviceExecuteSystemCommand(adbPath = "$adbPath -s $serial", command = command)
}.let {
it.forEach {
// return a failure if there's on in the list
if (it is Failure)
return it
val devices = listConnectedDevices(adbPath)
if (devices.isEmpty() || devices.size == 1) {
singleDeviceExecuteSystemCommand(adbPath = adbPath, command = command)
} else {
devices.map { serial ->
singleDeviceExecuteSystemCommand(adbPath = "$adbPath -s $serial", command = command)
}.let {
it.forEach {
// return a failure if there's on in the list
if (it is Failure)
return it
}
return it.firstOrNull() ?: Success("")
}
return it.firstOrNull() ?: Success("")
}
}
} catch (t: Throwable) {

View file

@ -3,6 +3,7 @@ package io.github.openflocon.data.core.deeplink.repository
import io.github.openflocon.data.core.deeplink.datasource.DeeplinkLocalDataSource
import io.github.openflocon.data.core.deeplink.datasource.DeeplinkRemoteDataSource
import io.github.openflocon.domain.Protocol
import io.github.openflocon.domain.adb.AdbCommandTargetDomainModel
import io.github.openflocon.domain.adb.repository.AdbRepository
import io.github.openflocon.domain.common.DispatcherProvider
import io.github.openflocon.domain.deeplink.models.DeeplinkDomainModel
@ -49,7 +50,7 @@ class DeeplinkRepositoryImpl(
override fun executeDeeplink(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, adbPath: String, deeplink: String) {
adbRepository.executeAdbCommand(
adbPath = adbPath,
// TODO inject the device serial
target = AdbCommandTargetDomainModel.Device(deviceIdAndPackageName.deviceId),
command = "shell am start -W -a android.intent.action.VIEW -d \"$deeplink\" ${deviceIdAndPackageName.packageName}",
)
}

View file

@ -0,0 +1,6 @@
package io.github.openflocon.domain.adb
sealed interface AdbCommandTargetDomainModel {
data class Device(val deviceId: String) : AdbCommandTargetDomainModel
data object AllDevices : AdbCommandTargetDomainModel
}

View file

@ -1,5 +1,6 @@
package io.github.openflocon.domain.adb.repository
import io.github.openflocon.domain.adb.AdbCommandTargetDomainModel
import io.github.openflocon.domain.common.Either
interface AdbRepository {
@ -7,7 +8,11 @@ interface AdbRepository {
fun getAdbSerial(deviceId: String) : String?
fun saveAdbSerial(deviceId: String, serial: String)
fun executeAdbCommand(adbPath: String, command: String): Either<Throwable, String>
fun executeAdbCommand(
adbPath: String,
target: AdbCommandTargetDomainModel,
command: String,
): Either<Throwable, String>
fun executeAdbAskSerialToAllDevices(
adbPath: String,

View file

@ -1,6 +1,7 @@
package io.github.openflocon.domain.settings.usecase
import io.github.openflocon.domain.Constant
import io.github.openflocon.domain.adb.AdbCommandTargetDomainModel
import io.github.openflocon.domain.adb.repository.AdbRepository
import io.github.openflocon.domain.common.Either
import io.github.openflocon.domain.common.Failure
@ -13,8 +14,11 @@ class StartAdbForwardUseCase(
operator fun invoke(): Either<Throwable, Unit> {
val adbPath = settingsRepository.getAdbPath()
return if (adbPath != null) {
adbRepository.executeAdbCommand(adbPath = adbPath, command = "reverse tcp:${Constant.SERVER_PORT} tcp:${Constant.SERVER_PORT}")
.mapSuccess { Unit }
adbRepository.executeAdbCommand(
adbPath = adbPath,
command = "reverse tcp:${Constant.SERVER_PORT} tcp:${Constant.SERVER_PORT}",
target = AdbCommandTargetDomainModel.AllDevices,
).mapSuccess { Unit }
} else {
Failure(Throwable("adb path is empty"))
}

View file

@ -1,5 +1,6 @@
package io.github.openflocon.domain.settings.usecase
import io.github.openflocon.domain.adb.AdbCommandTargetDomainModel
import io.github.openflocon.domain.adb.repository.AdbRepository
import io.github.openflocon.domain.common.Either
import io.github.openflocon.domain.common.Failure
@ -12,8 +13,11 @@ class TestAdbUseCase(
operator fun invoke(): Either<Throwable, Unit> {
val adbPath = settingsRepository.getAdbPath()
return if (adbPath != null) {
adbRepository.executeAdbCommand(adbPath = adbPath, command = "start-server")
.mapSuccess { Unit }
adbRepository.executeAdbCommand(
adbPath = adbPath,
command = "start-server",
target = AdbCommandTargetDomainModel.AllDevices,
).mapSuccess { Unit }
} else {
Failure(Throwable("adb path is empty"))
}