2014年7月8日火曜日


技適の認証を通過し、アプリ内部には日本語が追加されるなど、どんどん日本での販売が近づいてきているGoogle Glass。空港などでも活躍の場を増やしていますが、もっと身近な車との相性はどうだろうか?危なくて使えない?意外と使える?
そんな疑問を解決するべく実際に専用のGlasswareを開発して実験してみました。

本実験は、Google Glassと車の相性を確認するものです。 Google Glassを装着して車が正常に運転できることを証明するものではありません。
また、このような運転は通常の運転に比べて危険な可能性があるため、安全性に配慮した状況で実験を行っております。








■こんなアプリを作ってみた!


まず、車の中にスマートフォンを設置します。設置したスマートフォンから加速度、GPS、そしてカメラの映像をGoogle Glassに送ります。
Google Glassは無線での通信ができませんので実際には、
スマートフォン⇔(無線)⇔PC⇔(有線)⇔Google Glass
という何ともオーバーヘッドの多い方法でやり取りしています。
Google Glassでは、それらの情報を表示する3つの画面を持つアプリを作成しました。
1つめの画面は、加速度を表示する画面です。加速度は、円の中の赤い丸で表示します
2つめの画面は、コースと現在位置を表示する画面です。取得したGPSの情報を元に現在位置を赤い丸で表示します。
3つめの画面は、カメラの映像を表示する画面です。こちらは、ゲームの画面をイメージし、カメラ映像をベースにすべての情報を表示しています。
また、GPSの情報を元に、コースタイムを計測し、取得可能な速度も参考に表示しています。
3つの画面は、タッチパッドのスワイプで切り替え、二本指のタップで最初の画面戻る事が可能になっています。

・Google Glassの操作方法でいろいろ実験

車の操作中は、両手が塞がるため、Google Glassを操作する事ができません。
その為、タッチパットの操作以外に以下の2種類の操作を追加しました。

・音声でGoogle Glassを操作する

XE18.1よりアプリ実行中でも音声を認識するContextual voice commands機能が追加されました。
この機能を利用し、音声での操作を行います。

この機能を実装すると、画面の下に”ok glass”が表示されます。

 

この状態で、”ok glass”と話すと対応している音声一覧が表示されます。




音声一覧に表示されている単語を話す事で、音声コマンドが実行されます。
では、実際のソースを見ていきます。


Activity.onCreate()でFeatureにWindowUtils.FEATURE_VOICE_COMMANDSを設定します。

    
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().requestFeature(WindowUtils.FEATURE_VOICE_COMMANDS);
}


Activity.onCreatePanelMenu()でMenuを生成します。
今回は、menuリソースの情報を用いてメニューを生成しています。
android:titile部分の文字が音声コマンドとして使用されます。
     <item
        android:id="@+id/menu_stop"
        android:title="@string/menu_stop"/>  

@Override
public boolean onCreatePanelMenu(int featureId, Menu menu) {

    if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    // Pass through to super to setup touch menu.
    return super.onCreatePanelMenu(featureId, menu);
}   


Activity.onPrepareOptionsMenu()で音声認識させる項目を条件に応じて有効・無効にする事も可能です。

@Override  
public boolean onPrepareOptionsMenu(Menu menu) { 

    //条件に応じて、項目の有効、無効、表示、非表示を変更する
    setMenuState(menu.findItem(R.id.start),  !mGameInfo.isTimeCount);
    setMenuState(menu.findItem(R.id.stop),  mGameInfo.isTimeCount);     
    return super.onPrepareOptionsMenu(menu); 
}

private static void setMenuState(MenuItem menuItem, boolean enabled) {

    menuItem.setVisible(enabled);
    menuItem.setEnabled(enabled);
}


Activity.onMenuItemSelected()に音声認識時の処理を記述します。

@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {

    if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS) {
        switch (item.getItemId()) {
        case R.id.menu_start:
            break;
        case R.id.menu_stop:
            break;
        case R.id.menu_home:
            break;
        default:
            return true;
        }
        return true;
    }

    return super.onMenuItemSelected(featureId, item);     
}
      


・顔のジェスチャーで操作する

Google Glassの加速度センサーを利用して、画面の切り替えを行います。
今回のアプリでは右に振る事で画面の切り替え、下に頷く事で初期画面を表示します。

顔が振られたかどうかの判断は、かなりシンプルです。
指定された軸に一定以上の力が発生したかどうかで判断しています。
右方向に振られたかどうかはX軸、下に頷いたかどうかはY軸の情報をチェックします。


Y軸の頷いたかどうかのチェックは、タッチパットをタッチした場合にも同様の加速度が発生するため判断が難しく実際に使用するにはもう少し認識方法を考える必要があると思います。

センサーの初期化および開始と終了の処理です。(Androidで加速度センサーを利用する方法と同じです)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);       
    setContentView(R.layout.main);
    mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
}

@Override
protected void onResume(){
    super.onResume();
     
    if(mSensorManager != null){
        List<sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
        if(sensors.size()>0){
            mSensorManager.registerListener(this, sensors.get(0), SensorManager.SENSOR_DELAY_UI);
        }
    }
}

@Override
protected void onPause(){
    super.onPause();

    if(mSensorManager != null){
         mSensorManager.unregisterListener(this);
    }  
       
}


加速度の情報をローパスフィルタを利用し、重力を除きます。
加速度の強さが一定以上の場合は、ジェスチャーが実行されたと判断し処理を実行します。
今回は実験のため、緩めの数値にしています。値が高い場合、かなり強く振らないと認識しなくなるためです。ただ、弱すぎる場合は、思わぬ顔振りで画面が切り替わったり、Glassの取り外しで画面が変わってしまう事もありました。上手く使用するにはON/OFFの設定が必要かもしれません。

@Override
public void onSensorChanged(SensorEvent event) {

    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

        currentOrientationValues[0] = event.values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
        currentOrientationValues[1] = event.values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);
        currentOrientationValues[2] = event.values[2] * 0.1f + currentOrientationValues[2] * (1.0f - 0.1f);
   
        float acceleration_x = event.values[0] - currentOrientationValues[0];
        float acceleration_y = event.values[1] - currentOrientationValues[1];
        float acceleration_z = event.values[2] - currentOrientationValues[2];
     
        //x軸に4以上の加速度が発生した場合は、右振りと判断する
        if(acceleration_x > 4){
           return;
        }
   
        //y軸に5.5以上の加速度が発生した場合は、頷きと判断する
        //タッチパッド操作の誤認識を避けるために右振りよりも少し数値を強めにしています。
        if(acceleration_y > 5.5){
          return;    
        }
    }
}


■実際に試してみました!


っということで、実際にサーキット場で車(ラジコン)を運転してみました!



ラジコンには、データを配信するためのスマートフォンを搭載します。
そのままだとカメラが写らないので、鏡を使って無理やり前を映しています。
また、落ちないようにテープでぐるぐる巻きにしています。
※鏡の位置がポイントです。ここに設置しないとボディーが閉まりません。



残念ながら天気が悪く雨の中を走らせました。



ゆっくり走らせましたが最後は、水溜りにはまり帰らぬ車となりました。。
※スマホは防水のため無事でした。



・実際に走らせてみて

ラジコン用のコースでは、GPSの誤差による影響が大きく、コースから外れた場所を表示する事が多かったです。ただ、ある程度誤差を考慮に入れていたタイム測定は動いてくれました。正確な測定は難しいかもしれませんが、遊び程度には使えそうな気がします。

いろいろ実装した操作は、顔ジェスチャーが使いやすかったです。音声は、私の発音がダメでなかなか認識してくれませんでした。次回は、音声+ジェスチャーの組み合わせも試してみたいと思います。

期待していたカメラの映像は、個人的にはいい感じで、予想以上に車っぽい映像になっていました。
今回は、一台で試しましたが、複数台にこのシステム載せて走らせたら、リアルタイムでの順位や、前方の車までの距離の表示などまるでゲームのような表現も可能になるのではないかな?と考えています。
また、すべての車の映像を一括で表示すれば、観客にも今までとは違った楽しみ方を提案できるのではないかなと思います。


■最後に・・

