HTML5Experts.jp

HTMLとJavaScript、CSSだけで作ろう!Firefox OSアプリ

こんにちは、清水です。先日のHTML5 ConferenceでFirefox OS上で動作するアプリの開発と、Firefox Marketplaceの公開に関する講演をさせていただきました。今回はその講演内容のうち、アプリ作成に関する部分を重点的に説明します。

すべてWeb技術で

Firefox OSの特徴は、ユーザの目にする部分は全てHTML5で実装されている点にあります。音楽アプリ、カメラ、ギャラリーといったプリインストールされているものはもちろん、ダイヤラーや、SMS、スマホのロックスクリーンや、ホーム画面といったシステムよりのアプリもすべてHTMLとJavaScript、CSSだけで実装されています。

Firefoxをインストールして、開発スタート

Firefox OSのアプリ開発に必要なことはただ一つ、Firefoxのインストールです。開発者アカウントの作成も、ディベロッパープログラムへの登録も、専用SDKのインストールも必要ありません。

FirefoxにはWebIDEという機能があります。これはアプリ開発ツールとして必要な機能を一通り持っています:

リリース版のFirefoxでも十分開発に耐えますが、開発をするなら Firefox Developer Editionの利用をお勧めします。Mozillaの提供する開発者向けの機能をいち早く利用できる点が特徴です。

Firefox Developer Editionは、リリース版とは異なる場所にインストールされます。また初回起動時に開発用のプロファイルを作成するため、リリース版との併用が可能です。Firefox Syncを利用して同期しておけば、ブックマークや履歴、パスワードは通常利用しているものと共有できます。

Hello World!

ではさっそく、WebIDEを利用してHello Worldしてみましょう。Firefoxのメニューから「Web 開発」→「Web IDE」と進んでWeb IDEを起動します。起動すると次のような画面が表示されます。

画面の左上にある「アプリを開く」をクリックすると展開されるメニューから「新規アプリ」を選びます。

選ぶとテンプレートの選択を求められます。「HelloWorld」テンプレートを選び、プロジェクト名にアプリの名前を入力して、「OK」ボタンを押すとアプリのテンプレートが作成されます。

シミュレータのインストール

次にシミュレータをインストールします。シミュレータはブラウザのアドオンとして提供されています。画面右上のメニューから「シミュレータをインストール」を選び、表示された画面の中から開発を行う Firefox OSのバージョンに合わせたシミュレータをインストールします。

KDDIから販売されているFirefox OS端末、Fx0はFirefox OS 2.0で動作しています。国内向けにアプリを書く場合は、Firefox OS 2.0のシミュレータをインストールするとよいでしょう。

アプリの起動と修正の反映

作成したテンプレートを、インストールしたシミュレータ上で実行します。WebIDE上部にある「実行ボタン」を押すと、実行できます。

実行すると、次のような画面が表示されます。

このテンプレートでは、起動時にindex.htmlが表示されます。これを次のように修正します。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
    <title>Hello World</title>
    <link rel="stylesheet" href="app.css">
    <script src="app.js" defer></script>
  </head>
  <body>
    <h1>こんにちは世界!</h1>
  </body>
</html>

また、app.jsを次のように修正すると、アプリのロードが終了した時点でアラートダイアログが表示されます。

window.addEventListener("load", function() {
  alert("Hello World!");
});

WebIDEの実行ボタンをもう一度押すと、これらの修正が実行中のアプリに反映されます。

アプリの構造

テンプレートに従って作成されるファイルは以下のような構造をしています。

HTMLやJavaScriptに加えて、”manifest.webapp”というファイルがあります。これはアプリに関するメタデータで、これはアプリのインストールや、Mozillaの提供するアプリストアであるFirefox Maretplaceにアプリを登録時に参照されます。

manifest.webappの実体は次のようなJSONファイルです。

{
  "name": "こんにちは世界",
  "description": "A Hello World app",
  "launch_path": "/index.html",
  "icons": {
    "16": "/icons/icon16x16.png",
    "48": "/icons/icon48x48.png",
    "60": "/icons/icon60x60.png",
    "128": "/icons/icon128x128.png"
  },
  "type": "privileged",
  "permissions": {}
}

各項目の詳細については、Mozilla Developer Networkのアプリマニフェスト をご覧いただくのが確実ですが、上記の例にある項目は以下のような意味を持ちます。

項目名 説明
name 人間が読むための、アプリ名。最長 128 文字
description 人間のためのアプリ説明文。最長 1024 文字
launch_path アプリ起動時に読み込まれるHTMLファイルのパス。上記の例では、manifest.webappと同じフォルダにあるindex.htmlが読み込まれる
icons アイコンのリスト。128ピクセルx128ピクセルのアイコンは必須。また 512ピクセルx512ピクセルのものも含めることが推奨されている
type アプリの種別。特権アプリの場合は、privilegedと記入する。一般アプリの場合は省略可能だが、明示する場合はwebと記入する
permissions Camera APIやDevice Storage APIのように、注意が必要なAPIを利用する場合は、ここに列挙する

