開発者コンソール

WebViewとHTML5ウェブアプリを使用してタッチ対応のFire TV対応アプリを構築する方法

Mayur Ahir Jul 13, 2021
Share:
Automotive Fire TV How to
Blog_Header_Post_Img

Fire TVでは、開発者はさまざまな方法でユーザーにコンテンツを提供できます。HTML5ベースのウェブアプリを利用することで、そのほかに提供しているサービスとの一貫性があるユーザーエクスペリエンスを提供できます。また、公開までの時間を短縮でき、複数のマーケットプレイスやデバイスへのアプリの展開が容易になります。また、アプリ全体をビルドしてリリースしなくても、簡単にアップデートを公開できるようになります。

本チュートリアルでは、D-Padとタッチ操作の両方で動作するAmazon WebView(AWV)テクノロジーを使用して、Fire TVデバイス用のラッパーアプリを作成する方法を学びます。 アプリのナビゲーションと入力の基本事項については、UXのベストプラクティスに関するFire TVドキュメントを参照してください。

本チュートリアルでは、主に以下4つの手順について説明します。

  • 手順1: タッチスクリーンのハードウェア機能を宣言する
  • 手順2: Amazon WebView(AWV)のサポートを追加し、JavaScriptを有効にする
  • 手順3: ページナビゲーションとデバイス構成の変更を処理する
  • 手順4: WebViewとウェブアプリの間のやり取りを処理する


手順1: タッチスクリーンのハードウェア機能を宣言する

タップやドラッグなどの基本的なタッチ操作イベントを有効にするには、AndroidManifest.xmlファイルにandroid.hardware.touchscreen宣言を追加します。本機能は、デバイスがタッチスクリーンを備えている、またはタッチスクリーンをエミュレートしている(「疑似タッチ」インターフェイスの)場合に、アプリにそのデバイスとの互換性があることを示します。タッチスクリーン搭載Fire TVデバイスに対応するには、本機能を追加する必要があります。
AndroidManifest.xml

Copied to clipboard
<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...>

    <uses-feature
        android:name="android.hardware.touchscreen"
        android:required="false" />
    ...
</manifest>

ウェブアプリはオンラインでホストされるため、ラッパーアプリをインターネットに接続する必要があります。インターネットに接続するには、マニフェストファイルでINTERNETパーミッションをリクエストします。次に例を示します。

Copied to clipboard
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.myapplication"
    tools:ignore="MissingLeanbackLauncher">

    <uses-feature
        android:name="android.hardware.touchscreen"
        android:required="false" />

    <uses-feature
        android:name="android.software.leanback"
        android:required="false" />

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:banner="@drawable/amazon_firetv"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true">
        <activity
            android:name=".MainActivity"
            android:configChanges="fontScale"
            android:label="@string/player_activity_name"
            android:launchMode="singleTop"
            android:theme="@style/AppTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <uses-library
            android:name="com.amazon.webview"
            android:required="false" />
    </application>

</manifest>

手順2: Amazon WebView(AWV)のサポートを追加し、JavaScriptを有効にする

WebViewクラスは、AndroidのViewクラスの拡張で、Activityレイアウトの一部としてウェブページをレンダリングできるようにします。開発が完了したウェブブラウザの、ナビゲーションコントロールやアドレスバーといった機能は一切含まれていません。WebViewがデフォルトで処理できるのはウェブページの表示のみです。

Fire OS 5以降で動作するすべてのFire TVデバイスには、標準的なAndroid WebViewクラスの透過的な代替としてAmazon WebView(AWV)が搭載されています。AWVは、Fire TV向けに最適化されたChromiumのカスタムビルドを使用しています。これにより、高速で効率的なレンダリング、ビデオパフォーマンスの改善、リソース管理の向上が実現します。

レイアウト内でアプリにWebViewを追加するには、アクティビティのレイアウトXMLファイルに次のコードを追加します。

activity_main.xml

Copied to clipboard
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <WebView
        android:id="@+id/web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />

</FrameLayout>

 

Fire TV対応アプリ内でWebViewを含むActivityを作成し、それを使用してonCreate()でオンラインのウェブアプリを表示します。WebViewにウェブページを読み込むには、loadUrl()を使用します。以下の例では、オンラインでホストされているウェブアプリのアドレスをmBaseUriに格納します。

MainActivity.java

Copied to clipboard
public class MainActivity extends Activity {

    //オンラインでホストされているウェブアプリのアドレス
    private final String mBaseUri = "https://mysampleapp.com";