今回の実験は、安全を考慮してラジコンという車を運転しました。
しかし、実際の車で運転する場合は、道路交通法の問題や、そもそも運転自体に危険が伴う可能性もあります。
公道での使用は、ご遠慮いただきますよう宜しくお願い致します。

もし公道で使用したら・・
こんな怪しい人に追いかけられたり・・

こんな恐ろしい事故を起こすかもしれません。


くれぐれも公道での使用はご遠慮ください。


※Google Glassは、無線による通信を行っていません。すべて有線を使用しています。



㈱ブリリアントサービス 大阪開発部 開発課 田中雅也

Google Glass+車をサーキット場で試してみた!


技適の認証を通過し、アプリ内部には日本語が追加されるなど、どんどん日本での販売が近づいてきているGoogle Glass。空港などでも活躍の場を増やしていますが、もっと身近な車との相性はどうだろうか?危なくて使えない?意外と使える?
そんな疑問を解決するべく実際に専用のGlasswareを開発して実験してみました。

本実験は、Google Glassと車の相性を確認するものです。 Google Glassを装着して車が正常に運転できることを証明するものではありません。
また、このような運転は通常の運転に比べて危険な可能性があるため、安全性に配慮した状況で実験を行っております。








■こんなアプリを作ってみた!


まず、車の中にスマートフォンを設置します。設置したスマートフォンから加速度、GPS、そしてカメラの映像をGoogle Glassに送ります。
Google Glassは無線での通信ができませんので実際には、
スマートフォン⇔(無線)⇔PC⇔(有線)⇔Google Glass
という何ともオーバーヘッドの多い方法でやり取りしています。
Google Glassでは、それらの情報を表示する3つの画面を持つアプリを作成しました。
1つめの画面は、加速度を表示する画面です。加速度は、円の中の赤い丸で表示します
2つめの画面は、コースと現在位置を表示する画面です。取得したGPSの情報を元に現在位置を赤い丸で表示します。
3つめの画面は、カメラの映像を表示する画面です。こちらは、ゲームの画面をイメージし、カメラ映像をベースにすべての情報を表示しています。
また、GPSの情報を元に、コースタイムを計測し、取得可能な速度も参考に表示しています。
3つの画面は、タッチパッドのスワイプで切り替え、二本指のタップで最初の画面戻る事が可能になっています。

・Google Glassの操作方法でいろいろ実験

車の操作中は、両手が塞がるため、Google Glassを操作する事ができません。
その為、タッチパットの操作以外に以下の2種類の操作を追加しました。

・音声でGoogle Glassを操作する

XE18.1よりアプリ実行中でも音声を認識するContextual voice commands機能が追加されました。
この機能を利用し、音声での操作を行います。

この機能を実装すると、画面の下に”ok glass”が表示されます。

 

この状態で、”ok glass”と話すと対応している音声一覧が表示されます。




音声一覧に表示されている単語を話す事で、音声コマンドが実行されます。
では、実際のソースを見ていきます。


Activity.onCreate()でFeatureにWindowUtils.FEATURE_VOICE_COMMANDSを設定します。

    
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().requestFeature(WindowUtils.FEATURE_VOICE_COMMANDS);
}


Activity.onCreatePanelMenu()でMenuを生成します。
今回は、menuリソースの情報を用いてメニューを生成しています。
android:titile部分の文字が音声コマンドとして使用されます。
     <item
        android:id="@+id/menu_stop"
        android:title="@string/menu_stop"/>  

@Override
public boolean onCreatePanelMenu(int featureId, Menu menu) {

    if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    // Pass through to super to setup touch menu.
    return super.onCreatePanelMenu(featureId, menu);
}   


Activity.onPrepareOptionsMenu()で音声認識させる項目を条件に応じて有効・無効にする事も可能です。

@Override  
public boolean onPrepareOptionsMenu(Menu menu) { 

    //条件に応じて、項目の有効、無効、表示、非表示を変更する
    setMenuState(menu.findItem(R.id.start),  !mGameInfo.isTimeCount);
    setMenuState(menu.findItem(R.id.stop),  mGameInfo.isTimeCount);     
    return super.onPrepareOptionsMenu(menu); 
}

private static void setMenuState(MenuItem menuItem, boolean enabled) {

    menuItem.setVisible(enabled);
    menuItem.setEnabled(enabled);
}


Activity.onMenuItemSelected()に音声認識時の処理を記述します。

@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {

    if (featureId == WindowUtils.FEATURE_VOICE_COMMANDS) {
        switch (item.getItemId()) {
        case R.id.menu_start:
            break;
        case R.id.menu_stop:
            break;
        case R.id.menu_home:
            break;
        default:
            return true;
        }
        return true;
    }

    return super.onMenuItemSelected(featureId, item);     
}
      


・顔のジェスチャーで操作する

Google Glassの加速度センサーを利用して、画面の切り替えを行います。
今回のアプリでは右に振る事で画面の切り替え、下に頷く事で初期画面を表示します。

顔が振られたかどうかの判断は、かなりシンプルです。
指定された軸に一定以上の力が発生したかどうかで判断しています。
右方向に振られたかどうかはX軸、下に頷いたかどうかはY軸の情報をチェックします。


Y軸の頷いたかどうかのチェックは、タッチパットをタッチした場合にも同様の加速度が発生するため判断が難しく実際に使用するにはもう少し認識方法を考える必要があると思います。

センサーの初期化および開始と終了の処理です。(Androidで加速度センサーを利用する方法と同じです)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);       
    setContentView(R.layout.main);
    mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
}

@Override
protected void onResume(){
    super.onResume();
     
    if(mSensorManager != null){
        List<sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
        if(sensors.size()>0){
            mSensorManager.registerListener(this, sensors.get(0), SensorManager.SENSOR_DELAY_UI);
        }
    }
}

@Override
protected void onPause(){
    super.onPause();

    if(mSensorManager != null){
         mSensorManager.unregisterListener(this);
    }  
       
}


加速度の情報をローパスフィルタを利用し、重力を除きます。
加速度の強さが一定以上の場合は、ジェスチャーが実行されたと判断し処理を実行します。
今回は実験のため、緩めの数値にしています。値が高い場合、かなり強く振らないと認識しなくなるためです。ただ、弱すぎる場合は、思わぬ顔振りで画面が切り替わったり、Glassの取り外しで画面が変わってしまう事もありました。上手く使用するにはON/OFFの設定が必要かもしれません。

@Override
public void onSensorChanged(SensorEvent event) {

    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

        currentOrientationValues[0] = event.values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
        currentOrientationValues[1] = event.values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);
        currentOrientationValues[2] = event.values[2] * 0.1f + currentOrientationValues[2] * (1.0f - 0.1f);
   
        float acceleration_x = event.values[0] - currentOrientationValues[0];
        float acceleration_y = event.values[1] - currentOrientationValues[1];
        float acceleration_z = event.values[2] - currentOrientationValues[2];
     
        //x軸に4以上の加速度が発生した場合は、右振りと判断する
        if(acceleration_x > 4){
           return;
        }
   
        //y軸に5.5以上の加速度が発生した場合は、頷きと判断する
        //タッチパッド操作の誤認識を避けるために右振りよりも少し数値を強めにしています。
        if(acceleration_y > 5.5){
          return;    
        }
    }
}


■実際に試してみました!


っということで、実際にサーキット場で車(ラジコン)を運転してみました!



ラジコンには、データを配信するためのスマートフォンを搭載します。
そのままだとカメラが写らないので、鏡を使って無理やり前を映しています。
また、落ちないようにテープでぐるぐる巻きにしています。
※鏡の位置がポイントです。ここに設置しないとボディーが閉まりません。



残念ながら天気が悪く雨の中を走らせました。



ゆっくり走らせましたが最後は、水溜りにはまり帰らぬ車となりました。。
※スマホは防水のため無事でした。



・実際に走らせてみて

ラジコン用のコースでは、GPSの誤差による影響が大きく、コースから外れた場所を表示する事が多かったです。ただ、ある程度誤差を考慮に入れていたタイム測定は動いてくれました。正確な測定は難しいかもしれませんが、遊び程度には使えそうな気がします。

いろいろ実装した操作は、顔ジェスチャーが使いやすかったです。音声は、私の発音がダメでなかなか認識してくれませんでした。次回は、音声+ジェスチャーの組み合わせも試してみたいと思います。

