forked from upstream-mirrors/OpenTracks
Cutting service binding and preferences dependencies to the MyTracks singleton.
This commit is contained in:
@@ -23,26 +23,23 @@ import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
import com.google.android.apps.mytracks.io.file.TempFileCleaner;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceBinder;
|
||||
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.TabActivity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentUris;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
@@ -60,6 +57,7 @@ import android.widget.Toast;
|
||||
* The super activity that embeds our sub activities.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
private TrackDataHub dataHub;
|
||||
@@ -80,48 +78,12 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
*/
|
||||
private boolean startNewTrackRequested = false;
|
||||
|
||||
private ITrackRecordingService trackRecordingService;
|
||||
|
||||
/**
|
||||
* Utilities to deal with the database.
|
||||
*/
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
/**
|
||||
* The connection to the track recording service.
|
||||
*/
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
Log.d(Constants.TAG, "MyTracks: Service now connected.");
|
||||
// Delay setting the service until we are done with initialization.
|
||||
ITrackRecordingService trackRecordingService =
|
||||
ITrackRecordingService.Stub.asInterface(service);
|
||||
try {
|
||||
// TODO: Send a start service intent and broadcast service started
|
||||
// message to avoid the hack below and a race condition.
|
||||
if (startNewTrackRequested) {
|
||||
startNewTrackRequested = false;
|
||||
startRecordingNewTrack(trackRecordingService);
|
||||
}
|
||||
} finally {
|
||||
MyTracks.this.trackRecordingService = trackRecordingService;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
Log.d(TAG, "MyTracks: Service now disconnected.");
|
||||
trackRecordingService = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether {@link #serviceConnection} is bound or not.
|
||||
*/
|
||||
private boolean isBound = false;
|
||||
private TrackRecordingServiceBinder serviceBinder;
|
||||
|
||||
/*
|
||||
* Tabs/View navigation:
|
||||
@@ -135,6 +97,18 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable serviceBindCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (serviceBinder) {
|
||||
ITrackRecordingService service = serviceBinder.getServiceIfBound();
|
||||
if (startNewTrackRequested && service != null) {
|
||||
startRecordingNewTrack(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether we have a track recording session in progress.
|
||||
* In some cases, when the service has crashed or has been restarted
|
||||
@@ -145,6 +119,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
* based on settings from the shared preferences.
|
||||
*/
|
||||
public boolean isRecording() {
|
||||
ITrackRecordingService trackRecordingService = serviceBinder.getServiceIfBound();
|
||||
if (trackRecordingService == null) {
|
||||
// Fall back to alternative check method.
|
||||
return dataHub.isRecording();
|
||||
@@ -177,10 +152,10 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
}
|
||||
|
||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||
dataHub = new TrackDataHub(this, sharedPreferences, providerUtils);
|
||||
dataHub = new TrackDataHub(this, providerUtils);
|
||||
menuManager = new MenuManager(this);
|
||||
dialogManager = new DialogManager(this);
|
||||
serviceBinder = TrackRecordingServiceBinder.getInstance(this);
|
||||
|
||||
// The volume we want to control is the Text-To-Speech volume
|
||||
int volumeStream =
|
||||
@@ -230,7 +205,6 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
|
||||
dataHub.destroy();
|
||||
|
||||
tryUnbindTrackRecordingService();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -250,7 +224,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
// 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");
|
||||
tryUnbindTrackRecordingService();
|
||||
serviceBinder.unbindService();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@@ -259,7 +233,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
// Called when the current activity is being displayed or re-displayed
|
||||
// to the user.
|
||||
Log.d(TAG, "MyTracks.onResume");
|
||||
tryBindTrackRecordingService();
|
||||
serviceBinder.bindService(serviceBindCallback);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@@ -271,8 +245,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
|
||||
// Ensure that service is running if we're supposed to be recording
|
||||
if (dataHub.isRecording()) {
|
||||
Intent startIntent = new Intent(this, TrackRecordingService.class);
|
||||
startService(startIntent);
|
||||
serviceBinder.startService();
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
@@ -445,6 +418,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
* @throws RemoteException If the call on the service failed.
|
||||
*/
|
||||
public long insertWaypoint(WaypointCreationRequest request) throws RemoteException {
|
||||
ITrackRecordingService trackRecordingService = serviceBinder.getServiceIfBound();
|
||||
if (trackRecordingService == null) {
|
||||
throw new IllegalStateException("The recording service is not bound.");
|
||||
}
|
||||
@@ -485,14 +459,16 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
* Starts the track recording service (if not already running) and binds to
|
||||
* it. Starts recording a new track.
|
||||
*/
|
||||
public void startRecording() {
|
||||
if (trackRecordingService == null) {
|
||||
void startRecording() {
|
||||
synchronized (serviceBinder) {
|
||||
startNewTrackRequested = true;
|
||||
Intent startIntent = new Intent(this, TrackRecordingService.class);
|
||||
startService(startIntent);
|
||||
tryBindTrackRecordingService();
|
||||
} else {
|
||||
startRecordingNewTrack(trackRecordingService);
|
||||
serviceBinder.startService();
|
||||
|
||||
// Binding was already requested before, it either already happened
|
||||
// (in which case running the callback manually triggers the actual recording start)
|
||||
// or it will happen in the future
|
||||
// (in which case running the callback now will have no effect).
|
||||
serviceBindCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,8 +477,10 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
* "Stopped recording" and pop up the Track Details activity.
|
||||
*/
|
||||
public void stopRecording() {
|
||||
ITrackRecordingService trackRecordingService = serviceBinder.getServiceIfBound();
|
||||
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 {
|
||||
trackRecordingService.endCurrentTrack();
|
||||
@@ -519,14 +497,10 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
tryUnbindTrackRecordingService();
|
||||
try {
|
||||
stopService(new Intent(MyTracks.this, TrackRecordingService.class));
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Encountered a security exception when trying to stop service.", e);
|
||||
}
|
||||
trackRecordingService = null;
|
||||
|
||||
serviceBinder.stopService();
|
||||
}
|
||||
|
||||
void clearSelectedTrack() {
|
||||
dataHub.unloadCurrentTrack();
|
||||
}
|
||||
@@ -535,54 +509,10 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
return dataHub.getSelectedTrackId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds to track recording service if it is running.
|
||||
*/
|
||||
private void tryBindTrackRecordingService() {
|
||||
Log.d(TAG,
|
||||
"MyTracks: Trying to bind to track recording service...");
|
||||
bindService(new Intent(this, TrackRecordingService.class),
|
||||
serviceConnection, 0);
|
||||
Log.d(TAG, "MyTracks: ...bind finished!");
|
||||
isBound = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to unbind the track recording service. Catches exception silently in
|
||||
* case service is not registered anymore.
|
||||
*/
|
||||
private void tryUnbindTrackRecordingService() {
|
||||
if (isBound) {
|
||||
Log.d(TAG, "MyTracks: Trying to unbind from track recording service...");
|
||||
try {
|
||||
unbindService(serviceConnection);
|
||||
Log.d(TAG, "MyTracks: ...unbind finished!");
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(TAG, "MyTracks: Tried unbinding, but service was not registered.", e);
|
||||
}
|
||||
isBound = false;
|
||||
}
|
||||
}
|
||||
|
||||
public DialogManager getDialogManager() {
|
||||
return dialogManager;
|
||||
}
|
||||
|
||||
// @VisibleForTesting
|
||||
long getRecordingTrackId() {
|
||||
return sharedPreferences.getLong(getString(R.string.recording_track_key), -1);
|
||||
}
|
||||
|
||||
// @VisibleForTesting
|
||||
SharedPreferences getSharedPreferences() {
|
||||
return sharedPreferences;
|
||||
}
|
||||
|
||||
// @VisibleForTesting
|
||||
ITrackRecordingService getTrackRecordingService() {
|
||||
return trackRecordingService;
|
||||
}
|
||||
|
||||
public TrackDataHub getDataHub() {
|
||||
return dataHub;
|
||||
}
|
||||
@@ -593,8 +523,4 @@ public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
public static MyTracks getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void clearInstance() {
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks;
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceBinder;
|
||||
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
@@ -45,6 +46,7 @@ public class SensorStateActivity extends Activity {
|
||||
private static final long REFRESH_PERIOD_MS = 250;
|
||||
|
||||
private final StatsUtilities utils;
|
||||
private TrackRecordingServiceBinder serviceBinder;
|
||||
|
||||
/**
|
||||
* This timer periodically invokes the refresh timer task.
|
||||
@@ -79,6 +81,8 @@ public class SensorStateActivity extends Activity {
|
||||
|
||||
setContentView(R.layout.sensor_state);
|
||||
|
||||
serviceBinder = TrackRecordingServiceBinder.getInstance(this);
|
||||
serviceBinder.bindService(stateUpdater);
|
||||
updateState();
|
||||
}
|
||||
|
||||
@@ -97,11 +101,13 @@ public class SensorStateActivity extends Activity {
|
||||
timer = null;
|
||||
}
|
||||
|
||||
protected void updateState() {
|
||||
MyTracks mt = MyTracks.getInstance();
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
serviceBinder.unbindService();
|
||||
}
|
||||
|
||||
ITrackRecordingService service =
|
||||
mt == null ? null : mt.getTrackRecordingService();
|
||||
protected void updateState() {
|
||||
ITrackRecordingService service = serviceBinder.getServiceIfBound();
|
||||
if (service == null) {
|
||||
Log.d(Constants.TAG, "Could not get track recording service.");
|
||||
updateSensorState(Sensor.SensorState.NONE);
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.google.android.apps.mytracks.Constants.MAX_LOCATION_AGE_MS;
|
||||
import static com.google.android.apps.mytracks.Constants.MAX_NETWORK_AGE_MS;
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.DataSourceManager.DataSourceListener;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.DoubleBufferedLocationFactory;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||
@@ -195,10 +196,17 @@ public class TrackDataHub {
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public TrackDataHub(Context ctx, SharedPreferences preferences,
|
||||
public TrackDataHub(Context ctx, MyTracksProviderUtils providerUtils) {
|
||||
this(ctx, ctx.getSharedPreferences(Constants.SETTINGS_NAME, 0), providerUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intermediate constructor, used only for passing preferences into arguments.
|
||||
*/
|
||||
private TrackDataHub(Context ctx, SharedPreferences preferences,
|
||||
MyTracksProviderUtils providerUtils) {
|
||||
this(ctx, new DataSourcesWrapperImpl(ctx, preferences), new TrackDataListeners(),
|
||||
preferences, providerUtils, TARGET_DISPLAYED_TRACK_POINTS);
|
||||
preferences, providerUtils, TARGET_DISPLAYED_TRACK_POINTS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -785,7 +785,7 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
if (intent != null &&
|
||||
intent.getBooleanExtra(RESUME_TRACK_EXTRA_NAME, false)) {
|
||||
Log.d(TAG, "TrackRecordingService: requested resume");
|
||||
|
||||
|
||||
// Make sure that the current track exists and is fresh enough.
|
||||
if (recordingTrack == null || !shouldResumeTrack(recordingTrack)) {
|
||||
Log.i(TAG,
|
||||
@@ -796,7 +796,7 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
stopSelfResult(startId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Log.i(TAG, "TrackRecordingService: resuming");
|
||||
}
|
||||
}
|
||||
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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 helper for managing 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) {
|
||||
try {
|
||||
for (Runnable callback : pendingBindCallbacks.keySet()) {
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
pendingBindCallbacks.clear();
|
||||
TrackRecordingServiceBinder.this.trackRecordingService = trackRecordingService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
Log.d(TAG, "Service now disconnected.");
|
||||
synchronized (TrackRecordingServiceBinder.this) {
|
||||
trackRecordingService = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Pointer 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) {
|
||||
Context applicationContext = context.getApplicationContext();
|
||||
instance = new TrackRecordingServiceBinder(applicationContext);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal constructor.
|
||||
*
|
||||
* @param applicationContext the current application (not activity) context
|
||||
*/
|
||||
private TrackRecordingServiceBinder(Context applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
||||
@@ -36,56 +36,48 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
public MyTracksTest() {
|
||||
super(MyTracks.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
MyTracks.clearInstance();
|
||||
assertNull(MyTracks.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
clearSelectedAndRecordingTracks();
|
||||
waitForIdle();
|
||||
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
|
||||
public void testInitialization_mainAction() {
|
||||
// Make sure we can start MyTracks and the activity doesn't start recording.
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
assertInitialized();
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
assertEquals(-1, getRecordingTrackId());
|
||||
long selectedTrackId = getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
}
|
||||
|
||||
|
||||
public void testInitialization_viewActionWithNoData() {
|
||||
// Simulate start with ACTION_VIEW intent.
|
||||
Intent startIntent = new Intent();
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
setActivityIntent(startIntent);
|
||||
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
|
||||
assertInitialized();
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
assertEquals(-1, getRecordingTrackId());
|
||||
long selectedTrackId = getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
}
|
||||
|
||||
|
||||
public void testInitialization_viewActionWithValidData() throws Exception {
|
||||
// Simulate start with ACTION_VIEW intent.
|
||||
Intent startIntent = new Intent();
|
||||
@@ -93,24 +85,22 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
Uri uri = Uri.fromFile(File.createTempFile("valid", ".gpx"));
|
||||
|
||||
// TODO: Add a valid GPX.
|
||||
|
||||
|
||||
startIntent.setData(uri);
|
||||
setActivityIntent(startIntent);
|
||||
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
|
||||
assertInitialized();
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
assertEquals(-1, getRecordingTrackId());
|
||||
long selectedTrackId = getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
// TODO: Finish this test.
|
||||
|
||||
// TODO: Finish this test.
|
||||
}
|
||||
|
||||
|
||||
public void testInitialization_viewActionWithInvalidData() throws Exception {
|
||||
// Simulate start with ACTION_VIEW intent.
|
||||
Intent startIntent = new Intent();
|
||||
@@ -118,34 +108,29 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
Uri uri = Uri.fromFile(File.createTempFile("invalid", ".gpx"));
|
||||
startIntent.setData(uri);
|
||||
setActivityIntent(startIntent);
|
||||
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
|
||||
assertInitialized();
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
assertEquals(-1, getRecordingTrackId());
|
||||
long selectedTrackId = getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
|
||||
// TODO: Finish this test.
|
||||
}
|
||||
|
||||
|
||||
public void testRecording_startAndStop() throws Exception {
|
||||
// Make sure we can start MyTracks and the activity doesn't start recording.
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
assertInitialized();
|
||||
|
||||
// Check if not recording.
|
||||
clearSelectedAndRecordingTracks();
|
||||
waitForIdle();
|
||||
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
assertEquals(-1, getRecordingTrackId());
|
||||
long selectedTrackId = getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
@@ -153,16 +138,16 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
getActivity().startRecording();
|
||||
long recordingTrackId = awaitRecordingStatus(5000, true);
|
||||
assertTrue(recordingTrackId >= 0);
|
||||
|
||||
|
||||
// Wait until we are done and make sure that selectedTrack = recordingTrack.
|
||||
waitForIdle();
|
||||
assertEquals(recordingTrackId, getActivity().getSharedPreferences().getLong(
|
||||
assertEquals(recordingTrackId, getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.recording_track_key), -1));
|
||||
selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
selectedTrackId = getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(recordingTrackId, selectedTrackId);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
|
||||
// Watch for MyTracksDetails activity.
|
||||
ActivityMonitor monitor = getInstrumentation().addMonitor(
|
||||
TrackDetails.class.getName(), null, false);
|
||||
@@ -170,14 +155,14 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
// Now, stop the track and make sure that it is still selected, but
|
||||
// no longer recording.
|
||||
getActivity().stopRecording();
|
||||
|
||||
|
||||
// Check if we got back MyTracksDetails activity.
|
||||
Activity activity = getInstrumentation().waitForMonitor(monitor);
|
||||
assertTrue(activity instanceof TrackDetails);
|
||||
|
||||
|
||||
// TODO: Update track name and other properties and test if they were
|
||||
// properly saved.
|
||||
|
||||
|
||||
// Simulate a click on Save button.
|
||||
Button save = (Button) activity.findViewById(R.id.trackdetails_save);
|
||||
save.performClick();
|
||||
@@ -185,13 +170,18 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
// Check the remaining properties.
|
||||
recordingTrackId = awaitRecordingStatus(5000, false);
|
||||
assertEquals(-1, recordingTrackId);
|
||||
assertEquals(recordingTrackId, getActivity().getRecordingTrackId());
|
||||
assertEquals(recordingTrackId, getActivity().getSharedPreferences().getLong(
|
||||
assertEquals(recordingTrackId, getRecordingTrackId());
|
||||
assertEquals(recordingTrackId, getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.recording_track_key), -1));
|
||||
// Make sure this is the same track as the last recording track ID.
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
}
|
||||
|
||||
|
||||
private void assertInitialized() {
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the UI thread becomes idle.
|
||||
*/
|
||||
@@ -216,13 +206,13 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears {selected,recording}TrackId in the {@link SharedPreferences}.
|
||||
* Clears {selected,recording}TrackId in the {@link getSharedPreferences()}.
|
||||
*/
|
||||
private void clearSelectedAndRecordingTracks() {
|
||||
Editor editor = getActivity().getSharedPreferences().edit();
|
||||
Editor editor = getSharedPreferences().edit();
|
||||
editor.putLong(getActivity().getString(R.string.selected_track_key), -1);
|
||||
editor.putLong(getActivity().getString(R.string.recording_track_key), -1);
|
||||
|
||||
|
||||
editor.clear();
|
||||
editor.commit();
|
||||
}
|
||||
@@ -245,6 +235,17 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
}
|
||||
waitForIdle();
|
||||
assertEquals(isRecording, getActivity().isRecording());
|
||||
return getActivity().getRecordingTrackId();
|
||||
return getRecordingTrackId();
|
||||
}
|
||||
|
||||
private long getRecordingTrackId() {
|
||||
return getSharedPreferences().getLong(getActivity().getString(R.string.recording_track_key), -1);
|
||||
}
|
||||
|
||||
private SharedPreferences getSharedPreferences() {
|
||||
if (sharedPreferences == null) {
|
||||
sharedPreferences = getActivity().getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||
}
|
||||
return sharedPreferences;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user