The following code shows how to recongize user motion.
Code revised from
Android Recipes:A Problem-Solution Approach
http://www.apress.com/9781430234135
ISBN13: 978-1-4302-3413-5
Register permission for ACTIVITY_RECOGNITION.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.androidrecipes.usermotionactivity" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <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="com.androidrecipes.usermotionactivity.MainActivity" android:label="User Activity" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.androidrecipes.usermotionactivity.UserMotionService" /> </application> </manifest>
Main layout xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:stackFromBottom="true" android:transcriptMode="normal" /> <TextView android:id="@+id/blocker" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:clickable="true" android:textSize="32dp" android:textColor="#F55" android:text="It is not safe to operate your mobile device while in a vehicle!" android:background="#C333" android:visibility="gone" /> </FrameLayout>
Java code for Main activity
package com.androidrecipes.usermotionactivity; //from ww w . ja v a2 s.com import com.androidrecipes.usermotionactivity.UserMotionService.LocalBinder; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.location.ActivityRecognitionClient; import com.google.android.gms.location.ActivityRecognitionResult; import com.google.android.gms.location.DetectedActivity; import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.text.format.DateFormat; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements ServiceConnection, UserMotionService.OnActivityChangedListener, GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener { private static final String TAG = "UserActivity"; private Intent mServiceIntent; private PendingIntent mCallbackIntent; private UserMotionService mService; private ActivityRecognitionClient mRecognitionClient; //Custom list adapter to display our results private ActivityAdapter mListAdapter; private View mBlockingView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBlockingView = findViewById(R.id.blocker); //Construct a simple list adapter that will display all the // incoming activity change events from the service. ListView list = (ListView) findViewById(R.id.list); mListAdapter = new ActivityAdapter(this); list.setAdapter(mListAdapter); //When the list is clicked, display all the probable activities list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { showDetails(mListAdapter.getItem(position)); } }); //Check if Google Play Services is up to date. switch (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this)) { case ConnectionResult.SUCCESS: //Do nothing, move on break; case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED: Toast.makeText(this, "Activity service requires an update, please open Google Play.", Toast.LENGTH_SHORT).show(); finish(); return; default: Toast.makeText(this, "Activity service is not available on this device.", Toast.LENGTH_SHORT).show(); finish(); return; } //Create a client instance for talking to Google Services mRecognitionClient = new ActivityRecognitionClient(this, this, this); //Create an Intent to bind to the service mServiceIntent = new Intent(this, UserMotionService.class); //Create a PendingIntent that Google Services will use for callbacks mCallbackIntent = PendingIntent.getService(this, 0, mServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT); } @Override protected void onResume() { super.onResume(); //Connect to Google Services and our Service mRecognitionClient.connect(); bindService(mServiceIntent, this, BIND_AUTO_CREATE); } @Override protected void onPause() { super.onPause(); //Disconnect from all services mRecognitionClient.removeActivityUpdates(mCallbackIntent); mRecognitionClient.disconnect(); disconnectService(); unbindService(this); } /** ServiceConnection Methods */ public void onServiceConnected(ComponentName name, IBinder service) { //Attach ourselves to our Service as a callback for events mService = ((LocalBinder) service).getService(); mService.setOnActivityChangedListener(this); } @Override public void onServiceDisconnected(ComponentName name) { disconnectService(); } private void disconnectService() { if (mService != null) { mService.setOnActivityChangedListener(null); } mService = null; } /** Google Services Connection Callbacks */ @Override public void onConnected(Bundle connectionHint) { //We must wait until the services are connected // to request any updates. mRecognitionClient.requestActivityUpdates(5000, mCallbackIntent); } @Override public void onDisconnected() { Log.w(TAG, "Google Services Disconnected"); } @Override public void onConnectionFailed(ConnectionResult result) { Log.w(TAG, "Google Services Connection Failure"); } /** OnActivityChangedListener Methods */ @Override public void onUserActivityChanged(int bestChoice, int bestConfidence, ActivityRecognitionResult newActivity) { //Add latest event to the list mListAdapter.add(newActivity); mListAdapter.notifyDataSetChanged(); //Determine user action based on our custom algorithm switch (bestChoice) { case DetectedActivity.IN_VEHICLE: case DetectedActivity.ON_BICYCLE: mBlockingView.setVisibility(View.VISIBLE); break; case DetectedActivity.ON_FOOT: case DetectedActivity.STILL: mBlockingView.setVisibility(View.GONE); break; default: //Ignore other states break; } } /* * Utility that builds a simple Toast with all the probable * activity choices with their confidence values */ private void showDetails(ActivityRecognitionResult activity) { StringBuilder sb = new StringBuilder(); sb.append("Details:"); for(DetectedActivity element : activity.getProbableActivities()) { sb.append("\n"+UserMotionService.getActivityName(element) + ", " + element.getConfidence() + "% sure"); } Toast.makeText(this, sb.toString(), Toast.LENGTH_SHORT).show(); } /* * ListAdapter to display each activity result we receive from the service */ private static class ActivityAdapter extends ArrayAdapter<ActivityRecognitionResult> { public ActivityAdapter(Context context) { super(context, android.R.layout.simple_list_item_1); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); } //Display the most probable activity with its confidence in the list TextView tv = (TextView) convertView; ActivityRecognitionResult result = getItem(position); DetectedActivity newActivity = result.getMostProbableActivity(); String entry = DateFormat.format("hh:mm:ss", result.getTime()) + ": " + UserMotionService.getActivityName(newActivity) + ", " + newActivity.getConfidence() + "% confidence"; tv.setText(entry); return convertView; } } }
User motion service
//from w ww . j a v a 2 s . co m package com.androidrecipes.usermotionactivity; import com.google.android.gms.location.ActivityRecognitionResult; import com.google.android.gms.location.DetectedActivity; import android.app.IntentService; import android.content.Intent; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log; public class UserMotionService extends IntentService { private static final String TAG = "UserMotionService"; /* * Callback interface for detected activity type changes */ public interface OnActivityChangedListener{ public void onUserActivityChanged(int bestChoice, int bestConfidence, ActivityRecognitionResult newActivity); } /* Last detected activity type */ private DetectedActivity mLastKnownActivity; /* * Marshals requests from the background thread so the callbacks * can be made on the main (UI) thread. */ private CallbackHandler mHandler; private static class CallbackHandler extends Handler { /* Callback for activity changes */ private OnActivityChangedListener mCallback; public void setCallback(OnActivityChangedListener callback) { mCallback = callback; } @Override public void handleMessage(Message msg) { if (mCallback != null) { //Read payload data out of the message and fire callback ActivityRecognitionResult newActivity = (ActivityRecognitionResult) msg.obj; mCallback.onUserActivityChanged(msg.arg1, msg.arg2, newActivity); } } } public UserMotionService() { //String is used to name the background thread created super("UserMotionService"); mHandler = new CallbackHandler(); } public void setOnActivityChangedListener(OnActivityChangedListener listener) { mHandler.setCallback(listener); } @Override public void onDestroy() { super.onDestroy(); Log.w(TAG, "Service is stopping..."); } /* * Incoming action events from the framework will come * in here. This is called on a background thread, so * we can do long processing here if we wish. */ @Override protected void onHandleIntent(Intent intent) { if (ActivityRecognitionResult.hasResult(intent)) { //Extract the result from the Intent ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent); DetectedActivity activity = result.getMostProbableActivity(); Log.v(TAG, "New User Activity Event"); //If the highest probability is UNKNOWN, but the confidence is low, // check if another exists and select it instead if (activity.getType() == DetectedActivity.UNKNOWN && activity.getConfidence() < 60 && result.getProbableActivities().size() > 1) { //Select the next probable element activity = result.getProbableActivities().get(1); } //On a change in activity, alert the callback if (mLastKnownActivity == null || mLastKnownActivity.getType() != activity.getType() || mLastKnownActivity.getConfidence() != activity.getConfidence()) { //Pass the results to the main thread inside a Message Message msg = Message.obtain(null, 0, //what activity.getType(), //arg1 activity.getConfidence(), //arg2 result); mHandler.sendMessage(msg); } mLastKnownActivity = activity; } } /* * This is called when the Activity wants to bind to the * service. We have to provide a wrapper around this instance * to pass it back. */ @Override public IBinder onBind(Intent intent) { return mBinder; } /* * This is a simple wrapper that we can pass to the Activity * to allow it direct access to this service. */ private LocalBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { public UserMotionService getService() { return UserMotionService.this; } } /* * Utility to get a good display name for each state */ public static String getActivityName(DetectedActivity activity) { switch(activity.getType()) { case DetectedActivity.IN_VEHICLE: return "Driving"; case DetectedActivity.ON_BICYCLE: return "Biking"; case DetectedActivity.ON_FOOT: return "Walking"; case DetectedActivity.STILL: return "Not Moving"; case DetectedActivity.TILTING: return "Tilting"; case DetectedActivity.UNKNOWN: default: return "No Clue"; } } }