期待していたカメラの映像は、個人的にはいい感じで、予想以上に車っぽい映像になっていました。
今回は、一台で試しましたが、複数台にこのシステム載せて走らせたら、リアルタイムでの順位や、前方の車までの距離の表示などまるでゲームのような表現も可能になるのではないかな?と考えています。
また、すべての車の映像を一括で表示すれば、観客にも今までとは違った楽しみ方を提案できるのではないかなと思います。


■最後に・・

今回の実験は、安全を考慮してラジコンという車を運転しました。
しかし、実際の車で運転する場合は、道路交通法の問題や、そもそも運転自体に危険が伴う可能性もあります。
公道での使用は、ご遠慮いただきますよう宜しくお願い致します。

もし公道で使用したら・・
こんな怪しい人に追いかけられたり・・

こんな恐ろしい事故を起こすかもしれません。


くれぐれも公道での使用はご遠慮ください。


※Google Glassは、無線による通信を行っていません。すべて有線を使用しています。



㈱ブリリアントサービス 大阪開発部 開発課 田中雅也

2014年6月18日水曜日



はじめに


これまで3回に渡ってnRF51822ブランクモジュールをARM mbed 対応機器にするための方法について掲載してきましたが、今回から mbedによるBLEプログラミング実践編をスタートいたします。

BluetoothSMARTデバイスをmbed で開発する(4)



はじめに


これまで3回に渡ってnRF51822ブランクモジュールをARM mbed 対応機器にするための方法について掲載してきましたが、今回から mbedによるBLEプログラミング実践編をスタートいたします。

2014年6月4日水曜日



はじめに

前回はARM mbedを用いたソフトウェア開発手法を紹介いたしましたが、今回は連載3回目として mbed対応の要となるハードウェアの作成方法について紹介させていただきます。

BluetoothSMARTデバイスをmbed で開発する(3)



はじめに

前回はARM mbedを用いたソフトウェア開発手法を紹介いたしましたが、今回は連載3回目として mbed対応の要となるハードウェアの作成方法について紹介させていただきます。

2014年5月27日火曜日


はじめに

前回のブログで弊社の取り組みについて紹介させていただきましたが、今回は第二回目として実際にmbedを使った開発について紹介させて頂きます。


BluetoothSMARTデバイスをmbed で開発する(2)


はじめに

前回のブログで弊社の取り組みについて紹介させていただきましたが、今回は第二回目として実際にmbedを使った開発について紹介させて頂きます。


2014年5月23日金曜日


はじめに

NordicのnRF51ブランクモジュールは、法的面でBluetooth SMARTアクセサリ製造への敷居を下げてくれるという期待感から注目されていましたが、モジュール使用にあたって技術面でのハードルが高いところがあり、まだユーザーが増えていないのが現状のようです。

そこで今回は個人レベルでも試作が可能となるような試みとして、nRF51ブランクモジュールのDIP基板化と ARM® mbed™ を使った開発について紹介いたします。

BluetoothSMARTデバイスをmbed で開発する(1)


はじめに

NordicのnRF51ブランクモジュールは、法的面でBluetooth SMARTアクセサリ製造への敷居を下げてくれるという期待感から注目されていましたが、モジュール使用にあたって技術面でのハードルが高いところがあり、まだユーザーが増えていないのが現状のようです。

そこで今回は個人レベルでも試作が可能となるような試みとして、nRF51ブランクモジュールのDIP基板化と ARM® mbed™ を使った開発について紹介いたします。

2014年4月19日土曜日



ついに来ました。やっと来ました。XE16が公開されました。
以前から言われていましたがベースをICS4.0.3から、Kitkat4.4へ変更されています。
追加機能や、変更機能は、すでに他のサイトで紹介されていますので、どのように変わったのか古いバージョンのサンプルソースを利用して調べてみました。


■スクリーン録画って使えるの?

いきなり開発とは関係ない話になります。
スクリーン録画は、Android Kitkat4.4から追加された機能で、その名の通り動作中の動画撮れる機能です。
Google GlassもKitkat 4.4になったんだし、この機能も使えるんじゃないの?って事で試してみました。

実際に録画した動画がこちらになります。


どうでしょう?いい感じではないでしょうか?

Google Glassは内容を人に伝えづらい端末です。
録画した動画を見て頂くことで、Google Glassでの動きも伝えやすくなると思います。


■XE12からの変更点

では、本題です。
XE12から何が変わったのかを探っていきます。

まずは、GDKから。GoogleGlass XE16用のGDKは、アップデート前から配信されています。



実は、サンプルコードも配信される前からXE16用になってます。

※今回は、よく参考に使われるstopwatchを利用しています。


これらをもとに古いバージョンのサンプルを最新のGDKでビルドしてみました。
結果はこんな感じです。



・LiveCardの生成方法

StopwatchService.javaでエラーが発生している箇所を見てみると、LiveCardの生成するためのTimelineManagerクラスが削除されました。

[StopwatchService.java]

XE16以前の記述方法
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (mLiveCard == null) {
            Log.d(TAG, "Publishing LiveCard");
            mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_TAG);

            // Keep track of the callback to remove it before unpublishing.
            mCallback = new ChronometerDrawer(this);
            mLiveCard.setDirectRenderingEnabled(true).getSurfaceHolder().addCallback(mCallback);

            Intent menuIntent = new Intent(this, MenuActivity.class);
            menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));

            mLiveCard.publish(PublishMode.REVEAL);
            Log.d(TAG, "Done publishing LiveCard");
        } else {
            // TODO(alainv): Jump to the LiveCard when API is available.
        }

        return START_STICKY;
    }

こちらが新しいバージョンの記述方法です。
   @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (mLiveCard == null) {
            mLiveCard = new LiveCard(this, LIVE_CARD_TAG);

            // Keep track of the callback to remove it before unpublishing.
            mCallback = new ChronometerDrawer(this);
            mLiveCard.setDirectRenderingEnabled(true).getSurfaceHolder().addCallback(mCallback);

            Intent menuIntent = new Intent(this, MenuActivity.class);
            menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));
            mLiveCard.attach(this);
            mLiveCard.publish(PublishMode.REVEAL);
        } else {
            mLiveCard.navigate();
        }

        return START_STICKY;
    }


TimelineManagerからLiveCardを生成するのではなく、LiveCardを直接生成する方法に変わっています。

LiveCardの生成方法を変更することで、とりあえずはビルドが通る状態となりました。
では早速、Glassで動作確認をしてみます。


・音声で起動しない

インストールした後、「start a stopwatch」と話しても起動してくれません。
音声起動どころか、音声起動の一覧にすら表示されていません。

調べてみると音声起動の設定方法が変わっていました。
今回のバージョンより、決まった音声のみの起動可能となってます。

それだとテストも出来ないじゃないかということで、以下のuses permissionを利用することで今までと同じように音声での起動も可能となります。

<uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT" />   


ただし、開発時のオプションのため、リリース時には、Googleの承認が必要となりそうです。
音声起動の部分は、今後公開するうえでネックになりそうに思います。



これで起動もできたので、ストップウォッチの動作を確認します。



動作も問題なしだったので終了したところ・・


まさかの異常終了。
エラーログを見るとこんな感じに。



・openOptionMenuの呼び出しタイミング

問題を調べてみると、openOptionMenuが呼び出されるタイミングが変わっていました。


[MenuActivity.java]

XE16以前の記述方法
@Override
    public void onResume() {
        super.onResume();
        openOptionsMenu();
    }

こちらが新しいバージョンの記述方法です。
@Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        openOptionsMenu();
    }


ここまで来ると以前と同じように動作するようになりました。





今回調査をしてみてLiveCard周りの変更が大きいと感じました。
説明できていませんが、追加されている部分も多くあります(attachやnavigateなど)
音声起動は、着々と一般販売に向けて制限しているのかなという感じです。

Bluetooth周りも大幅に変わったと聞きます。
以前のバージョンでは、かなり苦労したので今回のバージョンアップで、少しでも素直になってくれたらと期待しています。
LiveCardの追加分も含めて、別の機会で報告できればと思います。





㈱ブリリアントサービス 大阪開発部 開発課 田中雅也



[コラム] Google Glass XE16 サンプルコードから見る変更点



