スマホ対応のアプリを作るうえで、PCや携帯にない最大の魅力は、センサーの存在だ...と、勝手に思ってます。

でも、スクラッチからJavaで書き起こすのは面倒だなあ、と思っていたら「PhoneGap」なるものがあると知りました。

OSのAPIをいじれるそうな...。

これで、UIをjQuery Mobileで作って、コンパス機能をPhoneGapで作れば、 いけるんじゃないか!とかなんとか...。 で、早速やってみました。

PhoneGapインストール・環境作成編

コンパス機能作成編

jQuery Mobile導入編

ネイティブアプリを作って、ダウンロード・インストール

プラグインを使って、機能を拡張

付録





PhoneGapインストール・環境作成編

Windows XP (SP3)
Eclipse 3.7(INDIGO)
PhoneGap 1.4.0   (0.9.6 1.0.0)



まずは、Eclipseの環境を構築します。この手の情報は多いので、簡単に行ってみます。

1:JDKをダウンロード・インストール

    ORACLEのサイトから、JDK SE 6(Windows x86)をダウンロードしてインストール

2:Android SDKをダウンロード・インストール

    Developersのサイトから、インストーラー(exe版)をダウンロードしてインストール

   

    通常は  「C:\Program Files\Android\android-sdk」  です。
    変更したい場合は、メモっておきます(後の手順で使います)。

    インストール後に、SDKマネージャーが起動するので、Android 2.3.3などの
    パッケージをインストール。

    AVDマネージャーを起動して、Android 2.3.3を仮想デバイスとして新規登録。

3:Eclipse 3.7をダウンロード・インストール

    Eclipseのサイトから、Eclipse Classic 3.7.1をダウンロード

    ダウンロードしたファイルを解凍して、適当な場所に移してください。インストールはこれだけ。

    eclipse.exeを起動すると、プロジェクトの格納場所を聞いてくるので、分かりやすいフォルダー
    を指定しておきます。

4:ADT(Android Development Tool)をインストール

    EclipseにAndroid SDK を認識させます。

    Eclipseのメニューから[Help]→[Install New Software]を選択。

    ウィンドウが開いたら、[Add]ボタンをクリック。
    [Name]に「ADT Plugin」、[Location]に「https://dl-ssl.google.com/android/eclipse/」と入力
    して、[OK]をクリック。

    「Pending...」が「Developer Tools」に変化したら、[Select All]→[Next]→[Next]→
    {I accept the terms ...}をチェック→[Finish]。インストールが開始されます。
    最後に、「Restart Now]をクリックして、Eclipseを再起動。

