はじめに

今回は、TYPE_STEP_COUNTERTYPE_STEP_DETECTER という、Android 4.4 から使用可能になった歩数計用の Sensor を使用してみたいと思います。 従来も各種センサの情報を組み合わせて、独自に歩数計を作成している例はありましたが、今回からAPIとして提供されたことで、とても簡単に使えるようになりました。(2014/02/現在、Nexus5 のみ) 現在、このセンサーに対応している端末は少ないですが、後々対応端末が増えていくようです。なので今回、使い方の説明や使い道の提案、精度などの実験結果を行い、今後の対応の手がかりになればと思います。

概要

まずは、今回追加された2つの API について順に説明していきたいと思います。
  • TYPE_STEP_COUNTER 端末が起動してからの累計歩数を取得することができます。 プログラム側で定期的のこの差を見ることで、任意の気管の歩数変化を得ることができます。
  • TYPE_STEP_DETECTOR ステップを感知したタイミングで通知が行われます。 同時にタイムスタンプを取得する事もできるため、どの時間帯にどのくらい歩いているかの集計を行うことも可能です。
このように、センサから取得することの出来る値には、それぞれ特徴がありますので状況に応じて使い分けると良いと思います。

実装

それでは早速実装してみたいと思います。 とはいえ、センサーから値を取得し、それを表示するだけならばそこまで難しいことはありません。

初期化

まずは、初期化を行います。 そのコードは以下のようになります。
private SensorManager mSensorManager;
private Sensor mStepDetectorSensor;
private Sensor mStepConterSensor;

@Override
protected void onCreate(Bundle savedInstanceState) {

    //省略…

    //センサーマネージャを取得  
    mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

    //センサマネージャから TYPE_STEP_DETECTOR についての情報を取得する
    mStepDetectorSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

    //センサマネージャから TYPE_STEP_COUNTER についての情報を取得する
    mStepConterSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
}
ここでは、各 Sensor の情報を Sensor Manager から取得しています。

コールバックとリスナ

アプリケーションで Sensor から値を受け取るためには、リスナを設定する必要があります。 Sensor Event Listener というインターフェースが有り、これには Sensor Event が発生するタイミングで呼ばれるコールバックメソッドが存在します。そのため、このインターフェースを実装します。そのコールバックメソッドを実装すると Sensor Event を取得することが可能になります。
public class MainActivity extends Activity implements SensorEventListener{

//省略…

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // accuracy に変更があった時の処理
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        Sensor sensor = event.sensor;
        float[] values = event.values;
        long timestamp = event.timestamp;
        //TYPE_STEP_COUNTER
        if(sensor.getType() == Sensor.TYPE_STEP_COUNTER){
            // sensor からの値を取得するなどの処理を行う
            Log.d("type_step_counter",String.valueOf(values[0]));
        }
    }
}
抽象メソッドは上記の2つです。これらの説明は以下になります。
メソッド 説明
onAccuracyChanged 使用しているセンサーの精度に変化が生じた時に呼び出される。
onSensorChanged 使用しているセンサーの状態に変化が生じた時に呼び出される。
onSensorChanged に Sensor Event クラスが渡されます。この Sensor Event から取得できる値は以下のようになります。
フィールド 説明
accuracy int センサーの精度を4種類の値を持つ enum 値
sensor Sensor センサー
timestamp long イベント発生時のタイムスタンプ
values float[] センサーから取得した情報
この内 values は、float型の配列ですが、その長さや内容は取得している Sensor によって異なりますので公式のリファレンスを見て確認しましょう。 今回使用する2つの Sensor に関しての valuse は以下のようにとてもシンプルなデータになっています。
Sensor Type 配列の長さ valuesの内容
TYPE_STEP_COUNTER 1 端末が起動してからの累計歩数
TYPE_STEP_DETECTOR 1 1.0 固定
また、accuracy は以下の4種類になります。
Accuracy Type 説明
SENSOR_STATUS_UNRELIABLE 信頼出来ない値 キャリブレーションが必要もしくは、取得できる環境にない
SENSOR_STATUS_ACCURACY_LOW 低い精度 キャリブレーションが必要
SENSOR_STATUS_ACCURACY_MEDIUM 平均的な精度 キャリブレーションにより精度の向上が期待できる
SENSOR_STATUS_ACCURACY_HIGH 最大の精度

リスナの設定と解除