ついに来ました。やっと来ました。XE16が公開されました。
以前から言われていましたがベースをICS4.0.3から、Kitkat4.4へ変更されています。
追加機能や、変更機能は、すでに他のサイトで紹介されていますので、どのように変わったのか古いバージョンのサンプルソースを利用して調べてみました。


■スクリーン録画って使えるの?

いきなり開発とは関係ない話になります。
スクリーン録画は、Android Kitkat4.4から追加された機能で、その名の通り動作中の動画撮れる機能です。
Google GlassもKitkat 4.4になったんだし、この機能も使えるんじゃないの?って事で試してみました。

実際に録画した動画がこちらになります。


どうでしょう?いい感じではないでしょうか?

Google Glassは内容を人に伝えづらい端末です。
録画した動画を見て頂くことで、Google Glassでの動きも伝えやすくなると思います。


■XE12からの変更点

では、本題です。
XE12から何が変わったのかを探っていきます。

まずは、GDKから。GoogleGlass XE16用のGDKは、アップデート前から配信されています。



実は、サンプルコードも配信される前からXE16用になってます。

※今回は、よく参考に使われるstopwatchを利用しています。


これらをもとに古いバージョンのサンプルを最新のGDKでビルドしてみました。
結果はこんな感じです。



・LiveCardの生成方法

StopwatchService.javaでエラーが発生している箇所を見てみると、LiveCardの生成するためのTimelineManagerクラスが削除されました。

[StopwatchService.java]

XE16以前の記述方法
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (mLiveCard == null) {
            Log.d(TAG, "Publishing LiveCard");
            mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_TAG);

            // Keep track of the callback to remove it before unpublishing.
            mCallback = new ChronometerDrawer(this);
            mLiveCard.setDirectRenderingEnabled(true).getSurfaceHolder().addCallback(mCallback);

            Intent menuIntent = new Intent(this, MenuActivity.class);
            menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));

            mLiveCard.publish(PublishMode.REVEAL);
            Log.d(TAG, "Done publishing LiveCard");
        } else {
            // TODO(alainv): Jump to the LiveCard when API is available.
        }

        return START_STICKY;
    }

こちらが新しいバージョンの記述方法です。
   @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (mLiveCard == null) {
            mLiveCard = new LiveCard(this, LIVE_CARD_TAG);

            // Keep track of the callback to remove it before unpublishing.
            mCallback = new ChronometerDrawer(this);
            mLiveCard.setDirectRenderingEnabled(true).getSurfaceHolder().addCallback(mCallback);

            Intent menuIntent = new Intent(this, MenuActivity.class);
            menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));
            mLiveCard.attach(this);
            mLiveCard.publish(PublishMode.REVEAL);
        } else {
            mLiveCard.navigate();
        }

        return START_STICKY;
    }


TimelineManagerからLiveCardを生成するのではなく、LiveCardを直接生成する方法に変わっています。

LiveCardの生成方法を変更することで、とりあえずはビルドが通る状態となりました。
では早速、Glassで動作確認をしてみます。


・音声で起動しない

インストールした後、「start a stopwatch」と話しても起動してくれません。
音声起動どころか、音声起動の一覧にすら表示されていません。

調べてみると音声起動の設定方法が変わっていました。
今回のバージョンより、決まった音声のみの起動可能となってます。

それだとテストも出来ないじゃないかということで、以下のuses permissionを利用することで今までと同じように音声での起動も可能となります。

<uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT" />   


ただし、開発時のオプションのため、リリース時には、Googleの承認が必要となりそうです。
音声起動の部分は、今後公開するうえでネックになりそうに思います。



これで起動もできたので、ストップウォッチの動作を確認します。



動作も問題なしだったので終了したところ・・


まさかの異常終了。
エラーログを見るとこんな感じに。



・openOptionMenuの呼び出しタイミング

問題を調べてみると、openOptionMenuが呼び出されるタイミングが変わっていました。


[MenuActivity.java]

XE16以前の記述方法
@Override
    public void onResume() {
        super.onResume();
        openOptionsMenu();
    }

こちらが新しいバージョンの記述方法です。
@Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        openOptionsMenu();
    }


ここまで来ると以前と同じように動作するようになりました。





今回調査をしてみてLiveCard周りの変更が大きいと感じました。
説明できていませんが、追加されている部分も多くあります(attachやnavigateなど)
音声起動は、着々と一般販売に向けて制限しているのかなという感じです。

Bluetooth周りも大幅に変わったと聞きます。
以前のバージョンでは、かなり苦労したので今回のバージョンアップで、少しでも素直になってくれたらと期待しています。
LiveCardの追加分も含めて、別の機会で報告できればと思います。





㈱ブリリアントサービス 大阪開発部 開発課 田中雅也



2014年4月17日木曜日


GDKで動的なGlasswareを作成する方法には、LiveCardを使用する方法とImmersionという方法が存在します。その二つの方法で同じアプリを作成してみたところ、処理の構築に大きな差異があることがわかりました。

その具体的な内容については本文から。



GDKでの動的なGlassware作成


GDKで動的なGlasswareを作成する方法には、LiveCardを使用する方法とImmersionという方法が存在します。その二つの方法で同じアプリを作成してみたところ、処理の構築に大きな差異があることがわかりました。

その具体的な内容については本文から。



2014年3月20日木曜日



f:id:bs-android:20140319172604p:image

早くも発売が待ち遠しいAndroid Wearの端末ですが、端末を買ってから環境を整えていたら開発者として楽しめません。

エミュレータを使って開発ができるので環境構築手順を紹介します。

Android Wear Developer Preview 開発環境構築



f:id:bs-android:20140319172604p:image

早くも発売が待ち遠しいAndroid Wearの端末ですが、端末を買ってから環境を整えていたら開発者として楽しめません。

エミュレータを使って開発ができるので環境構築手順を紹介します。



f:id:bs-android:20140319135838j:image

2週間近く前にGoogleのSundar Pichai氏がSXSWのイベントでAndroidのウェアラブル機器のSDKをリリースするという話をしていたのですが、宣言通りに開発者向けプレビュー版のSDKが本日公開されました。

公式ブログでのAndroid Wear発表

Android Wear開発者向けサイト

Android Wearとは


Android WearとはAndroid搭載スマートフォンと連携して動作するウェアラブルデバイスのことを指すようです。

現段階では腕時計だけが対象のようですが、今後他のウェアラブル機器にも広げていくとのこと。

音声でのテキスト入力、タッチパネルでの操作が可能なようです。

詳細なハードウェア仕様はまだ公開されていません。




恐らく6/25-6/26にサンフランシスコで開催されるGoogle I/O 2014でデバイスがお目見えすることになると思います。




モトローラが夏にAndroid Wear端末Moto 360を発売すると発表しているので恐らくこのモデルがGoogle I/O参加者に配布されることになるのではないかと予想しています。

まずは利用シーンの紹介動画。


D

開発者向け動画


D

下の画像は動画から抜き出したものですが、Google Nowで使える機能がほとんど使えるようです。天気予報、経路検索、音声入力でHangoutでテキストチャットもできている模様。


f:id:bs-android:20140319144846p:image:w360

f:id:bs-android:20140319144845p:image:w360

f:id:bs-android:20140319144844p:image:w360

f:id:bs-android:20140319144843p:image:w360

f:id:bs-android:20140319144842p:image:w360

f:id:bs-android:20140319144840p:image:w360

f:id:bs-android:20140319144841p:image:w360

f:id:bs-android:20140319144839p:image:w360


UIの動作イメージはこんな感じです。

画面が小さいので基本操作はスワイプ。音声入力にも対応しています。

「Ok google」と話しかけると、そこからアプリとインターネットへの道が開きます。


f:id:bs-android:20140319153819g:image




f:id:bs-android:20140319153820g:image




f:id:bs-android:20140319154729g:image


まずは概要だけお届けしましたが、開発者視点の記事を随時更新していきますのでお楽しみに。







ちなみにシンクロニシティなのかどうかわかりませんが、偶然にも(?)2日ほど前にTizen陣営からもTizen Software Developer Kit for Wearablesというスマートウォッチ向けのSDKがリリースされています。




[コラム] ウェアラブル機器開発の本命、Android Wear登場



f:id:bs-android:20140319135838j:image