5:AVD(Android Virtual Devices)の設定

    Andorid OSのエミュレータを設定します。

    Eclipseのメニューから、[Window]→[Preference]を選択。

    左パネルで[Android]を選択し、右パネルの[SDK Location]で[Browse」をクリック。

    Andorid SDKをインストールしたフォルダ(例えば、「C:\Program Files\Android\android-sdk」)

    を選択し、[OK]をクリック。







    Eclipseのメニューから、[Window]→[AVD Manager]を選択。

    Android Virtual Device Managerの画面、[New..]をクリック。





こんな感じで入力して、最後に、[Create AVD]をクリック。




    まず、お約束の「HelloWorld」用プロジェクトを作成しておきます。

    Eclipseを起動して、メニューから、[File]→[New]→[Project]を選択。

    Androidを選択。







    Project Nameを「Hellow_World」。







    Build Targetを「Android 2.3.3」。







    Application Infoで、Application Nameを「Hello_World」、
    Package Nameを「com.example.helloworld」
    とします。で、[Finish]。





    Eclipseメニューから、[Window]→[Open Perspective]→[Other]を選択、
    [Java(default)]選んで、[OK]をクリック。

    [Package Explorer]で、「HelloWorld」を右クリックし、[Properties]を選択





[Resouce]を選択し、[Text file encoding]部分で[Other]にチェックし、「UTF-8」にします。
    文字化け防止対策です。
   






PhoneGapを導入し、「HelloWorld」をスマホで表示

6:PhoneGapをダウンロード・解凍

    PhoneGapのサイトから、ダウンロード
    2012/02/02の最新バージョンは、1.4.0

    解凍後、以下のフォルダーを参照。
    callback-phonegap-4ad8cc9/lib/android
    こういう感じ。





    まず、作成したワークスペースに、「Hello_World」フォルダが作成されているので、
    その直下に「libs」フォルダを新規作成し、「phonegap.1.4.0.jar」を「libs」フォルダ
    の中にコピーします。

    同様に、「aseets」フォルダの中に「www」フォルダを作成し、「phonegap.1.4.0.js」を
    コピーします。
    「www」フォルダの中に「index.html」を作成し、UTF-8でエンコードしておきます。

    最後に、xmlフォルダーを、「Hello_World」フォルダーの「res」フォルダの中にコピーします。

    Eclipseに戻って
    [Package Explorer]で、「Hello_World」を右クリックして、[Refresh]。

    「/libs/phonegap.1.4.0.jar」を右クリックして、[Build Path]→[Add to Build Path]
    を選択。PhoneGapフレームワークがHello_Worldプロジェクトに導入されます。

「Hello World」表示のためのコーディング

    ●「src/com.example.helloworld/Hello_WorldActivity.java」をダブルクリック

    修正箇所
    1)importに以下を追加
      import com.phonegap.*;
    2)public class Hello_WorldActivity extends Activity
      extendsの後のActivityをDroidGap に変更
    3)setContentView(R.layout.main);をコメントアウト(あるいは削除)して
      super.loadUrl("file:///android_asset/www/index.html"); を追加

    ●「AndroidManifest.xml」を右クリック、[Open With]→[Text Editor]を選択。

    1)android:versionName="1.0">の後ろに以下を追加
      -------------------------------------------------
     
<supports-screens android:largeScreens="true"
    
                  android:normalScreens="true"
                  android:smallScreens="true"
                  android:resizeable="true"
                  android:anyDensity="true"
/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

      -------------------------------------------------

    2)
<activity
            android:name=".HelloActivity"
            android:label="@string/app_name"
の後ろに以下を追加
      -------------------------------------------------
      android:configChanges="orientation|keyboardHidden"
      -------------------------------------------------

    ●「「/assets/www/index.html」を右クリック、[Open With]→[Text Editor]を選択。

    以下のようなコードに編集。
      -------------------------------------------------
     
<!DOCTYPE HTML>
<html>
<head>
  <title>PhoneGap</title>
  <script type="text/javascript" charset="utf-8" src="phonegap.1.4.0.js"></script>
</head>
<body>
  <h1>Hello World</h1>
</body>
</html> 

     


テストは、Android SDKのエミュレーターを使うか、実機でテストします。

Android SDKのエミュレーターはとにかく遅いです。起動まで、数分待つ必要があります。

実機でテストしてみました。
    [Package Explorer]で、「Hello_World」を右クリックして、
    [Run As]→[Android Application]。

   

こんな感じ。







TOP






コンパス機能作成編

まだ、検証がしっかり終わっているわけではないですが、途中経過から申し上げますと

compass機能は、Eclipse 3.7 + PhoneGap 1.4.0 + Android 2.3.4(Experia acro)の環境

では動作しませんでした。正確に言うと、phonegap.jsの読み込み完了を検知する、deviceready

イベントが動作していないようです。PhoneGap 1.0+ はダメみたい
 1.0.0では動作確認できました。

動作が確認できたのは

Eclipse 3.7 + PhoneGap 0.9.6 + Android 2.3.4(Experia acro)の環境でした。

Eclipse 3.7 + PhoneGap 1.0.0 + Android 2.3.4(Experia acro)の環境でした。

さらに見てみると、1.1.0 + laterの環境だと、compass.watchHeadingが動作していないみたい。
compass.getCurrentHeadingはちゃんと動いている...どういうこっちゃ?要確認です。
ということで、compass.watchHeadingで設定していたfrequencyは、compass.getCurrentHeading使用の場合はJavascriptの setInterval()で代用します。問題はないようです。

PhoneGap 0.9.6の導入は1.0+と違って、xmlフォルダーのコピーは必要ありません。

