Add activity recognition.

This commit is contained in:
Jimmy Shih
2013-05-22 17:42:51 -07:00
parent 4eb8f4fff3
commit aa3d170e1f
7 changed files with 170 additions and 4 deletions
+4
View File
@@ -26,6 +26,8 @@ limitations under the License.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Permission for activity recognition -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Permissions to access sensors -->
<uses-permission android:name="com.dsi.ant.permission.ANT" />
<uses-permission android:name="com.dsi.ant.permission.ANT_ADMIN" />
@@ -243,6 +245,8 @@ limitations under the License.
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_adapter" />
</service>
<service
android:name="com.google.android.apps.mytracks.services.ActivityRecognitionIntentService" />
<service
android:exported="false"
android:name="com.google.android.apps.mytracks.services.ControlRecordingService" />
+1
View File
@@ -65,6 +65,7 @@ limitations under the License.
<string name="track_widget_item4">trackWidgetItem4</string>
<string name="voice_frequency_key">voiceFrequency</string>
<!-- Keys for persistend preferences. But they should not get backed up or restored. -->
<string name="activity_recognition_type_key">activityRecognitionType</string>
<string name="auto_resume_track_current_retry_key">autoResumeTrackCurrentRetry</string>
<string name="drive_deleted_list_key">driveDeletedList</string>
<string name="drive_largest_change_id_key">driveLargestChangeId</string>
@@ -44,6 +44,7 @@ public class PreferenceBackupHelper {
public PreferenceBackupHelper(Context context) {
doNotBackup = new HashSet<String>();
doNotBackup.add(context.getString(R.string.activity_recognition_type_key));
doNotBackup.add(context.getString(R.string.auto_resume_track_current_retry_key));
doNotBackup.add(context.getString(R.string.drive_deleted_list_key));
doNotBackup.add(context.getString(R.string.drive_largest_change_id_key));
@@ -0,0 +1,92 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
import com.google.android.maps.mytracks.R;
import android.app.IntentService;
import android.content.Intent;
/**
* Service that receives ActivityRecognition updates.
*
* @author Jimmy Shih
*/
public class ActivityRecognitionIntentService extends IntentService {
private static final int REQUIRED_CONFIDENCE = 90;
public ActivityRecognitionIntentService() {
super(ActivityRecognitionIntentService.class.getSimpleName());
}
@Override
protected void onHandleIntent(Intent intent) {
if (!ActivityRecognitionResult.hasResult(intent)) {
return;
}
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
DetectedActivity detectedActivity = result.getMostProbableActivity();
if (detectedActivity == null) {
return;
}
if (detectedActivity.getConfidence() < REQUIRED_CONFIDENCE) {
return;
}
long recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
if (recordingTrackId == PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
return;
}
boolean recordingTrackPaused = PreferencesUtils.getBoolean(
this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
if (recordingTrackPaused) {
return;
}
int currentType = PreferencesUtils.getInt(this, R.string.activity_recognition_type_key,
PreferencesUtils.ACTIVITY_RECOGNITION_TYPE_DEFAULT);
switch (detectedActivity.getType()) {
case DetectedActivity.IN_VEHICLE:
if (currentType != DetectedActivity.IN_VEHICLE) {
PreferencesUtils.setInt(
this, R.string.activity_recognition_type_key, DetectedActivity.IN_VEHICLE);
}
break;
case DetectedActivity.ON_BICYCLE:
if (currentType != DetectedActivity.IN_VEHICLE
&& currentType != DetectedActivity.ON_BICYCLE) {
PreferencesUtils.setInt(
this, R.string.activity_recognition_type_key, DetectedActivity.ON_BICYCLE);
}
break;
case DetectedActivity.ON_FOOT:
if (currentType != DetectedActivity.IN_VEHICLE && currentType != DetectedActivity.ON_BICYCLE
&& currentType != DetectedActivity.ON_FOOT) {
PreferencesUtils.setInt(
this, R.string.activity_recognition_type_key, DetectedActivity.ON_FOOT);
}
break;
default:
break;
}
}
}
@@ -41,6 +41,11 @@ import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.apps.mytracks.util.TrackNameUtils;
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.location.ActivityRecognitionClient;
import com.google.android.gms.location.DetectedActivity;
import com.google.android.gms.location.LocationListener;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
@@ -57,6 +62,7 @@ import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -81,6 +87,7 @@ import java.util.concurrent.Executors;
public class TrackRecordingService extends Service {
private static final String TAG = TrackRecordingService.class.getSimpleName();
private static final long ACTIVITY_RECOGNITION_INTERVAL = 60000; // 1 minute
/**
* The name of extra intent property to indicate whether we want to resume a
@@ -103,6 +110,8 @@ public class TrackRecordingService extends Service {
private Context context;
private MyTracksProviderUtils myTracksProviderUtils;
private MyTracksLocationManager myTracksLocationManager;
private ActivityRecognitionClient activityRecognitionClient;
private PendingIntent activityRecognitionPendingIntent;
private PeriodicTaskExecutor voiceExecutor;
private PeriodicTaskExecutor splitExecutor;
private ExecutorService executorService;
@@ -247,6 +256,23 @@ public class TrackRecordingService extends Service {
}
};
private final ConnectionCallbacks activityRecognitionCallbacks = new ConnectionCallbacks() {
@Override
public void onDisconnected() {}
@Override
public void onConnected(Bundle bundle) {
activityRecognitionClient.requestActivityUpdates(
ACTIVITY_RECOGNITION_INTERVAL, activityRecognitionPendingIntent);
}
};
private final OnConnectionFailedListener
activityRecognitionFailedListener = new OnConnectionFailedListener() {
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {}
};
/*
* Note that this service, through the AndroidManifest.xml, is configured to
* allow both MyTracks and third party apps to invoke it. For the onCreate
@@ -260,6 +286,12 @@ public class TrackRecordingService extends Service {
context = this;
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this);
myTracksLocationManager = new MyTracksLocationManager(this, Looper.myLooper());
activityRecognitionPendingIntent = PendingIntent.getService(context, 0,
new Intent(context, ActivityRecognitionIntentService.class),
PendingIntent.FLAG_UPDATE_CURRENT);
activityRecognitionClient = new ActivityRecognitionClient(
context, activityRecognitionCallbacks, activityRecognitionFailedListener);
activityRecognitionClient.connect();
voiceExecutor = new PeriodicTaskExecutor(this, new AnnouncementPeriodicTaskFactory());
splitExecutor = new PeriodicTaskExecutor(this, new SplitPeriodicTaskFactory());
executorService = Executors.newSingleThreadExecutor();
@@ -352,6 +384,13 @@ public class TrackRecordingService extends Service {
myTracksProviderUtils = null;
myTracksLocationManager.close();
myTracksLocationManager = null;
if (activityRecognitionClient.isConnected()) {
activityRecognitionClient.removeActivityUpdates(activityRecognitionPendingIntent);
}
activityRecognitionClient.disconnect();
activityRecognitionPendingIntent.cancel();
binder.detachFromService();
binder = null;
@@ -568,6 +607,8 @@ public class TrackRecordingService extends Service {
// Update shared preferences
updateRecordingState(trackId, false);
PreferencesUtils.setInt(this, R.string.auto_resume_track_current_retry_key, 0);
PreferencesUtils.setInt(this, R.string.activity_recognition_type_key,
PreferencesUtils.ACTIVITY_RECOGNITION_TYPE_DEFAULT);
// Update database
track.setId(trackId);
@@ -711,7 +752,31 @@ public class TrackRecordingService extends Service {
Track track = myTracksProviderUtils.getTrack(trackId);
if (track != null && !paused) {
insertLocation(track, lastLocation, getLastValidTrackPointInCurrentSegment(trackId));
updateRecordingTrack(track, myTracksProviderUtils.getLastTrackPointId(trackId), false);
int activityRecognitionType = PreferencesUtils.getInt(this,
R.string.activity_recognition_type_key,
PreferencesUtils.ACTIVITY_RECOGNITION_TYPE_DEFAULT);
if (activityRecognitionType != PreferencesUtils.ACTIVITY_RECOGNITION_TYPE_DEFAULT) {
String iconValue = null;
switch (activityRecognitionType) {
case DetectedActivity.IN_VEHICLE:
iconValue = TrackIconUtils.DRIVE;
break;
case DetectedActivity.ON_BICYCLE:
iconValue = TrackIconUtils.BIKE;
break;
case DetectedActivity.ON_FOOT:
iconValue = TrackIconUtils.WALK;
break;
default:
break;
}
if (iconValue != null) {
track.setIcon(iconValue);
track.setCategory(getString(TrackIconUtils.getIconActivityType(iconValue)));
}
}
updateRecordingTrack(track, myTracksProviderUtils.getLastTrackPointId(trackId), false);
}
endRecording(true, trackId);
@@ -17,6 +17,7 @@
package com.google.android.apps.mytracks.util;
import com.google.android.apps.mytracks.Constants;
import com.google.android.gms.location.DetectedActivity;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -30,6 +31,8 @@ import android.content.SharedPreferences.Editor;
*/
public class PreferencesUtils {
public static final int ACTIVITY_RECOGNITION_TYPE_DEFAULT = DetectedActivity.UNKNOWN;
/*
* Preferences values. The defaults need to match the defaults in the xml
* files.
@@ -35,13 +35,13 @@ import java.util.List;
public class TrackIconUtils {
private static final String AIRPLANE = "AIRPLANE";
private static final String BIKE = "BIKE";
public static final String BIKE = "BIKE";
private static final String BOAT = "BOAT";
private static final String DRIVE = "DRIVE";
public static final String DRIVE = "DRIVE";
private static final String RUN = "RUN";
private static final String SKI = "SKI";
private static final String SNOW_BOARDING = "SNOW_BOARDING";
private static final String WALK = "WALK";
public static final String WALK = "WALK";
private TrackIconUtils() {}