I coded an application to record user's location periodically (in the background). I used ActivityRecognitionClient. When activity is received it is compared to previous activity state and is recorded (or not) according to evaluation.
It's working as intended as long as my phone is awake. (log messages appear periodically in LogCat view on eclipse) Whenever screen goes off and device goes into stand by status it stops receiving activity recognition calls. On the other hand, I installed the app on my tablet too, and it keeps updating even the device goes into stand by status. (My phone is General Mobile Discovery btw)
I've been searching web (including stackoverflow questions) for 3 days and haven't been able to find anything that works for me so far. I'd appreciate any help... Thanks...
Following is my applications related code:
AndroidManifest.xml (some permissions are there even if not needed, they are probably leftovers from unsuccessful trials to fix the issue)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.o3n.android.familywathcer"
android:installLocation="internalOnly"
android:versionCode="2"
android:versionName="1.0.1" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>
<permission android:name="org.o3n.android.familywathcer.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
<uses-permission android:name="org.o3n.android.familywathcer.permission.MAPS_RECEIVE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="***maps api key *****"/>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name="org.o3n.android.familywathcer.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:label="Settings" android:name=".SettingsActivity">
<intent-filter>
<action android:name="org.o3n.android.familywatcher.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:enabled="true" android:name=".FamilyWatcherService" />
<service android:name=".ActivityRecognitionService" />
<receiver android:name=".StartFamilyWatcherServiceAtBootReceiver"
android:enabled="true"
android:exported="true"
android:label="StartFamilyWatcherServiceAtBootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
StartFamilyWatcherServiceAtBootReceiver.java (This receiver starts the FamilyWatcherService.java on device boot, also applications MainActivity.java class calls the FamilyWatcherService so it starts running when first installed.)
public class StartFamilyWatcherServiceAtBootReceiver extends BroadcastReceiver {
private static final String TAG = "o3nWatcherLog";
@Override
public void onReceive(Context context, Intent intent) {
//Toast.makeText(context, "StartFamilyWatcherServiceAtBootReceiver called", Toast.LENGTH_SHORT).show();
Log.d(TAG, "StartFamilyWatcherServiceAtBootReceiver onRecieve");
SettingsRetriever.getInstance(context);
Intent serviceIntent = new Intent(context, FamilyWatcherService.class);
context.startService(serviceIntent);
}
}
FamilyWatcherService.java (This service connects to ActivityRecognitionClient and registers a PendingIntend to be called with activity updates. When it works ActivityRecognitionService.onHandleIntend() method is called)
public class FamilyWatcherService extends Service implements ConnectionCallbacks, OnConnectionFailedListener {
private int period;
private static ActivityRecognitionClient arclient;
private static PendingIntent pIntent;
private static AlarmManager alarmManager;
private static PendingIntent alarmPI;
private static final String TAG = "o3nWatcherLog";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.d(TAG, "FamilyWatcherService onCreate");
period = SettingsRetriever.getInstance().getPeriod() * 60 * 1000;
}
@Override
public void onDestroy() {
Log.d(TAG, "FamilyWatcherService onDestroy");
if(arclient!=null){
arclient.removeActivityUpdates(pIntent);
arclient.disconnect();
}
}
@Override
public void onStart(Intent intent, int startid) {
Log.d(TAG, "FamilyWatcherService onStart");
processStart();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "FamilyWatcherService onStartCommand");
processStart();
return Service.START_STICKY;
}
public void processStart() {
int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
if (result != ConnectionResult.SUCCESS) {
Log.d("o3nWatcherLog", "Google Play service is not available (status=" + result + ")");
}
else{
arclient = new ActivityRecognitionClient(getApplicationContext(), this, this);
arclient.connect();
}
}
@Override
public void onConnectionFailed(ConnectionResult arg0) {
Log.d("o3nWatcherLog","Google activity recognition services connection failed");
}
@Override
public void onConnected(Bundle arg0) {
Log.d("o3nWatcherLog", "FamilyWathcerService onConnected method called...");
Intent intent = new Intent(this, ActivityRecognitionService.class);
pIntent = PendingIntent.getService(getApplicationContext(), 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
arclient.requestActivityUpdates(period, pIntent);
}
@Override
public void onDisconnected() {
Log.d("o3nWatcherLog", "Google activity recognition services disconnected");
}
}
ActivityRecognitionService.java (This service's onHandleIntent() method is called by Activity Recognition updates)
package org.o3n.android.familywathcer;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
import com.google.android.gms.location.LocationClient;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
public class ActivityRecognitionService extends IntentService implements ConnectionCallbacks, OnConnectionFailedListener {
private String TAG = "o3nWatcherLog";
private Context context;
private static int activityEvaluation = 0;
//TODO MAKE THESE PREFERENCES
private static final int MIN_RECORD_DISTANCE = 750;
private static final int MIN_RECORD_INTERVAL = 10 * 1000 * 60;
private static final int MIN_POST_INTERVAL = 2 * 1000 * 60;
//END MAKE THESE PREFERENCES
private LocationClient locationClient;
private static Location lastRecordedLocation;
private static int previousActivityCode = DetectedActivity.UNKNOWN;
private int activityCode = -1000;
private int activityConfidence = -1000;
public ActivityRecognitionService() {
super("My Activity Recognition Service");
}
@Override
protected void onHandleIntent(Intent intent) {
if(ActivityRecognitionResult.hasResult(intent)){
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
Log.i(TAG, getType(result.getMostProbableActivity().getType()) +"t" + result.getMostProbableActivity().getConfidence());
this.context = getApplicationContext();
Log.d("o3nWatcherLog", "ActivityRecognitionService onHandleIntent called...");
activityConfidence = result.getMostProbableActivity().getConfidence();
activityCode = result.getMostProbableActivity().getType();
Log.d("o3nWatcherLog", " ACTIVITY CODE : " + activityCode + " ACTIVITY CONFIDENCE : " + activityConfidence);
// Evaluate the avtivity recognition result
evaluateActivityResult();
// Get current location
// check Google Play service APK is available and up to date.
final int googlePlayServiceAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (googlePlayServiceAvailable != ConnectionResult.SUCCESS) {
Log.d("o3nWatcherLog", "Google Play service is not available (status=" + result + ")");
}
else {
locationClient = new LocationClient(context, this, this);
locationClient.connect();
}
}
}
// This method is only used in a log line to have readable status in logs
private String getType(int type){
if(type == DetectedActivity.UNKNOWN)
return "UNKNOWN";
else if(type == DetectedActivity.IN_VEHICLE)
return "IN_VEHICLE";
else if(type == DetectedActivity.ON_BICYCLE)
return "ON_BICYCLE";
else if(type == DetectedActivity.ON_FOOT)
return "ON_FOOT";
else if(type == DetectedActivity.STILL)
return "STILL";
else if(type == DetectedActivity.TILTING)
return "TILTING";
else
return "";
}
private void evaluateActivityResult() {
// (Based on previousActivityCode and current activityCode
// assign a value to activityEvaluation)
// compare activityCode to previousActivityCode
activityEvaluation = ...;
previousActivityCode = activityCode;
}
private void actOnEvaluation(Location loc) {
// Based on activityEvaluation decide to post or not
if ( activityEvaluation ....)
prepareTheLocationJsonAndRecord(loc);
}
private void prepareTheLocationJsonAndRecord(Location loc) {
// Record the location
}
@Override
public void onConnectionFailed(ConnectionResult arg0) {
//Toast.makeText(context, "Google location services connection failed", Toast.LENGTH_LONG).show();
Log.d("o3nWatcherLog","Google location services connection failed");
}
@Override
public void onDisconnected() {
//Toast.makeText(context, "Google location services disconnected", Toast.LENGTH_LONG).show();
Log.d("o3nWatcherLog", "Google location services disconnected");
}
@Override
public void onConnected(Bundle arg0) {
//Toast.makeText(context, "Google location services connected", Toast.LENGTH_LONG).show();
Log.d("o3nWatcherLog", "Google location services connected");
Location loc = locationClient.getLastLocation();
Log.d("o3nWatcherLog", "location= " + loc.toString());
if (loc!=null)
actOnEvaluation(loc);
}
}