开发者控制台

步骤9: 解释Alexa指令并对其做出反应 (VSK Fire TV)

步骤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日