先ほど実装したコールバックメソッドが呼び出されるようにするために、Sensor Manager にリスナを登録します。 また、アプリを終了する前にリスナを解除する方法も同時に示します。 以下がそのコードになります。
@Override
protected void onResume() {
    super.onResume();
    mSensorManager.registerListener (this, 
                                    mStepConterSensor,
                                    SensorManager.SENSOR_DELAY_NORMAL);

    mSensorManager.registerListener(this, 
                                    mStepDetectorSensor, 
                                    SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onPause() {
    super.onPause();
    mSensorManager.unregisterListener(this,mStepConterSensor);
    mSensorManager.unregisterListener(this,mStepDetectorSensor);
}
onResume で Sensor Manager にリスナを登録する処理を、行なっています。 ここで使用している Sensor Manager の registerListenerに渡している引数は以下のようになります。
引数 説明
listener SensorListener Sensor Manager に登録するリスナ。
sensors int 値を取得する Sensor の種類。
rate int イベントの発生率についてのデータ。あくまでシステムに目安として渡す値であり、イベントの発生率がこの値になるという保証はありません。
また、この時に rate に渡すことの出来る値は以下のようになります。
定数名 内容
SENSOR_DELAY_NORMAL デフォルトの発生率。スクリーンの回転の変更に最適
SENSOR_DELAY_UI User Interface に最適な発生率
SENSOR_DELAY_GAME Game に使用するのに最適な発生率
SENSOR_DELAY_FASTEST 可能な限り早くデータを取得する
この値も状況によって使い分けると良いでしょう。 onPause で行っているのがリスナを解除する処理です。 ここで使用している Sensor manager の unregisterListener の引数には 解除するリスナと解除するセンサーの情報を渡しています。

制度確認実験結果

この新しいセンサーの精度が、どの程度なのか気になるのではないでしょうか。
そこで、歩数計機能を有するデバイスを複数使用し、比較をするという実験を行いました。
更に、この様子を動画に取り、実際の歩数を目視でカウントすることで、その差分がわかるようにしました。 今回使用したのは、Android 4.0.3 の AQUS PHONE 104 SH に初期からプリインストールされていた歩数計、iOS 付属の歩数計、Nike+ FuelBand SE そして Android 4.4 を搭載した Nexus 5 の4つです。
今回はこれらを、ズボンの前ポケットに2つ、後ろポケットに一つ、手首に一つ身につけます。 結果は表のようになりました。
デバイス 結果
実際の歩数 574
Nike+ FuelBand SE 557
iPhone 5s 569
AQUS PHONE 104 SH 588
Nexus 5 596
結果を見てみると、Android 端末が少し多めに値を取得する傾向があるように思えます。これは、振動を検知するセンサを使用しているために、歩いた時の振動で余計に値を取得しているのではないでしょうか。 しかし、全体的に見ると今回実験に使用した歩数計の精度は比較的良いと言えるのではないかと思います。というのも、実際の歩数から一番外れている Android 4.4 でもずれは 5% 以内になっているためです。これは誤差の範囲内と言ってもいいのではないでしょうか。 この実験により観測された値は、その時の環境や方法によって大きく左右されます。例えば、カバン等に入れた場合では、精度が悪くなるのが予想できますし、上着のポケットに入れた時でも全く違った結果になると思います。そのため、この実験により得られた数値はあくまでも目安として捉えてください。

使い道の検討

さてここまでは使い方の説明を行ってきました。しかし、このセンサをどのように使えば面白いアプリを作成することができるでしょうか。 まずいちばん最初に思い浮かぶのは、やはりライフログを取るといった使い方だと思います。歩数計センサというくらいですから、いつ、どこで、どのくらい歩いたのかというのを自動で取ってくれるアプリを作るのが最もポピュラーではないでしょうか。しかし、どうにも普通過ぎるという気もします。この発想が悪いということは決してありませんが、これは皆が思いつくものであるため、アプリもユニークなものでは無くなってしまう可能性があります。 次に考えられるのは、ちょっとしたミニゲームなど、歩数を全く別な要素として使用する方法です。例えば、どれだけ歩数計の数値を増やさずに進めるのかとか企画してみると面白いかもしれません。他にも、ソーシャルゲームの一部にスタミナを回復する要素などに組み込んでみたりしても良いのではないでしょうか。

おわりに

今回の特集はどうだったでしょうか。少しでも、これから歩数を使用したアプリを作成する事を考えてる方の助けになれば幸いです。 この記事を見て、こんなに簡単ならアプリの一機能として歩数計を導入してみても良いと思っていただけると嬉しい限りです。 今回で Android 4.4 特集は最後となりますが、これからもギャップロでは様々な情報を展開していきたいと思っていますので、楽しみに待っていてください!