APLコマンド(1.4)



APLコマンド

コマンドは、画面上のコンテンツの視覚および音声の表現を変更するメッセージです。発話への応答として、コマンドとAlexa.Presentation.APL.ExecuteCommandsディレクティブをデバイスに送信します。ボタン押下への応答など、APLドキュメントのイベントハンドラーを使って、コマンドを直接トリガーします。

具体的なコマンドについては、APL標準コマンドAPLメディアコマンドを参照してください。

コマンドと画面アクション

コマンドがサポートするシーンでのアクションには、次のタイプがあります。

  • シーン内をナビゲートする
    • ScrollViewまたはSequenceをスクロールする
    • スクロールしてコンポーネントを表示する
    • Pagerで表示されるページを変更する
  • 既存シーン内でコンポーネントを変更する
  • 入力コントロールを更新して新しい状態を反映する
  • 既存コンポーネントの表示を変更する
  • 既存のシーン内でビデオクリップを再生/一時停止する
  • 音声
    • 単一コンポーネントのオーディオコンテンツを読む
    • 2つ以上のコンポーネントのオーディオコンテンツを読む
  • APL Extensionにコマンドを送信する

コマンド実行の概要

このセクションでは、イベントハンドラーまたは外部で生成されたコマンドによって開始されたコマンドの実行に使用するアルゴリズムの概要を説明します。

通常モードまたは高速モードで連続して実行されるコマンドの配列を例とします。

通常モードの評価

通常モードで実行するコマンドには、名前付きのシーケンサーが必要です。「メイン」のシーケンサー上で、別のシーケンサーに割り当てられるか、SequentialまたはParallelのサブコマンドであるかのいずれにも当てはまらない場合、デフォルトで実行されるコマンドです。

配列の各コマンドについて、以下が順に実行されます。

  1. コマンドのwhenプロパティを評価します。falseと評価された場合は、現在のコマンドをスキップして配列内の次のコマンドに進みます。

  2. コマンドのdelayプロパティを評価します。0より大きい場合は、ミリ秒単位で指定時間の間、一時停止します。

  3. コマンドのsequencerプロパティをチェックします。現在のシーケンサーと同じ値の場合、このコマンドを適切なシーケンサーに渡して、配列内の次のコマンドに進みます。

  4. コマンドのtypeプロパティを評価します。typeが認識できない場合、配列内の次のコマンドに進みます。

    有効なtype値には、次のものがあります。

    • SendEventなどのビルトインコマンド。すべての必須コマンドプロパティに値がある場合、コマンドを実行します。一部のコマンドはただちに返されます。コマンドが実行を終了するまでシーケンサーを一時停止するコマンドもあります。
    • ユーザー定義コマンド。ユーザー定義コマンドは、実行するコマンドの配列内にインフレートされ、このセクションのルールに従ってこれらのコマンドを実行してすべてのコマンドの実行が終了したら次に進みます。
    • Extensionコマンド。コマンドを実行します。一部のextensionコマンドはすぐに返されます。コマンドが実行を終了するまでシーケンサーを一時停止するコマンドもあります。

高速モードの評価

fast modeで実行されるコマンドには、名前付きのsequencerがありません。配列の各コマンドについて、以下が順に実行されます。

  1. コマンドのwhenプロパティを評価します。falseと評価された場合は、現在のコマンドをスキップして配列内の次のコマンドに進みます。

  2. コマンドのsequencerプロパティをチェックします。値がある場合、このコマンドを適切なシーケンサーに渡して、配列内の次のコマンドに進みます。渡されたコマンドは、高速モードではなく通常モードで実行されます。

  3. コマンドのtypeプロパティを評価します。typeが認識できない場合、配列内の次のコマンドに進みます。

    有効なtype値には、次のものがあります。

    • SetValueなど、高速モードをサポートするビルトインコマンド。すべての必須コマンドプロパティに設定されている場合、コマンドを実行します。高速モードをサポートするコマンドの一覧については、高速モードのコマンドを参照してください。
    • SendEventなど、高速モードをサポートしないビルトインコマンド。コマンドをスキップします。
    • ユーザー定義コマンド。ユーザー定義コマンドは、実行するコマンドの配列内にインフレートされ、このセクションのルールに従ってこれらのコマンドを実行してすべてのコマンドの実行が終了したら次に進みます。
    • 高速モードをサポートするextensionコマンド。コマンドを実行します。
    • 高速モードをサポートしないextensionコマンド。コマンドをスキップします。