    private Context mContext = null;
    private WebView mWebView;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mContext = this.getApplicationContext();

        //ユーザーが実行できる単一のアクティビティにフォーカスします。
        //Activityクラスは、UIを定義するレイアウトリソースを
         //配置するためのウィンドウを作成します。
        //こちらの例では、activity_main.xmlでレイアウトリソースが定義されています。
        setContentView(R.layout.activity_main);

        //操作する必要があるUI内のWebViewウィジェットを取得します。
        //こちらの例では、WebViewウィジェットはactivity_main.xmlの「web_view」で指定されます。
        mWebView = findViewById(R.id.web_view);

        mWebView.loadUrl(mBaseUrl);
    }
    ...
}

JavaScriptの有効化

ウェブアプリでは、さまざまなユーザー操作を可能にし、バックエンドサービスと通信するためにJavaScriptを使用する可能性が高いため、WebViewでJavaScriptを有効にする必要があります。

WebViewでは、JavaScriptはデフォルトで無効になっているので、WebViewに付属したWebSettingsを使って有効化します。それには、getSettings()を使用してWebSettingsを取得し、setJavaScriptEnabled()でJavaScriptを有効にします。

MainActivity.java

Copied to clipboard
public class MainActivity extends Activity {

    private final String mBaseUrl = "https://mysampleapp.com";

    private Context mContext = null;
    private WebView mWebView;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        mWebView = findViewById(R.id.web_view);
        
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        ...

        mWebView.loadUrl(mBaseUrl);
    }
    ...
}

WebSettingsを使用すると、ほかにもあらゆる便利な設定にアクセスできます。たとえば、Fire TV対応アプリでWebView向けのウェブアプリを開発している場合、setUserAgentString()でカスタムユーザーエージェント文字列を定義しておけば、ウェブページでカスタムユーザーエージェントを照会することで、ウェブページをリクエストしているクライアントが自分のFire TV対応アプリかどうかを確認できます。

手順3:ページナビゲーションとデバイス構成の変更を処理する

注: ウェブアプリがSPA(シングルページアプリ)として構築されていて、単一のウェブドキュメントのみを読み込み、その単一ドキュメントの本文コンテンツをJavaScript APIで更新する場合、こちらの手順は省略可能です

SPA以外のウェブアプリのナビゲーション

ウェブアプリでユーザーがアプリの別のセクション(ナビゲーションメニューなど)に移動するリンクをクリックする必要がある場合、ユーザーがWebViewのリンクをクリックすると、デフォルトの動作ではURLを処理するアプリをFire TVが起動します。通常は、デフォルトのウェブブラウザが開き、目的のURLが読み込まれるのですが、WebViewで本動作をオーバーライドし、WebView内でリンクが開くように設定することができます。これにより、WebViewが管理するウェブページ履歴により、ユーザーが前のページや次のページへと移動できるようになります。

注: セキュリティ上の理由から、Fire OSブラウザアプリのデータはウェブアプリと共有されません。

ユーザーがクリックしたすべてのリンクを開くには、setWebViewClient()を使用してWebView用のWebViewClientを提供します。shouldOverrideUrlLoading()をオーバーライドするカスタムWebViewClientを作成することで、使用中のWebViewにURLが読み込まれる際にホストアプリによる制御が可能になります。

次の例では、CustomWebViewClientをActivityの内部クラスとして作成し、WebView用にこちらの新しいWebViewClientのインスタンスを作成します。

MainActivity.java

Copied to clipboard
public class MainActivity extends Activity {

    private final String mBaseUrl = "https://mysampleapp.com";

    private WebView mWebView;
    private CustomWebViewClient webViewClient;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        mWebView = findViewById(R.id.web_view);
        
        webViewClient = new CustomWebViewClient();
        mWebView.setWebViewClient(webViewClient);
        ...

        mWebView.loadUrl(mBaseUrl);
    }
    ...
}

class CustomWebViewClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        view.loadUrl(request.getUrl().toString());

        return super.shouldOverrideUrlLoading(view, request);
    }
}

ユーザー補助機能のカスタマイズに応じたデバイス構成の変更

Fire TVにはさまざまなユーザー補助機能があります。そのうちの1つに画面上にテキストバナーを表示するText Bannerがあります。視覚障害のあるユーザーが画面のテキストを読むのに役立ちます。アプリ実行中にユーザーがユーザー補助機能を変更すると、アクティビティ状態が変わり、デバイス構成が変更されます。これにより、WebViewオブジェクトのアクティビティが破棄され、新しいアクティビティが作成されることがあります。また、破棄されたオブジェクトのURLを読み込む新しいWebViewオブジェクトも作成されます。