2週間近く前にGoogleのSundar Pichai氏がSXSWのイベントでAndroidのウェアラブル機器のSDKをリリースするという話をしていたのですが、宣言通りに開発者向けプレビュー版のSDKが本日公開されました。

公式ブログでのAndroid Wear発表

Android Wear開発者向けサイト

Android Wearとは


Android WearとはAndroid搭載スマートフォンと連携して動作するウェアラブルデバイスのことを指すようです。

現段階では腕時計だけが対象のようですが、今後他のウェアラブル機器にも広げていくとのこと。

音声でのテキスト入力、タッチパネルでの操作が可能なようです。

詳細なハードウェア仕様はまだ公開されていません。




恐らく6/25-6/26にサンフランシスコで開催されるGoogle I/O 2014でデバイスがお目見えすることになると思います。




モトローラが夏にAndroid Wear端末Moto 360を発売すると発表しているので恐らくこのモデルがGoogle I/O参加者に配布されることになるのではないかと予想しています。

まずは利用シーンの紹介動画。


D

開発者向け動画


D

下の画像は動画から抜き出したものですが、Google Nowで使える機能がほとんど使えるようです。天気予報、経路検索、音声入力でHangoutでテキストチャットもできている模様。


f:id:bs-android:20140319144846p:image:w360

f:id:bs-android:20140319144845p:image:w360

f:id:bs-android:20140319144844p:image:w360

f:id:bs-android:20140319144843p:image:w360

f:id:bs-android:20140319144842p:image:w360

f:id:bs-android:20140319144840p:image:w360

f:id:bs-android:20140319144841p:image:w360

f:id:bs-android:20140319144839p:image:w360


UIの動作イメージはこんな感じです。

画面が小さいので基本操作はスワイプ。音声入力にも対応しています。

「Ok google」と話しかけると、そこからアプリとインターネットへの道が開きます。


f:id:bs-android:20140319153819g:image




f:id:bs-android:20140319153820g:image




f:id:bs-android:20140319154729g:image


まずは概要だけお届けしましたが、開発者視点の記事を随時更新していきますのでお楽しみに。







ちなみにシンクロニシティなのかどうかわかりませんが、偶然にも(?)2日ほど前にTizen陣営からもTizen Software Developer Kit for Wearablesというスマートウォッチ向けのSDKがリリースされています。




2014年2月20日木曜日



Glassを身につけている人の視線(カメラプレビュー)をリアルタイムに共有できたらとても便利と思いませんか?

更にお互いに何かのアクションをやりとりできたらどんなに素敵でしょう。さまざまな仕事やエンターテイメントに応用できるはずです。

ということで、Glass の視線をiPadで共有し、ついでに、iPadからGlassに対して指示を送るサンプルを作ってみました。「ここを見て!」という指示が飛ぶようにします。



アプリの流れは以下のようになります。


GoogleGlassの視線(カメラプレビュー)をiPad でリアルタイムに共有して、かつ、指示を送ってみる その一



Glassを身につけている人の視線(カメラプレビュー)をリアルタイムに共有できたらとても便利と思いませんか?

更にお互いに何かのアクションをやりとりできたらどんなに素敵でしょう。さまざまな仕事やエンターテイメントに応用できるはずです。

ということで、Glass の視線をiPadで共有し、ついでに、iPadからGlassに対して指示を送るサンプルを作ってみました。「ここを見て!」という指示が飛ぶようにします。



アプリの流れは以下のようになります。


2014年2月14日金曜日



以前GDKでActivityを多様することは本体の発熱量が高くなるに少し触れましたが、今回はその発熱対策として放熱板の取り付けを行ってみました。

元々Google Glassには既存で放熱板が取り付けられています。

しかしコストの高いアプリを作成し、動作を続けるとタップやスワイプなどを行う右側部分(装着時)のフレームの発熱が非常に高くなることがわかりました。


f:id:bs-android:20140213193033j:image:w360



※ピンク:既存の耐熱板。Glassフレームを囲うように装着してある。

 赤 :発熱が最も高い部分。今回はここに自作の耐熱板を装着する。




少し温かくなる程度であれば問題ないのですが、カメラ機能を使い続けるようなコストの高いアプリを動作させ続けると、Glassをかけ続けることも辛いほどの発熱量となります。

そこで既存の放熱板だけでは不足な発熱の対策としてオリジナルの放熱板を取り付けることを試みました。




用意したもの




  • 熱伝導性シリコン

  • 熱伝導性接着剤

  • コイン

  • プラスチックの線

  • ハサミ

  • ペンチ

f:id:bs-android:20140213193034j:image:w360







手順


1)シリコンを発熱対策を行いたいフレームのサイズに合わせてカットし、熱伝導性の接着剤を使い重ね合わせます。

f:id:bs-android:20140213193035j:image:w360




2)更に放熱度をあげるため、重ねたシリコンの間にコインを挟みます。

f:id:bs-android:20140213193036j:image:w360




3)2)で作成したものをGlassの放熱を行いたい部分に装着します。

このとき、放熱板とGlassに隙間ができると放熱効果が下がるため、しっかり密着させることが重要。

f:id:bs-android:20140213193037j:image:w360







装着イメージ


f:id:bs-android:20140213193038j:image:w360

放熱板を装着したことによる違和感は特に気にならず。







成果


オリジナルの放熱板を取り付けることにより、以前に感じていた発熱を抑えることができました。

またカメラビューを使い続けるようなアプリをGlassで動作させ続けた際の動作時間を以前の約倍近くまで延ばすことができました。







今回苦労した点


Glassに放熱板の密着度を意識しながら取り付けたことぐらいです。

ですが、これが重要です!!!!




今回の放熱板の取り付けにより発熱対策は行いましたが、より多様な機能を搭載したアプリを開発するには設計段階からの省エネ対策が必要となります。




[Tips] Google Glassの発熱対策



以前GDKでActivityを多様することは本体の発熱量が高くなるに少し触れましたが、今回はその発熱対策として放熱板の取り付けを行ってみました。

元々Google Glassには既存で放熱板が取り付けられています。

しかしコストの高いアプリを作成し、動作を続けるとタップやスワイプなどを行う右側部分(装着時)のフレームの発熱が非常に高くなることがわかりました。


f:id:bs-android:20140213193033j:image:w360



※ピンク:既存の耐熱板。Glassフレームを囲うように装着してある。

 赤 :発熱が最も高い部分。今回はここに自作の耐熱板を装着する。




少し温かくなる程度であれば問題ないのですが、カメラ機能を使い続けるようなコストの高いアプリを動作させ続けると、Glassをかけ続けることも辛いほどの発熱量となります。

そこで既存の放熱板だけでは不足な発熱の対策としてオリジナルの放熱板を取り付けることを試みました。




用意したもの




  • 熱伝導性シリコン

  • 熱伝導性接着剤

  • コイン

  • プラスチックの線

  • ハサミ

  • ペンチ

f:id:bs-android:20140213193034j:image:w360







手順


1)シリコンを発熱対策を行いたいフレームのサイズに合わせてカットし、熱伝導性の接着剤を使い重ね合わせます。

f:id:bs-android:20140213193035j:image:w360




2)更に放熱度をあげるため、重ねたシリコンの間にコインを挟みます。

f:id:bs-android:20140213193036j:image:w360




3)2)で作成したものをGlassの放熱を行いたい部分に装着します。

このとき、放熱板とGlassに隙間ができると放熱効果が下がるため、しっかり密着させることが重要。

f:id:bs-android:20140213193037j:image:w360







装着イメージ


f:id:bs-android:20140213193038j:image:w360

放熱板を装着したことによる違和感は特に気にならず。







成果


オリジナルの放熱板を取り付けることにより、以前に感じていた発熱を抑えることができました。

またカメラビューを使い続けるようなアプリをGlassで動作させ続けた際の動作時間を以前の約倍近くまで延ばすことができました。







今回苦労した点


Glassに放熱板の密着度を意識しながら取り付けたことぐらいです。

ですが、これが重要です!!!!




今回の放熱板の取り付けにより発熱対策は行いましたが、より多様な機能を搭載したアプリを開発するには設計段階からの省エネ対策が必要となります。




2014年2月13日木曜日


2014年2月の日本アンドロイドの会定例会に参加してきました。

https://www.android-group.jp/event/event28.html