コマンドの評価

コマンドの個々のプロパティはデータバインディングをサポートします。コマンドのデータバインディングコンテキストには、コマンドが定義されたコンテキスト、コマンドがトリガーされる状況に関する情報を含むeventプロパティによって拡張されたコンテキスト、コマンドの対象となるコンポーネントがあります。

イベントの内容

コマンドはソースデータのバインディングコンテキストで評価を行います。

ExecuteCommandsディレクティブによってデバイスに送信されたコマンドが、最上位レベルのデータバインディングコンテキストで評価されます。名前付きリソースと共にviewportプロパティとenvironmentプロパティを含むデータバインディングコンテキストです。

画面タッチなどのAPLのイベントに応答して発行されたコマンドは、コマンドが定義されているローカルのデータバインディングコンテキストで評価を行います。

次にTouchWrapperのサンプルを示します。

{
  "type": "TouchWrapper",
  "bind": [
    "name": "myRandomData",
    "value": 24.3
  ],
  "onPress": {
    "type": "SendEvent",
    "arguments": [ "値は${myRandomData}です" ]
  }
}

TouchWrapperを押すと、最初の引数は「値は24.3です」と設定されます。

eventプロパティ

ソースデータのバインディングコンテキストは、コマンドが評価を行うときにイベントデータによって拡張されます。このイベントデータには、eventプロパティ内でアクセスします。

eventプロパティには、event.sourceevent.targetという2つの主要サブプロパティがあります。

  • event.sourceプロパティには、イベントを発生させたコンポーネントに関してシステムから提供される情報が含まれます。
  • event.targetプロパティには、イベントを受け取るコンポーネントに関してシステムから提供される情報が含まれます(該当する場合)。

event.targetプロパティがないコマンドもあります。たとえば、SendEventコマンドには、event.targetプロパティがありません。event.sourceプロパティはすべてのコマンドに存在します。event.sourceの詳細はそれぞれ異なるため、コンポーネントを表すとは限りません。たとえば、ソースがコンポーネントではなく、extensionの場合もあります。

データバインディングコンテキストに追加されるeventプロパティの形式は、次のとおりです。

"event": {
  "source": {                   // 常に存在します
    "type": "COMPONENT_TYPE",     // コンポーネントまたは"Document"
    "handler": "EVENT_HANDLER",   // イベントハンドラー名前コマンドのメモを参照してください)
    "id": "SOURCE_ID",            // ソースコンポーネントID
    "uid": "SOURCE_UID",          // ソースコンポーネントUID
    ...                         // 追加のソースコンポーネントプロパティ
  },
  "target": {                   // コマンドコンポーネントターゲットある場合にのみ存在します
    "type": "COMPONENT_TYPE",     // ターゲットコンポーネント
    "id": "TARGET_ID",            // ターゲットコンポーネントID
    "uid": "TARGET_UID",          // ターゲットコンポーネントUID
    ...                         // 追加のターゲットコンポーネントプロパティ
  },
  ...   // コマンド固有のプロパティ
}

event.sourceプロパティにはhandlerプロパティがありますが、event.targetプロパティにはありません。

以下の表は、さまざまなコンポーネントがレポートする標準のevent.sourceプロパティとevent.targetプロパティの一覧です。特定のコンポーネントのプロパティに関するドキュメント全文については、各コンポーネントのドキュメントを参照してください。

プロパティ 説明 レポート元
bind マップ データバインディングコンテキスト コンポーネント
checked ブール値 チェックの状態 コンポーネント
color 現在の色 Text
currentTime 整数 現在の再生位置 Video
disabled ブール値 無効にされた状態 コンポーネント
duration 整数 現在のビデオの長さ(ミリ秒) Video
ended ブール値 ビデオが終了している場合はTrue Video
focused ブール値 フォーカス状態 コンポーネント
height 数値 高さ(dp) コンポーネント
id 文字列 コンポーネントID コンポーネント
opacity 数値 ローカル不透明度(累積値ではない) コンポーネント
page 整数 現在表示されているページ Pager
paused ブール値 ビデオが一時停止している場合はtrue Video
position 数値 スクロール距離の割合 SequenceScrollView
pressed ブール値 押された状態 コンポーネント
text 文字列 表示されるテキスト Text
trackCount 整数 トラック数 Video
trackIndex 整数 現在のトラックのインデックス Video
type 文字列 コンポーネントタイプ("Frame"など) コンポーネント
uid 文字列 ランタイムで生成された固有ID コンポーネント
url 文字列 ソースURL ImageVectorGraphicVideo
width 数値 幅(dp) コンポーネント