特定の構成変更時にアプリがリソースを更新しなくても済むように実行時の構成変更を処理し、アクティビティの再起動を避けるには、アクティビティがfontScale構成変更そのものを処理することを宣言して、システムがアクティビティを再起動しないようにします。

AndroidManifest.xml

Copied to clipboard
<activity
    android:name=".MainActivity"
    android:configChanges="fontScale"
    android:label="@string/player_activity_name"
    android:launchMode="singleTop"
    android:theme="@style/AppTheme">
    ...
</activity>

これにより構成が変更されても、MainActivityは再起動されません。代わりに、MainActivityはonConfigurationChanged()の呼び出しを受け取ります。本メソッドでは、新しいデバイス構成を指定するConfigurationオブジェクトが渡されます。Configurationの各種フィールドを確認することで、新しい構成を特定し、インターフェイスで使用されているリソースを更新して適切な変更を加えることができます。本メソッドが呼び出されると、アクティビティのResourcesオブジェクトが更新され、新しい構成に基づくリソースが返されます。このため、システムがアクティビティを再起動しなくても簡単にUIの要素をリセットできます。

MainActivity.java

Copied to clipboard
public class MainActivity extends Activity {

    ...

    @Override
    public void onConfigurationChanged(final Configuration c) {
        super.onConfigurationChanged(c);

        mWebView.getSettings().setTextZoom((int) (c.fontScale * 100));
    }
}

以上がウェブページを表示する基本的なWebViewで必要な設定です。また、以下を変更してWebViewをカスタマイズすることもできます。

  • WebChromeClientで、全画面のサポートを有効にします。こちらのクラスは、ウィンドウの作成や終了、JavaScriptダイアログのユーザーへの表示などのためにWebViewがウェブアプリのUIを変更するパーミッションを必要とする際にも呼び出されます。
  • WebViewClientで、フォームの送信やナビゲーションの際のエラーなど、コンテンツのレンダリングに影響を与えるイベントを処理します。こちらのサブクラスを使用して、URLの読み込みをインターセプトすることもできます。

手順4: WebViewとウェブアプリの間のやり取りを処理する

WebViewからのD-PADイベントのディスパッチ

ほかのAndroid入力デバイスと同様に、Amazon Fire TVリモコンのボタンを押すとKeyEventイベントが生成されます。コントローラーのボタン入力は、標準のAndroidイベントリスナーインターフェイスとコールバックを使用して処理できます。Amazon Fire TVリモコンと音声認識リモコンではいずれの場合も(Android MotionEventクラスの)モーションイベントは発生しません。

注: Fire TV Edition(日本未対応)のリモコンには、音量アップ/ダウン、電源、特定アプリを直接開くボタンなど、いくつかのボタンが追加されています。ただし、これらのボタンはサードパーティ製アプリのイベントにはマッピングできません。

D-Padによる操作を管理することで、アプリのユーザーエクスペリエンスを大幅に向上させることができます。Androidフレームワークには、D-Pad(またはその他のコントローラー)の入力を検出して処理するためのAPIが用意されています。Activityクラスで、dispatchKeyEvent()メソッドをオーバーライドし、必要に応じてカスタマイズを追加します。以下の例では、D-Padの「戻る」キーを変更して、WebView内で常に前に戻り、移動する履歴がない場合にはアプリを終了するようにする方法を示します。dispatchKeyEvent()メソッドをオーバーライドすると同時にキーイベントをWebViewに送信可能です。これをウェブアプリでJavaScriptを使用して処理し、必要なアクションを実行できます。

MainActivity.java

Copied to clipboard
public class MainActivity extends Activity {

    private final String mBaseUrl = "https://mysampleapp.com";

    private Context mContext = null;
    private WebView mWebView;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }
    
    ...
    
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        Log.d(TAG, "dispatchKeyEvent : KeyCode = " + event.getKeyCode() + " (" + KeyEvent.keyCodeToString(event.getKeyCode()) + ")");

        switch (event.getKeyCode()) {
            //D-Padの戻るボタンを処理します
            case KeyEvent.KEYCODE_BACK:
                Log.d(TAG, "dispatchKeyEvent : mWebView.canGoBack = " + mWebView.canGoBack());
                if (mWebView.canGoBack()) {
                    //WebViewで前に戻ります
                    mWebView.goBack();
                } else {
                    //WebViewに履歴がない場合は、アプリを終了します
                    finish();
                }
        }

        return super.dispatchKeyEvent(event);
    }
}

