Skip to content

Commit

Permalink
November 2021 Release of the APL 1.7.1 compliant APL Viewhost Web
Browse files Browse the repository at this point in the history
For more details on this release refer to CHANGELOG.md

To learn about APL see: https://developer.amazon.com/docs/alexa-presentation-language/understand-apl.html
  • Loading branch information
TYLER CHONG committed Nov 10, 2021
1 parent 8a2c4b9 commit 8addbbc
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 27 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog for apl-viewhost-web

## [1.7.1]

## Changed

- Fixed scrolling issue with SpeakItem command when highlight mode set to line

## [1.7.0]

This release adds support for version 1.7 of the APL specification. Please also see APL Core Library for changes: [apl-core-library CHANGELOG](https://github.com/alexa/apl-core-library/blob/master/CHANGELOG.md)
Expand Down
70 changes: 51 additions & 19 deletions js/apl-html/src/APLRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,8 @@ export default abstract class APLRenderer<Options = {}> {
public init(metricRecorder?: (m: APL.DisplayMetric) => void) {
const startTime = performance.now();
if (this.mOptions.mode === 'TV') {
window.addEventListener('keydown', this.passWindowEventsToCore);
window.addEventListener('keydown', this.passKeyDownToCore);
window.addEventListener('keyup', this.passKeyUpToCore);
}
this.renderComponents();
const stopTime = performance.now();
Expand Down Expand Up @@ -713,7 +714,8 @@ export default abstract class APLRenderer<Options = {}> {
}
this.view = undefined;
}
window.removeEventListener('keydown', this.passWindowEventsToCore);
window.removeEventListener('keydown', this.passKeyDownToCore);
window.removeEventListener('keyup', this.passKeyUpToCore);
}

/**
Expand Down Expand Up @@ -1040,12 +1042,20 @@ export default abstract class APLRenderer<Options = {}> {
}
}

private canPassLocalKeyDown = (event: IAsyncKeyboardEvent) => {
return this.mOptions.mode !== 'TV' || !this.isDPadKey(event.code);
}

private handleKeyDown = async (evt: IAsyncKeyboardEvent) => {
await this.passKeyboardEventToCore(evt, KeyHandlerType.KeyDown);
if (this.canPassLocalKeyDown(evt)) {
await this.passKeyboardEventToCore(evt, KeyHandlerType.KeyDown);
}
}

private handleKeyUp = async (evt: IAsyncKeyboardEvent) => {
await this.passKeyboardEventToCore(evt, KeyHandlerType.KeyUp);
if (this.canPassLocalKeyDown(evt)) {
await this.passKeyboardEventToCore(evt, KeyHandlerType.KeyUp);
}
}

/**
Expand Down Expand Up @@ -1133,28 +1143,50 @@ export default abstract class APLRenderer<Options = {}> {
}
}

private recoverFocusOnEnter(id: string, code: string): void {
if (code === ENTER_KEY) {
const component = this.componentMap[id] as ActionableComponent;
if (component['focus']) {
component.focus();
}
}
private passKeyDownToCore = (event: IAsyncKeyboardEvent) => {
this.passWindowEventsToCore(event, KeyHandlerType.KeyDown);
}

private passWindowEventsToCore = async (event: IAsyncKeyboardEvent) => {
private passKeyUpToCore = (event: IAsyncKeyboardEvent) => {
this.passWindowEventsToCore(event, KeyHandlerType.KeyUp);
}

private passWindowEventsToCore = async (event: IAsyncKeyboardEvent, handler: KeyHandlerType) => {
if (!this.context) {
return;
}

const focused = await this.context.getFocused();
if (this.isDPadKey(event.code)
&& (!document.activeElement || document.activeElement === document.body)
&& focused) {
this.recoverFocusOnEnter(focused, event.code);
this.passKeyboardEventToCore(event, KeyHandlerType.KeyDown);
} else if (!focused) {
const focusedComponentId = await this.context.getFocused();

if (this.shouldPassWindowEventToCore(event, focusedComponentId)) {
this.ensureComponentIsFocused(focusedComponentId, event.code);
this.passKeyboardEventToCore(event, handler);
} else if (!focusedComponentId) {
this.focusTopLeft();
}
}

private shouldPassWindowEventToCore(event: IAsyncKeyboardEvent, focusedComponentId: string) {
const isViewAlreadyFocused = () => {
return this.view.contains(document.activeElement);
};

const isFocusLost = () => {
return !this.view.contains(document.activeElement)
&& !(document.activeElement instanceof HTMLTextAreaElement);
};

return this.isDPadKey(event.code)
&& focusedComponentId
&& (isFocusLost() || isViewAlreadyFocused());
}

private ensureComponentIsFocused(id: string, code: string): void {
if (code === ENTER_KEY) {
const component = this.componentMap[id] as ActionableComponent;
if (component['focus']) {
component.focus();
}
}
}
}
2 changes: 2 additions & 0 deletions js/apl-html/src/events/RequestFirstLineBounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export class RequestFirstLineBounds extends Event {
// Highlight first line as this event applicable only to line highlighting.
this.component.highlight(0);
this.event.resolveWithRect(top, 0, this.component.bounds.width, height);
} else if (this.component === undefined) {
this.event.resolveWithRect(0, 0, 0, 0);
}
}
}
14 changes: 9 additions & 5 deletions js/apl-html/src/media/audio/AudioPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ export abstract class AudioPlayer {
}

const onDecode = (audioBuffer: AudioBuffer) => {
const audioNode = this.getAudioNode(audioContext);
const audioNode = this.getConnectedAudioNode(audioContext);
this.currentSource = audioContext.createBufferSource();
this.currentSource.buffer = audioBuffer;
audioNode.connect(audioContext.destination);
this.currentSource.connect(audioNode);

this.currentSource.onended = (event: Event) => {
this.currentSource.disconnect();
audioNode.disconnect();
this._audioNode = null;
this.currentSource = null;
this.onPlaybackFinished(id);
this.resourceMap.delete(id);
Expand All @@ -145,14 +145,18 @@ export abstract class AudioPlayer {
onDecodeError);
}

// The gainNode passed in should be connected to the audiocontext destination
// The AudioNode passed in should be connected to the AudioContext destination
protected setCurrentAudioNode(node: IAudioNode): void {
this.disconnectCurrentAudioNode();
this._audioNode = node;
}

private getAudioNode(context: AudioContext): IAudioNode {
this._audioNode = this._audioNode || context.createGain();
// Gets an AudioNode connected to the AudioContext destination
private getConnectedAudioNode(context: AudioContext): IAudioNode {
if (!this._audioNode) {
this._audioNode = context.createGain();
this._audioNode.connect(context.destination);
}
return this._audioNode;
}

Expand Down
3 changes: 2 additions & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"clean:html": "cd apl-html && yarn clean",
"clean:wasm": "cd apl-wasm && yarn clean",
"clean:dts-packer": "cd dts-packer && yarn clean",
"clean": "rimraf node_modules && yarn clean:client && yarn clean:html && yarn clean:wasm && yarn clean:dts-packer"
"clean": "rimraf node_modules && yarn clean:client && yarn clean:html && yarn clean:wasm && yarn clean:dts-packer",
"lint": "yarn workspaces run lint"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apl-viewhost-web",
"version": "1.7.0",
"version": "1.7.1",
"description": "This is a Web-assembly version (WASM) of apl viewhost web.",
"license": "Apache 2.0",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion scripts/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const https = require('https');
const fs = require('fs');

const artifactUrl = 'https://d1gkjrhppbyzyh.cloudfront.net/apl-viewhost-web/fc1aa3e9-d453-46c4-919f-82b4274a2c68/index.js';
const artifactUrl = 'https://d1gkjrhppbyzyh.cloudfront.net/apl-viewhost-web/EFB6DBCC-A601-46D7-8F05-06D91E9A7E09/index.js';

const outputFilePath = 'index.js';
const outputFile = fs.createWriteStream(outputFilePath);
Expand Down

0 comments on commit 8addbbc

Please sign in to comment.