bindプロパティは、コンポーネントのデータバインディングコンテキストへのアクセスを提供します。次の例は、バインドされた値へのアクセス方法を示しています。IDがMyTextのコンポーネントが、SetValueコマンドのターゲットです。このため、event.target.bindプロパティには、MyTextコンポーネントにバインドされたデータが含まれ、コマンドはその値を使用できます。

[
  {
    "type": "TouchWrapper",
    "onPress": {
      "type": "SetValue",
      "componentId": "MyText",
      "property": "text",
      "value": "今日の単語は${event.target.bind.WordOfTheDay}です"
    },
    "items": [
      {
        "type": "Text",
        "text": "クリックして"
      }
    ]
  },
  {
    "type": "Text",
    "bind": [
      {
        "name": "WordOfTheDay",
        "value": "熊"
      }
    ],
    "id": "MyText"
  }
]

event.source

eventevent.sourceプロパティには、イベントをトリガーした状況に関するメタ情報が含まれます。APLランタイムはevent.sourceを生成し、ドキュメントでこの情報を使用できます。event.sourceプロパティには、すべての標準プロパティと以下の項目が含まれます。

プロパティ 説明
handler 文字列 このメッセージを開始したイベントハンドラーの名前です。たとえば、「Press」や「Checked」です。
value 任意 このメッセージを開始したコンポーネントの値です。

event.source.valueプロパティは、コンポーネントによって異なります。たとえば、TouchWrapperの場合、event.source.valueには、コンポーネントチェックの状態が含まれます。ScrollViewの場合、プロパティにはスクロール位置が含まれます。提供される特定のevent.source.valueプロパティについては、個々のコンポーネントの定義を参照してください。

APLの以前のバージョンとの下位互換性のため、event.sourceプロパティにはtypeプロパティと同じsourceサブプロパティも含まれています。つまり、${event.source.source == event.source.type}となります。event.source.sourceは廃止されているため、使用しないでください。

event.target

event.targetプロパティは、イベントを受け取るコンポーネントについての状態情報を提供します。targetの値は、コンポーネントによって異なります。想定されるtargetの値については、個々のコンポーネントの定義を参照してください。

APLの以前のバージョンとの下位互換性のため、イベントのtargetプロパティは、ImageVectorGraphicVideoコンポーネントのurlプロパティと同じ、sourceサブプロパティについても報告します。つまり、${event.target.source == event.target.url}となります。event.target.sourceは廃止されているため、使用しないでください。

評価に関する注意

  • ほとんどのコマンドはターゲットとしてcomponentIdを取得します。コマンド自体がターゲットの場合は、componentIdを省略できます。
  • ExecuteCommandsディレクティブを使ってデバイスにコマンドを送信する場合は、必ずcomponentIdを指定する必要があります。
  • コマンドにcomponentIdプロパティが含まれる場合、この値を使って、ツリー階層のルートからすべてのコンポーネントを深さ優先検索で検索し、値と一致した最初のコンポーネントを返します。コマンドが正しいコンポーネントをターゲットにしていることを保証するには、以下のいずれかを実行します。
    • idプロパティを持つターゲットコンポーネントに一意の値を割り当て、コマンドのcomponentIdをその値に設定します。
    • 視覚コンテキストで提供される、システムが生成したuidプロパティ値を使用します。
  • コマンドのデータバインディング式は、コマンドを定義するときではなく、実行するときに評価されます。たとえば、ExecuteCommandsディレクティブが、グローバルデータバインディングコンテキストを参照するデータバインディング式を含んでいる可能性があります。これらの式は、デバイスでコマンドが実行されたときに評価されます。コマンドがクラウドで作成されたときではありません。
  • イベントハンドラーとExecuteCommandsディレクティブが、実行するコマンドの配列を取り出します。この配列は、repeatCountが0であるSequentialコマンドとまったく同じように扱われます。

コマンドのシーケンス実行

