Tips : WebViewからAndroid側の機能を利用する

Androidアプリをデザインしていると、表現に苦労することがあります。
CSSなら簡単にできるのに、、、とおもってWebViewを使ってみることにしました。
(場合によって、やはり表示までの時間とか、JSのアニメーションとかのパフォーマンス課題もあるようですが…)

WebViewを使うにあたって、例えばボタンおしたらActivity遷移したいとか、Android側の機能も使いたいと思って、まずはその方法を試しました。
(外部のWebサイトとかにアクセスするようにすると、セキュリティの危険などもあるので必要に応じて内部で利用するのがいいかなと思っています)

WebViewのaddJavascriptInterfaceを使う

WebViewにはaddJavascriptInterfaceというメソッドがあり、それを通してAndroid側の機能をAPIとしてJS側に見せることができます。 これを使えばWebViewの外、つまりAndroidアプリ側の制御をJS側からできるようになるので、HTML/CSSでアプリUIをデザインするのに役立ちます。

例えばJavascriptAdapterというクラスを用意して、こんな感じでそのインスタンスをセットできます。
ここではandroidという名前をこのオブジェクトに与えているので、JS側からはandroid.xxxという形で利用できます。

        JavascriptAdapter jsobj = new JavascriptAdapter(position);
        webView.addJavascriptInterface(jsobj, "android");

WebViewのテキストをAndroidのTTSで読み上げる

このJavascriptInterfaceを使った例として、WebView内のコンテンツ読み上げをしてみたいと思います。

Java側のコードはこんな感じです。

public class MainActivity extends Activity implements TextToSpeech.OnInitListener {
    private TextToSpeech tts;
    private HashMap<String,String> param;

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

        WebView webView = new WebView(this);
        webView.getSettings().setUseWideViewPort(true);
        webView.getSettings().setLoadWithOverviewMode(true);
        webView.getSettings().setJavaScriptEnabled(true);

        JavascriptAdapter jsobj = new JavascriptAdapter();
        webView.addJavascriptInterface(jsobj, "android");

        webView.loadUrl("file:///android_asset/www/index.html");
        setContentView(webView);

        tts = new TextToSpeech(this,this);
        param = new HashMap<String, String>();
        param.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "any");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != tts) {
            tts.shutdown();
        }
    }

    @Override
    public void onInit(int status) {
        if (TextToSpeech.SUCCESS == status) {
            Locale locale = Locale.JAPANESE;
            if (tts.isLanguageAvailable(locale) >= TextToSpeech.LANG_AVAILABLE) {
                tts.setLanguage(locale);
            } else {
                Log.w("[TTS][Warning]","locale is not available"+locale);
            }
        } else {
            Log.e("[TTS][Error]","Init status:"+status );
        }
    }

    class JavascriptAdapter {
        public JavascriptAdapter() {
        }

        public void speachText(String text) {
            if (0 < text.length()) {
                if (tts.isSpeaking()) {
                    tts.stop();
                }
                tts.speak(text, TextToSpeech.QUEUE_FLUSH,param);
            }
        }
    }
}

まずWebViewを準備します。 setUseWideViewPort(true)setLoadWithOverviewMode(true)はHTMLコンテンツのViewPortや拡縮がおかしくならないようにするためのおまじないです。 ちなみに、日本語TTSエンジンはKDDIのN2を使っています。
今回はassets以下に配置したローカルのHTMLコンテンツを使います。

HTML/JS側のコードはこちらです。

<!doctype html>
<html lang="jp">
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
    <title>Page</title>
  </head>
  <body>
    <input type="button" value="Speach!" onclick="speach()">
    <script>
    function speach(){
      android.speachText("こんにちは!");
    }
    </script>
  </body>
</html>

これで、JSからAndroid側のTTSを利用することができました。
応用すればHTML/CSSで記述したUIから、Android側の制御もできるかと思います。