1.0.0 + laterの環境ではxmlフォルダーのコピーが必要です。

後は、同じです。

index.htmlのコードは、phonegap-fanのページに詳しく書かれています。

PhoneGap 0.9.6が必要な方は こちらからダウンロード

こんな感じ。
COMPASSの下の数字が、方位角です(真北を0度として時計回りに、359.99まで取得)。
矢印は、常に北を指すようにしています。





index.htmlのソースはこんな感じ。画像の回転はCanvas要素とjpegイメージを使っています。


<!DOCTYPE HTML>
<html>
<head>
    <title>compass</title>
    <script type="text/javascript" charset="utf-8" src="phonegap.1.1.0.js"></script>
    
    <script type="text/javascript" charset="utf-8">
        //
        var canvas;
        var ctx;
        var img = new Image();
        img.src = "img/direction.jpg";
        
        var timerID_compass = null; 
        
        
        // 
        function onLoad() { 
            document.addEventListener("deviceready", onDeviceReady, false); 
            //
            canvas = document.getElementById("rot_image");
        
            if ( ! canvas || ! canvas.getContext ) { return false; }
            
          ctx = canvas.getContext("2d");
        } 
 
        
        // 
        function onDeviceReady() { 
            startCompass();
            rotate_dir(0);
        } 
 
        
        // 
    function startCompass(){
            timerID_compass = setInterval(startWatch,500);
        document.getElementById('stat').value = "start";
        }
        
        function stopCompass(){
            clearInterval(timerID_compass);
        document.getElementById('stat').value = "stop";
        }
        
    
        function startWatch() { 
   
            navigator.compass.getCurrentHeading(onSuccess_compass, onError_compass);
            
        } 
  
        
        
        // 
        function onSuccess(heading) { 
            var element = document.getElementById('heading'); 
            element.innerHTML = 'heading: ' + heading.magneticHeading;
            rotate_dir(360 - heading.magneticHeading);
        } 
 
        
        // 
        function onError() {
            document.getElementById('stat').value = "error";
        } 

        //
        function rotate_dir(rotDig){
            
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            ctx.save();
            ctx.translate(canvas.width/2, canvas.height/2);
            //
            rot = rotDig/180*Math.PI;
            ctx.rotate(rot);
            ctx.translate(-img.width/2, -img.height/2);
            ctx.drawImage(img, 0, 0);
            ctx.restore();
            
            
        }
        
        
    </script>

</head>
<body onLoad="onLoad()">
  <h1>COMPASS</h1>
  <div id="heading"></div>
  <input type="button" value="STOP" onClick="stopCompass()">
  <input type="button" value="ReSTART" onClick="startCompass()">
  <div>
    <canvas id="rot_image" width=62 height=62></canvas>
    <input type="text" id="stat" value="start">
  </div>
</body>
</html> 








TOP






jQuery Mobile導入編

Eclipse 3.7 + jQuery Mobile + PhoneGap 0.9.6 + Android 2.3.4(Experia acro)の環境。

Eclipse 3.7 + jQuery Mobile + PhoneGap 1.0.0 + Android 2.3.4(Experia acro)の環境。

意外にすんなりいけました。
このページのソースと、他ページのjQuery Mobileの使用法を組み合わせれば簡単です。

こんな感じ。





なんか、いたく全体がちっちゃくなりました。

てっぺんの矢印がコンパス。

矢印がまっすぐ上を向くようにすれば、スマホの先頭が北ってことですね(^^)。

本来なら、地図が回転するようにしたいのですが、それをするなら、API For Flashか

Earthを使うことになるんでしょうかね?

でも、API For Flashは3年後にサポート停止になるとGoogleからアナウンスされてますし...。

Earthは本来の主旨からはずれますし...。

V2の3Dでしのぐってのもなんですし...。

V3にそういう機能が付加される可能性に期待したいです。

もしかしたら、CSSでいけるかも...って気がしましたんで、調べてみます。

New

一応、コンパスの値に対応してリアルタイムに地図が回転するようになりましたが、課題は地図の縦・横の幅ですね。

これが解決すれば、ベースはいけるっぽいです。