コマンドはAPLランタイム環境で実行されます。ユーザーと環境のアクションが、実行するコマンドをトリガーします。ParallelコマンドとSequentialコマンドも、実行するコマンドのセットをトリガーします。コマンドのシーケンス実行ルールによって、複数のコマンドが同時に実行される場合の動作が決まります。ルールがないと、コマンドが競合してしまうため、このルールは必要です。コマンドの定義方法に基づいて、この動作の一部を制御することができます。

このセクションでは次の用語を使用します。

  • リソース – コマンドが本来使用するもので、別のコマンドと共有することはできません。テキスト読み上げのコマンドは、"speech"リソースを使用します。一定の時間、特定のコンポーネント上で動作するコマンド(AnimateItemなど)の場合は、そのコンポーネントがリソースとなります。
  • 通常モード – コマンドを実行する標準の方法です。ほとんどのコマンドは、通常モードでのみ実行されます。通常モードのコマンドは、実行に時間がかかる場合があります。
  • 高速モード – コマンドを実行するもう1つの方法です。実行に時間のかかるコマンドが適切でない場合に使用します。高速モードでは、すべての遅延とほとんどのコマンドが無視されます。高速モードは、onScrollなどのイベントハンドラーで使用されます。ここでは、イベントハンドラーが1秒間に何度も起動されます。
  • シーケンサー – 1つのコマンドを同時に実行できる名前付きのエンティティです。normal modeで実行されるコマンドはそれぞれ、名前付きシーケンサーに関連付けられます。シーケンサーの指定には、command_sequencer_propertyプロパティを使用します。
  • サブコマンド – 別のコマンド内に含まれるコマンド(parallel_commandまたはsequential_command)です。サブコマンドの階層は、「コマンドツリー」と呼ばれることもあります。

コマンドのシーケンス実行ルール

APLランタイムは、コマンドをシーケンス実行する際、次のルールを使用します。

  1. 通常モードのコマンドはすべて、1つのシーケンサー上で実行されます。そのシーケンサーが既にコマンドを実行している場合、それらのコマンドは終了し、新しいコマンドが実行されます。
  2. 高速モードのコマンドはシーケンサーを使用しません。
  3. シーケンサーを明示的に指定するコマンドはすべて、通常モードで実行されます。
  4. サブコマンドは、サブコマンドが明示的にシーケンサーを指定しない限り、親コマンドと同じシーケンサー上で実行されます。サブコマンドがシーケンサーを指定している場合、ランタイムはそのコマンドをシーケンサーに引き渡して、親コマンド内でそのコマンドを完了とマークします。
  5. デフォルトのシーケンサー名は、「MAIN」です。サブコマンド以外の通常モードコマンドが明示的にシーケンサーを指定しない場合、コマンドは「MAIN」シーケンサーで実行されます。通常モードのサブコマンドは、ルール4に従います。
  6. 画面タッチやキーボード入力など、ユーザーが何らかのデバイス操作を行うと、「MAIN」sequencerで実行されているコマンドはすべて終了します。
  7. コマンドが実行を開始すると、新しいコマンドで必要なresourceを使用している現在実行中のコマンドは、すべて終了します。

たとえば、ユーザーが画面上のボタンをタッチしたとします。この操作によって、「MAIN」シーケンサー上で次のような一連のコマンドが開始されます。

  1. 画面上の変化をアニメーション化します。
  2. 特定の位置までリストを下にスクロールします。
  3. 項目を読み上げます。

ユーザーは、画面をもう一度タッチすることで、この一連のコマンドをいつでも中断できます。ユーザーが画面をタッチすると、「MAIN」シーケンサーが現在実行中のコマンドを終了します。

デフォルトのシーケンス実行動作を上書きするには、シーケンサーを直接指定できます。この手法を使うと、コマンドを並行して実行できます。シーケンス実行動作を変更するシナリオには、次のような場合が考えられます。

  1. 通常は高速モードで実行されるイベントハンドラーを使って、通常モードのコマンドを実行したい場合。たとえば、ScrollViewコンポーネントのonScrollイベントハンドラーで、SendEventコマンドを実行したいとします。通常、onScrollハンドラーは、通常モードのコマンドを無視します。この場合、SendEvent上でシーケンサー名を指定すると、ハンドラーに強制的に通常モードを使用させることができるため、SendEventが実行されます。
  2. 通常のユーザー対話動作を上書きして、ユーザーが画面をタッチしてもコマンドの実行を続けたい場合。たとえば、子ども用ゲームの画面上コンポーネントをアニメーション化するAnimateItemコマンドで、画面がタッチされてもアニメーションを停止させたくない場合などです。
  3. 複数コンポーネントのアニメーションがトリガーされた場合、新しいコンポーネントを表示して古いコンポーネントを非表示にします。

