mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 17:43:06 +02:00
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_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" />
|
||||
|
||||
@@ -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));
|
||||
|
||||
+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.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() {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user