iBeaconとはどんなもので、アンドロイドで実現する場合に注意すべきことは何かなどを、この方面で活躍されている方を招いて勉強するという内容でした。

まず、今度の2/15、16に会津でBLEハッカソンを企画されている、Aka Beacon で有名なGClue の佐々木陽氏が、iBeacon とは、UUID、Major、Minor でビーコンを区別し、Immediate/Near/Far/Unknown などの近さが取得できるものですよ、ということを自作したデバイスを使ってデモを行いながら、非常に軽やかに説明されました。

また、iOSのバックグラウンド処理についても触れて、アプリがバックグラウンドにいてもRegion IN/OUTのイベントを受信でき、15秒間は動作可能ということも述べられていました。結局、もっとバックグラウンド動作を伸ばす方法はあるみたいですが…。

アンドロイドは、各アプリがビーコンを監視する部分を独自でもつため、ビーコンを利用するアプリが立ち上がるほど、電力消費が大きくなるとのこと。対して、iOSは、フレームワーク部分がビーコンの監視を行ってくれているので、ビーコンを使うアプリがたくさんいても省電力になっているとのこと。だから、アンドロイドでもビーコンを扱うサービスを作り、省電力な設計を行う必要があるとおっしゃっていました。

あとは、エコシステム、Appcessoryを中心とする最近のビジネス状況について、BLEデバイスのモジュール化のベンチャーが増えていることなどを説明してくれました。

続いて、Applixで研究開発をされている日向さんがビーコンの電波特性について、非常にわかりやすく解説してくれました。屋内測位をやっていると、電波特性というものに敏感になるもので、なんでこんなに状況によって電波強度がかわるのか深く知りたくなってきます。日向さんの調べたところによると、床や壁などに当たって跳ね返ってくる反射波との干渉により、場所によって電波の強弱が変わってしまうということです。また、電波強度と距離の関係式も、2、3mくらいまでなら大体合っているが、距離が遠くなるに連れて、実際の値とのずれが大きくなってくるとのこと。屋内測位をやったときもFarのビーコンは無視すると非常にうまく行きましたから、納得です。日向さんの考えでは、RSSIからもとめる距離の精度も対数をとった値なら実用に耐えられるのでは、とのこと。

あとのお二人は、iBeaconを使った事例でした。

元頓知ドットさんのおでかけスクラップサービス"tab"は、失敗事例などいくつか紹介しており、非常に参考になりました。

残念ながら、帰阪するため参加できたのはここまででした。

iBeaconでブームとなったBLEの流れですが、アンドロイドでちゃんとしたサービスが展開されるのはまだまだ先なのかなという印象を受けました。

[コラム] 日本アンドロイドの会2月定例会に参加してきました。


2014年2月の日本アンドロイドの会定例会に参加してきました。

https://www.android-group.jp/event/event28.html

iBeaconとはどんなもので、アンドロイドで実現する場合に注意すべきことは何かなどを、この方面で活躍されている方を招いて勉強するという内容でした。

まず、今度の2/15、16に会津でBLEハッカソンを企画されている、Aka Beacon で有名なGClue の佐々木陽氏が、iBeacon とは、UUID、Major、Minor でビーコンを区別し、Immediate/Near/Far/Unknown などの近さが取得できるものですよ、ということを自作したデバイスを使ってデモを行いながら、非常に軽やかに説明されました。

また、iOSのバックグラウンド処理についても触れて、アプリがバックグラウンドにいてもRegion IN/OUTのイベントを受信でき、15秒間は動作可能ということも述べられていました。結局、もっとバックグラウンド動作を伸ばす方法はあるみたいですが…。

アンドロイドは、各アプリがビーコンを監視する部分を独自でもつため、ビーコンを利用するアプリが立ち上がるほど、電力消費が大きくなるとのこと。対して、iOSは、フレームワーク部分がビーコンの監視を行ってくれているので、ビーコンを使うアプリがたくさんいても省電力になっているとのこと。だから、アンドロイドでもビーコンを扱うサービスを作り、省電力な設計を行う必要があるとおっしゃっていました。

あとは、エコシステム、Appcessoryを中心とする最近のビジネス状況について、BLEデバイスのモジュール化のベンチャーが増えていることなどを説明してくれました。

続いて、Applixで研究開発をされている日向さんがビーコンの電波特性について、非常にわかりやすく解説してくれました。屋内測位をやっていると、電波特性というものに敏感になるもので、なんでこんなに状況によって電波強度がかわるのか深く知りたくなってきます。日向さんの調べたところによると、床や壁などに当たって跳ね返ってくる反射波との干渉により、場所によって電波の強弱が変わってしまうということです。また、電波強度と距離の関係式も、2、3mくらいまでなら大体合っているが、距離が遠くなるに連れて、実際の値とのずれが大きくなってくるとのこと。屋内測位をやったときもFarのビーコンは無視すると非常にうまく行きましたから、納得です。日向さんの考えでは、RSSIからもとめる距離の精度も対数をとった値なら実用に耐えられるのでは、とのこと。

あとのお二人は、iBeaconを使った事例でした。

元頓知ドットさんのおでかけスクラップサービス"tab"は、失敗事例などいくつか紹介しており、非常に参考になりました。

残念ながら、帰阪するため参加できたのはここまででした。

iBeaconでブームとなったBLEの流れですが、アンドロイドでちゃんとしたサービスが展開されるのはまだまだ先なのかなという印象を受けました。

2014年1月28日火曜日



GDKでは、以前お伝えしたCardの仕組みを使わずにAndroidと同様にActivityを使ってアプリを構築することができます。



注意点としては以下があります。



  • 640 × 360 の解像度

  • スワイプダウンが戻るボタンに該当する

  • Androidのようなタッチ操作はできない

しかしActivityだけではアプリが timeline 上に表示されない為、ユーザーがアプリを起動する手段がありません。そこで、アプリ起動用Serviceを用意してonStartCommand()のタイミングで任意のアクティビティを起動してやる必要があります。

なんだかとても面倒に感じますが、とても簡単。

まずサービスクラスを用意します。

単純にアクティビティを起動するだけのサービスクラスに必要なコードはたったこれだけ。

public class HelloService extends Service {
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Intent aIntent = new Intent();
        aIntent.setClassName("com.example.helloactivity","com.example.helloactivity.MainActivity");
        aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
        startActivity(aIntent);
        return START_STICKY;
    }
}


あとは timeline からサービスを呼ぶようにマニフェストファイルを設定するだけ。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloactivity"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
         android:icon="@drawable/ic_launcher"
            android:name="com.example.helloactivity.MainActivity"
            android:label="@string/app_name" >
        </activity>
        <service
            android:name="com.example.helloactivity.HelloService"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>
            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />
        </service>
    </application>
</manifest>


この intent-filter と meta-data が重要で、これを記述することによってサービスが timeline 上に表示されるようになります。

            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>
            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />


これだけで、Androidと同様のActivityを使ったアプリをGlassで実現することができます。

しかし、AndroidアプリをGlassに簡単に移植できる、とは考えないで下さい。

現時点で Activity は Glass にはコストが高く、非常に動作が重くなり、本体の発熱も凄いです。

現時点では、Glass に Activity を使うのは良い選択肢ではないと考えます。

それを示すように、Activity を使って描画を行っているサンプルは Google から提供されていません。

そもそもソースコードも公開されてませんし、Glass は Android と似て非なるもの なのです。




GDKでActivityを使ってみる



GDKでは、以前お伝えしたCardの仕組みを使わずにAndroidと同様にActivityを使ってアプリを構築することができます。



注意点としては以下があります。



  • 640 × 360 の解像度

  • スワイプダウンが戻るボタンに該当する

  • Androidのようなタッチ操作はできない

しかしActivityだけではアプリが timeline 上に表示されない為、ユーザーがアプリを起動する手段がありません。そこで、アプリ起動用Serviceを用意してonStartCommand()のタイミングで任意のアクティビティを起動してやる必要があります。

なんだかとても面倒に感じますが、とても簡単。

まずサービスクラスを用意します。

単純にアクティビティを起動するだけのサービスクラスに必要なコードはたったこれだけ。

public class HelloService extends Service {
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Intent aIntent = new Intent();
        aIntent.setClassName("com.example.helloactivity","com.example.helloactivity.MainActivity");
        aIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
        startActivity(aIntent);
        return START_STICKY;
    }
}


