From 5232b079231754473ef242c1aba812cebcd0a92a Mon Sep 17 00:00:00 2001 From: Brian Leighty Date: Fri, 1 Mar 2024 13:50:30 -0500 Subject: [PATCH] Device disconnect support and other improvements (#548) * Add Roku File System Panel and Roku App Overlays * small PR cleanup * Add support for bmp and gif overlays * Prevent text selection on file system entry selection * Open file in read only mode * Switch to using uuid for id generation * Switch to cursor: pointer * Switch to progress ring from question mark for better clarity of what is happening * Rename force to forceReload * Add documentation * Fix path issue on windows * Add UI for empty folder and use odcAvailable instead of deviceAvailable for new panels * add support for disconnecting from device in webview panels, avoid issues with deploying due to screenshot capture happening at same time, add support for copying node info response in scenegraph inspector detail view * resume screenshot capture after onDidTerminateDebugSession * Delete resumeScreenshotCapture after calling (since it's a promise.resolve()) --------- Co-authored-by: Brian Leighty Co-authored-by: Bronley Plumb --- package.json | 51 +++++++++++++++++-- src/commands/VscodeCommand.ts | 3 +- src/extension.ts | 4 +- src/managers/RtaManager.ts | 27 ++++++++-- .../WebviewViewProviderManager.spec.ts | 4 +- src/managers/WebviewViewProviderManager.ts | 12 +++++ src/viewProviders/BaseWebviewViewProvider.ts | 8 +++ .../RokuDeviceViewViewProvider.ts | 29 ++++++++++- .../RokuRegistryViewProvider.spec.ts | 2 +- .../NodeDetailPage.svelte | 22 ++++++-- 10 files changed, 143 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 7d30fc1f..abbb4200 100644 --- a/package.json +++ b/package.json @@ -264,31 +264,39 @@ "when": "debugType == 'brightscript' && view == rendezvousView", "group": "navigation" }, + { "command": "extension.brightscript.refreshDeviceList", "when": "view == onlineDevicesView", "group": "navigation" }, + { "command": "extension.brightscript.rokuRegistry.refreshRegistry", "when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable", - "group": "navigation" + "group": "navigation@1" }, { "command": "extension.brightscript.rokuRegistry.importRegistry", "when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable", - "group": "navigation" + "group": "navigation@2" }, { "command": "extension.brightscript.rokuRegistry.exportRegistry", "when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable", - "group": "navigation" + "group": "navigation@3" }, { "command": "extension.brightscript.rokuRegistry.clearRegistry", "when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable", - "group": "navigation" + "group": "navigation@4" }, + { + "command": "extension.brightscript.disconnectFromDevice", + "when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable", + "group": "navigation@5" + }, + { "command": "extension.brightscript.rokuDeviceView.refreshScreenshot", "when": "view == rokuDeviceView && !brightscript.rokuDeviceView.enableScreenshotCapture", @@ -319,6 +327,12 @@ "when": "view == rokuDeviceView", "group": "navigation@4" }, + { + "command": "extension.brightscript.disconnectFromDevice", + "when": "view == rokuDeviceView && brightscript.rokuDeviceView.isOnDeviceComponentAvailable", + "group": "navigation@5" + }, + { "command": "extension.brightscript.rokuAutomationView.startRecording", "when": "view == rokuAutomationView && !brightscript.rokuAutomationView.isRecording", @@ -344,6 +358,12 @@ "when": "view == rokuFileSystemView && brightscript.isOnDeviceComponentAvailable", "group": "navigation@1" }, + { + "command": "extension.brightscript.disconnectFromDevice", + "when": "view == rokuFileSystemView && brightscript.isOnDeviceComponentAvailable", + "group": "navigation@2" + }, + { "command": "extension.brightscript.rokuAppOverlaysView.addNewOverlay", "when": "view == rokuAppOverlaysView && brightscript.isOnDeviceComponentAvailable", @@ -353,6 +373,23 @@ "command": "extension.brightscript.rokuAppOverlaysView.removeAllOverlays", "when": "view == rokuAppOverlaysView && brightscript.isOnDeviceComponentAvailable", "group": "navigation@2" + }, + { + "command": "extension.brightscript.disconnectFromDevice", + "when": "view == rokuAppOverlaysView && brightscript.isOnDeviceComponentAvailable", + "group": "navigation@3" + }, + + { + "command": "extension.brightscript.disconnectFromDevice", + "when": "view == rokuCommandsView && brightscript.isOnDeviceComponentAvailable", + "group": "navigation@1" + }, + + { + "command": "extension.brightscript.disconnectFromDevice", + "when": "view == sceneGraphInspectorView && brightscript.isOnDeviceComponentAvailable", + "group": "navigation@1" } ], "webview/context": [ @@ -2901,6 +2938,12 @@ "title": "Refresh Nodetree", "category": "BrighterScript", "icon": "$(refresh)" + }, + { + "command": "extension.brightscript.disconnectFromDevice", + "title": "Disconnect From Roku Device", + "category": "BrighterScript", + "icon": "$(debug-disconnect)" } ], "keybindings": [ diff --git a/src/commands/VscodeCommand.ts b/src/commands/VscodeCommand.ts index 131c9322..38eae7b0 100644 --- a/src/commands/VscodeCommand.ts +++ b/src/commands/VscodeCommand.ts @@ -17,5 +17,6 @@ export enum VscodeCommand { disableRemoteControlMode = 'extension.brightscript.disableRemoteControlMode', rokuAppOverlaysViewAddNewOverlay = 'extension.brightscript.rokuAppOverlaysView.addNewOverlay', rokuAppOverlaysViewRemoveAllOverlays = 'extension.brightscript.rokuAppOverlaysView.removeAllOverlays', - rokuFileSystemViewRefresh = 'extension.brightscript.rokuFileSystemView.refresh' + rokuFileSystemViewRefresh = 'extension.brightscript.rokuFileSystemView.refresh', + disconnectFromDevice = 'extension.brightscript.disconnectFromDevice' } diff --git a/src/extension.ts b/src/extension.ts index 0a2ed83c..738a6922 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -77,7 +77,7 @@ export class Extension { userInputManager ); - this.rtaManager = new RtaManager(); + this.rtaManager = new RtaManager(context); this.webviewViewProviderManager = new WebviewViewProviderManager(context, this.rtaManager, this.brightScriptCommands); this.rtaManager.setWebviewViewProviderManager(this.webviewViewProviderManager); @@ -177,6 +177,7 @@ export class Extension { //if this is a brightscript debug session if (e.type === 'brightscript') { logOutputManager.onDidStartDebugSession(); + this.webviewViewProviderManager.onDidStartDebugSession(e); } this.diagnosticManager.clear(); }); @@ -189,6 +190,7 @@ export class Extension { if (config.remoteControlMode?.deactivateOnSessionEnd) { void this.remoteControlManager.setRemoteControlMode(false, 'launch'); } + this.webviewViewProviderManager.onDidTerminateDebugSession(e); } this.diagnosticManager.clear(); }); diff --git a/src/managers/RtaManager.ts b/src/managers/RtaManager.ts index 514275ef..5af9a3e3 100644 --- a/src/managers/RtaManager.ts +++ b/src/managers/RtaManager.ts @@ -1,12 +1,25 @@ import * as fs from 'fs'; import * as path from 'path'; import * as rta from 'roku-test-automation'; +import * as vscode from 'vscode'; import { ViewProviderEvent } from '../viewProviders/ViewProviderEvent'; import { ViewProviderId } from '../viewProviders/ViewProviderId'; import { vscodeContextManager } from './VscodeContextManager'; import type { WebviewViewProviderManager } from './WebviewViewProviderManager'; +import { VscodeCommand } from '../commands/VscodeCommand'; export class RtaManager { + constructor( + context: vscode.ExtensionContext + ) { + context.subscriptions.push(vscode.commands.registerCommand(VscodeCommand.disconnectFromDevice, () => { + void this.onDeviceComponent?.shutdown(); + this.onDeviceComponent = undefined; + void vscodeContextManager.set('brightscript.isOnDeviceComponentAvailable', false); + this.updateDeviceAvailabilityOnWebViewProviders(); + })); + } + public onDeviceComponent?: rta.OnDeviceComponent; public device?: rta.RokuDevice; @@ -44,11 +57,7 @@ export class RtaManager { } void vscodeContextManager.set('brightscript.isOnDeviceComponentAvailable', !!this.onDeviceComponent); - for (const webviewProvider of this.webviewViewProviderManager.getWebviewViewProviders()) { - if (typeof webviewProvider.updateDeviceAvailability === 'function') { - webviewProvider.updateDeviceAvailability(); - } - } + this.updateDeviceAvailabilityOnWebViewProviders(); if (config.disableScreenSaver) { void this.onDeviceComponent?.disableScreenSaver({ disableScreensaver: true }); @@ -95,4 +104,12 @@ export class RtaManager { public getStoredNodeReferences() { return this.lastStoreNodesResponse; } + + private updateDeviceAvailabilityOnWebViewProviders() { + for (const webviewProvider of this.webviewViewProviderManager.getWebviewViewProviders()) { + if (typeof webviewProvider.updateDeviceAvailability === 'function') { + webviewProvider.updateDeviceAvailability(); + } + } + } } diff --git a/src/managers/WebviewViewProviderManager.spec.ts b/src/managers/WebviewViewProviderManager.spec.ts index 4a26e5b7..190ed022 100644 --- a/src/managers/WebviewViewProviderManager.spec.ts +++ b/src/managers/WebviewViewProviderManager.spec.ts @@ -47,7 +47,7 @@ describe('WebviewViewProviderManager', () => { let spy; before(() => { spy = sinon.spy(vscode.window, 'registerWebviewViewProvider'); - rtaManager = new RtaManager(); + rtaManager = new RtaManager(context); webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager, brightScriptCommands); }); @@ -76,7 +76,7 @@ describe('WebviewViewProviderManager', () => { } }; - rtaManager = new RtaManager(); + rtaManager = new RtaManager(context); webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager, brightScriptCommands); rtaManager.setWebviewViewProviderManager(webviewViewProviderManager); }); diff --git a/src/managers/WebviewViewProviderManager.ts b/src/managers/WebviewViewProviderManager.ts index e7f8b7f1..ab9d8506 100644 --- a/src/managers/WebviewViewProviderManager.ts +++ b/src/managers/WebviewViewProviderManager.ts @@ -61,6 +61,18 @@ export class WebviewViewProviderManager { return providers; } + public onDidStartDebugSession(e: vscode.DebugSession) { + for (const webview of this.webviewViews) { + webview.provider.onDidStartDebugSession(e); + } + } + + public onDidTerminateDebugSession(e: vscode.DebugSession) { + for (const webview of this.webviewViews) { + webview.provider.onDidTerminateDebugSession(e); + } + } + // Notification from extension public onChannelPublishedEvent(e: ChannelPublishedEvent) { const config = e.body.launchConfiguration as BrightScriptLaunchConfiguration; diff --git a/src/viewProviders/BaseWebviewViewProvider.ts b/src/viewProviders/BaseWebviewViewProvider.ts index 01c928d1..b66c28db 100644 --- a/src/viewProviders/BaseWebviewViewProvider.ts +++ b/src/viewProviders/BaseWebviewViewProvider.ts @@ -47,6 +47,14 @@ export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvi this.webviewViewProviderManager = manager; } + public onDidStartDebugSession(e: vscode.DebugSession) { + // Can be overwritten in a child to notify on debug session start + } + + public onDidTerminateDebugSession(e: vscode.DebugSession) { + // Can be overwritten in a child to notify on debug session end + } + public onChannelPublishedEvent(e: ChannelPublishedEvent) { // Can be overwritten in a child to notify on channel publish } diff --git a/src/viewProviders/RokuDeviceViewViewProvider.ts b/src/viewProviders/RokuDeviceViewViewProvider.ts index b4a3c2aa..fc75dfca 100644 --- a/src/viewProviders/RokuDeviceViewViewProvider.ts +++ b/src/viewProviders/RokuDeviceViewViewProvider.ts @@ -1,4 +1,5 @@ import type * as vscode from 'vscode'; +import type { ChannelPublishedEvent } from 'roku-debug'; import { VscodeCommand } from '../commands/VscodeCommand'; import { BaseRdbViewProvider } from './BaseRdbViewProvider'; import { ViewProviderId } from './ViewProviderId'; @@ -7,6 +8,9 @@ import { ViewProviderCommand } from './ViewProviderCommand'; export class RokuDeviceViewViewProvider extends BaseRdbViewProvider { public readonly id = ViewProviderId.rokuDeviceView; + private temporarilyDisableScreenshotCapture = false; + private resumeScreenshotCapture?: () => void; + constructor(context: vscode.ExtensionContext, dependencies) { super(context, dependencies); @@ -20,9 +24,15 @@ export class RokuDeviceViewViewProvider extends BaseRdbViewProvider { this.view.show(false); }); - this.addMessageCommandCallback(ViewProviderCommand.getScreenshot, async (message) => { try { + if (this.temporarilyDisableScreenshotCapture) { + // Sometimes we need to temporarily stop screenshot capture as it can prevent successful package deployment to the device + // Originally was just returning true here but now we just pause until we resume capturing + await new Promise((resolve) => { + this.resumeScreenshotCapture = resolve; + }); + } const result = await this.dependencies.rtaManager.device.getScreenshot(); this.postOrQueueMessage({ ...message, @@ -42,4 +52,21 @@ export class RokuDeviceViewViewProvider extends BaseRdbViewProvider { return true; }); } + + public onDidStartDebugSession(e: vscode.DebugSession) { + this.temporarilyDisableScreenshotCapture = true; + } + + public onDidTerminateDebugSession(e: vscode.DebugSession) { + // In case we failed to start debugging we want to allow screenshots again + this.temporarilyDisableScreenshotCapture = false; + this.resumeScreenshotCapture?.(); + delete this.resumeScreenshotCapture; + } + + public onChannelPublishedEvent(e: ChannelPublishedEvent) { + this.temporarilyDisableScreenshotCapture = false; + this.resumeScreenshotCapture?.(); + delete this.resumeScreenshotCapture; + } } diff --git a/src/viewProviders/RokuRegistryViewProvider.spec.ts b/src/viewProviders/RokuRegistryViewProvider.spec.ts index 91fdcc22..c8e02453 100644 --- a/src/viewProviders/RokuRegistryViewProvider.spec.ts +++ b/src/viewProviders/RokuRegistryViewProvider.spec.ts @@ -24,7 +24,7 @@ afterEach(() => { }); describe('RokuRegistryViewProvider', () => { - const rtaManager = new RtaManager(); + const rtaManager = new RtaManager(vscode.context); const provider = new RokuRegistryViewProvider(vscode.context, { rtaManager: rtaManager }); diff --git a/webviews/src/views/SceneGraphInspectorView/NodeDetailPage.svelte b/webviews/src/views/SceneGraphInspectorView/NodeDetailPage.svelte index 37dec305..e7bb0bd9 100644 --- a/webviews/src/views/SceneGraphInspectorView/NodeDetailPage.svelte +++ b/webviews/src/views/SceneGraphInspectorView/NodeDetailPage.svelte @@ -1,3 +1,4 @@ +