以上のように、HTML、CSS、JavaScriptにmanifest.webappを足したものが、Firefox OSアプリの最小構成となります。

Firefox OS で利用できる API

Firefox OS のアプリは、すべてJavaScriptで記述します。そのため カメラやGPSといったセンサー類へのアクセスや、バイブのコントロール、SDカードへのデータ保存などは、HTML5で利用できるAPIを通して行います。利用できるAPIの一部分を挙げると、以下のようになります。

例えばDevice Storage APIを利用して、SDカード中のファイルをすべてコンソールに出力するプログラムは、次のようになります。

var sdcard = navigator.getDeviceStorage("sdcard");
var cursor = sdcard.enumerate();
cursor.onsuccess = function(){
  var file = this.result;
  console.log(file.name);
  if(!this.done){
    this.continue();
  }
};

WebActivities:アプリの機能を利用する API

音楽のライブラリや、写真のギャラリー、カメラでの撮影といった機能を実装する方法は2つあります。1つは上記にあげたAPIを利用して1から機能を作る方法、もう1つはWebActivitiesを利用して他のアプリを呼び出す方法です。

標準でインストールされている音楽アプリや、ギャラリー、カメラアプリは、「ライブラリから音楽の選択する」「ギャラリーから写真を選ぶ」「写真を撮る」といったアプリの機能の一部を他のアプリから利用できるようになっています。

これを利用しているアプリの例には、Cameran があります。Cameranは写真にフィルタや効果を追加するアプリなのですが、自分で写真撮影や管理機能を実装しているのではなく、インストールされている他のアプリの提供する「画像を選ぶ機能」を利用しています。

他のアプリが提供している機能を、自分のアプリから呼び出して利用するためのAPIが、WebActivitiesです。例えば、他のアプリが提供するJPEG形式の画像を選択する機能の呼び出しは、次のようにWebActivitiesを利用して実現できます。

var activity = new MozActivity({
  name: "pick",
  data: {
    type: "image/jpeg"
  }
});
activity.onsuccess = function() {
  var picture = this.result;
  console.log("A picture has been retrieved");
};

尚、WebActivitiesを利用して呼び出せるのは機能の名前であって、アプリではありません。おなじ名前の機能が複数のアプリから提供されている場合、どのアプリを利用するかはユーザの選択に任されます。

WebActivitiesの詳細、特に標準でインストールされているシステムアプリが提供する機能名については、Mozilla Developer Network の Web アクティビティをご覧ください。

アプリケーションの権限

上記のAPIすべてを、アプリから利用できるわけではありません。強力なAPIの利用には、それに見合った権限が必要となります。

Firefox OSのアプリは権限に応じて次の3つに分類されます。

最後の内部アプリ、以前は認定アプリ (Certified App)と呼ばれていたアプリは、キャリアもしくはそれに準じるベンダしか作成を許されていません。そのため私たちが作成するアプリは、一般アプリもしくは特権アプリのどちらかになります。

権限と、利用できるAPIの対応関係は、OSのバージョンによって異なります。対応関係はWebIDEの「許可設定一覧」で確認できます。

許可設定一覧は以下のようになっています。

✓は利用可能、✕ は不可能、! は利用可能だが利用時にユーザに対して通知されることを表しています。

以下の例では、Camera APIを利用するためには、アプリを特権アプリとしなければならないことがわかります。

名前 type:web type:privileged
geolocation ! !
camera !
alarms

実際に作ってみよう

以上がFirefox OSアプリの概要でした。これを踏まえて、ボイスレコーダーアプリの作成を例に、アプリの作成例をみてみましょう。

今回作成するアプリは次のような1画面のアプリとなります。画面をタップすると録音を開始し、もう1度のタップで録音を終了します。録音した音声は ogg ファイルとして音楽ライブラリに保存されるものとします。

なお今回作成したサンプルは、 gistで公開しています。 こちらもご利用ください。

使用するAPIとアプリの権限

今回は次のAPIを利用します。

API名 目的 Firefox OS 2.0で必要な権限
GetUserMedia マイクからの音声入力を扱うため 通常
MediaRecorderAPI 音声を録音するため 通常
Web Audio API 音声の分析を行うため 通常
DeviceStorage API 音声をファイルとして保存するため 特権

DeviceStorage APIを利用するためには、アプリの種別を特権アプリとする必要があります。またいくつかのAPIを利用するためには、manifest.webappのpermissionsの項目に記述を行う必要があります。そのため manifest.webappを以下のように記述します。typeと permissionsの項目が、それぞれ設定されていることを確認してください。

