APLコマンド

APLコマンド

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

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

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

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

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

コマンド概要

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

コマンド配列は順に実行されることを想定しています。これらのコマンドは、通常モードまたは高速モードで実行されます。

通常モードの評価

通常モードで実行するコマンドには、名前付きのシーケンサーが必要です。

デフォルトでは、コマンドはMAINシーケンサーで実行されます。ただし、実行するコマンドが次の場合を除きます。

  • sequencerプロパティに値が含まれている。
  • SequentialまたはParallelのサブコマンドであり、SequentialコマンドまたはParallelコマンドのsequencerが異なる。

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

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

  2. コマンドのdelayプロパティの値をチェックします。delayが0より大きい場合は、指定された時間(ミリ秒単位)だけ一時停止します。

  3. コマンドのsequencerプロパティの値をチェックします。sequencerの値が現在のシーケンサーと異なる場合、このコマンドを指定されたシーケンサーに渡して、配列内の次のコマンドに進みます。

  4. コマンドのtypeプロパティを評価します。typeに認識されない値が含まれている場合は、そのコマンドをスキップして、配列内の次のコマンドに進みます。

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

    • SendEventなどのビルトインコマンド。すべての必須コマンドプロパティに値がある場合、コマンドを実行します。一部のコマンドはただちに返されます。このコマンドの実行が終了するまで、ほかのコマンドはシーケンサーを一時停止します。
    • ユーザー定義コマンド。ユーザー定義コマンドがインフレートしてコマンドの配列になります。通常モードの評価規則に従ってこれらのコマンドを実行し、すべてのコマンドが完了したら続行します。
    • Extensionコマンド。コマンドを実行します。一部のextensionコマンドはすぐに返されます。このコマンドの実行が終了するまで、ほかのコマンドはシーケンサーを一時停止します。

高速モードの評価

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

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

  2. コマンドのsequencerプロパティの値をチェックします。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をタップすると、デバイスはスキルにUserEventを送信します。このリクエストのarguments配列には、"値は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

コンポーネント

layoutDirection

文字列

現在のlayoutDirectionLTRまたはRTL

コンポーネント

opacity

数値

ローカル不透明度(累積値ではない)

コンポーネント

page

整数

現在表示されているページ

Pager

paused

ブール値

ビデオが一時停止している場合はtrue

Video

position

数値

スクロール距離の割合

pressed

ブール値

押された状態

コンポーネント

text

文字列

表示されるテキスト

Text

trackCount

整数

トラック数

Video

trackIndex

整数

現在のトラックのインデックス

Video

type

文字列

コンポーネントタイプ("Frame"など)

コンポーネント

uid

文字列

ランタイムで生成された固有ID

コンポーネント

url

文字列

ソースURL

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 文字列 このメッセージを開始したイベントハンドラーの名前です。たとえば、PressCheckedです。
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つのコマンドを実行できる名前付きのエンティティです。通常モードの各コマンドには、名前付きのシーケンサーがあります。シーケンサーを指定するには、sequencerプロパティを設定します。
  • サブコマンド – 別のコマンド内に含まれるコマンドです。ParallelコマンドとSequentialコマンドには、それぞれサブコマンドを含めることができます。サブコマンドの階層は、「コマンドツリー」と呼ばれることもあります。

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

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

  1. 通常モードのコマンドは、それぞれ1つのシーケンサー上で実行されます。そのシーケンサーが既にコマンドを実行している場合、ランタイムは実行中のコマンドを停止し、新しいコマンドを開始します。
  2. 高速モードのコマンドはシーケンサーを使用しません。
  3. シーケンサーを明示的に指定するコマンドは、いずれも通常モードで実行されます。
  4. サブコマンドは、サブコマンドが明示的にシーケンサーを指定しない限り、親コマンドと同じシーケンサー上で実行されます。サブコマンドがシーケンサーを指定している場合、ランタイムはそのコマンドをシーケンサーに引き渡して、親コマンド内でそのコマンドを完了とマークします。
  5. デフォルトのシーケンサーはMAINです。サブコマンド以外の通常モードコマンドでシーケンサーが明示的に指定されていない場合、そのコマンドはMAINシーケンサーで実行されます。通常モードのサブコマンドは、ルール4に従います。
  6. 画面タッチやキーボード入力など、ユーザーが何らかの物理的なデバイス操作を行うと、MAIN sequencerで実行されているコマンドはすべて停止します。このルールは、音声対話には適用されません。コマンドシーケンスの実行中、ユーザーがウェイクワードを発して割り込んだ場合、MAINシーケンサーは続行され、停止しません。
  7. コマンドの実行が開始されると、既に実行されているコマンドで使用中のリソースがコマンドに必要な場合、ランタイムは実行中のコマンドを停止します。
  8. 設定変更ハンドラーが実行されると、以前の設定で実行されているコマンドはすべて停止します。

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

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

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

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

  1. 通常は高速モードで実行されるイベントハンドラーを使って、通常モードのコマンドを実行したい場合。たとえば、ScrollViewコンポーネントのonScrollイベントハンドラーで、SendEventコマンドをトリガーしたいとします。通常、onScrollハンドラーは、通常モードのコマンドを無視します。SendEvent上でシーケンサー名を指定すると、onScrollハンドラーは通常モードでコマンドを実行するため、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)。
  • ExecuteCommandsディレクティブまたは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 通常モード
ドキュメントのonConfigChange 高速モード
ドキュメントのonDisplayStateChange 高速モード
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イベントハンドラーを呼び出すことができます。

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

次の例は、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は次のように想定して、デバイスを一貫した状態にします。

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