diff --git a/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java b/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java index c76afc34b..6b77f28a6 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/SensorStateActivity.java @@ -73,18 +73,18 @@ public class SensorStateActivity extends Activity { runOnUiThread(stateUpdater); } }; - + /** * A temporary sensor manager, when none is available. */ private SensorManager tempSensorManager = null; - + /** * A state flag set to true when the activity is active/visible, - * i.e. after resume, and before pause - * - * Used to avoid updating after the pause event, because sometimes an update + * i.e. after resume, and before pause + * + * Used to avoid updating after the pause event, because sometimes an update * event occurs even after the timer is cancelled. In this case, * it could cause the {@link #tempSensorManager} to be recreated, after it * is destroyed at the pause event. @@ -111,7 +111,7 @@ public class SensorStateActivity extends Activity { @Override protected void onResume() { super.onResume(); - + isVisible = true; serviceConnection.bindIfRunning(); @@ -123,7 +123,7 @@ public class SensorStateActivity extends Activity { @Override protected void onPause() { isVisible = false; - + timer.cancel(); timer.purge(); timer = null; @@ -139,9 +139,9 @@ public class SensorStateActivity extends Activity { private void updateState() { Log.d(TAG, "Updating SensorStateActivity"); - + ITrackRecordingService service = serviceConnection.getServiceIfBound(); - + // Check if service is available, and recording. boolean isRecording = false; if (service != null) { @@ -151,7 +151,7 @@ public class SensorStateActivity extends Activity { Log.e(TAG, "Unable to determine if service is recording.", e); } } - + // If either service isn't available, or not recording. if (!isRecording) { updateFromTempSensorManager(); @@ -159,19 +159,17 @@ public class SensorStateActivity extends Activity { updateFromSysSensorManager(); } } - + private void updateFromTempSensorManager() { // Use variables to hold the sensor state and data set. Sensor.SensorState currentState = null; Sensor.SensorDataSet currentDataSet = null; - + // If no temp sensor manager is present, create one, and start it. if (tempSensorManager == null) { - tempSensorManager = SensorManagerFactory.getSensorManager(this); - if (tempSensorManager != null) - tempSensorManager.onStartTrack(); + tempSensorManager = SensorManagerFactory.getInstance().getSensorManager(this); } - + // If a temp sensor manager is available, use states from temp sensor // manager. if (tempSensorManager != null) { @@ -182,15 +180,15 @@ public class SensorStateActivity extends Activity { // Update the sensor state, and sensor data, using the variables. updateSensorStateAndData(currentState, currentDataSet); } - + private void updateFromSysSensorManager() { // Use variables to hold the sensor state and data set. Sensor.SensorState currentState = null; Sensor.SensorDataSet currentDataSet = null; - + ITrackRecordingService service = serviceConnection.getServiceIfBound(); - - // If a temp sensor manager is present, shut it down, + + // If a temp sensor manager is present, shut it down, // probably recording just started. stopTempSensorManager(); @@ -208,7 +206,7 @@ public class SensorStateActivity extends Activity { } catch (InvalidProtocolBufferException e) { Log.e(TAG, "Could not read sensor data.", e); } - + try { currentState = Sensor.SensorState.valueOf(service.getSensorState()); } catch (RemoteException e) { @@ -220,17 +218,17 @@ public class SensorStateActivity extends Activity { // Update the sensor state, and sensor data, using the variables. updateSensorStateAndData(currentState, currentDataSet); } - + /** - * Stops the temporary sensor manager, if one exists. + * Stops the temporary sensor manager, if one exists. */ private void stopTempSensorManager() { if (tempSensorManager != null) { - tempSensorManager.shutdown(); + SensorManagerFactory.getInstance().releaseSensorManager(tempSensorManager); tempSensorManager = null; } } - + private void updateSensorStateAndData(Sensor.SensorState state, Sensor.SensorDataSet dataSet) { updateSensorState(state == null ? Sensor.SensorState.NONE : state); updateSensorData(dataSet); diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index 8b81a44df..e00eef013 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -296,7 +296,7 @@ public class TrackRecordingService extends Service { handleStartCommand(intent, startId); return START_STICKY; } - + private void handleStartCommand(Intent intent, int startId) { Log.d(TAG, "TrackRecordingService.handleStartCommand: " + startId); @@ -313,7 +313,7 @@ public class TrackRecordingService extends Service { private boolean isTrackInProgress() { return recordingTrackId != -1 || isRecording; } - + private void resumeTrack(int startId) { Log.d(TAG, "TrackRecordingService: requested resume"); @@ -358,7 +358,7 @@ public class TrackRecordingService extends Service { unregisterLocationListener(); shutdownTaskExecutors(); if (sensorManager != null) { - sensorManager.shutdown(); + SensorManagerFactory.getInstance().releaseSensorManager(sensorManager); sensorManager = null; } @@ -499,12 +499,12 @@ public class TrackRecordingService extends Service { protected void startForegroundService(Notification notification) { startForeground(1, notification); } - + @VisibleForTesting protected void stopForegroundService() { stopForeground(true); } - + private void setUpTaskExecutors() { announcementExecutor = new PeriodicTaskExecutor(this, new StatusAnnouncerFactory()); splitExecutor = new PeriodicTaskExecutor(this, new SplitTask.Factory()); @@ -559,7 +559,7 @@ public class TrackRecordingService extends Service { Log.d(TAG, "Location listener now unregistered w/ TrackRecordingService."); } - + private String getDefaultActivityType(Context context) { SharedPreferences prefs = context.getSharedPreferences( Constants.SETTINGS_NAME, Context.MODE_PRIVATE); @@ -601,10 +601,7 @@ public class TrackRecordingService extends Service { length = 0; showNotification(); registerLocationListener(); - sensorManager = SensorManagerFactory.getSensorManager(this); - if (sensorManager != null) { - sensorManager.onStartTrack(); - } + sensorManager = SensorManagerFactory.getInstance().getSensorManager(this); // Reset the number of auto-resume retries. setAutoResumeTrackRetries(0); @@ -1038,7 +1035,7 @@ public class TrackRecordingService extends Service { prefManager.setRecordingTrack(recordingTrackId = -1); if (sensorManager != null) { - sensorManager.shutdown(); + SensorManagerFactory.getInstance().releaseSensorManager(sensorManager); sensorManager = null; } @@ -1056,7 +1053,7 @@ public class TrackRecordingService extends Service { .setAction(getString(actionResId)) .putExtra(getString(R.string.track_id_broadcast_extra), trackId); sendBroadcast(broadcastIntent, getString(R.string.permission_notification_value)); - + SharedPreferences sharedPreferences = getSharedPreferences( Constants.SETTINGS_NAME, Context.MODE_PRIVATE); if (sharedPreferences.getBoolean(getString(R.string.allow_access_key), false)) { @@ -1202,17 +1199,17 @@ public class TrackRecordingService extends Service { throw new IllegalStateException("The service has been already detached!"); } } - + /** * Returns true if the RPC caller is from the same application or if the * "Allow access" setting indicates that another app can invoke this service's - * RPCs. + * RPCs. */ private boolean canAccess() { - + // As a precondition for access, must check if the service is available. checkService(); - + if (Process.myPid() == Binder.getCallingPid()) { return true; } else { @@ -1297,7 +1294,7 @@ public class TrackRecordingService extends Service { public int getSensorState() { if (!canAccess()) { return Sensor.SensorState.NONE.getNumber(); - } + } if (service.sensorManager == null) { Log.d(TAG, "No sensor manager for data."); return Sensor.SensorState.NONE.getNumber(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java index 8b859f519..9d98410bc 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java @@ -31,15 +31,28 @@ import android.util.Log; */ public class SensorManagerFactory { + private String activeSensorType; + private SensorManager activeSensorManager; + private int refCount; + + private static SensorManagerFactory instance = new SensorManagerFactory(); + private SensorManagerFactory() { } /** - * Get a new sensor manager. + * Get the factory instance. + */ + public static SensorManagerFactory getInstance() { + return instance; + } + + /** + * Get and start a new sensor manager. * @param context Context to fetch system preferences. * @return The sensor manager that corresponds to the sensor type setting. */ - public static SensorManager getSensorManager(Context context) { + public SensorManager getSensorManager(Context context) { SharedPreferences prefs = context.getSharedPreferences( Constants.SETTINGS_NAME, Context.MODE_PRIVATE); if (prefs == null) { @@ -52,18 +65,54 @@ public class SensorManagerFactory { Log.i(Constants.TAG, "Creating sensor of type: " + sensor); if (sensor == null) { + reset(); return null; - } else if (sensor.equals(context.getString(R.string.sensor_type_value_ant))) { - return new AntDirectSensorManager(context); + } + if (sensor.equals(activeSensorType)) { + Log.i(Constants.TAG, "Returning existing sensor manager."); + refCount++; + return activeSensorManager; + } + reset(); + + if (sensor.equals(context.getString(R.string.sensor_type_value_ant))) { + activeSensorManager = new AntDirectSensorManager(context); } else if (sensor.equals(context.getString(R.string.sensor_type_value_srm_ant_bridge))) { - return new AntSrmBridgeSensorManager(context); + activeSensorManager = new AntSrmBridgeSensorManager(context); } else if (sensor.equals(context.getString(R.string.sensor_type_value_zephyr))) { - return new ZephyrSensorManager(context); + activeSensorManager = new ZephyrSensorManager(context); } else if (sensor.equals(context.getString(R.string.sensor_type_value_polar))) { - return new PolarSensorManager(context); + activeSensorManager = new PolarSensorManager(context); } else { Log.w(Constants.TAG, "Unable to find sensor type: " + sensor); return null; } + activeSensorType = sensor; + refCount = 1; + activeSensorManager.onStartTrack(); + return activeSensorManager; + } + + /** + * Finish using a sensor manager. + */ + public void releaseSensorManager(SensorManager sensorManager) { + Log.i(Constants.TAG, "releaseSensorManager: " + activeSensorType + " " + refCount); + if (sensorManager != activeSensorManager) { + Log.e(Constants.TAG, "invalid parameter to releaseSensorManager"); + } + if (--refCount > 0) { + return; + } + reset(); + } + + private void reset() { + activeSensorType = null; + if (activeSensorManager != null) { + activeSensorManager.shutdown(); + } + activeSensorManager = null; + refCount = 0; } } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java index 172f6acdf..310da60cd 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java @@ -17,23 +17,23 @@ public class SensorManagerFactoryTest extends AndroidTestCase { @Override protected void setUp() throws Exception { super.setUp(); - + sharedPreferences = getContext().getSharedPreferences( Constants.SETTINGS_NAME, Context.MODE_PRIVATE); // Let's use default values. sharedPreferences.edit().clear().apply(); } - + @SmallTest public void testDefaultSettings() throws Exception { - assertNull(SensorManagerFactory.getSensorManager(getContext())); + assertNull(SensorManagerFactory.getInstance().getSensorManager(getContext())); } - + @SmallTest public void testCreateZephyr() throws Exception { assertClassForName(ZephyrSensorManager.class, R.string.sensor_type_value_zephyr); } - + @SmallTest public void testCreateAnt() throws Exception { assertClassForName(AntDirectSensorManager.class, R.string.sensor_type_value_ant); @@ -49,8 +49,9 @@ public class SensorManagerFactoryTest extends AndroidTestCase { .putString(getContext().getString(R.string.sensor_type_key), getContext().getString(i)) .apply(); - SensorManager sm = SensorManagerFactory.getSensorManager(getContext()); + SensorManager sm = SensorManagerFactory.getInstance().getSensorManager(getContext()); assertNotNull(sm); assertTrue(c.isInstance(sm)); + SensorManagerFactory.getInstance().releaseSensorManager(sm); } }