Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Roku File System Panel and Roku App Overlays Panel #545

Merged
Merged
57 changes: 51 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,20 @@
},
{
"id": "rokuDeviceView",
"contextualTitle": "Roku",
"name": "Device View",
"contextualTitle": "Roku Device View",
"name": "Roku Device View",
"type": "webview"
},
{
"id": "rokuFileSystemView",
"contextualTitle": "Roku File System",
"name": "Roku File System",
"type": "webview"
},
{
"id": "rokuAppOverlaysView",
"contextualTitle": "Roku App Overlays",
"name": "Roku App Overlays",
"type": "webview"
},
{
Expand Down Expand Up @@ -258,22 +270,22 @@
},
{
"command": "extension.brightscript.rokuRegistry.refreshRegistry",
"when": "view == rokuRegistryView",
"when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable",
"group": "navigation"
},
{
"command": "extension.brightscript.rokuRegistry.importRegistry",
"when": "view == rokuRegistryView",
"when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable",
"group": "navigation"
},
{
"command": "extension.brightscript.rokuRegistry.exportRegistry",
"when": "view == rokuRegistryView",
"when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable",
"group": "navigation"
},
{
"command": "extension.brightscript.rokuRegistry.clearRegistry",
"when": "view == rokuRegistryView",
"when": "view == rokuRegistryView && brightscript.isOnDeviceComponentAvailable",
"group": "navigation"
},
{
Expand Down Expand Up @@ -325,6 +337,21 @@
"command": "extension.brightscript.rokuAutomationView.disableAutorunOnDeploy",
"when": "view == rokuAutomationView && brightscript.rokuAutomationView.autorunOnDeploy",
"group": "navigation@2"
},
{
"command": "extension.brightscript.rokuFileSystemView.refresh",
"when": "view == rokuFileSystemView && brightscript.isOnDeviceComponentAvailable",
"group": "navigation@1"
},
{
"command": "extension.brightscript.rokuAppOverlaysView.addNewOverlay",
"when": "view == rokuAppOverlaysView && brightscript.isOnDeviceComponentAvailable",
"group": "navigation@1"
},
{
"command": "extension.brightscript.rokuAppOverlaysView.removeAllOverlays",
"when": "view == rokuAppOverlaysView && brightscript.isOnDeviceComponentAvailable",
"group": "navigation@2"
}
],
"webview/context": [
Expand Down Expand Up @@ -2803,6 +2830,24 @@
"category": "BrighterScript",
"icon": "$(pass-filled)"
},
{
"command": "extension.brightscript.rokuFileSystemView.refresh",
"title": "Refresh",
"category": "BrightScript",
"icon": "$(refresh)"
},
{
"command": "extension.brightscript.rokuAppOverlaysView.addNewOverlay",
"title": "Add New Overlay",
"category": "BrighterScript",
"icon": "$(new-file)"
},
{
"command": "extension.brightscript.rokuAppOverlaysView.removeAllOverlays",
"title": "Remove All Overlays",
"category": "BrighterScript",
"icon": "$(trash)"
},
{
"command": "extension.brightscript.languageServer.restart",
"title": "Restart Language Server",
Expand Down
5 changes: 4 additions & 1 deletion src/commands/VscodeCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ export enum VscodeCommand {
rokuAutomationViewStartRecording = 'extension.brightscript.rokuAutomationView.startRecording',
rokuAutomationViewStopRecording = 'extension.brightscript.rokuAutomationView.stopRecording',
enableRemoteControlMode = 'extension.brightscript.enableRemoteControlMode',
disableRemoteControlMode = 'extension.brightscript.disableRemoteControlMode'
disableRemoteControlMode = 'extension.brightscript.disableRemoteControlMode',
rokuAppOverlaysViewAddNewOverlay = 'extension.brightscript.rokuAppOverlaysView.addNewOverlay',
rokuAppOverlaysViewRemoveAllOverlays = 'extension.brightscript.rokuAppOverlaysView.removeAllOverlays',
rokuFileSystemViewRefresh = 'extension.brightscript.rokuFileSystemView.refresh'
}
34 changes: 15 additions & 19 deletions src/managers/RtaManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as fs from 'fs';
import * as path from 'path';
import * as rta from 'roku-test-automation';
import { ViewProviderEvent } from '../viewProviders/ViewProviderEvent';
import { ViewProviderId } from '../viewProviders/ViewProviderId';
Expand Down Expand Up @@ -56,25 +58,7 @@ export class RtaManager {
public async sendOdcRequest(requestorId: string, command: string, context: { args: any; options: any }) {
const { args, options } = context;

if (command === rta.RequestType.findNodesAtLocation) {
if (!this.lastStoreNodesResponse) {
args.includeBoundingRectInfo = true;
await this.sendOdcRequest(requestorId, rta.RequestType.storeNodeReferences, args);
}
context.args.nodeTreeResponse = this.lastStoreNodesResponse;
let { matches } = await rta.odc.findNodesAtLocation(args, options);
if (requestorId === ViewProviderId.rokuDeviceView) {
if (matches.length) {
const match = { ...matches[0] };
// Remove children as this is where most of the payload is and we don't need this info
match.children = [];
matches = [match];
}
}
return {
matches: matches
};
} else if (command === rta.RequestType.storeNodeReferences) {
if (command === rta.RequestType.storeNodeReferences) {
this.lastStoreNodesResponse = await rta.odc.storeNodeReferences(args, options);

const viewIds = [];
Expand All @@ -87,6 +71,18 @@ export class RtaManager {
event: ViewProviderEvent.onStoredNodeReferencesUpdated
});
return this.lastStoreNodesResponse;
} else if (command === rta.RequestType.writeFile) {
// We can't access files from the webview so we just store the path and access it in node instead
const directoryPath = path.dirname(args.destinationPath);
// We always try to make the directory. Doesn't fail if it already exists
await rta.odc.createDirectory({
path: directoryPath
});

return rta.odc.writeFile({
binaryPayload: fs.readFileSync(args.sourcePath),
path: args.destinationPath
}, options);
} else {
return this.onDeviceComponent[command](args, options);
}
Expand Down
23 changes: 15 additions & 8 deletions src/managers/WebviewViewProviderManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type { BrightScriptCommands } from '../BrightScriptCommands';
import * as vscode from 'vscode';
import { RokuCommandsViewProvider } from '../viewProviders/RokuCommandsViewProvider';
import { RokuDeviceViewViewProvider } from '../viewProviders/RokuDeviceViewViewProvider';
import { RokuFileSystemViewViewProvider } from '../viewProviders/RokuFileSystemViewViewProvider';
import { RokuAppOverlaysViewViewProvider } from '../viewProviders/RokuAppOverlaysViewViewProvider';
import { RokuRegistryViewProvider } from '../viewProviders/RokuRegistryViewProvider';
import { SceneGraphInspectorViewProvider } from '../viewProviders/SceneGraphInspectorViewProvider';
import { RokuAutomationViewViewProvider } from '../viewProviders/RokuAutomationViewViewProvider';
Expand All @@ -15,7 +17,6 @@ export class WebviewViewProviderManager {
private rtaManager: RtaManager,
brightScriptCommands: BrightScriptCommands
) {

for (const webview of this.webviewViews) {
if (!webview.provider) {
webview.provider = new webview.constructor(context, {
Expand All @@ -30,20 +31,26 @@ export class WebviewViewProviderManager {
}

private webviewViews = [{
constructor: SceneGraphInspectorViewProvider,
provider: undefined as SceneGraphInspectorViewProvider
}, {
constructor: RokuRegistryViewProvider,
provider: undefined as RokuRegistryViewProvider
constructor: RokuAutomationViewViewProvider,
provider: undefined as RokuAutomationViewViewProvider
}, {
constructor: RokuCommandsViewProvider,
provider: undefined as RokuCommandsViewProvider
}, {
constructor: RokuDeviceViewViewProvider,
provider: undefined as RokuDeviceViewViewProvider
}, {
constructor: RokuAutomationViewViewProvider,
provider: undefined as RokuAutomationViewViewProvider
constructor: RokuFileSystemViewViewProvider,
provider: undefined as RokuFileSystemViewViewProvider
}, {
constructor: RokuRegistryViewProvider,
provider: undefined as RokuRegistryViewProvider
}, {
constructor: RokuAppOverlaysViewViewProvider,
provider: undefined as RokuAppOverlaysViewViewProvider
}, {
constructor: SceneGraphInspectorViewProvider,
provider: undefined as SceneGraphInspectorViewProvider
}];

public getWebviewViewProviders() {
Expand Down
13 changes: 13 additions & 0 deletions src/viewProviders/BaseWebviewViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvi
} else if (command === ViewProviderCommand.sendMessageToWebviews) {
const context = message.context;
this.webviewViewProviderManager.sendMessageToWebviews(context.viewIds, context.message);
} else if (command === ViewProviderCommand.updateWorkspaceState) {
const context = message.context;
await this.extensionContext.workspaceState.update(context.key, context.value);
this.postOrQueueMessage({
...message
});
} else if (command === ViewProviderCommand.getWorkspaceState) {
const context = message.context;
const response = await this.extensionContext.workspaceState.get(context.key, context.defaultValue);
this.postOrQueueMessage({
...message,
response: response
});
} else {
const callback = this.messageCommandCallbacks[command];
if (!callback || !await callback(message)) {
Expand Down
49 changes: 49 additions & 0 deletions src/viewProviders/RokuAppOverlaysViewViewProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { VscodeCommand } from '../commands/VscodeCommand';
import { BaseRdbViewProvider } from './BaseRdbViewProvider';
import { ViewProviderId } from './ViewProviderId';
import { ViewProviderEvent } from './ViewProviderEvent';

export class RokuAppOverlaysViewViewProvider extends BaseRdbViewProvider {
public readonly id = ViewProviderId.rokuAppOverlaysView;

constructor(context: vscode.ExtensionContext, dependencies) {
super(context, dependencies);

const subscriptions = context.subscriptions;

this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuAppOverlaysViewRemoveAllOverlays);

subscriptions.push(vscode.commands.registerCommand(VscodeCommand.rokuAppOverlaysViewAddNewOverlay, async () => {
const options: vscode.OpenDialogOptions = {
canSelectMany: false,
openLabel: 'Add Overlay',
canSelectFiles: true,
canSelectFolders: false,
filters: {
Images: ['png', 'jpg', 'jpeg', 'webp']
triwav marked this conversation as resolved.
Show resolved Hide resolved
}
};
const [file] = await vscode.window.showOpenDialog(options);
const name = path.basename(file.path);
const extension = path.extname(file.path);
const destinationFileName = path.basename(file.path, extension) + '_' + Date.now() + extension;

const message = this.createEventMessage(ViewProviderEvent.onRokuAppOverlayAdded, {
id: this.randomStringGenerator(),
name: name,
sourcePath: file.path,
destinationFileName: destinationFileName
});

this.postOrQueueMessage(message);
}));
}

private randomStringGenerator(length = 7) {
const p = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
// eslint-disable-next-line no-bitwise
return [...Array(length)].reduce((a) => a + p[~~(Math.random() * p.length)], '');
}
}
11 changes: 5 additions & 6 deletions src/viewProviders/RokuAutomationViewViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BaseRdbViewProvider } from './BaseRdbViewProvider';
import { ViewProviderId } from './ViewProviderId';
import { ViewProviderCommand } from './ViewProviderCommand';
import { ViewProviderEvent } from './ViewProviderEvent';
import { WorkspaceStateKey } from './WorkspaceStateKey';

export class RokuAutomationViewViewProvider extends BaseRdbViewProvider {
public readonly id = ViewProviderId.rokuAutomationView;
Expand All @@ -20,7 +21,7 @@ export class RokuAutomationViewViewProvider extends BaseRdbViewProvider {
this.addMessageCommandCallback(ViewProviderCommand.storeRokuAutomationConfigs, async (message) => {
this.rokuAutomationConfigs = message.context.configs;
// Make sure to use JSON.stringify or weird stuff happens
await context.workspaceState.update(this.configStorageKey, JSON.stringify(message.context));
await context.workspaceState.update(WorkspaceStateKey.rokuAutomationConfigs, JSON.stringify(message.context));
return true;
});

Expand Down Expand Up @@ -70,7 +71,7 @@ export class RokuAutomationViewViewProvider extends BaseRdbViewProvider {
await this.setAutorunOnDeploy(false);
}));

let autorunOnDeploy: boolean = this.extensionContext.workspaceState.get(this.autorunOnDeployStorageKey);
let autorunOnDeploy: boolean = this.extensionContext.workspaceState.get(WorkspaceStateKey.rokuAutomationAutorunOnDeploy);
// Default to true if not set
if (autorunOnDeploy !== false) {
autorunOnDeploy = true;
Expand All @@ -86,13 +87,12 @@ export class RokuAutomationViewViewProvider extends BaseRdbViewProvider {
private async setAutorunOnDeploy(autorunOnDeploy: boolean) {
this.rokuAutomationAutorunOnDeploy = autorunOnDeploy;
await vscodeContextManager.set('brightscript.rokuAutomationView.autorunOnDeploy', autorunOnDeploy);
await this.context.workspaceState.update(this.autorunOnDeployStorageKey, autorunOnDeploy);
await this.context.workspaceState.update(WorkspaceStateKey.rokuAutomationAutorunOnDeploy, autorunOnDeploy);
}

private context: vscode.ExtensionContext;

private isRecording = false;
private configStorageKey = 'rokuAutomationConfigs';
private rokuAutomationConfigs: {
name: string;
steps: {
Expand All @@ -101,7 +101,6 @@ export class RokuAutomationViewViewProvider extends BaseRdbViewProvider {
}[];
}[];

private autorunOnDeployStorageKey = 'rokuAutomationAutorunOnDeploy';
private rokuAutomationAutorunOnDeploy = false;

private currentRunningStep = -1;
Expand Down Expand Up @@ -157,7 +156,7 @@ export class RokuAutomationViewViewProvider extends BaseRdbViewProvider {
// Always post back the device status so we make sure the client doesn't miss it if it got refreshed
this.updateDeviceAvailability();

const json = this.extensionContext.workspaceState.get(this.configStorageKey);
const json = this.extensionContext.workspaceState.get(WorkspaceStateKey.rokuAutomationConfigs);
if (typeof json === 'string') {
const result = JSON.parse(json);
this.rokuAutomationConfigs = result.configs;
Expand Down
32 changes: 32 additions & 0 deletions src/viewProviders/RokuFileSystemViewViewProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { VscodeCommand } from '../commands/VscodeCommand';
import { BaseRdbViewProvider } from './BaseRdbViewProvider';
import { ViewProviderId } from './ViewProviderId';
import { ViewProviderCommand } from './ViewProviderCommand';

export class RokuFileSystemViewViewProvider extends BaseRdbViewProvider {
public readonly id = ViewProviderId.rokuFileSystemView;

constructor(context: vscode.ExtensionContext, dependencies) {
super(context, dependencies);

this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuFileSystemViewRefresh);

this.addMessageCommandCallback(ViewProviderCommand.openRokuFile, async (message) => {
const pathContentsInfo = message.context;
const result = await this.dependencies.rtaManager.onDeviceComponent.readFile({
path: pathContentsInfo.path
});

const filePath = path.join(os.tmpdir(), path.basename(pathContentsInfo.path));

// Write some content to the new file
fs.writeFileSync(filePath, result.binaryPayload);
await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(filePath));
triwav marked this conversation as resolved.
Show resolved Hide resolved
return true;
});
}
}
15 changes: 10 additions & 5 deletions src/viewProviders/ViewProviderCommand.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
export enum ViewProviderCommand {
viewReady = 'viewReady',
getStoredNodeReferences = 'getStoredNodeReferences',
getScreenshot = 'getScreenshot',
setManualIpAddress = 'setManualIpAddress',
getStoredNodeReferences = 'getStoredNodeReferences',
getStoredRokuAppOverlays = 'getStoredRokuAppOverlays',
getWorkspaceState = 'getWorkspaceState',
openRokuFile = 'openRokuFile',
runRokuAutomationConfig = 'runRokuAutomationConfig',
sendMessageToWebviews = 'sendMessageToWebviews',
setManualIpAddress = 'setManualIpAddress',
setVscodeContext = 'setVscodeContext',
updateWorkspaceState = 'updateWorkspaceState',
stopRokuAutomationConfig = 'stopRokuAutomationConfig',
storeRokuAppOverlays = 'storeRokuAppOverlays',
storeRokuAutomationConfigs = 'storeRokuAutomationConfigs',
runRokuAutomationConfig = 'runRokuAutomationConfig',
stopRokuAutomationConfig = 'stopRokuAutomationConfig'
viewReady = 'viewReady'
}
Loading