diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-08-18 14:36:41 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-08-18 14:36:41 +0000 |
commit | 7caeb9e35b4a8b5fb90ed28176ac512ddc1b3637 (patch) | |
tree | 80fbbbbbc8f36dde705f932887ee791171c618e7 | |
parent | 9e16099e51e323d6ec92921049818dada167652c (diff) | |
parent | 487ff396c18c6fbfa2ed79ece71fc8ffe7dd6797 (diff) | |
download | testing-studio-2022.1.1-canary.tar.gz |
Snap for 8962753 from 487ff396c18c6fbfa2ed79ece71fc8ffe7dd6797 to studio-ee-releasestudio-2022.1.1-canarystudio-2022.1.1-beta4studio-2022.1.1-beta2studio-canarystudio-2022.1.1-canary
Change-Id: I63230f972257150f8e30a098eeef04f5af0010ec
14 files changed, 343 insertions, 51 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg new file mode 100644 index 0000000..cd2c36d --- /dev/null +++ b/PREUPLOAD.cfg @@ -0,0 +1,11 @@ +[Hook Scripts] + +[Builtin Hooks] +ktfmt = true + +[Builtin Hooks Options] +ktfmt = --include-dirs=directaccess --google-style + +[Tool Paths] +ktfmt = ${REPO_ROOT}/prebuilts/tools/common/ktfmt/ktfmt + diff --git a/directaccess/BUILD b/directaccess/BUILD index 75c5f26..2b7c9e4 100644 --- a/directaccess/BUILD +++ b/directaccess/BUILD @@ -17,5 +17,12 @@ iml_module( "//tools/adt/idea/device-manager:intellij.android.device-manager[module]", "//tools/vendor/google/directaccess-client[module]", "//tools/adt/idea/artwork:intellij.android.artwork[module]", + "//tools/adt/idea/android-adb:intellij.android.adb[module]", + "//tools/base/device-provisioner:studio.android.sdktools.device-provisioner[module]", + "//tools/base/adblib:studio.android.sdktools.adblib[module]", + "//tools/base/adblib-tools:studio.android.sdktools.adblib.tools[module]", + "//tools/studio/google/cloud/tools/google-login-plugin:google-login-as[module]", + "//tools/adt/idea/.idea/libraries:studio-proto", + "//tools/adt/idea/.idea/libraries:directaccess_client_proto", ], ) diff --git a/directaccess/directaccess.iml b/directaccess/directaccess.iml index 1f6efc7..3b404fb 100644 --- a/directaccess/directaccess.iml +++ b/directaccess/directaccess.iml @@ -16,5 +16,12 @@ <orderEntry type="module" module-name="intellij.android.device-manager" /> <orderEntry type="module" module-name="directaccess-client" /> <orderEntry type="module" module-name="intellij.android.artwork" /> + <orderEntry type="module" module-name="intellij.android.adb" /> + <orderEntry type="module" module-name="android.sdktools.device-provisioner" /> + <orderEntry type="module" module-name="android.sdktools.adblib" /> + <orderEntry type="module" module-name="android.sdktools.adblib.tools" /> + <orderEntry type="module" module-name="google-login-as" /> + <orderEntry type="library" name="studio-proto" level="project" /> + <orderEntry type="library" name="directaccess_client_proto" level="project" /> </component> </module>
\ No newline at end of file diff --git a/directaccess/src/META-INF/plugin.xml b/directaccess/src/META-INF/plugin.xml index b05d0f1..c3c1d7d 100644 --- a/directaccess/src/META-INF/plugin.xml +++ b/directaccess/src/META-INF/plugin.xml @@ -31,4 +31,8 @@ <deviceManagerTab implementation="com.google.gct.directaccess.ui.FirebaseDeviceTab"/> </extensions> + + <extensions defaultExtensionNs="com.android.tools.idea"> + <deviceProvisioner implementation="com.google.gct.directaccess.provisioner.FirebaseDeviceProvisionerFactory"/> + </extensions> </idea-plugin> diff --git a/directaccess/src/com/google/gct/directaccess/DirectAccessService.kt b/directaccess/src/com/google/gct/directaccess/DirectAccessService.kt new file mode 100644 index 0000000..2fdadb2 --- /dev/null +++ b/directaccess/src/com/google/gct/directaccess/DirectAccessService.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.directaccess + +import com.android.tools.adbbridge.DeviceSession +import com.android.tools.idea.flags.StudioFlags +import com.google.gct.login.GoogleLogin +import com.google.services.firebase.directaccess.client.device.directaccess.DirectAccessClient +import com.google.services.firebase.directaccess.client.device.remote.service.adb.forwardingdaemon.directaccess.DirectAccessServiceClient +import com.google.services.firebase.directaccess.client.session.models.resourcenames.DeviceAssociationName +import com.intellij.openapi.components.Service +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.util.concurrency.AppExecutorUtil + +@Service +class DirectAccessService(val project: Project) { + private val gcpProject: String + get() = StudioFlags.DIRECT_ACCESS_PROJECT.get() + + var serviceClient: DirectAccessServiceClient? = null + get() { + return field + ?: GoogleLogin.instance.fetchOAuth2Token()?.let { token -> + DirectAccessServiceClient(gcpProject, AppExecutorUtil.getAppExecutorService(), token) + .also { + field = it + GoogleLogin.instance.activeUser?.googleLoginState?.addLoginListener { loggedIn -> + if (!loggedIn) field = null + } + } + } + } + private set + + // Port to client + val deviceClients: MutableMap<Int, DirectAccessClient> = mutableMapOf() + + fun acquireAndConnect(device: String, api: String) { + val directAccessClient = + serviceClient?.let { DirectAccessClient(gcpProject, device, api, it) } + ?: run { + Messages.showWarningDialog("Please log in first", "Log In Required") + return + } + directAccessClient.reserveAndStartStreaming() + directAccessClient.port?.let { port -> deviceClients.put(port, directAccessClient) } + } + + fun reconnect(session: DeviceSession) { + val device = session.androidDeviceList.getAndroidDevices(0) + val directAccessClient = + serviceClient?.let { + DirectAccessClient(gcpProject, device.androidModelId, device.androidVersionId, it) + } + ?: run { + Messages.showWarningDialog("Please log in first", "Log In Required") + return + } + directAccessClient.startStreaming( + DeviceAssociationName.parseFrom( + session.connectionInfo.adbConnectInfo.adbDevicesList.single().device + ) + ) + directAccessClient.port?.let { port -> deviceClients.put(port, directAccessClient) } + } + + fun listDevices() = serviceClient?.listDeviceSessions() + fun getDeviceSession(deviceSessionName: String) = + serviceClient?.getDeviceSession(deviceSessionName) +} diff --git a/directaccess/src/com/google/gct/directaccess/FirebaseDevice.kt b/directaccess/src/com/google/gct/directaccess/FirebaseDevice.kt index e0deb22..ace26fa 100644 --- a/directaccess/src/com/google/gct/directaccess/FirebaseDevice.kt +++ b/directaccess/src/com/google/gct/directaccess/FirebaseDevice.kt @@ -25,7 +25,9 @@ import javax.swing.Icon class FirebaseDevice private constructor(builder: Builder) : Device(builder) { - constructor(info: DeviceInfo) : this( + constructor( + info: DeviceInfo + ) : this( Builder() .setName("${info.brand} ${info.name}") .setApi(info.api) @@ -40,13 +42,14 @@ class FirebaseDevice private constructor(builder: Builder) : Device(builder) { override fun build(): FirebaseDevice = FirebaseDevice(this) init { - myKey = object : Key() { - override fun getConnectionType(): ConnectionType = ConnectionType.UNKNOWN + myKey = + object : Key() { + override fun getConnectionType(): ConnectionType = ConnectionType.UNKNOWN - override fun getSerialNumber(): SerialNumber = SerialNumber("none") + override fun getSerialNumber(): SerialNumber = SerialNumber("none") - override fun isPersistent() = true - } + override fun isPersistent() = true + } } fun setName(name: String): Builder { diff --git a/directaccess/src/com/google/gct/directaccess/provisioner/FirebaseDeviceProvisioner.kt b/directaccess/src/com/google/gct/directaccess/provisioner/FirebaseDeviceProvisioner.kt new file mode 100644 index 0000000..baecf80 --- /dev/null +++ b/directaccess/src/com/google/gct/directaccess/provisioner/FirebaseDeviceProvisioner.kt @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.directaccess.provisioner + +import com.android.adblib.ConnectedDevice +import com.android.adblib.deviceProperties +import com.android.sdklib.deviceprovisioner.ActivationAction +import com.android.sdklib.deviceprovisioner.ActivationParams +import com.android.sdklib.deviceprovisioner.Connected +import com.android.sdklib.deviceprovisioner.DeactivationAction +import com.android.sdklib.deviceprovisioner.DeviceHandle +import com.android.sdklib.deviceprovisioner.DeviceProperties +import com.android.sdklib.deviceprovisioner.DeviceProvisionerPlugin +import com.android.sdklib.deviceprovisioner.DeviceState +import com.android.sdklib.deviceprovisioner.DeviceTemplate +import com.android.tools.idea.concurrency.executeOnPooledThread +import com.google.gct.directaccess.DirectAccessService +import com.google.services.firebase.directaccess.client.catalog.FirebaseDirectAccessClient +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * Provides access to physical devices run by Firebase. Supports configuring Firebase device + * templates and activating / deactivating them. + */ +class FirebaseDeviceProvisioner(val project: Project) : DeviceProvisionerPlugin { + // TODO: find a proper priority + override val priority: Int = 120 + + override suspend fun claim(device: ConnectedDevice): Boolean { + val sn = device.deviceInfoFlow.value.serialNumber + if (sn.matches(Regex("^localhost:\\d+$"))) { + val port = sn.substringAfter(':').toInt() + val service = project.service<DirectAccessService>() + if (service.deviceClients.containsKey(port)) { + val properties = device.deviceProperties().allReadonly() + val deviceProperties = + DirectAccessDeviceProperties.build { readCommonProperties(properties) } + _devices.emit( + devices.value.plus( + DirectAccessDeviceHandle(MutableStateFlow(Connected(deviceProperties, device))) + ) + ) + return true + } + } + return false + } + + private val _devices = MutableStateFlow(emptyList<DeviceHandle>()) + override val devices: StateFlow<List<DeviceHandle>> = _devices.asStateFlow() + private val _templates = MutableStateFlow(emptyList<DeviceTemplate>()) + override val templates: StateFlow<List<DeviceTemplate>> = _templates.asStateFlow() + + init { + _templates.value = + FirebaseDirectAccessClient.availableDevices.map { info -> + object : DeviceTemplate { + override val displayName: String = "${info.manufacturer} ${info.codename}" + override val activationAction: ActivationAction + get() = + object : ActivationAction { + override suspend fun activate(params: ActivationParams) { + executeOnPooledThread { + project + .service<DirectAccessService>() + .acquireAndConnect(info.codename, info.api.toString()) + } + } + override val label: String = "Acquire" + override val isEnabled: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow() + } + override val editAction = null + } + } + } +} + +class DirectAccessDeviceHandle(override val stateFlow: StateFlow<DeviceState>) : DeviceHandle { + override val deactivationAction = + object : DeactivationAction { + override suspend fun deactivate() { + TODO("Not yet implemented") + } + override val label: String + get() = "Disconnect" + override val isEnabled: StateFlow<Boolean> + get() = MutableStateFlow(true) + } +} + +class DirectAccessDeviceProperties(base: DeviceProperties) : DeviceProperties by base { + class Builder : DeviceProperties.Builder() + companion object { + fun build(block: Builder.() -> Unit) = + Builder().apply(block).run { DirectAccessDeviceProperties(buildBase()) } + } +} diff --git a/directaccess/src/com/google/gct/directaccess/provisioner/FirebaseDeviceProvisionerFactory.kt b/directaccess/src/com/google/gct/directaccess/provisioner/FirebaseDeviceProvisionerFactory.kt new file mode 100644 index 0000000..d28c4f9 --- /dev/null +++ b/directaccess/src/com/google/gct/directaccess/provisioner/FirebaseDeviceProvisionerFactory.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gct.directaccess.provisioner + +import com.android.sdklib.deviceprovisioner.DeviceProvisionerPlugin +import com.android.tools.idea.deviceprovisioner.DeviceProvisionerFactory +import com.intellij.openapi.project.Project + +class FirebaseDeviceProvisionerFactory : DeviceProvisionerFactory { + override fun create(project: Project): DeviceProvisionerPlugin = + FirebaseDeviceProvisioner(project) +} diff --git a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceDetailsPanel.kt b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceDetailsPanel.kt index 2d3231a..6ee7b51 100644 --- a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceDetailsPanel.kt +++ b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceDetailsPanel.kt @@ -18,5 +18,4 @@ package com.google.gct.directaccess.ui import com.android.tools.idea.devicemanager.DetailsPanel import com.intellij.openapi.project.Project -class FirebaseDeviceDetailsPanel(project: Project): DetailsPanel("test") { -} +class FirebaseDeviceDetailsPanel(project: Project) : DetailsPanel("test") {} diff --git a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceManagerTab.kt b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceManagerTab.kt index 9524bcf..d37856a 100644 --- a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceManagerTab.kt +++ b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceManagerTab.kt @@ -21,7 +21,7 @@ import com.android.tools.idea.flags.StudioFlags import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project -class FirebaseDeviceTab: DeviceManagerTab { +class FirebaseDeviceTab : DeviceManagerTab { override fun isApplicable() = StudioFlags.DIRECT_ACCESS.get() diff --git a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDevicePanel.kt b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDevicePanel.kt index 7264a2d..75a33d2 100644 --- a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDevicePanel.kt +++ b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDevicePanel.kt @@ -18,28 +18,58 @@ package com.google.gct.directaccess.ui import com.android.tools.adtui.stdui.CommonButton import com.android.tools.idea.devicemanager.DetailsPanel import com.android.tools.idea.devicemanager.DevicePanel +import com.android.tools.idea.deviceprovisioner.DeviceProvisionerService +import com.google.gct.directaccess.DirectAccessService import com.google.services.firebase.directaccess.client.catalog.FirebaseDirectAccessClient import com.intellij.icons.AllIcons import com.intellij.openapi.Disposable +import com.intellij.openapi.components.service import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.Disposer import com.intellij.ui.scale.JBUIScale import com.intellij.util.ui.JBDimension import javax.swing.GroupLayout -import javax.swing.JButton import javax.swing.JSeparator import javax.swing.JTable import javax.swing.SwingConstants class FirebaseDevicePanel(project: Project, parent: Disposable) : DevicePanel(project) { - private val table = FirebaseDeviceTable(FirebaseDeviceTableModel(FirebaseDirectAccessClient.availableDevices)) + private val table = + FirebaseDeviceTable( + FirebaseDeviceTableModel(FirebaseDirectAccessClient.availableDevices, project) + ) + + private val separator: JSeparator = + JSeparator(SwingConstants.VERTICAL).apply { + preferredSize = JBDimension(3, 20) + maximumSize = preferredSize + } + private val reloadButton = + CommonButton(AllIcons.Actions.Refresh).apply { + addActionListener { + val service = project.service<DirectAccessService>() + val devices = + service.listDevices() + ?: run { + Messages.showWarningDialog("Not connected", "Not Connected") + return@addActionListener + } + devices + .filter { existingSession -> + !service + .deviceClients + .values + .map { it.sessionName.toString() } + .contains(existingSession.name) + } + .forEach { + val session = service.getDeviceSession(it.name)!! + service.reconnect(session) + } + } + } - private val createButton = JButton("Add Device") - private val separator: JSeparator = JSeparator(SwingConstants.VERTICAL).apply { - preferredSize = JBDimension(3, 20) - maximumSize = preferredSize - } - private val reloadButton = CommonButton(AllIcons.Actions.Refresh) private val helpButton = CommonButton(AllIcons.Actions.Help) init { @@ -49,6 +79,9 @@ class FirebaseDevicePanel(project: Project, parent: Disposable) : DevicePanel(pr layOut() Disposer.register(parent, this) + + // Temporarily get the provisioner service to ensure it's initialized + project.service<DeviceProvisionerService>() } override fun newTable(): JTable { @@ -61,26 +94,27 @@ class FirebaseDevicePanel(project: Project, parent: Disposable) : DevicePanel(pr private fun layOut() { val layout = GroupLayout(this) - val horizontalGroup: GroupLayout.Group = layout.createParallelGroup() - .addGroup( - layout.createSequentialGroup() - .addGap(JBUIScale.scale(5)) - .addComponent(createButton) - .addGap(JBUIScale.scale(4)) - .addComponent(separator) - .addComponent(reloadButton) - .addComponent(helpButton) - ) - .addComponent(myDetailsPanelPanel) - val verticalGroup: GroupLayout.Group = layout.createSequentialGroup() - .addGroup( - layout.createParallelGroup(GroupLayout.Alignment.CENTER) - .addComponent(createButton) - .addComponent(separator) - .addComponent(reloadButton) - .addComponent(helpButton) - ) - .addComponent(myDetailsPanelPanel) + val horizontalGroup: GroupLayout.Group = + layout + .createParallelGroup() + .addGroup( + layout + .createSequentialGroup() + .addGap(JBUIScale.scale(5)) + .addComponent(reloadButton) + .addComponent(helpButton) + ) + .addComponent(myDetailsPanelPanel) + val verticalGroup: GroupLayout.Group = + layout + .createSequentialGroup() + .addGroup( + layout + .createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(reloadButton) + .addComponent(helpButton) + ) + .addComponent(myDetailsPanelPanel) layout.setHorizontalGroup(horizontalGroup) layout.setVerticalGroup(verticalGroup) setLayout(layout) diff --git a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTable.kt b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTable.kt index 332414a..acd9e24 100644 --- a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTable.kt +++ b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTable.kt @@ -19,19 +19,17 @@ import com.android.tools.idea.concurrency.executeOnPooledThread import com.android.tools.idea.devicemanager.Device import com.android.tools.idea.devicemanager.DeviceTable import com.android.tools.idea.devicemanager.IconButtonTableCellEditor +import com.google.gct.directaccess.DirectAccessService import com.google.gct.directaccess.FirebaseDevice -import com.google.services.firebase.directaccess.client.device.directaccess.testConnectDirectAccess import com.intellij.openapi.Disposable -import com.intellij.util.concurrency.AppExecutorUtil +import com.intellij.openapi.components.service import icons.StudioIcons import java.awt.Component import javax.swing.Icon import javax.swing.JTable class FirebaseDeviceTable(model: FirebaseDeviceTableModel) : - DeviceTable<FirebaseDevice>( - model, - FirebaseDevice::class.java), Disposable { + DeviceTable<FirebaseDevice>(model, FirebaseDevice::class.java), Disposable { init { setDefaultRenderer(Device::class.java, FirebaseDeviceTableCellRenderer()) @@ -40,8 +38,7 @@ class FirebaseDeviceTable(model: FirebaseDeviceTableModel) : override fun deviceViewColumnIndex() = convertColumnIndexToView(DEVICE_MODEL_COLUMN_INDEX) - override fun dispose() { - } + override fun dispose() {} } // TODO: replace this with better UI @@ -52,10 +49,15 @@ class LaunchButtonTableCellEditor(model: FirebaseDeviceTableModel) : init { myButton.addActionListener { - executeOnPooledThread { testConnectDirectAccess( - AppExecutorUtil.getAppExecutorService(), - model.devices[row].target.substringAfter(' '), - model.devices[row].androidVersion.apiString) } + executeOnPooledThread { + model + .project + .service<DirectAccessService>() + .acquireAndConnect( + model.devices[row].target.substringAfter(' '), + model.devices[row].androidVersion.apiString, + ) + } } } diff --git a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableCellRenderer.kt b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableCellRenderer.kt index 90ded5e..17ff142 100644 --- a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableCellRenderer.kt +++ b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableCellRenderer.kt @@ -18,5 +18,5 @@ package com.google.gct.directaccess.ui import com.android.tools.idea.devicemanager.DeviceTableCellRenderer import com.google.gct.directaccess.FirebaseDevice -class FirebaseDeviceTableCellRenderer: DeviceTableCellRenderer<FirebaseDevice>(FirebaseDevice::class.java) { -} +class FirebaseDeviceTableCellRenderer : + DeviceTableCellRenderer<FirebaseDevice>(FirebaseDevice::class.java) {} diff --git a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableModel.kt b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableModel.kt index af8560e..7954625 100644 --- a/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableModel.kt +++ b/directaccess/src/com/google/gct/directaccess/ui/FirebaseDeviceTableModel.kt @@ -18,6 +18,7 @@ package com.google.gct.directaccess.ui import com.android.tools.idea.devicemanager.Device import com.google.gct.directaccess.FirebaseDevice import com.google.services.firebase.directaccess.client.DeviceInfo +import com.intellij.openapi.project.Project import icons.StudioIcons import javax.swing.Icon import javax.swing.table.AbstractTableModel @@ -26,7 +27,8 @@ const val DEVICE_MODEL_COLUMN_INDEX = 0 const val API_MODEL_COLUMN_INDEX = 1 const val ACTIONS_COLUMN_INDEX = 2 -class FirebaseDeviceTableModel(devices: List<DeviceInfo>) : AbstractTableModel() { +class FirebaseDeviceTableModel(devices: List<DeviceInfo>, val project: Project) : + AbstractTableModel() { val devices = devices.map { FirebaseDevice(it) }.toMutableList() override fun getRowCount(): Int { |