@@ -11,24 +11,33 @@ import androidx.compose.material.Card
1111import androidx.compose.material.ExperimentalMaterialApi
1212import androidx.compose.material.Text
1313import androidx.compose.runtime.Composable
14+ import androidx.compose.runtime.getValue
1415import androidx.compose.runtime.key
16+ import androidx.compose.runtime.produceState
1517import androidx.compose.ui.Alignment
1618import androidx.compose.ui.Modifier
1719import androidx.compose.ui.graphics.Color
1820import androidx.compose.ui.unit.dp
21+ import kotlinx.coroutines.delay
1922
2023/* *
2124 * Only give back the specific emulator device, i.e. "emulator-5554"
2225 */
2326private val emulatorRegex = Regex (""" \bemulator-\d+\b""" )
27+ private const val ADB_DEVICE_LIST_POLLING_INTERVAL_MS = 3000L
2428
2529@OptIn(ExperimentalMaterialApi ::class )
2630@Composable
2731internal fun DisplayDevices (
2832 onDeviceSelect : (String ) -> Unit ,
29- devices : List <String >,
3033 modifier : Modifier = Modifier ,
3134) {
35+ val devices by produceState(initialValue = listDevices()) {
36+ while (true ) {
37+ delay(ADB_DEVICE_LIST_POLLING_INTERVAL_MS )
38+ value = listDevices()
39+ }
40+ }
3241 Box (
3342 modifier = modifier
3443 .fillMaxWidth(),
@@ -67,3 +76,46 @@ internal fun DisplayDevices(
6776 }
6877 }
6978}
79+
80+ /* *
81+ * Allows users to select from multiple devices that are currently running.
82+ */
83+ private fun listDevices (): List <String > {
84+ if (adb == null ) return emptyList()
85+ val process = ProcessBuilder (adb, " devices" , " -l" ).start()
86+ process.waitFor()
87+ // We drop the header "List of devices attached"
88+ val devices = process.inputStream.use {
89+ it.bufferedReader().readLines().drop(1 ).dropLast(1 )
90+ }
91+
92+ return devices.mapNotNull { device ->
93+ if (device.isBlank()) return @mapNotNull null
94+ val deviceId = device.split(' ' ).first()
95+ val deviceName = ProcessBuilder (adb, " -s" , deviceId, " emu" , " avd" , " name" ).start()
96+ deviceName.waitFor()
97+ " $deviceId " + deviceName.inputStream.use {
98+ it.bufferedReader().readLines().firstOrNull() ? : " "
99+ }
100+ }
101+ }
102+
103+ val adb: String? by lazy {
104+ listOfNotNull(
105+ System .getenv(" ANDROID_HOME" )?.let { " $it /platform-tools/adb" },
106+ // Common macOS Android SDK locations
107+ " ${System .getProperty(" user.home" )} /Library/Android/sdk/platform-tools/adb" ,
108+ " /Users/${System .getProperty(" user.name" )} /Library/Android/sdk/platform-tools/adb" ,
109+ ).firstOrNull { path ->
110+ try {
111+ val process = ProcessBuilder (path, " version" ).start()
112+ if (process.waitFor() == 0 ) {
113+ return @firstOrNull true
114+ }
115+ } catch (e: Exception ) {
116+ println (e)
117+ }
118+ return @firstOrNull false
119+ }
120+ }
121+
0 commit comments