391 lines
13 KiB
Java
391 lines
13 KiB
Java
package com.jens.automation2.receivers;
|
|
|
|
import android.app.IntentService;
|
|
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.GooglePlayServicesUtil;
|
|
import com.google.android.gms.common.api.GoogleApiClient;
|
|
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
|
|
import com.google.android.gms.location.ActivityRecognition;
|
|
import com.google.android.gms.location.ActivityRecognitionApi;
|
|
import com.google.android.gms.location.ActivityRecognitionResult;
|
|
import com.google.android.gms.location.DetectedActivity;
|
|
import com.jens.automation2.ActivityPermissions;
|
|
import com.jens.automation2.AutomationService;
|
|
import com.jens.automation2.Miscellaneous;
|
|
import com.jens.automation2.R;
|
|
import com.jens.automation2.Rule;
|
|
import com.jens.automation2.Settings;
|
|
import com.jens.automation2.Trigger.Trigger_Enum;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
|
|
// See also: http://developer.android.com/reference/com/google/android/gms/location/ActivityRecognitionClient.html
|
|
// https://www.sitepoint.com/google-play-services-location-activity-recognition/
|
|
|
|
public class ActivityDetectionReceiver extends IntentService implements AutomationListenerInterface, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener
|
|
{
|
|
protected static ActivityRecognitionApi activityRecognitionClient = null;
|
|
protected static boolean connected = false;
|
|
protected static enum LastRequestEnum { start, stop, restart };
|
|
protected static LastRequestEnum lastRequest = null;
|
|
protected static GoogleApiClient googleApiClient = null;
|
|
protected static ActivityRecognitionResult activityDetectionLastResult = null;
|
|
protected static long lastUpdate = 0;
|
|
protected static Date currentTime;
|
|
protected static ActivityDetectionReceiver instance = null;
|
|
|
|
protected static ActivityDetectionReceiver getInstance()
|
|
{
|
|
if(instance == null)
|
|
instance = new ActivityDetectionReceiver();
|
|
|
|
return instance;
|
|
}
|
|
|
|
protected static boolean activityDetectionReceiverRunning = false;
|
|
protected static ActivityDetectionReceiver activityDetectionReceiverInstance = null;
|
|
|
|
public static boolean isActivityDetectionReceiverRunning()
|
|
{
|
|
return activityDetectionReceiverRunning;
|
|
}
|
|
|
|
public static ActivityRecognitionResult getActivityDetectionLastResult()
|
|
{
|
|
return activityDetectionLastResult;
|
|
}
|
|
|
|
public static GoogleApiClient getApiClient()
|
|
{
|
|
if(googleApiClient == null)
|
|
{
|
|
googleApiClient = new GoogleApiClient.Builder(AutomationService.getInstance())
|
|
.addConnectionCallbacks(getInstance())
|
|
.addOnConnectionFailedListener(getInstance())
|
|
.addApi(ActivityRecognition.API)
|
|
.build();
|
|
}
|
|
|
|
return googleApiClient;
|
|
}
|
|
|
|
private static void requestUpdates()
|
|
{
|
|
long frequency = Settings.activityDetectionFrequency * 1000;
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Requesting ActivityDetection updates with frequency " + String.valueOf(frequency) + " milliseconds.", 4);
|
|
|
|
|
|
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(getApiClient(), 1000, getInstance().getActivityDetectionPendingIntent());
|
|
}
|
|
private void reloadUpdates()
|
|
{
|
|
long frequency = Settings.activityDetectionFrequency * 1000;
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Re-requesting ActivityDetection updates with frequency " + String.valueOf(frequency) + " milliseconds.", 4);
|
|
|
|
activityRecognitionClient.removeActivityUpdates(getApiClient(), getInstance().getActivityDetectionPendingIntent());
|
|
try
|
|
{
|
|
Thread.sleep(1000);
|
|
}
|
|
catch (InterruptedException e)
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Error reloading updates for ActivityDetectionReceiver: " + Log.getStackTraceString(e), 5);
|
|
}
|
|
|
|
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(getApiClient(), frequency, getInstance().getActivityDetectionPendingIntent());
|
|
}
|
|
|
|
private static void stopUpdates()
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Unsubscribing from ActivityDetection-updates.", 4);
|
|
|
|
ActivityRecognition.ActivityRecognitionApi.removeActivityUpdates(getApiClient(), getInstance().getActivityDetectionPendingIntent());
|
|
// activityRecognitionClient.removeActivityUpdates(getApiClient(), getInstance().getActivityDetectionPendingIntent());
|
|
// activityRecognitionClient.disconnect();
|
|
}
|
|
public static void startActivityDetectionReceiver()
|
|
{
|
|
try
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Starting ActivityDetectionReceiver", 3);
|
|
|
|
if(activityDetectionReceiverInstance == null)
|
|
activityDetectionReceiverInstance = new ActivityDetectionReceiver();
|
|
|
|
if(!activityDetectionReceiverRunning && Rule.isAnyRuleUsing(Trigger_Enum.activityDetection))
|
|
{
|
|
if(isPlayServiceAvailable())
|
|
{
|
|
/*if(activityRecognitionClient == null)
|
|
activityRecognitionClient = new ActivityRecognitionClient(Miscellaneous.getAnyContext(), activityDetectionReceiverInstance, activityDetectionReceiverInstance);*/
|
|
|
|
lastRequest = LastRequestEnum.start;
|
|
|
|
if(!connected)
|
|
getApiClient().connect();
|
|
else
|
|
requestUpdates();
|
|
|
|
activityDetectionReceiverRunning = true;
|
|
}
|
|
}
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
Miscellaneous.logEvent("e", "ActivityDetectionReceiver", "Error starting ActivityDetectionReceiver: " + Log.getStackTraceString(ex), 3);
|
|
}
|
|
}
|
|
|
|
public static void restartActivityDetectionReceiver()
|
|
{
|
|
try
|
|
{
|
|
if(!activityDetectionReceiverRunning && Rule.isAnyRuleUsing(Trigger_Enum.activityDetection))
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Restarting ActivityDetectionReceiver", 3);
|
|
|
|
if(activityDetectionReceiverInstance == null)
|
|
activityDetectionReceiverInstance = new ActivityDetectionReceiver();
|
|
|
|
if(isPlayServiceAvailable())
|
|
{
|
|
// if(activityRecognitionClient == null)
|
|
// activityRecognitionClient = new ActivityRecognitionClient(Miscellaneous.getAnyContext(), activityDetectionReceiverInstance, activityDetectionReceiverInstance);
|
|
|
|
lastRequest = LastRequestEnum.restart;
|
|
|
|
if(!connected)
|
|
getApiClient().connect();
|
|
else
|
|
requestUpdates();
|
|
}
|
|
}
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
Miscellaneous.logEvent("e", "ActivityDetectionReceiver", "Error starting ActivityDetectionReceiver: " + Log.getStackTraceString(ex), 3);
|
|
}
|
|
|
|
}
|
|
|
|
public static void stopActivityDetectionReceiver()
|
|
{
|
|
try
|
|
{
|
|
if(activityDetectionReceiverRunning)
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Stopping ActivityDetectionReceiver", 3);
|
|
|
|
if(isPlayServiceAvailable())
|
|
{
|
|
lastRequest = LastRequestEnum.stop;
|
|
|
|
if(!connected)
|
|
getApiClient().connect();
|
|
else
|
|
stopUpdates();
|
|
|
|
activityDetectionReceiverRunning = false;
|
|
}
|
|
}
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
Miscellaneous.logEvent("e", "ActivityDetectionReceiver", "Error stopping ActivityDetectionReceiver: " + Log.getStackTraceString(ex), 3);
|
|
}
|
|
|
|
|
|
@Override
|
|
public void onConnectionFailed(ConnectionResult arg0)
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Connection to Play Services failed.", 4);
|
|
if(connected && getApiClient().isConnected())
|
|
{
|
|
connected = false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onConnected(Bundle arg0)
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Connected to Play Services.", 4);
|
|
|
|
connected = true;
|
|
|
|
if(lastRequest == null)
|
|
{
|
|
Miscellaneous.logEvent("w", "ActivityDetectionReceiver", "Request type not specified. Start or stop listening to activity detection updates?", 4);
|
|
return;
|
|
}
|
|
|
|
if(lastRequest.equals(LastRequestEnum.start))
|
|
requestUpdates();
|
|
else if(lastRequest.equals(LastRequestEnum.stop))
|
|
stopUpdates();
|
|
else //reload, e.g. to set a new update time
|
|
reloadUpdates();
|
|
}
|
|
|
|
@Override
|
|
public void onConnectionSuspended(int arg0)
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Connection to Play Services suspended.", 4);
|
|
// activityRecognitionClient.disconnect();
|
|
connected = false;
|
|
}
|
|
|
|
public ActivityDetectionReceiver()
|
|
{
|
|
super("ActivityDetectionIntentService");
|
|
if(instance == null)
|
|
instance = this;
|
|
}
|
|
|
|
@Override
|
|
protected void onHandleIntent(Intent intent)
|
|
{
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "onHandleIntent(): Received some status.", 5);
|
|
|
|
try
|
|
{
|
|
if(isActivityDetectionReceiverRunning())
|
|
{
|
|
currentTime = new Date();
|
|
|
|
if(lastUpdate == 0 | currentTime.getTime() >= lastUpdate + Settings.activityDetectionFrequency * 1000 - 1000) // -1000 to include updates only marginaly below the threshold
|
|
{
|
|
lastUpdate = currentTime.getTime();
|
|
|
|
if(ActivityRecognitionResult.hasResult(intent))
|
|
{
|
|
activityDetectionLastResult = ActivityRecognitionResult.extractResult(intent);
|
|
|
|
for(DetectedActivity activity : activityDetectionLastResult.getProbableActivities())
|
|
{
|
|
int loglevel = 3;
|
|
if(activity.getConfidence() < Settings.activityDetectionRequiredProbability)
|
|
loglevel = 4;
|
|
|
|
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Detected activity (probability " + String.valueOf(activity.getConfidence()) + "%): " + getDescription(activity.getType()), loglevel);
|
|
}
|
|
|
|
/*
|
|
* Returns the list of activities that where detected with the confidence value associated with each activity.
|
|
* The activities are sorted by most probable activity first.
|
|
* The sum of the confidences of all detected activities this method returns does not have to be <= 100
|
|
* since some activities are not mutually exclusive (for example, you can be walking while in a bus)
|
|
* and some activities are hierarchical (ON_FOOT is a generalization of WALKING and RUNNING).
|
|
*/
|
|
|
|
ArrayList<Rule> allRulesWithActivityDetection = Rule.findRuleCandidatesByActivityDetection();
|
|
for(int i=0; i<allRulesWithActivityDetection.size(); i++)
|
|
{
|
|
if(allRulesWithActivityDetection.get(i).applies(Miscellaneous.getAnyContext()))
|
|
allRulesWithActivityDetection.get(i).activate(AutomationService.getInstance(), false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
Miscellaneous.logEvent("w", "ActivityDetectionReceiver", String.format(getResources().getString(R.string.ignoringActivityDetectionUpdateTooSoon), String.valueOf(Settings.activityDetectionFrequency)), 5);
|
|
}
|
|
else
|
|
Miscellaneous.logEvent("w", "ActivityDetectionReceiver", "I am not running. I shouldn't be getting updates. Ignoring it.", 5);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
// some error, don't care.
|
|
Miscellaneous.logEvent("e", "ActivityDetectionReceiver", "onHandleIntent(): Error while receiving status: " + Log.getStackTraceString(e), 4);
|
|
}
|
|
}
|
|
|
|
public static int[] getAllTypes()
|
|
{
|
|
return new int[] {
|
|
DetectedActivity.IN_VEHICLE,
|
|
DetectedActivity.ON_BICYCLE,
|
|
DetectedActivity.ON_FOOT,
|
|
DetectedActivity.STILL,
|
|
DetectedActivity.TILTING,
|
|
DetectedActivity.WALKING,
|
|
DetectedActivity.RUNNING,
|
|
DetectedActivity.UNKNOWN
|
|
};
|
|
}
|
|
|
|
public static String getDescription(int type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case(DetectedActivity.IN_VEHICLE):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityInVehicle);
|
|
case(DetectedActivity.ON_BICYCLE):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityOnBicycle);
|
|
case(DetectedActivity.ON_FOOT):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityOnFoot);
|
|
case(DetectedActivity.STILL):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityStill);
|
|
case(DetectedActivity.TILTING):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityTilting);
|
|
case(DetectedActivity.WALKING):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityWalking);
|
|
case(DetectedActivity.RUNNING):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityRunning);
|
|
case(DetectedActivity.UNKNOWN):
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityUnknown);
|
|
default:
|
|
return Miscellaneous.getAnyContext().getResources().getString(R.string.detectedActivityInvalidStatus);
|
|
}
|
|
}
|
|
|
|
public static String[] getAllDescriptions()
|
|
{
|
|
ArrayList<String> types = new ArrayList<String>();
|
|
|
|
for(int type : getAllTypes())
|
|
types.add(getDescription(type));
|
|
|
|
return types.toArray(new String[types.size()]);
|
|
}
|
|
|
|
@Override
|
|
public void startListener(AutomationService automationService)
|
|
{
|
|
ActivityDetectionReceiver.startActivityDetectionReceiver();
|
|
}
|
|
|
|
@Override
|
|
public void stopListener(AutomationService automationService)
|
|
{
|
|
ActivityDetectionReceiver.stopActivityDetectionReceiver();
|
|
}
|
|
|
|
public static boolean haveAllPermission()
|
|
{
|
|
return ActivityPermissions.havePermission("com.google.android.gms.permission.ACTIVITY_RECOGNITION", Miscellaneous.getAnyContext());
|
|
}
|
|
|
|
@Override
|
|
public boolean isListenerRunning()
|
|
{
|
|
return ActivityDetectionReceiver.isActivityDetectionReceiverRunning();
|
|
}
|
|
|
|
@Override
|
|
public Trigger_Enum[] getMonitoredTrigger()
|
|
{
|
|
return new Trigger_Enum[] { Trigger_Enum.activityDetection };
|
|
}
|
|
|
|
private PendingIntent getActivityDetectionPendingIntent()
|
|
{
|
|
Intent intent = new Intent(AutomationService.getInstance(), ActivityDetectionReceiver.class);
|
|
PendingIntent returnValue = PendingIntent.getService(AutomationService.getInstance(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
return returnValue;
|
|
}
|
|
} |