リソース

各コマンドは、実行の際に1つ以上のシステムリソースを使用できます。シーケンサールールで定義されているように、特定のリソースを使用するコマンドは1回に1つしか実行できません。そのリソースを必要とする新しいコマンドが実行を開始すると、古いコマンドは終了します。

以下は、リソースと、そのリソースを使用するコマンドの一覧です。

  • フォアグラウンドオーディオ再生
    • SpeakItem
    • SpeakList
    • PlayMedia(フォアグラウンドのオーディオトラックを使用する場合)
    • ControlMedia(フォアグラウンドのオーディオトラックでplayコマンドを実行する場合)
  • バックグラウンドオーディオ再生
    • PlayMedia(バックグラウンドのオーディオトラックを使用する場合)
    • ControlMedia(バックグラウンドのオーディオトラックでplayコマンドを実行する場合)
  • スクロール位置(スクロールコンポーネントによって、ScrollViewSequenceのどちらか)
  • ページャー位置(Pagerコンポーネント)
  • コンポーネントアニメーション(コンポーネントプロパティのアニメーション化)

SpeakItemSpeakListは、複数リソースを使用できます。

通常モード

APLランタイムは、次のシナリオではコマンドを通常モードで実行します。

  • ドキュメントが最初に読み込まれるとき(ドキュメントのonMountおよびコンポーネントのonMount)。
  • ユーザーがタッチまたはTouchWrapperを選択したとき(onPress)。
  • ユーザーがPager上でスワイプして表示されるページが変わったとき(onPageChanged)。
  • ビデオコンポーネントがビデオの再生を終了するか、トラックを変更したとき(onEndonPauseonPlayonTrackUpdate)。
  • ExecuteCommandディレクティブまたはextensionイベントハンドラーなどの外部ソースから新しいコマンドセットを受信したとき。
  • ユーザーがキーボードのキーを押したり、放したりしたとき(handleKeyDownおよびhandleKeyUp)。

デフォルトでは、すべての通常モードコマンドが「MAIN」シーケンサーを使用します。

次の例では、onPressイベントハンドラーにコマンド配列があります。この配列は、シーケンス実行される配列として扱われます。このため、SpeakItemが最初に実行されます。読み上げが完了すると、Scrollコマンドが実行されます。Scrollが完了すると、SendEventコマンドが実行され、スキルにUserEventが送信されます。これらのコマンドはすべて、「MAIN」シーケンサー上で実行されます。

{
  "type": "TouchWrapper",
  "items": {
    "type": "Text",
    "id": "myText",
    "speech": "読み上げる値"
  },
  "onPress": [
    {
      "type": "SpeakItem",
      "componentId": "myText"
    },
    {
      "type": "Scroll",
      "componentId": "myScrollRegion",
      "distance": 2
    },
    {
      "type": "SendEvent",
      "arguments": [
        "ボタンが押されたので読み上げました"
      ]
    }
  ]
}

上の例では、Scrollコマンドを実行するコンポーネントがスクロールイベントを生成し、コンポーネントのonScrollイベントハンドラーで定義されたコマンドを呼び出すことができます。これらのコマンドは高速モードで実行されるため、現在実行中のコマンドシーケンスに影響しません。

ユーザーがTouchWrapperを複数回タップすると、コマンドシーケンスは終了し、タップごとに再開します。onPressハンドラーを設定してTouchWrapperを無効にすることもできますが、これでは問題を解決できません。ユーザーが画面をタップすると、「MAIN」シーケンサー上のコマンドは必ず終了します。

代わりに、コマンドに別のシーケンサーを割り当てることで、コマンドのシーケンス実行が完了することを保証できます。次の例はいずれも、TouchWrapperを無効にして、シーケンス実行が終了して別のシーケンサーを使用するまでシーケンス実行を再開できないようにしています。これにより、画面をタッチしても、コマンドがキャンセルされません。

