diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml
index 699528160..57b22400a 100755
--- a/MyTracks/AndroidManifest.xml
+++ b/MyTracks/AndroidManifest.xml
@@ -26,6 +26,8 @@ limitations under the License.
+
+
@@ -243,6 +245,8 @@ limitations under the License.
android:name="android.content.SyncAdapter"
android:resource="@xml/sync_adapter" />
+
diff --git a/MyTracks/res/values/keys.xml b/MyTracks/res/values/keys.xml
index 731da2ad3..3d01f0162 100644
--- a/MyTracks/res/values/keys.xml
+++ b/MyTracks/res/values/keys.xml
@@ -65,6 +65,7 @@ limitations under the License.
trackWidgetItem4
voiceFrequency
+ activityRecognitionType
autoResumeTrackCurrentRetry
driveDeletedList
driveLargestChangeId
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java
index 8be9c86ff..d6d8eeef7 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java
@@ -44,6 +44,7 @@ public class PreferenceBackupHelper {
public PreferenceBackupHelper(Context context) {
doNotBackup = new HashSet();
+ 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));
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/ActivityRecognitionIntentService.java b/MyTracks/src/com/google/android/apps/mytracks/services/ActivityRecognitionIntentService.java
new file mode 100644
index 000000000..bde919d25
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/ActivityRecognitionIntentService.java
@@ -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;
+ }
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
index ebfe0b369..a094155f9 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
@@ -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);
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java
index 0bd4fd413..b110d8286 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java
@@ -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.
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/TrackIconUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/TrackIconUtils.java
index 741680c55..d5a210d6e 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/TrackIconUtils.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/TrackIconUtils.java
@@ -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() {}