{
  "name": "audio-caputre-sample",
  "description": "An sample app to capture audio through microphone",
  "launch_path": "/index.html",
  "icons": {
    "16": "/icons/icon16x16.png",
    "48": "/icons/icon48x48.png",
    "60": "/icons/icon60x60.png",
    "128": "/icons/icon128x128.png"
  },
  "type": "privileged",
  "permissions": {
    "audio-capture": {
      "description": "To record audio"
    },
    "device-storage:music": {
      "description": "To store captured audio",
      "access": "readwrite"
    }
  }
}

HTML と CSS の記述

アプリのビューは HTML と CSS で定義します。今回のアプリでは次のものを利用しました。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
    <title>audio capture test</title>
     <script src="app.js" defer></script>
    <link rel="stylesheet" href="app.css">
  </head>
  <body>
    <canvas id="canvas"></canvas>
    <p id="recording">●REC</p>
  </body>
</html>

CSS は以下のようになっています。

 body{
   margin: 0;
   padding: 0;
 }
 #canvas{
   width: 100%;
   height: 400px;
   margin: 0;
   padding: 0;
   box-sizing: border-box;
 }
 #recording{
   color: red;
   position: fixed;
   top: .5em;
   left: .5em;
   display: none;
 }

JavaScript を使った録音

コードの全体像は gist に公開したものをご覧いただくこととして、 ここではマイクからの入力ストリームの取得、録音、そしてファイルへの出力部分について解説します。

まずは、マイクからの入力ストリームの取得方法です。manifest.webapp中のpermissionsの項目に”audio-capture”を追加すると、 つぎのようにGetUserMediaを使っ、てマイクからの入力を扱うための入力ストリームを取得できます。

navigator.getUserMedia({video: false, audio: true}, 
                       streamAquired, 
                       error =>{
                         console.log(error);
                       });

この実行が成功すると、コールバック関数が呼ばれます。取得した入力ストリームは、関数の第1引数として与えられます。上記の例では streamAquired という関数が呼ばれます。この関数は入力ストリームを利用する様々なオブジェクトの初期化を行います。

ここで初期化されるオブジェクトの中には MediaRecorder オブジェクトがあります。初期化は次の様に行われています。

var initRecorder = function(stream){
  recorder = new MediaRecorder(stream);
  recorder.ondataavailable = (event) => {
    console.log(event); 
    if(storage != null){
      var filename = createFileName(event);
      console.log("attempt to save as " + filename);
      var req = storage.addNamed(event.data, filename);
      req.onsuccess = function(){
        console.log("saved as " + this.result);
      };
      req.onerror = function(){
        console.log(this.error.name);
      };
    }
  };
};

MediaRecorderオブジェクトは、その名の通りメディアデータの記録を行うオブジェクトです。startメソッドを実行すると記録を開始し、stopメソッドを実行すると記録を停止し、記録したデータを用意します。データの用意できると、dataavailableイベントが発生します。用意されたデータは、dataavailableのdata属性でBlobとして参照できます。

上記の例では取得したBlobオブジェクトを、DeviceStorage APIのaddNamedメソッドを使って保存をしています。ここで利用しているstorageオブジェクトは次の様に作成されています。

var storage = navigator.getDeviceStorage("music");

これは音楽データ用のストレージを取得しています。取得できるストレージの種類は次の4種類です。これ以外にもappsというアプリ用ストレージがありますが、これはシステムアプリからのみアクセスが許可されており、我々の作成するアプリは操作できません。

ストレージ名 役割
music 音楽データ用ストレージ
pictures 写真データ用ストレージ
videos 動画データ用ストレージ
sdcard 汎用データストレージ

取得したストレージに対してメソッド呼び出しを行うと、DOMRequestオブジェクトを返します。このオブジェクトのonsuccess属性やonerror属性に関数を登録することで、その操作が成功したか、失敗したかを知ることができます。上記の例では単に成否をログに出力しているだけですが、エラー処理が必要な場合などは、こちらにその処理を記述することになります。

気軽に作ろう!

以上 Firefox OSアプリの構成と作成方法について概観しました。

Firefox OSに向けたアプリの作成についてまとめると、次の3点になるかと思います。

とにかく開発を始めるための敷居が低く、HTMLや CSS、JavaScriptといったWeb標準技術のみで、ハードウェアへアクセスし、コントロールするようなアプリが作れます。

しかもブラウザエンジンがOSに組み込まれているため、作成したアプリのコンパイルやビルドが必要ありません。Webページを作るように、修正したアプリを再読み込みして即座に実行できます。また本文中では解説しませんでしたが、実機へのアプリの転送やデバッグもシミュレータと同様の手順で行えます。

このように開発の敷居が低く、簡単にアプリを作り始められるFirefox OSで、みなさんも作る自由を謳歌してください。

当日のセッションの動画・スライドは、こちらからご覧ください。

Youtube / Slideshare

参考資料