{
  "type": "TouchWrapper",
  "items": {
    "type": "Text",
    "id": "myText",
    "speech": "読み上げる値"
  },
  "onPress": [
    {
      "type": "Sequential",
      "sequencer": "MySequencer",
      "commands": [
        {
          "type": "SetState",
          "state": "disabled",
          "value": true
        },
        {
          "type": "SpeakItem",
          "componentId": "myText"
        },
        {
          "type": "Scroll",
          "componentId": "myScrollRegion",
          "distance": 2
        },
        {
          "type": "SendEvent",
          "arguments": [
            "ボタンが押されたので読み上げました"
          ]
        }
      ],
      "finally": {
        "type": "SetState",
        "state": "disabled",
        "value": false
      }
    }
  ]
}

カスタムの"MySequencer"は「MAIN」のシーケンサーではないため、画面がもう一度タッチされてもコマンドはキャンセルされません。この例では、最後のSetStateコマンドがfinallyブロックで実行されていることに注意してください。何らかの理由でSequentialが終了しても、finallyで指定されたコマンドは実行され、TouchWrapperを再度有効にします。

前の例は、すべてのコマンドがSequentialのサブコマンドであったことから、一連のコマンドで機能しました。シーケンサールールでは、サブコマンドは独自のシーケンサーを指定しない限り、親と同じシーケンサー上で実行されます。

一方、onPressに割り当てられた以下のコマンド配列は機能しません。ユーザーがこのTouchWrapperをタップしても、何も起こりません。

{
  "type": "TouchWrapper",
  "items": {
    "type": "Text",
    "id": "myText",
    "speech": "読み上げる値"
  },
  "onPress": [
    {
      "type": "SetState",
      "state": "disabled",
      "value": true,
      "sequencer": "BadIdea"
    },
    {
      "type": "SpeakItem",
      "componentId": "myText",
      "sequencer": "BadIdea"
    },
    {
      "type": "Scroll",
      "componentId": "myScrollRegion",
      "distance": 2,
      "sequencer": "BadIdea"
    },
    {
      "type": "SendEvent",
      "arguments": [
        "ボタンが押されたので読み上げました"
      ],
      "sequencer": "BadIdea"
    },
    {
      "type": "SetState",
      "state": "disabled",
      "value": false,
      "sequencer": "BadIdea"
    }
  ]
}

onPressコマンドはデフォルトでMAINシーケンサー上で実行されるため、この例は失敗します。別のシーケンサーを指定すると、ハンドラーはコマンドを別のシーケンサーに引き渡して、配列内の「次の」コマンドを実行します。シーケンサーが新しいコマンドを受け取ると、既存のコマンドはすべて終了し、新しいコマンドで置き換えられます。

失敗する例のイベントシーケンスは、次のようになります。

  1. onPressハンドラーは、SetStateBadIdeaシーケンサーに引き渡します。
  2. onPressハンドラーがSpeakItemに進み、すぐにSpeakItemBadIdeaに引き渡します。
  3. BadIdeaシーケンサーは、SetStateをキャンセルし、SpeakItemを開始します。
  4. onPressハンドラーは、SendEventBadIdeaに引き渡します。
  5. BadIdeaシーケンサーは、SpeakItemをキャンセルし、SendEventを開始します。
  6. onPressハンドラーは、SetStateBadIdeaに引き渡します。
  7. BadIdeaシーケンサーは、SendEventをキャンセルし、SetStateを開始します。
  8. SetStateコマンドは正常に実行されますが、TouchWrapperが既に有効になっているため何も起こりません。

シーケンサーを使って、コマンドを実行するかしないかを切り替えることもできます。次の例では、最初のTouchWrapperAnimateItemコマンドを実行して、ボールの動きをアニメーション化します。2つ目のTouchWrapperが、AnimateItemコマンドをキャンセルする新しいコマンドを同じシーケンサーに送ることで、アニメーションを強制的に停止させます。

{
  "type": "Container",
  "direction": "row",
  "items": [
    {
      "type": "Frame",
      "id": "Ball",
      "width": 100,
      "height": 100,
      "borderRadius": 50,
      "backgroundColor": "red"
    },
    {
      "type": "TouchWrapper",
      "items": {
        "type": "Text",
        "text": “移動を開始”
      },
      "onPress": [
        {
          "type": "AnimateItem",
          "easing": "ease-in-out",
          "duration": 1000,
          "repeatCount": 10000000,
          "repeateMode": "reverse",
          "componentId": "Ball",
          "sequencer": "BallSequencer",
          "value": {
            "property": "transform",
            "from": {
              "translateY": 0
            },
            "to": {
              "translateY": 300
            }
          }
        }
      ]
    },
    {
      "type": "TouchWrapper",
      "items": {
        "type": "Text",
        "text": “移動を停止”
      },
      "onPress": [
        {
          "type": "Idle",
          "sequencer": "BallSequencer"
        }
      ]
    }
  ]
}

