forked from upstream-mirrors/OpenTracks
Changing service connection to be a non-singleton, and properly handle its state.
This commit is contained in:
@@ -25,7 +25,7 @@ import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
|||||||
import com.google.android.apps.mytracks.io.file.TempFileCleaner;
|
import com.google.android.apps.mytracks.io.file.TempFileCleaner;
|
||||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceBinder;
|
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||||
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
|
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
|
||||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||||
@@ -64,11 +64,16 @@ import android.widget.Toast;
|
|||||||
public class MyTracks extends TabActivity implements OnTouchListener {
|
public class MyTracks extends TabActivity implements OnTouchListener {
|
||||||
private TrackDataHub dataHub;
|
private TrackDataHub dataHub;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Menu manager.
|
* Menu manager.
|
||||||
*/
|
*/
|
||||||
private MenuManager menuManager;
|
private MenuManager menuManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preferences.
|
||||||
|
*/
|
||||||
|
private SharedPreferences preferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if a new track should be created after the track recording service
|
* True if a new track should be created after the track recording service
|
||||||
* binds.
|
* binds.
|
||||||
@@ -85,8 +90,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
*/
|
*/
|
||||||
private GoogleAnalyticsTracker tracker;
|
private GoogleAnalyticsTracker tracker;
|
||||||
|
|
||||||
private TrackRecordingServiceBinder serviceBinder;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tabs/View navigation:
|
* Tabs/View navigation:
|
||||||
*/
|
*/
|
||||||
@@ -99,23 +102,27 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recording service interaction:
|
||||||
|
*/
|
||||||
|
|
||||||
private final Runnable serviceBindCallback = new Runnable() {
|
private final Runnable serviceBindCallback = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized (serviceBinder) {
|
synchronized (serviceConnection) {
|
||||||
ITrackRecordingService service = serviceBinder.getServiceIfBound();
|
ITrackRecordingService service = serviceConnection.getServiceIfBound();
|
||||||
if (startNewTrackRequested && service != null) {
|
if (startNewTrackRequested && service != null) {
|
||||||
Log.i(TAG, "Starting recording");
|
Log.i(TAG, "Starting recording");
|
||||||
startNewTrackRequested = false;
|
startNewTrackRequested = false;
|
||||||
startRecordingNewTrack(service);
|
startRecordingNewTrack(service);
|
||||||
} else {
|
} else if (startNewTrackRequested) {
|
||||||
Log.w(TAG, "Not yet starting recording");
|
Log.w(TAG, "Not yet starting recording");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private SharedPreferences preferences;
|
private TrackRecordingServiceConnection serviceConnection;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Application lifetime events:
|
* Application lifetime events:
|
||||||
@@ -142,7 +149,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||||
dataHub = TrackDataHub.newInstance(this);
|
dataHub = TrackDataHub.newInstance(this);
|
||||||
menuManager = new MenuManager(this);
|
menuManager = new MenuManager(this);
|
||||||
serviceBinder = TrackRecordingServiceBinder.getInstance(this);
|
serviceConnection = new TrackRecordingServiceConnection(this, serviceBindCallback);
|
||||||
|
|
||||||
// The volume we want to control is the Text-To-Speech volume
|
// The volume we want to control is the Text-To-Speech volume
|
||||||
int volumeStream =
|
int volumeStream =
|
||||||
@@ -186,6 +193,45 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
Eula.showEulaRequireAcceptance(this);
|
Eula.showEulaRequireAcceptance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
Log.d(TAG, "MyTracks.onStart");
|
||||||
|
super.onStart();
|
||||||
|
dataHub.start();
|
||||||
|
|
||||||
|
// Ensure that service is running and bound if we're supposed to be recording
|
||||||
|
if (ServiceUtils.isRecording(this, null, preferences)) {
|
||||||
|
serviceConnection.startAndBind();
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String action = intent.getAction();
|
||||||
|
Uri data = intent.getData();
|
||||||
|
if ((Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action))
|
||||||
|
&& TracksColumns.CONTENT_ITEMTYPE.equals(intent.getType())
|
||||||
|
&& UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
|
||||||
|
long trackId = ContentUris.parseId(data);
|
||||||
|
dataHub.loadTrack(trackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
// Called when the current activity is being displayed or re-displayed
|
||||||
|
// to the user.
|
||||||
|
Log.d(TAG, "MyTracks.onResume");
|
||||||
|
serviceConnection.bindIfRunning();
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
// Called when activity is going into the background, but has not (yet) been
|
||||||
|
// killed. Shouldn't block longer than approx. 2 seconds.
|
||||||
|
Log.d(TAG, "MyTracks.onPause");
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
Log.d(TAG, "MyTracks.onStop");
|
Log.d(TAG, "MyTracks.onStop");
|
||||||
@@ -201,50 +247,13 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onDestroy() {
|
||||||
// Called when activity is going into the background, but has not (yet) been
|
Log.d(TAG, "MyTracks.onDestroy");
|
||||||
// killed. Shouldn't block longer than approx. 2 seconds.
|
serviceConnection.unbind();
|
||||||
Log.d(TAG, "MyTracks.onPause");
|
|
||||||
serviceBinder.unbindService();
|
super.onDestroy();
|
||||||
super.onPause();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
// Called when the current activity is being displayed or re-displayed
|
|
||||||
// to the user.
|
|
||||||
Log.d(TAG, "MyTracks.onResume");
|
|
||||||
serviceBinder.bindService(serviceBindCallback);
|
|
||||||
super.onResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
Log.d(TAG, "MyTracks.onStart");
|
|
||||||
super.onStart();
|
|
||||||
dataHub.start();
|
|
||||||
|
|
||||||
// Ensure that service is running if we're supposed to be recording
|
|
||||||
if (ServiceUtils.isRecording(this, preferences)) {
|
|
||||||
serviceBinder.startService();
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent intent = getIntent();
|
|
||||||
String action = intent.getAction();
|
|
||||||
Uri data = intent.getData();
|
|
||||||
if ((Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action))
|
|
||||||
&& TracksColumns.CONTENT_ITEMTYPE.equals(intent.getType())
|
|
||||||
&& UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
|
|
||||||
long trackId = ContentUris.parseId(data);
|
|
||||||
dataHub.loadTrack(trackId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Menu events:
|
|
||||||
* ============
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
@@ -254,7 +263,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
menuManager.onPrepareOptionsMenu(menu, providerUtils.getLastTrack() != null,
|
menuManager.onPrepareOptionsMenu(menu, providerUtils.getLastTrack() != null,
|
||||||
ServiceUtils.isRecording(this, preferences),
|
ServiceUtils.isRecording(this, serviceConnection.getServiceIfBound(), preferences),
|
||||||
dataHub.isATrackSelected());
|
dataHub.isATrackSelected());
|
||||||
return super.onPrepareOptionsMenu(menu);
|
return super.onPrepareOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
@@ -273,8 +282,8 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTrackballEvent(MotionEvent event) {
|
public boolean onTrackballEvent(MotionEvent event) {
|
||||||
if (ServiceUtils.isRecording(this, preferences)) {
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
if (ServiceUtils.isRecording(this, serviceConnection.getServiceIfBound(), preferences)) {
|
||||||
try {
|
try {
|
||||||
insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -285,6 +294,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onTrackballEvent(event);
|
return super.onTrackballEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +412,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
* @throws RemoteException If the call on the service failed.
|
* @throws RemoteException If the call on the service failed.
|
||||||
*/
|
*/
|
||||||
private long insertWaypoint(WaypointCreationRequest request) throws RemoteException {
|
private long insertWaypoint(WaypointCreationRequest request) throws RemoteException {
|
||||||
ITrackRecordingService trackRecordingService = serviceBinder.getServiceIfBound();
|
ITrackRecordingService trackRecordingService = serviceConnection.getServiceIfBound();
|
||||||
if (trackRecordingService == null) {
|
if (trackRecordingService == null) {
|
||||||
throw new IllegalStateException("The recording service is not bound.");
|
throw new IllegalStateException("The recording service is not bound.");
|
||||||
}
|
}
|
||||||
@@ -443,9 +453,9 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
* it. Starts recording a new track.
|
* it. Starts recording a new track.
|
||||||
*/
|
*/
|
||||||
void startRecording() {
|
void startRecording() {
|
||||||
synchronized (serviceBinder) {
|
synchronized (serviceConnection) {
|
||||||
startNewTrackRequested = true;
|
startNewTrackRequested = true;
|
||||||
serviceBinder.startService();
|
serviceConnection.startAndBind();
|
||||||
|
|
||||||
// Binding was already requested before, it either already happened
|
// Binding was already requested before, it either already happened
|
||||||
// (in which case running the callback manually triggers the actual recording start)
|
// (in which case running the callback manually triggers the actual recording start)
|
||||||
@@ -459,30 +469,29 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
|||||||
* Stops the track recording service and unbinds from it. Will display a toast
|
* Stops the track recording service and unbinds from it. Will display a toast
|
||||||
* "Stopped recording" and pop up the Track Details activity.
|
* "Stopped recording" and pop up the Track Details activity.
|
||||||
*/
|
*/
|
||||||
public void stopRecording() {
|
void stopRecording() {
|
||||||
ITrackRecordingService trackRecordingService = serviceBinder.getServiceIfBound();
|
// Save the track id as the shared preference will overwrite the recording track id.
|
||||||
|
SharedPreferences sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||||
|
long currentTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1);
|
||||||
|
|
||||||
|
ITrackRecordingService trackRecordingService = serviceConnection.getServiceIfBound();
|
||||||
if (trackRecordingService != null) {
|
if (trackRecordingService != null) {
|
||||||
// Save the track id as the shared preference will overwrite the recording track id.
|
|
||||||
SharedPreferences sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
|
||||||
long currentTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1);
|
|
||||||
try {
|
try {
|
||||||
trackRecordingService.endCurrentTrack();
|
trackRecordingService.endCurrentTrack();
|
||||||
// TODO: We catch Exception, because after eliminating the service process
|
|
||||||
// all exceptions it may throw are no longer wrapped in a RemoteException.
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Unable to stop recording.", e);
|
Log.e(TAG, "Unable to stop recording.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTrackId > 0) {
|
|
||||||
Intent intent = new Intent(MyTracks.this, TrackDetails.class);
|
|
||||||
intent.putExtra("trackid", currentTrackId);
|
|
||||||
intent.putExtra("hasCancelButton", false);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
serviceBinder.stopService();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
serviceConnection.stop();
|
||||||
|
|
||||||
|
if (currentTrackId > 0) {
|
||||||
|
Intent intent = new Intent(MyTracks.this, TrackDetails.class);
|
||||||
|
intent.putExtra("trackid", currentTrackId);
|
||||||
|
intent.putExtra("hasCancelButton", false);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void clearSelectedTrack() {
|
void clearSelectedTrack() {
|
||||||
dataHub.unloadCurrentTrack();
|
dataHub.unloadCurrentTrack();
|
||||||
|
|||||||
@@ -16,9 +16,10 @@
|
|||||||
package com.google.android.apps.mytracks;
|
package com.google.android.apps.mytracks;
|
||||||
|
|
||||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.Sensor;
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceBinder;
|
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||||
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
|
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
@@ -46,7 +47,6 @@ public class SensorStateActivity extends Activity {
|
|||||||
private static final long REFRESH_PERIOD_MS = 250;
|
private static final long REFRESH_PERIOD_MS = 250;
|
||||||
|
|
||||||
private final StatsUtilities utils;
|
private final StatsUtilities utils;
|
||||||
private TrackRecordingServiceBinder serviceBinder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This timer periodically invokes the refresh timer task.
|
* This timer periodically invokes the refresh timer task.
|
||||||
@@ -59,6 +59,11 @@ public class SensorStateActivity extends Activity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection to the recording service.
|
||||||
|
*/
|
||||||
|
private TrackRecordingServiceConnection serviceConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A task which will update the U/I.
|
* A task which will update the U/I.
|
||||||
*/
|
*/
|
||||||
@@ -81,39 +86,45 @@ public class SensorStateActivity extends Activity {
|
|||||||
|
|
||||||
setContentView(R.layout.sensor_state);
|
setContentView(R.layout.sensor_state);
|
||||||
|
|
||||||
serviceBinder = TrackRecordingServiceBinder.getInstance(this);
|
// TODO: Allow reading from sensors even if service is not running.
|
||||||
serviceBinder.bindService(stateUpdater);
|
serviceConnection = new TrackRecordingServiceConnection(this, stateUpdater);
|
||||||
|
serviceConnection.bindIfRunning();
|
||||||
updateState();
|
updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
|
serviceConnection.bindIfRunning();
|
||||||
|
|
||||||
timer = new Timer();
|
timer = new Timer();
|
||||||
timer.schedule(new RefreshTask(), REFRESH_PERIOD_MS, REFRESH_PERIOD_MS);
|
timer.schedule(new RefreshTask(), REFRESH_PERIOD_MS, REFRESH_PERIOD_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
timer.purge();
|
timer.purge();
|
||||||
timer = null;
|
timer = null;
|
||||||
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
serviceBinder.unbindService();
|
serviceConnection.unbind();
|
||||||
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateState() {
|
protected void updateState() {
|
||||||
ITrackRecordingService service = serviceBinder.getServiceIfBound();
|
ITrackRecordingService service = serviceConnection.getServiceIfBound();
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.d(Constants.TAG, "Could not get track recording service.");
|
Log.d(Constants.TAG, "Could not get track recording service.");
|
||||||
updateSensorState(Sensor.SensorState.NONE);
|
updateSensorState(Sensor.SensorState.NONE);
|
||||||
updateSensorData(null);
|
updateSensorData(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sensor.SensorDataSet sds = null;
|
Sensor.SensorDataSet sds = null;
|
||||||
try {
|
try {
|
||||||
byte[] buff = service.getSensorData();
|
byte[] buff = service.getSensorData();
|
||||||
@@ -159,8 +170,8 @@ public class SensorStateActivity extends Activity {
|
|||||||
|
|
||||||
if (sds.hasPower() && sds.getPower().hasValue()
|
if (sds.hasPower() && sds.getPower().hasValue()
|
||||||
&& sds.getPower().getState() == Sensor.SensorState.SENDING) {
|
&& sds.getPower().getState() == Sensor.SensorState.SENDING) {
|
||||||
utils.setText(R.id.power_state_register,
|
utils.setText(R.id.power_state_register,
|
||||||
Integer.toString(sds.getPower().getValue()));
|
Integer.toString(sds.getPower().getValue()));
|
||||||
} else {
|
} else {
|
||||||
utils.setText(R.id.power_state_register,
|
utils.setText(R.id.power_state_register,
|
||||||
SensorUtils.getStateAsString(
|
SensorUtils.getStateAsString(
|
||||||
@@ -186,14 +197,14 @@ public class SensorStateActivity extends Activity {
|
|||||||
if (sds.hasHeartRate() && sds.getHeartRate().hasValue()
|
if (sds.hasHeartRate() && sds.getHeartRate().hasValue()
|
||||||
&& sds.getHeartRate().getState() == Sensor.SensorState.SENDING) {
|
&& sds.getHeartRate().getState() == Sensor.SensorState.SENDING) {
|
||||||
utils.setText(R.id.heart_rate_register,
|
utils.setText(R.id.heart_rate_register,
|
||||||
Integer.toString(sds.getHeartRate().getValue()));
|
Integer.toString(sds.getHeartRate().getValue()));
|
||||||
} else {
|
} else {
|
||||||
utils.setText(R.id.heart_rate_register,
|
utils.setText(R.id.heart_rate_register,
|
||||||
SensorUtils.getStateAsString(
|
SensorUtils.getStateAsString(
|
||||||
sds.hasHeartRate()
|
sds.hasHeartRate()
|
||||||
? sds.getHeartRate().getState()
|
? sds.getHeartRate().getState()
|
||||||
: Sensor.SensorState.NONE,
|
: Sensor.SensorState.NONE,
|
||||||
this));
|
this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class StatsActivity extends Activity implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.i(TAG, "Started UI update thread");
|
Log.i(TAG, "Started UI update thread");
|
||||||
while (ServiceUtils.isRecording(StatsActivity.this, preferences)) {
|
while (ServiceUtils.isRecording(StatsActivity.this, null, preferences)) {
|
||||||
runOnUiThread(updateResults);
|
runOnUiThread(updateResults);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1000L);
|
Thread.sleep(1000L);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2008 Google Inc.
|
* Copyright 2008 Google Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
* the License at
|
* the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
@@ -19,6 +19,7 @@ import com.google.android.apps.mytracks.content.TracksColumns;
|
|||||||
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendActivity;
|
import com.google.android.apps.mytracks.io.sendtogoogle.SendActivity;
|
||||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||||
|
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
@@ -79,8 +80,7 @@ public class TrackList extends ListActivity
|
|||||||
R.string.tracklist_show_track);
|
R.string.tracklist_show_track);
|
||||||
menu.add(0, Constants.MENU_EDIT, 0,
|
menu.add(0, Constants.MENU_EDIT, 0,
|
||||||
R.string.tracklist_edit_track);
|
R.string.tracklist_edit_track);
|
||||||
if (!ServiceUtils.isRecording(TrackList.this, null)
|
if (!isRecording() || trackId != recordingTrackId) {
|
||||||
|| trackId != recordingTrackId) {
|
|
||||||
menu.add(0, Constants.MENU_SEND_TO_GOOGLE, 0,
|
menu.add(0, Constants.MENU_SEND_TO_GOOGLE, 0,
|
||||||
R.string.tracklist_send_to_google);
|
R.string.tracklist_send_to_google);
|
||||||
SubMenu share = menu.addSubMenu(0, Constants.MENU_SHARE, 0,
|
SubMenu share = menu.addSubMenu(0, Constants.MENU_SHARE, 0,
|
||||||
@@ -112,6 +112,16 @@ public class TrackList extends ListActivity
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private final Runnable serviceBindingChanged = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
updateButtonsEnabled();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private TrackRecordingServiceConnection serviceConnection;
|
||||||
|
private SharedPreferences preferences;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(
|
public void onSharedPreferenceChanged(
|
||||||
SharedPreferences sharedPreferences, String key) {
|
SharedPreferences sharedPreferences, String key) {
|
||||||
@@ -214,16 +224,17 @@ public class TrackList extends ListActivity
|
|||||||
listView = getListView();
|
listView = getListView();
|
||||||
listView.setOnCreateContextMenuListener(contextMenuListener);
|
listView.setOnCreateContextMenuListener(contextMenuListener);
|
||||||
|
|
||||||
SharedPreferences preferences =
|
preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||||
getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
serviceConnection = new TrackRecordingServiceConnection(this, serviceBindingChanged);
|
||||||
|
|
||||||
View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
|
View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
|
||||||
View exportAll = findViewById(R.id.tracklist_btn_export_all);
|
|
||||||
boolean notRecording = !ServiceUtils.isRecording(this, preferences);
|
|
||||||
deleteAll.setOnClickListener(this);
|
deleteAll.setOnClickListener(this);
|
||||||
deleteAll.setEnabled(notRecording);
|
|
||||||
|
View exportAll = findViewById(R.id.tracklist_btn_export_all);
|
||||||
exportAll.setOnClickListener(this);
|
exportAll.setOnClickListener(this);
|
||||||
exportAll.setEnabled(notRecording);
|
|
||||||
|
updateButtonsEnabled();
|
||||||
|
|
||||||
findViewById(R.id.tracklist_btn_import_all).setOnClickListener(this);
|
findViewById(R.id.tracklist_btn_import_all).setOnClickListener(this);
|
||||||
|
|
||||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||||
@@ -238,6 +249,29 @@ public class TrackList extends ListActivity
|
|||||||
setListAdapter();
|
setListAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
serviceConnection.bindIfRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
serviceConnection.unbind();
|
||||||
|
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtonsEnabled() {
|
||||||
|
View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
|
||||||
|
View exportAll = findViewById(R.id.tracklist_btn_export_all);
|
||||||
|
|
||||||
|
boolean notRecording = !isRecording();
|
||||||
|
deleteAll.setEnabled(notRecording);
|
||||||
|
exportAll.setEnabled(notRecording);
|
||||||
|
}
|
||||||
|
|
||||||
private void setListAdapter() {
|
private void setListAdapter() {
|
||||||
// Get a cursor with all tracks
|
// Get a cursor with all tracks
|
||||||
SimpleCursorAdapter adapter = new SimpleCursorAdapter(
|
SimpleCursorAdapter adapter = new SimpleCursorAdapter(
|
||||||
@@ -301,4 +335,8 @@ public class TrackList extends ListActivity
|
|||||||
});
|
});
|
||||||
setListAdapter(adapter);
|
setListAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isRecording() {
|
||||||
|
return ServiceUtils.isRecording(TrackList.this, serviceConnection.getServiceIfBound(), preferences);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.apps.mytracks;
|
package com.google.android.apps.mytracks;
|
||||||
|
|
||||||
|
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceBinder;
|
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
@@ -63,6 +65,7 @@ public class WaypointsList extends ListActivity
|
|||||||
private Button insertStatisticsButton = null;
|
private Button insertStatisticsButton = null;
|
||||||
private long recordingTrackId = -1;
|
private long recordingTrackId = -1;
|
||||||
private MyTracksProviderUtils providerUtils;
|
private MyTracksProviderUtils providerUtils;
|
||||||
|
private TrackRecordingServiceConnection serviceConnection;
|
||||||
|
|
||||||
private Cursor waypointsCursor = null;
|
private Cursor waypointsCursor = null;
|
||||||
|
|
||||||
@@ -128,6 +131,7 @@ public class WaypointsList extends ListActivity
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||||
|
serviceConnection = new TrackRecordingServiceConnection(this, null);
|
||||||
|
|
||||||
// We don't need a window title bar:
|
// We don't need a window title bar:
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
@@ -174,6 +178,20 @@ public class WaypointsList extends ListActivity
|
|||||||
setListAdapter();
|
setListAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
serviceConnection.bindIfRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
serviceConnection.unbind();
|
||||||
|
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
WaypointCreationRequest request;
|
WaypointCreationRequest request;
|
||||||
@@ -201,8 +219,7 @@ public class WaypointsList extends ListActivity
|
|||||||
|
|
||||||
private long insertWaypoint(WaypointCreationRequest request) {
|
private long insertWaypoint(WaypointCreationRequest request) {
|
||||||
try {
|
try {
|
||||||
ITrackRecordingService trackRecordingService =
|
ITrackRecordingService trackRecordingService = serviceConnection.getServiceIfBound();
|
||||||
TrackRecordingServiceBinder.getInstance(this).getServiceIfBound();
|
|
||||||
if (trackRecordingService != null) {
|
if (trackRecordingService != null) {
|
||||||
long waypointId = trackRecordingService.insertWaypoint(request);
|
long waypointId = trackRecordingService.insertWaypoint(request);
|
||||||
if (waypointId >= 0) {
|
if (waypointId >= 0) {
|
||||||
@@ -210,6 +227,8 @@ public class WaypointsList extends ListActivity
|
|||||||
Toast.LENGTH_LONG).show();
|
Toast.LENGTH_LONG).show();
|
||||||
return waypointId;
|
return waypointId;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Not connected to service, not inserting waypoint");
|
||||||
}
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(Constants.TAG, "Cannot insert marker.", e);
|
Log.e(Constants.TAG, "Cannot insert marker.", e);
|
||||||
|
|||||||
@@ -20,11 +20,16 @@ import static com.google.android.apps.mytracks.Constants.TAG;
|
|||||||
import com.google.android.apps.mytracks.Constants;
|
import com.google.android.apps.mytracks.Constants;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.app.ActivityManager.RunningServiceInfo;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for reading service state.
|
* Helper for reading service state.
|
||||||
*
|
*
|
||||||
@@ -32,9 +37,17 @@ import android.util.Log;
|
|||||||
*/
|
*/
|
||||||
public class ServiceUtils {
|
public class ServiceUtils {
|
||||||
|
|
||||||
public static boolean isRecording(Context ctx, SharedPreferences preferences) {
|
/**
|
||||||
TrackRecordingServiceBinder serviceBinder = TrackRecordingServiceBinder.getInstance(ctx);
|
* Checks whether we're currently recording.
|
||||||
ITrackRecordingService service = serviceBinder.getServiceIfBound();
|
* The checking is done by calling the service, if provided, or alternatively by reading
|
||||||
|
* recording state saved to preferences.
|
||||||
|
*
|
||||||
|
* @param ctx the current context
|
||||||
|
* @param service the service, or null if not bound to it
|
||||||
|
* @param preferences the preferences, or null if not available
|
||||||
|
* @return true if the service is recording (or supposed to be recording), false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isRecording(Context ctx, ITrackRecordingService service, SharedPreferences preferences) {
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
try {
|
try {
|
||||||
return service.isRecording();
|
return service.isRecording();
|
||||||
@@ -51,5 +64,25 @@ public class ServiceUtils {
|
|||||||
return preferences.getLong(ctx.getString(R.string.recording_track_key), -1) > 0;
|
return preferences.getLong(ctx.getString(R.string.recording_track_key), -1) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the recording service is currently running.
|
||||||
|
*
|
||||||
|
* @param ctx the current context
|
||||||
|
* @return true if the service is running, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isServiceRunning(Context ctx) {
|
||||||
|
ActivityManager activityManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
|
List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
for (RunningServiceInfo serviceInfo : services) {
|
||||||
|
ComponentName componentName = serviceInfo.service;
|
||||||
|
String serviceName = componentName.getClassName();
|
||||||
|
if (serviceName.equals(TrackRecordingService.class.getName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private ServiceUtils() {}
|
private ServiceUtils() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1116,17 +1116,50 @@ public class TrackRecordingService extends Service {
|
|||||||
*/
|
*/
|
||||||
private static class ServiceBinder extends ITrackRecordingService.Stub {
|
private static class ServiceBinder extends ITrackRecordingService.Stub {
|
||||||
private TrackRecordingService service;
|
private TrackRecordingService service;
|
||||||
|
private DeathRecipient deathRecipient;
|
||||||
|
|
||||||
public ServiceBinder(TrackRecordingService service) {
|
public ServiceBinder(TrackRecordingService service) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logic for letting the actual service go up and down.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBinderAlive() {
|
||||||
|
// Pretend dead if the service went down.
|
||||||
|
return service != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean pingBinder() {
|
||||||
|
return isBinderAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void linkToDeath(DeathRecipient recipient, int flags) {
|
||||||
|
deathRecipient = recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
|
||||||
|
if (!isBinderAlive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
deathRecipient = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the reference to the outer class to minimize the leak.
|
* Clears the reference to the outer class to minimize the leak.
|
||||||
*/
|
*/
|
||||||
private void detachFromService() {
|
private void detachFromService() {
|
||||||
this.service = null;
|
this.service = null;
|
||||||
attachInterface(null, null);
|
attachInterface(null, null);
|
||||||
|
|
||||||
|
if (deathRecipient != null) {
|
||||||
|
deathRecipient.binderDied();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkService() {
|
private void checkService() {
|
||||||
|
|||||||
-202
@@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 static com.google.android.apps.mytracks.Constants.TAG;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A manager for the binding to the track recording service.
|
|
||||||
* This uses reference counting so multiple callers can share a binding.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public class TrackRecordingServiceBinder {
|
|
||||||
/** Singleton instance. */
|
|
||||||
private static TrackRecordingServiceBinder instance;
|
|
||||||
|
|
||||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
|
||||||
Log.d(Constants.TAG, "Service now connected.");
|
|
||||||
|
|
||||||
// Delay setting the service until we are done with initialization.
|
|
||||||
ITrackRecordingService trackRecordingService =
|
|
||||||
ITrackRecordingService.Stub.asInterface(service);
|
|
||||||
synchronized (TrackRecordingServiceBinder.this) {
|
|
||||||
TrackRecordingServiceBinder.this.trackRecordingService = trackRecordingService;
|
|
||||||
try {
|
|
||||||
for (Runnable callback : pendingBindCallbacks.keySet()) {
|
|
||||||
if (callback != null) {
|
|
||||||
callback.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
pendingBindCallbacks.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
|
||||||
Log.d(TAG, "Service now disconnected.");
|
|
||||||
synchronized (TrackRecordingServiceBinder.this) {
|
|
||||||
trackRecordingService = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Reference to the service, if bound. */
|
|
||||||
private ITrackRecordingService trackRecordingService;
|
|
||||||
|
|
||||||
/** Count of bindings to the service. */
|
|
||||||
private int bindCount = 0;
|
|
||||||
|
|
||||||
/** Set of callbacks to execute when the service connects. */
|
|
||||||
private final WeakHashMap<Runnable, Object> pendingBindCallbacks =
|
|
||||||
new WeakHashMap<Runnable, Object>();
|
|
||||||
|
|
||||||
/** Application context. */
|
|
||||||
private final Context applicationContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the service with the default intent.
|
|
||||||
*
|
|
||||||
* This should only be called when the service will be put in recording mode,
|
|
||||||
* as the service being started means it'll be listening to the GPS.
|
|
||||||
* Notice that it is perfectly fine to call {@link #bindService} without calling
|
|
||||||
* this at all.
|
|
||||||
*/
|
|
||||||
public void startService() {
|
|
||||||
Intent intent = new Intent(applicationContext, TrackRecordingService.class);
|
|
||||||
applicationContext.startService(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds to the service, and calls the given callback afterwards.
|
|
||||||
* Calls to this method should be balanced with calls to {@link #unbindService}.
|
|
||||||
*
|
|
||||||
* @param onBindCallback the callback for when the service is connected
|
|
||||||
*/
|
|
||||||
public void bindService(Runnable onBindCallback) {
|
|
||||||
synchronized (this) {
|
|
||||||
bindCount++;
|
|
||||||
if (trackRecordingService != null) {
|
|
||||||
if (onBindCallback != null) {
|
|
||||||
onBindCallback.run();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onBindCallback != null) {
|
|
||||||
pendingBindCallbacks.put(onBindCallback, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bindCount == 1) {
|
|
||||||
Intent intent = new Intent(applicationContext, TrackRecordingService.class);
|
|
||||||
int flags = SystemUtils.isRelease(applicationContext) ? 0 : Context.BIND_DEBUG_UNBIND;
|
|
||||||
applicationContext.bindService(intent, serviceConnection, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unbinds from the service.
|
|
||||||
*
|
|
||||||
* This will unbind from the service, unless another caller is still bound to it.
|
|
||||||
* Calls to this method should be balanced with calls to {@link #bindService}.
|
|
||||||
*/
|
|
||||||
public void unbindService() {
|
|
||||||
synchronized (this) {
|
|
||||||
bindCount--;
|
|
||||||
if (bindCount > 0) {
|
|
||||||
// Someone else may still be using it.
|
|
||||||
return;
|
|
||||||
} else if (bindCount < 0) {
|
|
||||||
Log.e(TAG, "Unbalanced binding calls.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
applicationContext.unbindService(serviceConnection);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
Log.d(TAG, "Tried unbinding, but service was not registered.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the service, and consequently its GPS listening.
|
|
||||||
*/
|
|
||||||
public void stopService() {
|
|
||||||
try {
|
|
||||||
applicationContext.stopService(new Intent(applicationContext, TrackRecordingService.class));
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
Log.e(TAG, "Encountered a security exception when trying to stop service.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
if (bindCount > 0) {
|
|
||||||
Log.e(TAG, "Leaked bindings: " + bindCount + " bindings left on finalize.");
|
|
||||||
}
|
|
||||||
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the connected service instance, or null if not connected.
|
|
||||||
*/
|
|
||||||
public ITrackRecordingService getServiceIfBound() {
|
|
||||||
return trackRecordingService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the singleton instance of this class.
|
|
||||||
* Notice that although a context is required, it is safe to call this from
|
|
||||||
* multiple different contexts to obtain the same instance.
|
|
||||||
*
|
|
||||||
* @param context the current context
|
|
||||||
* @return the singleton instance
|
|
||||||
*/
|
|
||||||
public synchronized static TrackRecordingServiceBinder getInstance(Context context) {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new TrackRecordingServiceBinder(context.getApplicationContext());
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal constructor.
|
|
||||||
*
|
|
||||||
* @param applicationContext the current application (not activity) context
|
|
||||||
*/
|
|
||||||
private TrackRecordingServiceBinder(Context applicationContext) {
|
|
||||||
this.applicationContext = applicationContext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
package com.google.android.apps.mytracks.services;
|
||||||
|
|
||||||
|
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.IBinder.DeathRecipient;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class TrackRecordingServiceConnection {
|
||||||
|
private ITrackRecordingService boundService;
|
||||||
|
|
||||||
|
private final DeathRecipient deathRecipient = new DeathRecipient() {
|
||||||
|
@Override
|
||||||
|
public void binderDied() {
|
||||||
|
Log.d(TAG, "Service died");
|
||||||
|
setBoundService(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
|
Log.i(TAG, "Connected to service");
|
||||||
|
try {
|
||||||
|
service.linkToDeath(deathRecipient, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Failed to bind a death recipient", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setBoundService(ITrackRecordingService.Stub.asInterface(service));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
Log.i(TAG, "Disconnected from service");
|
||||||
|
setBoundService(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private final Runnable bindChangedCallback;
|
||||||
|
|
||||||
|
public TrackRecordingServiceConnection(Context context, Runnable bindChangedCallback) {
|
||||||
|
this.context = context;
|
||||||
|
this.bindChangedCallback = bindChangedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startAndBind() {
|
||||||
|
bindService(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bindIfRunning() {
|
||||||
|
bindService(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
unbind();
|
||||||
|
|
||||||
|
Log.d(TAG, "Stopping service");
|
||||||
|
Intent intent = new Intent(context, TrackRecordingService.class);
|
||||||
|
context.stopService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbind() {
|
||||||
|
Log.d(TAG, "Unbinding from the service");
|
||||||
|
try {
|
||||||
|
context.unbindService(serviceConnection);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Means we weren't bound, which is ok.
|
||||||
|
}
|
||||||
|
|
||||||
|
setBoundService(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITrackRecordingService getServiceIfBound() {
|
||||||
|
return boundService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindService(boolean startIfNeeded) {
|
||||||
|
if (boundService != null) {
|
||||||
|
// Already bound.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!startIfNeeded && !ServiceUtils.isServiceRunning(context)) {
|
||||||
|
// Not running, start not requested.
|
||||||
|
Log.d(TAG, "Service not running, not binding to it.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startIfNeeded) {
|
||||||
|
Log.i(TAG, "Starting the service");
|
||||||
|
Intent intent = new Intent(context, TrackRecordingService.class);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Binding to the service");
|
||||||
|
Intent intent = new Intent(context, TrackRecordingService.class);
|
||||||
|
int flags = SystemUtils.isRelease(context) ? 0 : Context.BIND_DEBUG_UNBIND;
|
||||||
|
context.bindService(intent, serviceConnection, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBoundService(ITrackRecordingService service) {
|
||||||
|
boundService = service;
|
||||||
|
if (bindChangedCallback != null) {
|
||||||
|
bindChangedCallback.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010 Google Inc.
|
* Copyright 2010 Google Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
* the License at
|
* the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
package com.google.android.apps.mytracks;
|
package com.google.android.apps.mytracks;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||||
|
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -33,11 +34,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A unit test for {@link MyTracks} activity.
|
* A unit test for {@link MyTracks} activity.
|
||||||
*
|
*
|
||||||
* @author Bartlomiej Niechwiej
|
* @author Bartlomiej Niechwiej
|
||||||
*/
|
*/
|
||||||
public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
|
private TrackRecordingServiceConnection serviceConnection;
|
||||||
|
|
||||||
public MyTracksTest() {
|
public MyTracksTest() {
|
||||||
super(MyTracks.class);
|
super(MyTracks.class);
|
||||||
@@ -126,7 +128,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
|||||||
assertInitialized();
|
assertInitialized();
|
||||||
|
|
||||||
// Check if not recording.
|
// Check if not recording.
|
||||||
clearSelectedAndRecordingTracks();
|
clearSelectedAndRecordingTracks();
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
|
|
||||||
assertFalse(isRecording());
|
assertFalse(isRecording());
|
||||||
@@ -137,6 +139,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
|||||||
|
|
||||||
// Start a new track.
|
// Start a new track.
|
||||||
getActivity().startRecording();
|
getActivity().startRecording();
|
||||||
|
serviceConnection.bindIfRunning();
|
||||||
long recordingTrackId = awaitRecordingStatus(5000, true);
|
long recordingTrackId = awaitRecordingStatus(5000, true);
|
||||||
assertTrue(recordingTrackId >= 0);
|
assertTrue(recordingTrackId >= 0);
|
||||||
|
|
||||||
@@ -149,7 +152,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
|||||||
assertEquals(recordingTrackId, selectedTrackId);
|
assertEquals(recordingTrackId, selectedTrackId);
|
||||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||||
|
|
||||||
// Watch for MyTracksDetails activity.
|
// Watch for MyTracksDetails activity.
|
||||||
ActivityMonitor monitor = getInstrumentation().addMonitor(
|
ActivityMonitor monitor = getInstrumentation().addMonitor(
|
||||||
TrackDetails.class.getName(), null, false);
|
TrackDetails.class.getName(), null, false);
|
||||||
|
|
||||||
@@ -157,7 +160,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
|||||||
// no longer recording.
|
// no longer recording.
|
||||||
getActivity().stopRecording();
|
getActivity().stopRecording();
|
||||||
|
|
||||||
// Check if we got back MyTracksDetails activity.
|
// Check if we got back MyTracksDetails activity.
|
||||||
Activity activity = getInstrumentation().waitForMonitor(monitor);
|
Activity activity = getInstrumentation().waitForMonitor(monitor);
|
||||||
assertTrue(activity instanceof TrackDetails);
|
assertTrue(activity instanceof TrackDetails);
|
||||||
|
|
||||||
@@ -180,13 +183,15 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
|||||||
|
|
||||||
private void assertInitialized() {
|
private void assertInitialized() {
|
||||||
assertNotNull(getActivity());
|
assertNotNull(getActivity());
|
||||||
|
|
||||||
|
serviceConnection = new TrackRecordingServiceConnection(getActivity(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits until the UI thread becomes idle.
|
* Waits until the UI thread becomes idle.
|
||||||
*/
|
*/
|
||||||
private void waitForIdle() throws InterruptedException {
|
private void waitForIdle() throws InterruptedException {
|
||||||
// Note: We can't use getInstrumentation().waitForIdleSync() here.
|
// Note: We can't use getInstrumentation().waitForIdleSync() here.
|
||||||
final Object semaphore = new Object();
|
final Object semaphore = new Object();
|
||||||
synchronized (semaphore) {
|
synchronized (semaphore) {
|
||||||
final AtomicBoolean isIdle = new AtomicBoolean();
|
final AtomicBoolean isIdle = new AtomicBoolean();
|
||||||
@@ -216,10 +221,10 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
|||||||
editor.clear();
|
editor.clear();
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits until the recording state changes to the given status.
|
* Waits until the recording state changes to the given status.
|
||||||
*
|
*
|
||||||
* @param timeout the maximum time to wait, in milliseconds.
|
* @param timeout the maximum time to wait, in milliseconds.
|
||||||
* @param isRecording the final status to await.
|
* @param isRecording the final status to await.
|
||||||
* @return the recording track ID.
|
* @return the recording track ID.
|
||||||
@@ -250,6 +255,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRecording() {
|
private boolean isRecording() {
|
||||||
return ServiceUtils.isRecording(getActivity(), getSharedPreferences());
|
return ServiceUtils.isRecording(getActivity(),
|
||||||
|
serviceConnection.getServiceIfBound(), getSharedPreferences());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-112
@@ -49,9 +49,9 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the MyTracks track recording service.
|
* Tests for the MyTracks track recording service.
|
||||||
*
|
*
|
||||||
* @author Bartlomiej Niechwiej
|
* @author Bartlomiej Niechwiej
|
||||||
*
|
*
|
||||||
* TODO: The original class, ServiceTestCase, has a few limitations, e.g.
|
* TODO: The original class, ServiceTestCase, has a few limitations, e.g.
|
||||||
* it's not possible to properly shutdown the service, unless tearDown()
|
* it's not possible to properly shutdown the service, unless tearDown()
|
||||||
* is called, which prevents from testing multiple scenarios in a single
|
* is called, which prevents from testing multiple scenarios in a single
|
||||||
@@ -63,7 +63,7 @@ public class TrackRecordingServiceTest
|
|||||||
private Context context;
|
private Context context;
|
||||||
private MyTracksProviderUtils providerUtils;
|
private MyTracksProviderUtils providerUtils;
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In order to support starting and binding to the service in the same
|
* In order to support starting and binding to the service in the same
|
||||||
* unit test, we provide a workaround, as the original class doesn't allow
|
* unit test, we provide a workaround, as the original class doesn't allow
|
||||||
@@ -72,19 +72,19 @@ public class TrackRecordingServiceTest
|
|||||||
|
|
||||||
private boolean bound;
|
private boolean bound;
|
||||||
private Intent serviceIntent;
|
private Intent serviceIntent;
|
||||||
|
|
||||||
public TrackRecordingServiceTest() {
|
public TrackRecordingServiceTest() {
|
||||||
super(TrackRecordingService.class);
|
super(TrackRecordingService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A context wrapper with the user provided {@link ContentResolver}.
|
* A context wrapper with the user provided {@link ContentResolver}.
|
||||||
*
|
*
|
||||||
* TODO: Move to test utils package.
|
* TODO: Move to test utils package.
|
||||||
*/
|
*/
|
||||||
public static class MockContext extends ContextWrapper {
|
public static class MockContext extends ContextWrapper {
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
public MockContext(ContentResolver contentResolver, Context base) {
|
public MockContext(ContentResolver contentResolver, Context base) {
|
||||||
super(base);
|
super(base);
|
||||||
this.contentResolver = contentResolver;
|
this.contentResolver = contentResolver;
|
||||||
@@ -108,7 +108,7 @@ public class TrackRecordingServiceTest
|
|||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IBinder bindService(Intent intent) {
|
protected IBinder bindService(Intent intent) {
|
||||||
if (getService() != null) {
|
if (getService() != null) {
|
||||||
@@ -190,7 +190,7 @@ public class TrackRecordingServiceTest
|
|||||||
startService(startIntent);
|
startService(startIntent);
|
||||||
assertNotNull(getService());
|
assertNotNull(getService());
|
||||||
|
|
||||||
// We expect to resume the previous track.
|
// We expect to resume the previous track.
|
||||||
assertTrue(getService().isRecording());
|
assertTrue(getService().isRecording());
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
assertEquals(123, service.getRecordingTrackId());
|
assertEquals(123, service.getRecordingTrackId());
|
||||||
@@ -293,7 +293,7 @@ public class TrackRecordingServiceTest
|
|||||||
assertTrue(tracks.isEmpty());
|
assertTrue(tracks.isEmpty());
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
// Test if we start in no-recording mode by default.
|
// Test if we start in no-recording mode by default.
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
assertEquals(-1, service.getRecordingTrackId());
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
@@ -463,92 +463,19 @@ public class TrackRecordingServiceTest
|
|||||||
context.getString(R.string.recording_track_key), 0));
|
context.getString(R.string.recording_track_key), 0));
|
||||||
assertEquals(-1, service.getRecordingTrackId());
|
assertEquals(-1, service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testIntegration_completeRecordingSession() throws Exception {
|
public void testIntegration_completeRecordingSession() throws Exception {
|
||||||
List<Track> tracks = providerUtils.getAllTracks();
|
List<Track> tracks = providerUtils.getAllTracks();
|
||||||
assertTrue(tracks.isEmpty());
|
assertTrue(tracks.isEmpty());
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testDeleteAllTracks_noRecording() throws Exception {
|
|
||||||
createDummyTrack(123, -1, false);
|
|
||||||
assertEquals(1, providerUtils.getAllTracks().size());
|
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
|
|
||||||
// Deleting all tracks should succeed.
|
|
||||||
service.deleteAllTracks();
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
assertTrue(providerUtils.getAllTracks().isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testDeleteAllTracks_noTracks() throws Exception {
|
|
||||||
assertTrue(providerUtils.getAllTracks().isEmpty());
|
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
|
|
||||||
// Deleting all tracks should succeed.
|
|
||||||
service.deleteAllTracks();
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
assertTrue(providerUtils.getAllTracks().isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testDeleteAllTracks_trackInProgress() throws Exception {
|
|
||||||
createDummyTrack(123, -1, true);
|
|
||||||
assertEquals(1, providerUtils.getAllTracks().size());
|
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
|
||||||
assertTrue(service.isRecording());
|
|
||||||
|
|
||||||
// Since we have a track in progress, we expect to fail.
|
|
||||||
try {
|
|
||||||
service.deleteAllTracks();
|
|
||||||
fail("Expecting IllegalStateException");
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// Expected.
|
|
||||||
}
|
|
||||||
assertTrue(service.isRecording());
|
|
||||||
assertEquals(1, providerUtils.getAllTracks().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testHasRecorded_noTracks() throws Exception {
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
assertFalse(service.hasRecorded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testHasRecorded_trackInProgress() throws Exception {
|
|
||||||
createDummyTrack(123, -1, true);
|
|
||||||
assertEquals(1, providerUtils.getAllTracks().size());
|
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
|
||||||
assertTrue(service.isRecording());
|
|
||||||
assertTrue(service.hasRecorded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
public void testHasRecorded_oldTracks() throws Exception {
|
|
||||||
createDummyTrack(123, -1, false);
|
|
||||||
assertEquals(1, providerUtils.getAllTracks().size());
|
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
assertTrue(service.hasRecorded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testInsertStatisticsMarker_noRecordingTrack() throws Exception {
|
public void testInsertStatisticsMarker_noRecordingTrack() throws Exception {
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||||
fail("Expecting IllegalStateException");
|
fail("Expecting IllegalStateException");
|
||||||
@@ -556,17 +483,17 @@ public class TrackRecordingServiceTest
|
|||||||
// Expected.
|
// Expected.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testInsertStatisticsMarker_validLocation() throws Exception {
|
public void testInsertStatisticsMarker_validLocation() throws Exception {
|
||||||
createDummyTrack(123, -1, true);
|
createDummyTrack(123, -1, true);
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||||
assertEquals(2, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
assertEquals(2, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||||
|
|
||||||
Waypoint wpt = providerUtils.getWaypoint(1);
|
Waypoint wpt = providerUtils.getWaypoint(1);
|
||||||
assertEquals(getContext().getString(R.string.stats_icon_url),
|
assertEquals(getContext().getString(R.string.stats_icon_url),
|
||||||
wpt.getIcon());
|
wpt.getIcon());
|
||||||
@@ -586,7 +513,7 @@ public class TrackRecordingServiceTest
|
|||||||
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER);
|
service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER);
|
||||||
fail("Expecting IllegalStateException");
|
fail("Expecting IllegalStateException");
|
||||||
@@ -594,14 +521,14 @@ public class TrackRecordingServiceTest
|
|||||||
// Expected.
|
// Expected.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
||||||
createDummyTrack(123, -1, true);
|
createDummyTrack(123, -1, true);
|
||||||
|
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER));
|
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER));
|
||||||
Waypoint wpt = providerUtils.getWaypoint(1);
|
Waypoint wpt = providerUtils.getWaypoint(1);
|
||||||
assertEquals(getContext().getString(R.string.waypoint_icon_url),
|
assertEquals(getContext().getString(R.string.waypoint_icon_url),
|
||||||
@@ -614,7 +541,7 @@ public class TrackRecordingServiceTest
|
|||||||
assertNotNull(wpt.getLocation());
|
assertNotNull(wpt.getLocation());
|
||||||
assertNull(wpt.getStatistics());
|
assertNull(wpt.getStatistics());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testWithProperties_noAnnouncementFreq() throws Exception {
|
public void testWithProperties_noAnnouncementFreq() throws Exception {
|
||||||
functionalTest(R.string.announcement_frequency_key, (Object) null);
|
functionalTest(R.string.announcement_frequency_key, (Object) null);
|
||||||
@@ -624,7 +551,7 @@ public class TrackRecordingServiceTest
|
|||||||
public void testWithProperties_defaultAnnouncementFreq() throws Exception {
|
public void testWithProperties_defaultAnnouncementFreq() throws Exception {
|
||||||
functionalTest(R.string.announcement_frequency_key, 1);
|
functionalTest(R.string.announcement_frequency_key, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testWithProperties_noMaxRecordingDist() throws Exception {
|
public void testWithProperties_noMaxRecordingDist() throws Exception {
|
||||||
functionalTest(R.string.max_recording_distance_key, (Object) null);
|
functionalTest(R.string.max_recording_distance_key, (Object) null);
|
||||||
@@ -654,7 +581,7 @@ public class TrackRecordingServiceTest
|
|||||||
public void testWithProperties_defaultSplitFreqByDist() throws Exception {
|
public void testWithProperties_defaultSplitFreqByDist() throws Exception {
|
||||||
functionalTest(R.string.split_frequency_key, 5);
|
functionalTest(R.string.split_frequency_key, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testWithProperties_defaultSplitFreqByTime() throws Exception {
|
public void testWithProperties_defaultSplitFreqByTime() throws Exception {
|
||||||
functionalTest(R.string.split_frequency_key, -2);
|
functionalTest(R.string.split_frequency_key, -2);
|
||||||
@@ -674,7 +601,7 @@ public class TrackRecordingServiceTest
|
|||||||
public void testWithProperties_metricUnitsDisabled() throws Exception {
|
public void testWithProperties_metricUnitsDisabled() throws Exception {
|
||||||
functionalTest(R.string.metric_units_key, false);
|
functionalTest(R.string.metric_units_key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testWithProperties_noMinRecordingInterval() throws Exception {
|
public void testWithProperties_noMinRecordingInterval() throws Exception {
|
||||||
functionalTest(R.string.min_recording_interval_key, (Object) null);
|
functionalTest(R.string.min_recording_interval_key, (Object) null);
|
||||||
@@ -690,23 +617,23 @@ public class TrackRecordingServiceTest
|
|||||||
public void testWithProperties_noMinRequiredAccuracy() throws Exception {
|
public void testWithProperties_noMinRequiredAccuracy() throws Exception {
|
||||||
functionalTest(R.string.min_required_accuracy_key, (Object) null);
|
functionalTest(R.string.min_required_accuracy_key, (Object) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testWithProperties_defaultMinRequiredAccuracy() throws Exception {
|
public void testWithProperties_defaultMinRequiredAccuracy() throws Exception {
|
||||||
functionalTest(R.string.min_required_accuracy_key, 500);
|
functionalTest(R.string.min_required_accuracy_key, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testWithProperties_noSensorType() throws Exception {
|
public void testWithProperties_noSensorType() throws Exception {
|
||||||
functionalTest(R.string.sensor_type_key, (Object) null);
|
functionalTest(R.string.sensor_type_key, (Object) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public void testWithProperties_zephyrSensorType() throws Exception {
|
public void testWithProperties_zephyrSensorType() throws Exception {
|
||||||
functionalTest(R.string.sensor_type_key,
|
functionalTest(R.string.sensor_type_key,
|
||||||
context.getString(R.string.zephyr_sensor_type));
|
context.getString(R.string.zephyr_sensor_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ITrackRecordingService bindAndGetService(Intent intent) {
|
private ITrackRecordingService bindAndGetService(Intent intent) {
|
||||||
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(
|
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(
|
||||||
bindService(intent));
|
bindService(intent));
|
||||||
@@ -724,7 +651,7 @@ public class TrackRecordingServiceTest
|
|||||||
addTrack(dummyTrack, isRecording);
|
addTrack(dummyTrack, isRecording);
|
||||||
return dummyTrack;
|
return dummyTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAutoResumePrefs(int attempts, int timeoutMins) {
|
private void updateAutoResumePrefs(int attempts, int timeoutMins) {
|
||||||
Editor editor = sharedPreferences.edit();
|
Editor editor = sharedPreferences.edit();
|
||||||
editor.putInt(context.getString(
|
editor.putInt(context.getString(
|
||||||
@@ -733,20 +660,20 @@ public class TrackRecordingServiceTest
|
|||||||
R.string.auto_resume_track_timeout_key), timeoutMins);
|
R.string.auto_resume_track_timeout_key), timeoutMins);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Intent createStartIntent() {
|
private Intent createStartIntent() {
|
||||||
Intent startIntent = new Intent();
|
Intent startIntent = new Intent();
|
||||||
startIntent.setClass(context, TrackRecordingService.class);
|
startIntent.setClass(context, TrackRecordingService.class);
|
||||||
return startIntent;
|
return startIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTrack(Track track, boolean isRecording) {
|
private void addTrack(Track track, boolean isRecording) {
|
||||||
assertTrue(track.getId() >= 0);
|
assertTrue(track.getId() >= 0);
|
||||||
providerUtils.insertTrack(track);
|
providerUtils.insertTrack(track);
|
||||||
assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
||||||
setRecordingTrack(isRecording ? track.getId() : -1);
|
setRecordingTrack(isRecording ? track.getId() : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setRecordingTrack(long id) {
|
private void setRecordingTrack(long id) {
|
||||||
Editor editor = sharedPreferences.edit();
|
Editor editor = sharedPreferences.edit();
|
||||||
editor.putLong(context.getString(R.string.recording_track_key), id);
|
editor.putLong(context.getString(R.string.recording_track_key), id);
|
||||||
@@ -760,11 +687,11 @@ public class TrackRecordingServiceTest
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
final String key = context.getString(resourceId);
|
final String key = context.getString(resourceId);
|
||||||
for (Object value : values) {
|
for (Object value : values) {
|
||||||
// Remove all properties and set the property for the given key.
|
// Remove all properties and set the property for the given key.
|
||||||
Editor editor = sharedPreferences.edit();
|
Editor editor = sharedPreferences.edit();
|
||||||
editor.clear();
|
editor.clear();
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
editor.putString(key, (String) value);
|
editor.putString(key, (String) value);
|
||||||
} else if (value instanceof Long) {
|
} else if (value instanceof Long) {
|
||||||
editor.putLong(key, (Long) value);
|
editor.putLong(key, (Long) value);
|
||||||
} else if (value instanceof Integer) {
|
} else if (value instanceof Integer) {
|
||||||
@@ -775,15 +702,15 @@ public class TrackRecordingServiceTest
|
|||||||
// Do nothing, as clear above has already removed this property.
|
// Do nothing, as clear above has already removed this property.
|
||||||
}
|
}
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fullRecordingSession() throws Exception {
|
private void fullRecordingSession() throws Exception {
|
||||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
// Start a track.
|
// Start a track.
|
||||||
long id = service.startNewTrack();
|
long id = service.startNewTrack();
|
||||||
assertTrue(id >= 0);
|
assertTrue(id >= 0);
|
||||||
@@ -806,14 +733,14 @@ public class TrackRecordingServiceTest
|
|||||||
loc.setTime(startTime + i * 10000);
|
loc.setTime(startTime + i * 10000);
|
||||||
loc.setBearing(3.0f);
|
loc.setBearing(3.0f);
|
||||||
service.recordLocation(loc);
|
service.recordLocation(loc);
|
||||||
|
|
||||||
if (i % 10 == 0) {
|
if (i % 10 == 0) {
|
||||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||||
} else if (i % 7 == 0) {
|
} else if (i % 7 == 0) {
|
||||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER);
|
service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the track. Validate if it has correct data.
|
// Stop the track. Validate if it has correct data.
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|||||||
Reference in New Issue
Block a user