回転しても、なんとなぁく、無事収まるようになりましたです。

Javaで直接APIを呼んでいるわけじゃないので、「リアルタイム」と言い切るには、おこがましいとこがありますんで、 「ほぼ、リアルタイム」ってことにしときます。

ただ、課題もありまして、CSSで地図を回転させると、フリックの方向とMapsが認識している方向が一致しない..。

まあ、これは当然といえば当然なわけでMapsとしては、自分の「北」は常にデバイスの上と一致しているはず、という

前提でスクロールしているわけですから、これを修正してやらにゃいけません。

何とかしませう。

jQuery Mobileでは、swipeのイベントは、右・左しか取れないようなので、自作しましょう。

細かいとこは後回しで、swipeした際に角度を取って、その分をスクロールする感じです。

ロジック的にはこんな感じ。

●Maps側で、Mapのdraggable属性をfalseにする。

●Javascriptで、touchstart,touchmove,touchendを使って、新しいイベントリスナーを実装する。

●swipeのようなジェスチャーがあった場合、位置を取得して、headingの角度に合わせて値を変換。

●swipeの距離をとって、V3のメソッドでpanByする。

まあまあの感じで、スクロールできました。

これは、新しいページ(ルートマップ jQuery Mobile)で公開しますんで、よろしく。





ルートマップ jQuery Mobileのページはこちら(鋭意、制作中)。

TOP






ネイティブアプリを作って、ダウンロード・インストール

ネイティブアプリをダウンロード・インストールする場合

1)アンドロイド・マーケットに登録する
2)開発用PCからスマホにダイレクトに実行する

の2種類があるようです。今回は、2)の方法でやってみますが、1)の場合でも

デジタル署名したapkファイルは必要なんですが....。

1)の方法は、こちらのサイト(Tao Software) を参考にしてください。

まず、開発用PCとスマホは、実機での動作確認するために既にUSBでつながっているという前提で。

作業は3つです。

1:自分を証明する証明書(キーストア)を作成する。
2:キーストアを使ってデジタル署名したapkファイルを作成する。
3:Android SDK の adb.exeを使用してインストールする。

1:自分を証明する証明書(キーストア)を作成

dos窓を開きます。

JDKインストール時にシステム変数にパスを通していればそのまま。

していなければ、JDKのbinフォルダーに移動。

keytool.exeを使います。

事前に以下のことを決めておきます。

これは、例です。

●格納場所:E:\eclipse_indigoWork\android_keystore
●証明書のファイル名:foo.keystore
●別名(エイリアス):fookey
●キーストアのパスワード:foo12345
●姓名:foo
●組織単位名:android
●組織名:example.com
●都市名または地域名:osaka
●州名または地方名:osaka
●国番号:jp

dos窓から、以下を実行。

>keytool -genkey -v -keyalg RSA -keystore E:\eclipse_indigoWork\android_keystore\foo.keystore -alias fookey -validity 10000

これで、キーストアファイルが作成されます。

2:キーストアを使ってデジタル署名したapkファイルを作成

コマンドを使う方法もありますが、今回はEclipseでアプリにキーストアを使ってデジタル署名をしてみます。

適当な場所に、apkファイル格納用のフォルダーを作っておきます。

公開するプロジェクトを右クリック → [Android Tools] → [Export Signed Application Package]を選択。

「Export Android Application」ウインドウが開きます。

Project名を確認して、[Next]。

Locationに格納場所のパス、Passwordにキーストアのパスワードを入れて、[Next]。

こんな感じ。





Aliasに別名(エイリアス)、Passwordにキーストアのパスワードを入れて、[Next]。

こんな感じ。





Distination APK fileにapkファイル格納用のフォルダーのパスを入れて、[Finish]。

3:Android SDK の adb.exeを使用してインストール

adb.exeの場所は、EclipseでAndroid SDKを紐付けしたときに、platform-toolsに移動している場合があります。

dos窓を開いて、adb.exeのフォルダーに移動。

>adb install [apkファイルのフルパス]

こんな感じ。





これで実機にインストールされます。



TOP






プラグイン