2つ目のTouchWrapperonPressハンドラーは、BallSequencer上でIdleコマンドを実行し、AnimateItemを終了します。Idleコマンドは何もしませんが、シーケンサー上で現在実行中のコマンドをすべて終了します。BallSequencerに送信されたほかのコマンドもすべて、同じ結果になります。

高速モード

イベントハンドラーの中には、1秒間に何回もトリガーできるものもあります。たとえば、ScrollView上のonScrollハンドラーは、スクロール位置が移動するたびに起動します。同時に、実行に時間がかかるコマンドもあります。たとえば、画面上のリストのスクロールやテキストの読み上げといったコマンドです。

頻繁に起動されるイベントハンドラーから時間のかかるコマンドを実行することによる問題を回避するため、こうしたハンドラーはコマンドを「高速モード」で実行します。

フレームレートで実行されるイベントハンドラーからトリガーされるコマンドのセットはすべて、「高速」モードを使用します。それ以外のコマンドのシーケンスは、すべて通常モードで実行されます。高速モードでは、ハンドラーはコマンドのすべての「遅延」設定を無視し、実行に時間がかかるコマンドをスキップします。イベントハンドラーは次のように動作します。

イベントハンドラー 動作
アクション可能なonBlur 高速モード
アクション可能なonFocus 高速モード
アクション可能なhandleKeyDown 通常モード
アクション可能なhandleKeyUP 通常モード
コンポーネントのonCursorEnter 高速モード
コンポーネントのonCursorExit 高速モード
コンポーネントのonMount 通常モード
ドキュメントのonMount 通常モード
PagerのonPageChanged 通常モード/高速モード
ScrollViewのonScroll 高速モード
SequenceのonScroll 高速モード
タッチ可能なonDown 高速モード
タッチ可能なonMove 高速モード
タッチ可能なonUp 高速モード
タッチ可能なonPress 通常モード
VideoのonEnd 通常モード/高速モード
VideoのonPause 通常モード/高速モード
VideoのonPlay 通常モード/高速モード
VideoのonTimeUpdate 高速モード
VideoのonTrackUpdate 通常モード/高速モード

ユーザーの操作(TouchWrapperをタップするなど)によって発生する1回限りのイベントは、常に通常モードで実行されます。スクロールや時間の更新などのイベントは、高速モードで実行されます。いずれのモードでも、実行できるイベントは通常のアクション(ビデオトラックの終了など)または最終的に高速モードでトリガーされたコマンド(スクロールイベントによるビデオの一時停止など)によってトリガーされます。外部コマンドおよびextensionコマンドは通常モードで実行されます。

各コマンドの説明には、高速モードでの動作が記載されています。次の表は、高速モードの動作をまとめたものです。

コマンド 高速モードの動作
Idle 無視されます。
Sequential 実行されます。
Parallel 実行されます。
SendEvent 無視されます。
SetValue 実行されます。
SetState 実行されます。
SetFocus 実行されます。
ClearFocus 実行されます。
SpeakItem 無視されます。
SpeakList 無視されます。
Scroll 無視されます。
ScrollToIndex 無視されます。
ScrollToComponent 無視されます。
SetPage 無視されます。
AutoPage 無視されます。
PlayMedia 無視されます。
ControlMedia command="play"の場合は無視されます。それ以外の場合は実行されます。
OpenUrl 無視されます。
AnimateItem 終了状態にジャンプします。
Finsih 実行されます。
Select 実行されます。

sequencerプロパティに値を設定して高速モードで実行されるコマンドはすべて、指定されたシーケンサーに引き渡され、通常モードで実行されます。この機能を使って、高速モードのイベントハンドラーから通常モードのコマンドを実行します。

以下の例は、スクロール表示の位置が移動するたびにその位置をチェックし、スクロール位置が最上部に来たらイベントを送信します。イベント送信にかかった時間を記録することでレート制限を適用し、イベント間が少なくとも1秒以上空くようにします。sequencerプロパティがない場合、SendEventは実行されません。