dispatchKeyEvent()メソッドをオーバーライドした後、D-Padの再生/一時停止ボタンを押すと、ADBログに次の行が表示されます。 KEYCODE_MEDIA_PLAY_PAUSEは、KeyCode=85として表示されます。Android KeyEvent定数、およびFire TVデバイスにおけるこれらのボタンのデフォルト動作の詳細については、こちらのリンクを参照してください。

Copied to clipboard
D/MainActivity: dispatchKeyEvent : KeyCode = 85 (KEYCODE_MEDIA_PLAY_PAUSE)

JavaScriptでのD-Pad操作の処理

ユーザーがD-Padを操作すると、ウェブアプリのJavaScriptはKeyboardEventイベントを受け取ります。KeyboardEventを処理するには、次の手順に従います。

  1. イベントが発生する[Element](https://developer.mozilla.org/ja/docs/Web/API/Element)を選択します。Fire TVラッパーアプリではウェブアプリのコンテンツのレンダリングにWebViewを使用しているので、[Window](https://developer.mozilla.org/ja/docs/Web/API/Window)オブジェクトでイベントをリッスンすることをお勧めします。
  2. [element.addEventListener()](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)を使用してイベントハンドラーを登録します。D-Padでキーが押されるたびに、JavaScriptは次の2つのイベントを受け取ります。
    1. keydown - D-Padのキーを押したとき(長押しの場合は自動的に繰り返します)。
    2. keyup - D-Padのキーを離したとき。

以下の例では、Video.jsというオープンソースのHTML5ビデオプレーヤーを使用して、基本的なメディアプレーヤーのエクスペリエンスを構築しています。keydownイベントは、D-Pad操作を開始するとすぐにトリガーされます。ここではデモンストレーションとしてドキュメント全体の背景を緑色に設定します。キーを離すと、keyupイベントがトリガーされます。カスタムのキー処理メカニズムは、handleKeyEvent()メソッドで定義されています。

index.html

Copied to clipboard
// https://videojs.com/getting-started/
const player = videojs("video", {...});

//カスタムのキーイベントハンドラー
const moveTime = 10; //秒数分移動します
const handleKeyEvent = (event) => {
    switch(event.code) {
        case "MediaPlayPause":
        case "NumpadEnter":
            //再生が一時停止していることを確認します
            if (player.paused()) {
                videojs.log('handleKeyEvent:', 'Play', player.currentTime());
                //再生を再開します
                player.play();
            } else {
                videojs.log('handleKeyEvent:', 'Pause', player.currentTime());
                //再生を一時停止します
                player.pause();
            }
            break;
        case "MediaRewind":
        case "ArrowLeft":
            //再生時間を10秒戻します
            player.currentTime((player.currentTime() - moveTime) < 0 ? 0 : (player.currentTime() - moveTime));
            videojs.log('handleKeyEvent:', 'Rewind', player.currentTime());
            break;
        case "MediaFastForward":
        case "ArrowRight":
            //再生時間を10秒進めます
            player.currentTime(player.currentTime() + moveTime);
            videojs.log('handleKeyEvent:', 'Fast Forward', player.currentTime());
            break;
        default:
            videojs.log('handleKeyEvent:', 'Unhandled event.code =', event.code);
            break;
    }
}

//Fire TV対応アプリのWebViewからディスパッチされるキーボードイベントをリッスンします
window.addEventListener("keyup", (event) => {
    //押下したキーを離すと背景色がリセットされます
    document.body.style.background = "";

    if (event.key !== undefined) {
        console.log('keyup:', 'event.key =', event.key);
        //KeyboardEvent.keyを使用してイベントを処理します
        handleKeyEvent(event);
    }
});

window.addEventListener("keydown", (event) => {
    //キーを押すと背景色が変わります
    document.body.style.background = "green";
});

WebViewからのタッチイベントのディスパッチ

タッチジェスチャー操作を利用できないFire TVデバイスもあるため、アプリの基本的な動作について完全にタッチジェスチャーに頼ることはできません。しかしながら、ラッパーアプリにタッチベースの操作を追加すると、利便性と魅力を大幅に向上させることが可能です。

タッチジェスチャーが発生するのは、ユーザーがタッチ対応デバイスの画面に1本または複数の指を置き、アプリがそのタッチパターンを特定のジェスチャーとして解釈したときです。ジェスチャーの検出には、次の2つの段階があります。

  1. タッチイベントに関するデータ収集:ユーザーが画面上に指を置くと、タッチイベントを受け取ったビューのコールバックonTouchEvent()がトリガーされます。最終的にジェスチャーとして識別される一連のタッチイベント(位置、押す強さ、サイズ、ほかの指を添えるなど)ごとに、onTouchEvent()が複数回起動されます。
  2. データを解釈して、アプリでサポートしているジェスチャーの基準を満たしているかどうかを確認します。ジェスチャーは、ユーザーが最初に画面に触れたときに始まり、システムがユーザーの指の位置を追跡する間継続します。指が画面から離れる、という最終イベントをキャプチャすることで終了します。こちらの操作をとおしてonTouchEvent()に送られるMotionEventが全操作の詳細を提供します。アプリはMotionEventが提供するデータを使用して、処理すべきジェスチャーが発生したかどうかを判断できます。

以下の例は、アプリのActivityクラスのタッチイベントをインターセプトして、onTouchEvent()コールバックをオーバーライドする方法を示しています。こちらのスニペットでは、getActionMasked()を使用して、イベントパラメーターからユーザーが実行したアクションを抽出します。これにより、処理すべきジェスチャーが発生したかどうかを判断するのに必要な未加工データを取得できます。

MainActivity.java

Copied to clipboard
public class MainActivity extends Activity {
    
    ...
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getActionMasked();

        switch(action) {
            case (MotionEvent.ACTION_DOWN):
                Log.d(TAG,"Action was DOWN");
                return true;
            case (MotionEvent.ACTION_MOVE):
                Log.d(TAG,"Action was MOVE");
                return true;
            case (MotionEvent.ACTION_UP):
                Log.d(TAG,"Action was UP");
                return true;
            case (MotionEvent.ACTION_CANCEL):
                Log.d(TAG,"Action was CANCEL");
                return true;
            case (MotionEvent.ACTION_OUTSIDE) :
                Log.d(TAG,"Movement occurred outside bounds of current screen element");
                return true;
            default:
                return super.onTouchEvent(event);
        }
    }
}