今回は、音声認識用のプラグインを使ってみます。

プラグインのダウンロードはこちらから

Download as zipと書かれたリンクから一括ダウンロードできます。

プラグインにはいくつか便利なものがあります、今回はSpeechRecognizerを使ってみます。

PhoneGapのバージョンは、1.0.0(0.9.6では動作しませんでした)。

因みに、初期画面はこんな感じ。





[音声入力]ボタンをクリックして、「とうきょうすかいつりー」と音声入力した場合は、こんな感じ。





ダウンロードしたプラグインファイルを解凍して、Androidフォルダーの中の「SpeechRecognizer」フォルダーを確認。

SpeechRecognizer.javaとSpeechRecognizer.jsの2つを使います。

上記の方法で、プロジェクトを作成後、プロジェクトフォルダーの「src」フォルダーにcom/urbtek/phonegapというフォルダー を作成します。これは、SpeechRecognizer.javaに記述されているpackageの記述がそうなっているからです。

このフォルダーにSpeechRecognizer.javaをコピーします。

「res/xml」フォルダーの中の、plugins.xmlを編集します。

pluginsタグの中に以下を追加。
<plugin name="SpeechRecognizer" value="com.urbtek.phonegap.SpeechRecognizer"/> 


「assets/www」フォルダーにSpeechRecognizer.jsコピーして、Eclipseでプロジェクトをrefresh。

index.htmlのベースになるコードは、以下を記述しておきます。


<!DOCTYPE HTML>
<html>
<head>
    
    <title>speechRecognize</title>
    <meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1, maximum-scale=1">
    <meta charset="utf-8">
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
    
    <script type="text/javascript" charset="utf-8" src="phonegap-1.1.0.js"></script>
    <script type="text/javascript" charset="utf-8" src="SpeechRecognizer.js"></script>
    <script type="text/javascript" charset="utf-8">
        document.addEventListener("deviceready", onDeviceReady, false); 
        
        
        function onDeviceReady() {
            window.plugins.speechrecognizer.init(speechInitOk, speechInitFail);
        } 
        
        function speechInitOk() {
            //
        }
        
        function speechInitFail(m) {
            alert("speechInitFail: " + m.toString());
        }
        
        function speechRecognize() {
            var requestCode = 1234;
            var maxMatches = 5;//取得候補の最大数
            var promptString = "なんか話して!";
            window.plugins.speechrecognizer.startRecognize(speechOk, speechFail,requestCode, maxMatches, promptString);
        }
        
        function speechOk(result) {
            var match;
            var respObj;
            var requestCode; 
            if (result) {
                respObj = JSON.parse(result);
                if (respObj) {
                    requestCode = respObj.speechMatches.requestCode;
                    
                    for(var i = 0; i < respObj.speechMatches.speechMatch.length; i++) {
                        var res = respObj.speechMatches.speechMatch[i];
                        alert(res);
                    } 
                    
                }
            }
        }
        
        function speechFail(m) {
            //alert("speechFail: " + m.toString());
        } 
        
    </script>
</head>
<body
  <h1>音声認識</h1>
<div>
    <input type="button" id="speech" value="音声入力" onClick="speechRecognize()"/>
    
</div> 
</body>
</html> 


TOP






付録

0.9.6のDocument

1.4.1のDocument

日本語のDocument(特にバージョン別にはなっていませんでした)

PhoneGapのその他のバージョンをダウンロード

   1.0.0

   1.1.0

   1.3.0

   1.4.0

   1.4.1

ネイティブアプリのアイコンと名称変更

【アイコン】

Eclipseのworkフォルダー/resフォルダーの中。

drawable-hdpi(72x72)、drawable-ldpi(36x36)、drawable-mdpi(48x48)にpng形式で画像保存。

xxx.pngファイルの場合。

AndroidManifest.xmlの記述変更。

android:icon="@drawable/xxx"

【名称】

android:label="@string/app_name"が参照しているファイルを変更。

Eclipseのworkフォルダー/resフォルダー/valuesフォルダーの中。

strings.xmlの記述変更。

「私のアプリ」という名称の場合。

<string name="app_name">私のアプリ</string>


TOP