{
  "type": "ScrollView",
  "bind": [
    {
      "name": "LastEventSentTime",
      "value": 0
    }
  ],
  "onScroll": [
    {
      "type": "Sequential",
      "when": "${event.source.value == 0 && utcTime > LastEventSentTime + 1000}",
      "commands": [
        {
          "type": "SetValue",
          "property": "LastEventSentTime",
          "value": "${utcTime}"
        },
        {
          "type": "SendEvent",
          "arguments": [
            "スクロールが最上部に到達しました"
          ],
          "sequencer": "ScrollSender"
        }
      ]
    }
  ]
}

コマンドツリー

コマンドツリーとは、実行される一連のコマンドのことです。コマンドツリーが発生するのは、コマンドがネストされている場合や、あるコマンドによって新しいイベントハンドラーが起動される場合です。次のようなプリミティブコマンドに、ネストされたコマンドが含まれます。

  • Parallel
  • Sequential
  • OpenURL

ユーザー定義のコマンドもコマンドのネストをサポートします。

プリミティブコマンドもイベントハンドラーをトリガーできます。たとえば、ScrollScrollToComponentSpeakListコマンドは、すべてonScrollイベントハンドラーを呼び出すことができます。

コマンドツリーは最後まで実行することも、途中で終了することもできます。コマンドツリーを終了すると、直ちにすべてのコマンドの実行が中止されます。ソースはコマンドツリーの実行をトリガーします。その後、コマンドツリーは途中で終了されない限り、最後まで実行されます。

次の例は、ScrollSpeakItemPlayVideoコマンドを含むExecuteCommandsディレクティブのコマンドツリーを示しています。

ExecuteCommand
  + Scroll (distance=-10000)           // 上方向にスクロールする
    + onScroll                         // 一番上までスクロールするたびに起動される
      + SetValue (name="opacity", value=event.source.value * 10) // 不透明度を変更する
  + SpeakItem (id)                     // 項目をスクロールして表示させ、カラオケ状態にする
    + onScroll                         // スクロールされるたびに起動する
      + SetValue (name="opacity"....)
  + PlayVideo (synchronously)
    + onStart                          // 1回起動する
    + onTrackUpdate                    // 新しいトラックが表示されるたびに起動する
      + SetValue (name="progress"...)  // 進捗バーの表示を更新する
    + onStop                           // 1回起動する

この一連のコマンドは、画面の最上部までスクロールし、項目の1つを読み上げ、ビデオを再生します。再生中にユーザーが画面にタッチすると、実行中の会話、スクロール、ビデオの再生が停止します。

一連のコマンド内の個々のコマンドが、別のシーケンサーを指定することも可能です。そのコマンドに到達すると、適切なシーケンサーに引き渡され、すぐにシーケンス内の次のコマンドが実行されます。sequencerプロパティが明示的に指定されていないコマンドは、現在のシーケンサー上で実行されます。コマンドは、コマンドの遅延が処理された後にのみ、次のシーケンサーで実行されます。たとえば、メインのシーケンサー上で実行される次の一連のコマンドを考えてみましょう。

+ Sequential
  + AnimateItem A (delay=100, duration=1000)
  + AnimateItem B (delay=200, sequencer="other", duration=2000)
  + Parallel (delay=200)
    + AnimateItem C (duration=1000)
    + AnimateItem D (sequencer="other", duration=2000)
  + AnimateItem E (delay=100, duration=1000)

次の表は、このコマンドツリーで発生するタイムラインアクションをまとめたものです。

時間 アクション
0 Sequentialコマンドが開始します
100 AnimateItem Aが開始します
1100 AnimateItem Aが終了します
1300 「other」シーケンサー上でAnimateItem Bが開始します
1500 Parallelコマンドが開始します
  AnimateItem Cが開始します
  「other」シーケンサー上でAnimateItem Dが開始します。AnimateItem Bは終了しています
2500 AnimateItem Cが終了します
  Parallelコマンドが終了します
2600 AnimateItem Eが開始します
3500 「other」シーケンサー上でAnimateItem Dが終了します
3600 AnimateItem Eが終了します
  Sequentialが終了します

コマンドツリーが終了したときは、一定の状態に遷移する必要があります。APLでは次のように想定しています。

  • スクロールを停止します。
  • ページめくりをキャンセルして、元のページか次のページのどちらか近い方に移動します。
  • 会話を直ちに中止します。
  • シーンの変更やレイアウトの構造的な変更の場合は、最終的な位置に「ジャンプ」します。