JavaScriptでのタッチ操作処理

タッチイベントはマウスイベントと似ていますが、同時タッチや、タッチ面上の複数位置でのタッチをサポートしている点が違います。TouchEventインターフェイスは、現在アクティブなすべてのタッチポイントをカプセル化します。Touchインターフェイスは、単一のタッチポイントを表し、ブラウザのビューポートに対するタッチポイントの位置などの情報を保持します。

タッチイベントは、3つのインターフェイス(TouchTouchEventTouchList)と次のイベントタイプで構成されます。

  • touchstart - タッチ面にタッチポイントが置かれた際に発生します。
  • touchmove - タッチ面に沿ってタッチポイントが移動した際に発生します。
  • touchend - タッチポイントがタッチ面から取り除かれた際に発生します。
  • touchcancel - タッチポイントが実装固有の方法(たとえば、作成されたタッチポイントが多すぎるなど)で中断された場合に発生します。

前述のHTML5ビデオプレーヤーの例を続けると、ここでは、プレーヤーのメディアコントロール内で定義されたボタン要素に基本的なタッチ操作を追加することができます。playPauseControl要素はプレーヤーの再生/一時停止ボタンを処理し、muteControlは音声ミュート/ミュート解除ボタンを処理します。JavaScriptタッチイベントの詳細については、MDNウェブドキュメントを参照してください。また、アプリにタッチイベントを追加する方法については、Google Web Fundamentalsを参照してください。

index.html

Copied to clipboard
const player = videojs("video", {...});

//Fire TV対応アプリのWebViewからディスパッチされるタッチイベントをリッスンします
//再生/一時停止
let playPauseControl = document.getElementsByClassName('vjs-play-control')[0];
playPauseControl.addEventListener("click touchstart", (event) => {
    videojs.log('playPauseControl', 'touched');
});

//ミュート/ミュート解除
let muteControl = document.getElementsByClassName('vjs-mute-control')[0];
muteControl.addEventListener("click touchstart", (event) => {
    videojs.log('muteControl', 'touched');
});

 

まとめ

既存のHTML5ベースのウェブアプリをクライアントアプリの一部としてFire TVに配信する場合は、本ガイドを利用して、優れたユーザーエクスペリエンスの実現に必要な操作とナビゲーションを実装することができます。Fireデバイス向けにウェブアプリを最適化する方法の詳細については、Amazon開発者ポータルを参照してください。

詳細については、以下のドキュメントを参照してください。

関連記事

ニュースレターを購読してみませんか?

最新のAmazon開発者向けニュース、業界の動向、ブログの記事をお届けします。