步骤9: 解释Alexa指令并对其做出反应 (VSK Fire TV)
在此步骤中,您将根据需要为希望处理的指令自定义Lambda代码。此时,假设您不再使用示例应用,而是在使用实际应用。
声明支持的功能
Alexa发送的指令取决于您在Alexa客户端库配置中声明的功能和Lambda代码。在此示例中,Alexa将发送所有指令:
// 创建技能中支持的功能的列表。
List<String> capabilities = new ArrayList<>();
capabilities.add(AlexaClientManager.CAPABILITY_REMOTE_VIDEO_PLAYER);
capabilities.add(AlexaClientManager.CAPABILITY_PLAY_BACK_CONTROLLER);
capabilities.add(AlexaClientManager.CAPABILITY_SEEK_CONTROLLER);
capabilities.add(AlexaClientManager.CAPABILITY_CHANNEL_CONTROLLER);
capabilities.add(AlexaClientManager.CAPABILITY_RECORD_CONTROLLER);
capabilities.add(AlexaClientManager.CAPABILITY_VIDEO_RECORDER);
capabilities.add(AlexaClientManager.CAPABILITY_KEYPAD_CONTROLLER);
如果您声明所有功能,则您的Lambda将需要处理所有传入的指令。(有关更多信息,请参阅步骤3: 集成Alexa客户端库。) 下表列出了您可以声明的所有功能:
Alexa客户端库功能 | 指令功能 | 指令 |
---|---|---|
CAPABILITY_CHANNEL_CONTROLLER |
Alexa.ChannelController |
ChangeChannel |
CAPABILITY_REMOTE_VIDEO_PLAYER |
Alexa.RemoteVideoPlayer |
SearchAndDisplayResults SearchAndPlay |
CAPABILITY_PLAY_BACK_CONTROLLER |
Alexa.PlaybackController |
Pause Play Stop Resume Next Previous FastForward Rewind StartOver |
CAPABILITY_SEEK_CONTROLLER |
Alexa.SeekController |
AdjustSeekPosition |
CAPABILITY_KEYPAD_CONTROLLER |
Alexa.KeypadController |
SendKeystroke |
除了在应用的代码中侦听功能外,您还必须在您的Lambda代码中列出这些功能。当您的Lambda收到Discover
指令时,您的Lambda必须发回Discover.Response
以指明您的技能支持哪些功能。例如,以下是对Discover
指令的示例响应:
{
"event": {
"header": {
"namespace":"Alexa.Discovery",
"name":"Discover.Response",
"payloadVersion":"3",
"messageId":"2aa731f1-b6dd-471c-98fe-3ac5fa5d6554"
},
"payload": {
"endpoints": [
{
"capabilities": [
{
"interface": "Alexa.RemoteVideoPlayer",
"type": "AlexaInterface",
"version": "1.0"
},
{
"type": "AlexaInterface",
"interface": "Alexa.PlaybackController",
"version": "3",
"supportedOperations" : ["Play", "Pause", "Stop", "StartOver", "Next", "Previous", "Rewind", "FastForward"]
}
{
"interface": "Alexa.SeekController",
"type": "AlexaInterface",
"version": "1.0"
},
{
"interface": "Alexa.ChannelController",
"type": "AlexaInterface",
"version": "1.0"
},
{
"interface": "Alexa.KeypadController",
"type": "AlexaInterface",
"version": "3",
"keys": [
"INFO", "MORE", "SELECT",
"UP", "DOWN", "LEFT", "RIGHT",
"PAGE_UP", "PAGE_DOWN", "PAGE_LEFT", "PAGE_RIGHT"
]
}
]
}
]
}
}
};
有关更多详细信息,请参阅Discovery
接口。简而言之,通过您的应用和Lambda可以检测您的技能功能。
API参考文档中详细描述了实现和处理上述指令的细节。Alexa期望每个传入指令都有一个特定的响应和操作。
请注意,大多数应用并不实现Video Skill API中的每项功能。例如,如果您的应用没有频道,则频道更改可能无关紧要。如果您的应用没有录制功能等,则录制可能无关紧要。
自定义您指令的处理
再次打开firetv-lambda
项目中的index.js
文件(如步骤6: 创建和部署Lambda程序包中所述)。在这个index.js
文件中,您将看到必须自行编写的自定义逻辑的占位符。例如,您会看到诸如以下的内嵌注释:
// 处理指令的合作伙伴特定逻辑位于此处
// 用于从Lambda向应用发送指令的代码位于此处
在您看到这些关于需要合作伙伴特定逻辑的注释的每个位置,写下处理这些指令的逻辑。有关每条指令、其有效负载、预期操作和响应的详细信息,请参阅API参考。
建议的方法是采用Lambda函数收到的指令,然后通过ADM将这些指令发送到您的应用。任何进一步的处理都可能发生在您的应用中。这就是示例应用处理指令的方式。
例如,在示例应用中,如果您打开TenFootApp.java
并浏览到第119行,您将看到以下处理ADM向应用发送的传入指令的代码:
if (directive != null) {
String directiveName = directive.getDirective().getHeader().getName();
if ("SearchAndPlay".equals(directiveName)) {
Content playContent = null;
String directivePayload = directive.getDirective().getPayload().getEntities().get(0).getValue().toLowerCase();
List<ContentContainer> containers = browser.getContentLoader().getRootContentContainer().getContentContainers();
for(ContentContainer container : containers) {
if(null != playContent) break;
for(Content c : container.getContents()) {
// 仅检查第一个有效负载实体
if(c.getTitle().toLowerCase().contains( directive.getDirective().getPayload().getEntities().get(0).getValue().toLowerCase() )) {
playContent = c;
break;
}
}
}
// 如果我们找到一些匹配指令有效负载的内容,则播放视频
i@@ f (playContent == null ) {
playContent = containers.get(0).getContents().get(0);
}
if(null != playContent) {
browser.switchToRendererScreen(playContent, ContentBrowser.CONTENT_ACTION_WATCH_FROM_BEGINNING);
} else {
// 搜索内容
searchForContent(app, browser, directivePayload);
}
} else if ("SearchAndDisplayResults".equals(directiveName)) {
String directivePayload = directive.getDirective().getPayload().getEntities().get(0).getValue().toLowerCase();
searchForContent(app, browser, directivePayload);
} else if ("Pause".equals(directiveName)) {
try {
PlaybackActivity playbackActivity = (PlaybackActivity) app.getCurrentActivity();
if(playbackActivity.isPlaying()) {
playbackActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
playbackActivity.pause();
}
});
}
} catch (Exception castE) {
Log.e(TAG, "Could not cast to PlayBackActivity!");
return;
}
} else if ("Play".equals(directiveName) ) {
try {
PlaybackActivity playbackActivity = (PlaybackActivity) app.getCurrentActivity();
playbackActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
playbackActivity.play();
}
});
} catch (Exception castE) {
Log.e(TAG, "Could not cast to PlayBackActivity!");
return;
}
}
}
}
/**
* @param searchQuery - 从指令中搜索文本
*/
private void searchForContent(TenFootApp app, ContentBrowser browser, String searchQuery) {
browser.switchToScreen(ContentBrowser.CONTENT_SEARCH_SCREEN);
// 最多等待2秒以进行屏幕切换
for(int i = 0; i < 5; i++) {
Activity newActivity = app.getCurrentActivity();
if(newActivity instanceof ContentSearchActivity) break;
try {
Thread.sleep(400);
} catch (InterruptedException e) {
return;
}
}
ContentSearchActivity searchActivity = (ContentSearchActivity) app.getCurrentActivity();
searchActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
searchActivity.getmFragment().setSearchQuery(searchQuery, true);
}
});
}
}
在此实现中,代码从实体
数组中的第一项获取有效负载并在可用时播放它;否则,它将搜索内容。index.js
文件有其他内嵌注释,为自定义Lambda函数提供提示和说明。
在更新Lambda代码时,请注意,不应修改返回至Alexa的响应,只需修改用于处理/发送指令的代码。另请注意,您应该从自己的Lambda函数向您的应用发送指令,但除了简单的成功响应外,您不应尝试将应用中的消息发回Lambda函数。来自您的Lambda函数的成功响应仅表示该消息由Lambda接收,而不是您的应用。
上传最终的Lambda
编写完上一部分中指定的指令的处理逻辑后,重复创建Lambda部署程序包(运行zip -r firetv-lambda.zip
)中的步骤,并将生成的zip文件上传到您在AWS中的Lambda中。在生产环境中使用任何代码之前,请务必对其进行测试和验证。
后续步骤
继续执行下一步: 步骤10: 将您的应用推送到动态应用测试。
(如果遇到任何问题而无法继续,请参阅云端集成故障排除。)
Last updated: 2021年10月13日