forked from upstream-mirrors/OpenTracks
Add activity recognition.
This commit is contained in:
@@ -26,6 +26,8 @@ limitations under the License.
|
|||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<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 -->
|
<!-- Permissions to access sensors -->
|
||||||
<uses-permission android:name="com.dsi.ant.permission.ANT" />
|
<uses-permission android:name="com.dsi.ant.permission.ANT" />
|
||||||
<uses-permission android:name="com.dsi.ant.permission.ANT_ADMIN" />
|
<uses-permission android:name="com.dsi.ant.permission.ANT_ADMIN" />
|
||||||
@@ -243,6 +245,8 @@ limitations under the License.
|
|||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/sync_adapter" />
|
android:resource="@xml/sync_adapter" />
|
||||||
</service>
|
</service>
|
||||||
|
<service
|
||||||
|
android:name="com.google.android.apps.mytracks.services.ActivityRecognitionIntentService" />
|
||||||
<service
|
<service
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:name="com.google.android.apps.mytracks.services.ControlRecordingService" />
|
android:name="com.google.android.apps.mytracks.services.ControlRecordingService" />
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ limitations under the License.
|
|||||||
<string name="track_widget_item4">trackWidgetItem4</string>
|
<string name="track_widget_item4">trackWidgetItem4</string>
|
||||||
<string name="voice_frequency_key">voiceFrequency</string>
|
<string name="voice_frequency_key">voiceFrequency</string>
|
||||||
<!-- Keys for persistend preferences. But they should not get backed up or restored. -->
|
<!-- 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="auto_resume_track_current_retry_key">autoResumeTrackCurrentRetry</string>
|
||||||
<string name="drive_deleted_list_key">driveDeletedList</string>
|
<string name="drive_deleted_list_key">driveDeletedList</string>
|
||||||
<string name="drive_largest_change_id_key">driveLargestChangeId</string>
|
<string name="drive_largest_change_id_key">driveLargestChangeId</string>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class PreferenceBackupHelper {
|
|||||||
|
|
||||||
public PreferenceBackupHelper(Context context) {
|
public PreferenceBackupHelper(Context context) {
|
||||||
doNotBackup = new HashSet<String>();
|
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.auto_resume_track_current_retry_key));
|
||||||
doNotBackup.add(context.getString(R.string.drive_deleted_list_key));
|
doNotBackup.add(context.getString(R.string.drive_deleted_list_key));
|
||||||
doNotBackup.add(context.getString(R.string.drive_largest_change_id_key));
|
doNotBackup.add(context.getString(R.string.drive_largest_change_id_key));
|
||||||
|
|||||||
+92
@@ -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.PreferencesUtils;
|
||||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||||
import com.google.android.apps.mytracks.util.TrackNameUtils;
|
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.gms.location.LocationListener;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
@@ -57,6 +62,7 @@ import android.location.Location;
|
|||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -81,6 +87,7 @@ import java.util.concurrent.Executors;
|
|||||||
public class TrackRecordingService extends Service {
|
public class TrackRecordingService extends Service {
|
||||||
|
|
||||||
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
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
|
* 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 Context context;
|
||||||
private MyTracksProviderUtils myTracksProviderUtils;
|
private MyTracksProviderUtils myTracksProviderUtils;
|
||||||
private MyTracksLocationManager myTracksLocationManager;
|
private MyTracksLocationManager myTracksLocationManager;
|
||||||
|
private ActivityRecognitionClient activityRecognitionClient;
|
||||||
|
private PendingIntent activityRecognitionPendingIntent;
|
||||||
private PeriodicTaskExecutor voiceExecutor;
|
private PeriodicTaskExecutor voiceExecutor;
|
||||||
private PeriodicTaskExecutor splitExecutor;
|
private PeriodicTaskExecutor splitExecutor;
|
||||||
private ExecutorService executorService;
|
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
|
* Note that this service, through the AndroidManifest.xml, is configured to
|
||||||
* allow both MyTracks and third party apps to invoke it. For the onCreate
|
* allow both MyTracks and third party apps to invoke it. For the onCreate
|
||||||
@@ -260,6 +286,12 @@ public class TrackRecordingService extends Service {
|
|||||||
context = this;
|
context = this;
|
||||||
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this);
|
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this);
|
||||||
myTracksLocationManager = new MyTracksLocationManager(this, Looper.myLooper());
|
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());
|
voiceExecutor = new PeriodicTaskExecutor(this, new AnnouncementPeriodicTaskFactory());
|
||||||
splitExecutor = new PeriodicTaskExecutor(this, new SplitPeriodicTaskFactory());
|
splitExecutor = new PeriodicTaskExecutor(this, new SplitPeriodicTaskFactory());
|
||||||
executorService = Executors.newSingleThreadExecutor();
|
executorService = Executors.newSingleThreadExecutor();
|
||||||
@@ -352,6 +384,13 @@ public class TrackRecordingService extends Service {
|
|||||||
myTracksProviderUtils = null;
|
myTracksProviderUtils = null;
|
||||||
myTracksLocationManager.close();
|
myTracksLocationManager.close();
|
||||||
myTracksLocationManager = null;
|
myTracksLocationManager = null;
|
||||||
|
|
||||||
|
if (activityRecognitionClient.isConnected()) {
|
||||||
|
activityRecognitionClient.removeActivityUpdates(activityRecognitionPendingIntent);
|
||||||
|
}
|
||||||
|
activityRecognitionClient.disconnect();
|
||||||
|
activityRecognitionPendingIntent.cancel();
|
||||||
|
|
||||||
binder.detachFromService();
|
binder.detachFromService();
|
||||||
binder = null;
|
binder = null;
|
||||||
|
|
||||||
@@ -568,6 +607,8 @@ public class TrackRecordingService extends Service {
|
|||||||
// Update shared preferences
|
// Update shared preferences
|
||||||
updateRecordingState(trackId, false);
|
updateRecordingState(trackId, false);
|
||||||
PreferencesUtils.setInt(this, R.string.auto_resume_track_current_retry_key, 0);
|
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
|
// Update database
|
||||||
track.setId(trackId);
|
track.setId(trackId);
|
||||||
@@ -711,6 +752,30 @@ public class TrackRecordingService extends Service {
|
|||||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||||
if (track != null && !paused) {
|
if (track != null && !paused) {
|
||||||
insertLocation(track, lastLocation, getLastValidTrackPointInCurrentSegment(trackId));
|
insertLocation(track, lastLocation, getLastValidTrackPointInCurrentSegment(trackId));
|
||||||
|
|
||||||
|
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);
|
updateRecordingTrack(track, myTracksProviderUtils.getLastTrackPointId(trackId), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.google.android.apps.mytracks.util;
|
package com.google.android.apps.mytracks.util;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.Constants;
|
import com.google.android.apps.mytracks.Constants;
|
||||||
|
import com.google.android.gms.location.DetectedActivity;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -30,6 +31,8 @@ import android.content.SharedPreferences.Editor;
|
|||||||
*/
|
*/
|
||||||
public class PreferencesUtils {
|
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
|
* Preferences values. The defaults need to match the defaults in the xml
|
||||||
* files.
|
* files.
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ import java.util.List;
|
|||||||
public class TrackIconUtils {
|
public class TrackIconUtils {
|
||||||
|
|
||||||
private static final String AIRPLANE = "AIRPLANE";
|
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 BOAT = "BOAT";
|
||||||
private static final String DRIVE = "DRIVE";
|
public static final String DRIVE = "DRIVE";
|
||||||
private static final String RUN = "RUN";
|
private static final String RUN = "RUN";
|
||||||
private static final String SKI = "SKI";
|
private static final String SKI = "SKI";
|
||||||
private static final String SNOW_BOARDING = "SNOW_BOARDING";
|
private static final String SNOW_BOARDING = "SNOW_BOARDING";
|
||||||
private static final String WALK = "WALK";
|
public static final String WALK = "WALK";
|
||||||
|
|
||||||
private TrackIconUtils() {}
|
private TrackIconUtils() {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user