ottoとActivityRecognition

2014-01-27T00:00:00+00:00 Android Java

ottoな勉強をするにあたってそういう要件が必要にならないと例にならない気がしたので、ActivityRecognitionを使って行動認識をActivityへottoを使って通知する仕組み的な事やってみた

記事下部に参考を書くと分かりづらいので以下を参考にした

参考1: Google I/O 2013 で発表された行動認識(Activity Recognition)を使ってみる (※この例だとBroadcastReceiverで受け取ってる)

参考2: Recognizing the User's Current Activity

ottoに関しては公式なドキュメント辺りを参照

プロジェクトの準備

ActivityRecognitionはGoogle Play Serviceなライブラリプロジェクトに含まれているのでAndroid Package Managerを使ってEclipseにプロジェクトをインポートする。で端末が古いのでGoogle Play Serviceのfroyoサポート辺りを使った。何やら最近のGoogle Play Serviceでは

<application>
    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
</application>

的な所が必要らしい。どうやらバージョンチェックをしているんだろうとは思うのだけど。それはおいといて

あとはottoのライブラリはダウンロードしてくるかビルドするかのどっちかをやってアプリプロジェクトのlibsに入れておけば良い

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="sample.test"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" />
    <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".RecognitionIntentService" android:exported="false" />

    </application>

</manifest>

RecognitionIntentService.java

package sample.test;

import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

public class RecognitionIntentService extends IntentService {

    private static final String TAG = RecognitionIntentService.class.getName();

    public RecognitionIntentService() {
        super("RecognitionIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.v(TAG, "onHandleIntent");

        if (!ActivityRecognitionResult.hasResult(intent))
            return;

        ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
        DetectedActivity mostProbableActiity = result.getMostProbableActivity();

        // Activityで@Subscribeしているイベントバスにプッシュ
        BusProvider.getInstance().post(
            new RecognitionEvent(getTypeName(mostProbableActiity.getType()))
        );
    }

    private String getTypeName(int type) {
        switch (type) {
            case DetectedActivity.IN_VEHICLE:
                return "in_vehicle";
            case DetectedActivity.ON_BICYCLE:
                return "on_bicycle";
            case DetectedActivity.ON_FOOT:
                return "on_foot";
            case DetectedActivity.STILL:
                return "still";
            case DetectedActivity.UNKNOWN:
                return "unknown";
            case DetectedActivity.TILTING:
                return "tilting";
        }

        return "unknown";
    }
}

DetectedActivity#getTypeの返り値は公式リファレンスのSummaryセクションを参考に

BusProvider.java

package sample.test;

import com.squareup.otto.Bus;
import com.squareup.otto.ThreadEnforcer;

public class BusProvider {

    // ThreadEnforcer.ANYにしないとIntentServiceとかみたいなメインスレッドkらでは無い場合だとエラーになる
    private static Bus bus = new Bus(ThreadEnforcer.ANY);

    private BusProvider() {
    }

    public static Bus getInstance() {
        return bus;
    }
}

MainActivity.java

package sample.test;


import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.ActivityRecognitionClient;

import com.squareup.otto.Subscribe;


public class MainActivity extends Activity
    implements ConnectionCallbacks, OnConnectionFailedListener {

    private static final String TAG = MainActivity.class.getName();

    ActivityRecognitionClient mActivityRecognitionClient;
    PendingIntent mPendingIntent;

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        BusProvider.getInstance().register(this);

        mActivityRecognitionClient = new ActivityRecognitionClient(
            this,
            this, // ConnectionCallbacks
            this // OnConnectionFailedListener
        );
        mActivityRecognitionClient.connect();
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.v(TAG, "onPause");
        onDisconnected();
    }

    @Override
    public void onConnected(Bundle connectionHint) {
        Log.v(TAG, "onConnected");
        mPendingIntent = PendingIntent.getService(
            this,
            0,
            new Intent(this, RecognitionIntentService.class),
            PendingIntent.FLAG_UPDATE_CURRENT
        );

        mActivityRecognitionClient.requestActivityUpdates(3000, mPendingIntent);
    }

    @Override
    public void onDisconnected() {
        Log.v(TAG, "onDisconnected");

        if (mActivityRecognitionClient != null) {
            mActivityRecognitionClient.removeActivityUpdates(mPendingIntent);
            mActivityRecognitionClient.disconnect();
            mActivityRecognitionClient = null;
        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        Log.v(TAG, "onConnectionFailed");
    }

    @Subscribe
    public void onRecognitionEvent(RecognitionEvent event) {
        Log.v(TAG, "onRecognitionEvent");
        Log.v(TAG, "eventName: " + event.getEventName());
    }
}

ざっくり書いてるけど、開始したらまずBusをregister。それをする事でpostされたイベントを@Subscribeアノテーションなメソッドを受け取れる的な感じかと。ようはAcitvityRecognitionでアップデート通知されたらRecognitionIntentService#onHandleIntentが発生して、その結果がBusを通じてActivityにイベントがpushされる

んまぁんな感じでottoとActivityRecognitionの適当にやってみた

Android4.4(Kitkat)のデフォルトSMSアプリに関して android-maven-plugin