From 6c8566a704124695d4edb3e367b826d670b88784 Mon Sep 17 00:00:00 2001 From: Liam McLoughlin Date: Fri, 17 Jan 2020 17:13:02 +0000 Subject: [PATCH] Add simulated input support Signed-off-by: Liam McLoughlin --- packages/fdb-debugger/src/index.ts | 39 +++++++ .../fdb-protocol/src/FDBTypes/Initialize.ts | 2 + packages/fdb-protocol/src/FDBTypes/Input.ts | 66 ++++++++++++ .../fdb-protocol/src/FDBTypes/Structures.ts | 12 +++ packages/fdb-protocol/src/FDBTypes/index.ts | 1 + packages/sdk-cli/src/cli.ts | 2 + packages/sdk-cli/src/commands/input.ts | 94 ++++++++++++++++ yarn.lock | 101 +++++++++++++++++- 8 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 packages/fdb-protocol/src/FDBTypes/Input.ts create mode 100644 packages/sdk-cli/src/commands/input.ts diff --git a/packages/fdb-debugger/src/index.ts b/packages/fdb-debugger/src/index.ts index ee576676..ccf40525 100644 --- a/packages/fdb-debugger/src/index.ts +++ b/packages/fdb-debugger/src/index.ts @@ -449,16 +449,55 @@ export class RemoteHost extends EventEmitter { FDBTypes.AppDebugEvalResult, ); + private sendButtonInput = this.bindMethod( + 'input.button', + FDBTypes.ButtonInput, + t.null, + ); + + private sendTouchInput = this.bindMethod( + 'input.touch', + FDBTypes.TouchInput, + t.null, + ); + hasEvalSupport() { return this.hasCapability('appHost.debug.app.evalToString.supported') && this.info.capabilities.appHost!.debug!.app!.evalToString!.supported && !FBOS3_EVAL_QUIRK.test(this.info.device); } + hasTouchInputSupport() { + return this.hasCapability('appHost.input.touch') && + this.info.capabilities.appHost!.input!.touch!; + } + + hasButtonInputSupport() { + return this.hasCapability('appHost.input.buttons') && + this.info.capabilities.appHost!.input!.buttons! && + this.info.capabilities.appHost!.input!.buttons!.length > 0; + } + + buttons() { + if (!this.hasButtonInputSupport) return []; + return this.info.capabilities.appHost!.input!.buttons!; + } + eval(cmd: string) { return this.sendEvalCmd({ cmd }); } + simulateButtonPress(button: FDBTypes.Button) { + return this.sendButtonInput({ button }); + } + + simulateTouch(location: FDBTypes.Point, state: FDBTypes.TouchState) { + return this.sendTouchInput({ + location, + state, + }); + } + supportsPartialAppInstall() { return this.hasCapability('appHost.install.partialBundle') && this.info.capabilities.appHost!.install!.partialBundle!; diff --git a/packages/fdb-protocol/src/FDBTypes/Initialize.ts b/packages/fdb-protocol/src/FDBTypes/Initialize.ts index 5c644b25..4b3f17b9 100644 --- a/packages/fdb-protocol/src/FDBTypes/Initialize.ts +++ b/packages/fdb-protocol/src/FDBTypes/Initialize.ts @@ -5,6 +5,7 @@ import { IOCapabilities } from './BulkData'; import { ConsoleDebuggerCapabilities } from './Console'; import { EvalToStringCapability } from './Eval'; import { HeapSnapshotCapability } from './HeapSnapshot'; +import { InputCapabilities } from './Input'; import { LaunchCapabilities } from './Launch'; import { ProtocolCapabilities } from './Meta'; import { ScreenshotCapabilities } from './Screenshot'; @@ -84,6 +85,7 @@ export const ApplicationHostCapabilities = t.partial( launch: LaunchCapabilities, screenshot: ScreenshotCapabilities, debug: DebugCapabilities, + input: InputCapabilities, }, 'ApplicationHostCapabilities', ); diff --git a/packages/fdb-protocol/src/FDBTypes/Input.ts b/packages/fdb-protocol/src/FDBTypes/Input.ts new file mode 100644 index 00000000..96619057 --- /dev/null +++ b/packages/fdb-protocol/src/FDBTypes/Input.ts @@ -0,0 +1,66 @@ +import * as t from 'io-ts'; + +import { Point } from './Structures'; + +// Runtime types are variables which are used like types, which is +// reflected in their PascalCase naming scheme. +/* tslint:disable:variable-name */ + +export const Button = t.union( + [t.literal('up'), t.literal('down'), t.literal('back')], + 'Button', +); +export type Button = t.TypeOf; + +export const ButtonInput = t.interface( + { + /** + * Which button is being pressed. + */ + button: Button, + }, + 'ButtonInput', +); +export type ButtonInput = t.TypeOf; + +export const TouchState = t.union( + [t.literal('up'), t.literal('down'), t.literal('move')], + 'TouchState', +); +export type TouchState = t.TypeOf; + +export const TouchInput = t.interface( + { + /** + * Status of simulated touch. + * 'move' must only be sent in the period between a 'down' input and its corresponding 'up'. + */ + state: TouchState, + + /** + * Location of touch event. + */ + location: Point, + }, + 'TouchInput', +); +export type TouchInput = t.TypeOf; + +/** + * Capabilities specific to inputs. + */ +export const InputCapabilities = t.partial( + { + /** + * The Host supports sending simulated button presses. + */ + buttons: t.array(Button), + + /** + * The Host supports sending simulated touch screen presses. + */ + touch: t.boolean, + }, + 'InputCapabilities', +); +export type InputCapabilities = t.TypeOf; diff --git a/packages/fdb-protocol/src/FDBTypes/Structures.ts b/packages/fdb-protocol/src/FDBTypes/Structures.ts index 41711b69..cde7336e 100644 --- a/packages/fdb-protocol/src/FDBTypes/Structures.ts +++ b/packages/fdb-protocol/src/FDBTypes/Structures.ts @@ -182,3 +182,15 @@ export const ComponentBundleKind = t.union([ t.literal('companion'), ]); export type ComponentBundleKind = t.TypeOf; + +/** + * Describes a point on the simulated device's screen, relative to the top-left corner at (0,0). + */ +export const Point = t.interface( + { + x: NonNegativeInteger, + y: NonNegativeInteger, + }, + 'Point', +); +export type Point = t.TypeOf; diff --git a/packages/fdb-protocol/src/FDBTypes/index.ts b/packages/fdb-protocol/src/FDBTypes/index.ts index bc3d998d..2d5391b7 100644 --- a/packages/fdb-protocol/src/FDBTypes/index.ts +++ b/packages/fdb-protocol/src/FDBTypes/index.ts @@ -6,6 +6,7 @@ export * from './ContentsList'; export * from './Eval'; export * from './HeapSnapshot'; export * from './Initialize'; +export * from './Input'; export * from './Launch'; export * from './Meta'; export * from './Screenshot'; diff --git a/packages/sdk-cli/src/cli.ts b/packages/sdk-cli/src/cli.ts index 0d00ee7f..43fcba93 100644 --- a/packages/sdk-cli/src/cli.ts +++ b/packages/sdk-cli/src/cli.ts @@ -20,6 +20,7 @@ import buildAndInstall from './commands/buildAndInstall'; import connect from './commands/connect'; import heapSnapshot from './commands/heapSnapshot'; import hosts from './commands/hosts'; +import input from './commands/input'; import install from './commands/install'; import logout from './commands/logout'; import mockHost from './commands/mockHost'; @@ -38,6 +39,7 @@ cli.use(build); cli.use(buildAndInstall({ hostConnections, appContext })); cli.use(connect({ hostConnections })); cli.use(heapSnapshot({ hostConnections })); +cli.use(input({ hostConnections })); cli.use(install({ hostConnections, appContext })); cli.use(screenshot({ hostConnections })); cli.use(setAppPackage({ appContext })); diff --git a/packages/sdk-cli/src/commands/input.ts b/packages/sdk-cli/src/commands/input.ts new file mode 100644 index 00000000..70e6448a --- /dev/null +++ b/packages/sdk-cli/src/commands/input.ts @@ -0,0 +1,94 @@ +import { FDBTypes } from '@fitbit/fdb-protocol'; +import { isRight } from 'fp-ts/lib/Either'; +import vorpal from 'vorpal'; + +import HostConnections from '../models/HostConnections'; + +function isSupportedButton( + supportedButtons: FDBTypes.Button[], + button: string, +): button is FDBTypes.Button { + return supportedButtons.includes(button as FDBTypes.Button); +} + +function isValidTouchState(state: string): state is FDBTypes.TouchState { + return isRight(FDBTypes.TouchState.decode(state)); +} + +const wait = (durationMs: number) => + new Promise(resolve => setTimeout(resolve, durationMs)); + +export default function input(stores: { hostConnections: HostConnections }) { + return (cli: vorpal) => { + cli + .command('input button