あとは timeline からサービスを呼ぶようにマニフェストファイルを設定するだけ。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloactivity"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
         android:icon="@drawable/ic_launcher"
            android:name="com.example.helloactivity.MainActivity"
            android:label="@string/app_name" >
        </activity>
        <service
            android:name="com.example.helloactivity.HelloService"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>
            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />
        </service>
    </application>
</manifest>


この intent-filter と meta-data が重要で、これを記述することによってサービスが timeline 上に表示されるようになります。

            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>
            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />


これだけで、Androidと同様のActivityを使ったアプリをGlassで実現することができます。

しかし、AndroidアプリをGlassに簡単に移植できる、とは考えないで下さい。

現時点で Activity は Glass にはコストが高く、非常に動作が重くなり、本体の発熱も凄いです。

現時点では、Glass に Activity を使うのは良い選択肢ではないと考えます。

それを示すように、Activity を使って描画を行っているサンプルは Google から提供されていません。

そもそもソースコードも公開されてませんし、Glass は Android と似て非なるもの なのです。









本ブログ「屋内測位をやってみました」で、始めた屋内測位ですが、あれからいろいろやってみて、だいぶまともに動くようになってきました。今回はある会議室にビーコンをだいたい3m四方に12個配置しました。使用したビーコンは、Estimote社のものを使っています。このビーコン出力が弱いため、精度を出すには3mという短い間隔で置くしかありませんでした。使用した測位アルゴリズムも、トリラテレーション(Trilateration)ではなく、近辺のビーコン位置にRSSI値をもとにした加重平均をとる方法を採用しました。

では、実際の実験の様子をご覧ください。

iBeaconで屋内測位成功のヒントをつかんだ!






本ブログ「屋内測位をやってみました」で、始めた屋内測位ですが、あれからいろいろやってみて、だいぶまともに動くようになってきました。今回はある会議室にビーコンをだいたい3m四方に12個配置しました。使用したビーコンは、Estimote社のものを使っています。このビーコン出力が弱いため、精度を出すには3mという短い間隔で置くしかありませんでした。使用した測位アルゴリズムも、トリラテレーション(Trilateration)ではなく、近辺のビーコン位置にRSSI値をもとにした加重平均をとる方法を採用しました。

では、実際の実験の様子をご覧ください。

2014年1月15日水曜日



Google からGlass Development Kit(以後GDK)が発表されてしばらく経ちました。

合わせて開発者用Glassの販売も行われ、今後市場投入に向けての動きが活発になることが予想されます。

しかしこの開発者用Glass、2014年1月時点で日本からは購入することができません。

USAでも開発者向けの招待メールでしか購入ができないようになっています。

弊社は現地支社がある為、いくつか購入することができました。

Glassを使った開発ノウハウが徐々に溜まってきたので、GDKでHelloWorldを書く方法をお伝えします。



さて、まず一言。

HelloWorld を書くのにこんなに苦労したのは久しぶりでした

完成されたSDKであれば普通、HelloWorldというのはプロジェクトを新規作成すると自動的に生成されるスケルトンプロジェクトに、ちょちょいと追記するだけで簡単に作れるものなのですが、現在のGDK(Rev.2)にはスケルトンコードを出力する機能がありません。

従って今回は、GDKの中に含まれるサンプルコードをリファクタリングしながらHelloWorldを作ります。

Google Glassの開発環境


Glassの開発環境は基本的にAndroidと同じで、IDEはEclipseを使うのが最も簡単でしょう。

Android SDK Managerから Android 4.0.3 > GDK を選択して開発環境をインストールします。

f:id:bs-android:20140110095919p:image:w640

HelloWorldを作ってみる


それではGDKに含まれるサンプルコードをリファクタリングしながらHelloWorldを作ってみます。

新規プロジェクト > Android Sample Project > GDK > Timer を選択します。最後の画面でプロジェクト名をHelloWorldに変えておきます。

f:id:bs-android:20140110095920p:image:w360

f:id:bs-android:20140110095921p:image:w360

f:id:bs-android:20140110095922p:image:w360

このサンプルはその名の通りタイマーアプリで、設定した時間をカウントダウンすることができます。

まず、アプリがどのような動作をするか確認します。

遷移図を作りました。

f:id:bs-android:20140110095924j:image:w640

Glassはタップによる文字入力ができませんので入力項目は全て選択式になります。従って、シンプルなアプリですが画面数はとても多くなる傾向にあります。

Timerサンプルを解析してみる


次はTimerサンプルのコードを見て行きます。

まずはManifestから。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.android.glass.sample.timer"
    android:versionCode="2"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_timer"
        android:label="@string/app_name" >

        <activity
            android:name="com.google.android.glass.sample.timer.MenuActivity"
            android:label="@string/app_name"
            android:theme="@style/MenuTheme"
            android:enabled="true" >
        </activity>

        <activity
            android:name="com.google.android.glass.sample.timer.SetTimerActivity"
            android:label="@string/app_name"
            android:enabled="true" >
        </activity>

        <activity
            android:name="com.google.android.glass.sample.timer.SelectValueActivity"
            android:label="@string/app_name"
            android:enabled="true" >
        </activity>

        <service
            android:name="com.google.android.glass.sample.timer.TimerService"
            android:icon="@drawable/ic_timer"
            android:label="@string/app_name"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>
            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />
        </service>

    </application>

</manifest>


Activity が三つにServiceが一つ宣言されています。

こんなに小さなアプリでActivityが三つもあるのは何故か?

Glassでは基本的にActivityはコンテンツを描画する目的では無くメニューを表示する目的で使われます。従って、メニュー(設定項目や選択項目)が増えるとActivityがどんどん増えていく傾向があります。

Service内のmeta-dataは音声認識によるアプリ起動をサポートします。

次はソースコードを見て行きます。



  • MenuActivity.java

  • SelectValueActivity.java

  • SelectValueScrollAdapter.java

  • SetTimerActivity.java

  • SetTimerScrollAdapter.java

  • Timer.java

  • TimerDrawer.java

  • TimerService.java

  • TimerView.java

この中で最低限必要なクラスは以下です。



  • MenuActivity.java

  • TimerDrawer.java

  • TimerService.java

一つのGlassアプリには必ず一つのサービスが必要です。

サービスである為、アプリを終了する為のメニューが必要となるでしょう。必然的にメニューを表示するActivityも一つは必要となります。

さっき、Activityはメニューを表示する為に使うと説明しました。では、肝心のコンテンツはどのように描画するか?

GlassではCardというAndroidには無い仕組みが有ります。

Cardとは


Androidでは一つのActivityで一画面を表現しますが、Glassでは一つのCardで一つの画面を表現することができます。

CardにはStatic CardとLive Cardの二種類があり、画面の更新頻度に応じて使い分けるとDocには書かれています。

TimerサンプルはLive Cardを利用している為、今回はLive Cardを使うことにします。

Live Cardは以下のような仕組みになっています。

http://developers.google.com/glass/images/diagrams/live-card-service.png

Glassではアプリ一覧が並ぶUIの事をTimelineと呼びます。

Timeline からアプリのサービスが起動し、サービスでLive Cardを生成しコンテンツの描画を行います。

HelloWorldを作ってみた


Timerサンプルを元に作成したHelloWorldのプロジェクト一式はこちら。




重要なのはTimerServiceのonStartCommand()で、それ以外はAndroidと全く同じです。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (mLiveCard == null) {
        //LiveCardを生成したい
        mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_TAG);

        //レンダリング用Drawerを追加したい
        mLiveCard.setDirectRenderingEnabled(true).getSurfaceHolder().addCallback(mTimerDrawer);
        
        //Menu用Activityの生成と追加をしたい
        Intent menuIntent = new Intent(this, MenuActivity.class);
        menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));

        //LiveCardを表示したい
        mLiveCard.publish(PublishMode.REVEAL);
    } else {
        // TODO(alainv): Jump to the LiveCard when API is available.
    }

    return START_STICKY;
}


尚、Cardを使わずAndroidと同様にActivityのみでアプリを構築する方法もあります。

しかしこの場合、そのままではTimelineからアプリを起動することができない為、アプリを起動する為だけのGlassアプリを用意する必要があります。これに関してはまた別の機会に書きます。

Glassの開発難易度


残念ながら現時点ではGlass用のシミュレータはありません。実機が無いとアプリのテストができない為、早急なシミュレータ環境の提供を期待するばかりです。

コードを書く事だけを見れば、Cardの概念さえ理解すれば後はAndroidとほとんど同じである為、Androidアプリ開発経験者はスムーズに開発を行うことができるでしょう。

しかし、実際にGlassを身につけるとよくわかるのですが、この最低限のアプリケーションでも、起動すると本体が非常に熱くなり、継続した装着が困難になります。

Glassで実用的なアプリケーションを構築する為には高度な省エネのスキルが要求されることでしょう。

しかし、Glassはソースコードが公開されていません。その為、基本的に Java 層で省エネを実現する必要があります。

実験的なアプリケーションはAndroidの延長線上で作れるものの、現時点のGlassの性能で実用的なアプリケーションをFixする為には非常に高度な開発スキルが求められると考えます。

Java で省エネって、アメ車に低燃費を求めるようなもんですよね




GDKでHelloWorldを作ってみる



Google からGlass Development Kit(以後GDK)が発表されてしばらく経ちました。

合わせて開発者用Glassの販売も行われ、今後市場投入に向けての動きが活発になることが予想されます。

しかしこの開発者用Glass、2014年1月時点で日本からは購入することができません。

USAでも開発者向けの招待メールでしか購入ができないようになっています。

弊社は現地支社がある為、いくつか購入することができました。

Glassを使った開発ノウハウが徐々に溜まってきたので、GDKでHelloWorldを書く方法をお伝えします。



さて、まず一言。

HelloWorld を書くのにこんなに苦労したのは久しぶりでした

完成されたSDKであれば普通、HelloWorldというのはプロジェクトを新規作成すると自動的に生成されるスケルトンプロジェクトに、ちょちょいと追記するだけで簡単に作れるものなのですが、現在のGDK(Rev.2)にはスケルトンコードを出力する機能がありません。

従って今回は、GDKの中に含まれるサンプルコードをリファクタリングしながらHelloWorldを作ります。

Google Glassの開発環境


Glassの開発環境は基本的にAndroidと同じで、IDEはEclipseを使うのが最も簡単でしょう。

Android SDK Managerから Android 4.0.3 > GDK を選択して開発環境をインストールします。

f:id:bs-android:20140110095919p:image:w640

HelloWorldを作ってみる


それではGDKに含まれるサンプルコードをリファクタリングしながらHelloWorldを作ってみます。

新規プロジェクト > Android Sample Project > GDK > Timer を選択します。最後の画面でプロジェクト名をHelloWorldに変えておきます。

f:id:bs-android:20140110095920p:image:w360

f:id:bs-android:20140110095921p:image:w360

f:id:bs-android:20140110095922p:image:w360

このサンプルはその名の通りタイマーアプリで、設定した時間をカウントダウンすることができます。

まず、アプリがどのような動作をするか確認します。

遷移図を作りました。

f:id:bs-android:20140110095924j:image:w640

Glassはタップによる文字入力ができませんので入力項目は全て選択式になります。従って、シンプルなアプリですが画面数はとても多くなる傾向にあります。

Timerサンプルを解析してみる


次はTimerサンプルのコードを見て行きます。

まずはManifestから。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.android.glass.sample.timer"
    android:versionCode="2"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_timer"
        android:label="@string/app_name" >

        <activity
            android:name="com.google.android.glass.sample.timer.MenuActivity"
            android:label="@string/app_name"
            android:theme="@style/MenuTheme"
            android:enabled="true" >
        </activity>

        <activity
            android:name="com.google.android.glass.sample.timer.SetTimerActivity"
            android:label="@string/app_name"
            android:enabled="true" >
        </activity>

        <activity
            android:name="com.google.android.glass.sample.timer.SelectValueActivity"
            android:label="@string/app_name"
            android:enabled="true" >
        </activity>

        <service
            android:name="com.google.android.glass.sample.timer.TimerService"
            android:icon="@drawable/ic_timer"
            android:label="@string/app_name"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>
            <meta-data
                android:name="com.google.android.glass.VoiceTrigger"
                android:resource="@xml/voice_trigger_start" />
        </service>

    </application>

</manifest>


Activity が三つにServiceが一つ宣言されています。

こんなに小さなアプリでActivityが三つもあるのは何故か?

Glassでは基本的にActivityはコンテンツを描画する目的では無くメニューを表示する目的で使われます。従って、メニュー(設定項目や選択項目)が増えるとActivityがどんどん増えていく傾向があります。

Service内のmeta-dataは音声認識によるアプリ起動をサポートします。

次はソースコードを見て行きます。



  • MenuActivity.java

  • SelectValueActivity.java

  • SelectValueScrollAdapter.java

  • SetTimerActivity.java

  • SetTimerScrollAdapter.java

  • Timer.java

  • TimerDrawer.java

  • TimerService.java

  • TimerView.java

この中で最低限必要なクラスは以下です。



  • MenuActivity.java

  • TimerDrawer.java

  • TimerService.java

一つのGlassアプリには必ず一つのサービスが必要です。

サービスである為、アプリを終了する為のメニューが必要となるでしょう。必然的にメニューを表示するActivityも一つは必要となります。

さっき、Activityはメニューを表示する為に使うと説明しました。では、肝心のコンテンツはどのように描画するか?

GlassではCardというAndroidには無い仕組みが有ります。

Cardとは


Androidでは一つのActivityで一画面を表現しますが、Glassでは一つのCardで一つの画面を表現することができます。

CardにはStatic CardとLive Cardの二種類があり、画面の更新頻度に応じて使い分けるとDocには書かれています。

TimerサンプルはLive Cardを利用している為、今回はLive Cardを使うことにします。

Live Cardは以下のような仕組みになっています。

http://developers.google.com/glass/images/diagrams/live-card-service.png

Glassではアプリ一覧が並ぶUIの事をTimelineと呼びます。

Timeline からアプリのサービスが起動し、サービスでLive Cardを生成しコンテンツの描画を行います。

HelloWorldを作ってみた


Timerサンプルを元に作成したHelloWorldのプロジェクト一式はこちら。




重要なのはTimerServiceのonStartCommand()で、それ以外はAndroidと全く同じです。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (mLiveCard == null) {
        //LiveCardを生成したい
        mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_TAG);

        //レンダリング用Drawerを追加したい
        mLiveCard.setDirectRenderingEnabled(true).getSurfaceHolder().addCallback(mTimerDrawer);
        
        //Menu用Activityの生成と追加をしたい
        Intent menuIntent = new Intent(this, MenuActivity.class);
        menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0));

        //LiveCardを表示したい
        mLiveCard.publish(PublishMode.REVEAL);
    } else {
        // TODO(alainv): Jump to the LiveCard when API is available.
    }

    return START_STICKY;
}


尚、Cardを使わずAndroidと同様にActivityのみでアプリを構築する方法もあります。

しかしこの場合、そのままではTimelineからアプリを起動することができない為、アプリを起動する為だけのGlassアプリを用意する必要があります。これに関してはまた別の機会に書きます。

Glassの開発難易度


残念ながら現時点ではGlass用のシミュレータはありません。実機が無いとアプリのテストができない為、早急なシミュレータ環境の提供を期待するばかりです。

コードを書く事だけを見れば、Cardの概念さえ理解すれば後はAndroidとほとんど同じである為、Androidアプリ開発経験者はスムーズに開発を行うことができるでしょう。

しかし、実際にGlassを身につけるとよくわかるのですが、この最低限のアプリケーションでも、起動すると本体が非常に熱くなり、継続した装着が困難になります。

Glassで実用的なアプリケーションを構築する為には高度な省エネのスキルが要求されることでしょう。

しかし、Glassはソースコードが公開されていません。その為、基本的に Java 層で省エネを実現する必要があります。

実験的なアプリケーションはAndroidの延長線上で作れるものの、現時点のGlassの性能で実用的なアプリケーションをFixする為には非常に高度な開発スキルが求められると考えます。

Java で省エネって、アメ車に低燃費を求めるようなもんですよね




Related Posts Plugin for WordPress, Blogger...