diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/DataSource.java b/MyTracks/src/com/google/android/apps/mytracks/content/DataSource.java new file mode 100644 index 000000000..5cda864d7 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/content/DataSource.java @@ -0,0 +1,192 @@ +/* + * 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.content; + +import static com.google.android.apps.mytracks.Constants.MAX_LOCATION_AGE_MS; + +import com.google.android.apps.mytracks.Constants; +import com.google.android.maps.mytracks.R; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.net.Uri; +import android.util.Log; +import android.widget.Toast; + +/** + * Data source on the phone. + * + * @author Rodrigo Damazio + */ +public class DataSource { + + private static final int NETWORK_PROVIDER_MIN_TIME = 5 * 60 * 1000; // 5 minutes + private static final String TAG = DataSource.class.getSimpleName(); + + private final Context context; + private final ContentResolver contentResolver; + private final LocationManager locationManager; + private final SensorManager sensorManager; + private final SharedPreferences sharedPreferences; + + public DataSource(Context context) { + this.context = context; + contentResolver = context.getContentResolver(); + locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE); + } + + /** + * Registers a content observer. + * + * @param uri the uri + * @param observer the observer + */ + public void registerContentObserver(Uri uri, ContentObserver observer) { + contentResolver.registerContentObserver(uri, false, observer); + } + + /** + * Unregisters a content observer. + * + * @param observer the observer + */ + public void unregisterContentObserver(ContentObserver observer) { + contentResolver.unregisterContentObserver(observer); + } + + /** + * Registers a location listener. + * + * @param listener the listener + */ + public void registerLocationListener(LocationListener listener) { + // Check if the GPS provider exists + if (locationManager.getProvider(LocationManager.GPS_PROVIDER) == null) { + listener.onProviderDisabled(LocationManager.GPS_PROVIDER); + unregisterLocationListener(listener); + return; + } + + // Listen for GPS location + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener); + + // Update the listener with the current provider state + if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { + listener.onProviderEnabled(LocationManager.GPS_PROVIDER); + } else { + listener.onProviderDisabled(LocationManager.GPS_PROVIDER); + } + + // Listen for network location + try { + locationManager.requestLocationUpdates( + LocationManager.NETWORK_PROVIDER, NETWORK_PROVIDER_MIN_TIME, 0, listener); + } catch (RuntimeException e) { + // Network location is optional, so just log the exception + Log.w(TAG, "Could not register for network location.", e); + } + } + + /** + * Unregisters a location listener. + * + * @param listener the listener + */ + public void unregisterLocationListener(LocationListener listener) { + locationManager.removeUpdates(listener); + } + + /** + * Gets the last known location. + */ + public Location getLastKnownLocation() { + Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + if (!isLocationRecent(location)) { + // Try network location + location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + int id = isLocationRecent(location) + ? R.string.my_location_approximate_location : R.string.my_location_no_location; + Toast.makeText(context, id, Toast.LENGTH_LONG).show(); + } + return location; + } + + /** + * Returns true if the location is recent. + * + * @param location the location + */ + private boolean isLocationRecent(Location location) { + if (location == null) { + return false; + } + return location.getTime() > System.currentTimeMillis() - MAX_LOCATION_AGE_MS; + } + + /** + * Registers a compass listener. + * + * @param listener the listener + */ + public void registerCompassListener(SensorEventListener listener) { + Sensor compass = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); + if (compass == null) { + Log.d(TAG, "No compass sensor."); + return; + } + sensorManager.registerListener(listener, compass, SensorManager.SENSOR_DELAY_UI); + } + + /** + * Unregisters a compass listener. + * + * @param listener the listener + */ + public void unregisterCompassListener(SensorEventListener listener) { + sensorManager.unregisterListener(listener); + } + + /** + * Registers a shared preference change listener. + * + * @param listener the listener + */ + public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { + sharedPreferences.registerOnSharedPreferenceChangeListener(listener); + } + + /** + * Unregisters a shared preference change listener. + * + * @param listener the listener + */ + public void unregisterOnSharedPreferenceChangeListener( + OnSharedPreferenceChangeListener listener) { + sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener); + } +} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/DataSourceListener.java b/MyTracks/src/com/google/android/apps/mytracks/content/DataSourceListener.java new file mode 100644 index 000000000..01cc1e7ef --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/content/DataSourceListener.java @@ -0,0 +1,78 @@ +/* + * 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.content; + +import android.location.Location; + +/** + * Listener to be invoked when {@link DataSource} changes. + * + * @author Jimmy Shih + */ +public interface DataSourceListener { + + /** + * Notifies when the tracks table is updated. + */ + public void notifyTracksTableUpdated(); + + /** + * Notifies when the waypoints table is updated. + */ + public void notifyWaypointsTableUpdated(); + + /** + * Notifies when the track points table is updated. + */ + public void notifyTrackPointsTableUpdated(); + + /** + * Notifies when the current location changes. + * + * @param location the location + */ + public void notifyLocationChanged(Location location); + + + /** + * Notifies when the location provider enabled status changes. + * + * @param enabled true if enabled, false if not + */ + public void notifyLocationProviderEnabled(boolean enabled); + + /** + * Notifies when the location provider availability status changes. + * + * @param available true if available, false if not + */ + public void notifyLocationProviderAvailable(boolean available); + + /** + * Notifies when the compass heading changes. + * + * @param heading the new heading + */ + public void notifyHeadingChanged(float heading); + + /** + * Notifies when a preference changes. + * + * @param key the preference key + */ + public void notifyPreferenceChanged(String key); +} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/DataSourceManager.java b/MyTracks/src/com/google/android/apps/mytracks/content/DataSourceManager.java index 90c1e914d..8da7841a7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/DataSourceManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/DataSourceManager.java @@ -1,16 +1,29 @@ +/* + * 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.content; import static com.google.android.apps.mytracks.Constants.TAG; -import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType; - import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.database.ContentObserver; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; -import android.hardware.SensorManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; @@ -23,243 +36,281 @@ import java.util.EnumSet; import java.util.Set; /** - * External data source manager, which converts system-level events into My Tracks data events. - * + * Data source manager. Creates observers/listeners and manages their + * registration with {@link DataSource}. The observers/listeners calls + * {@link DataSourceListener} when data changes. + * * @author Rodrigo Damazio */ -class DataSourceManager { +public class DataSourceManager { - /** Single interface for receiving system events that were registered for. */ - interface DataSourceListener { - void notifyTrackUpdated(); - void notifyWaypointUpdated(); - void notifyPointsUpdated(); - void notifyPreferenceChanged(String key); - void notifyLocationProviderEnabled(boolean enabled); - void notifyLocationProviderAvailable(boolean available); - void notifyLocationChanged(Location loc); - void notifyHeadingChanged(float heading); - } + /** + * Observer when the tracks table is updated. + * + * @author Jimmy Shih + */ + private class TracksTableObserver extends ContentObserver { - private final DataSourceListener listener; - - /** Observer for when the tracks table is updated. */ - private class TrackObserver extends ContentObserver { - public TrackObserver() { - super(contentHandler); + public TracksTableObserver() { + super(handler); } @Override public void onChange(boolean selfChange) { - listener.notifyTrackUpdated(); + dataSourceListener.notifyTracksTableUpdated(); } } - /** Observer for when the waypoints table is updated. */ - private class WaypointObserver extends ContentObserver { - public WaypointObserver() { - super(contentHandler); + /** + * Observer when the waypoints table is updated. + * + * @author Jimmy Shih + */ + private class WaypointsTableObserver extends ContentObserver { + + public WaypointsTableObserver() { + super(handler); } @Override public void onChange(boolean selfChange) { - listener.notifyWaypointUpdated(); + dataSourceListener.notifyWaypointsTableUpdated(); } } - /** Observer for when the points table is updated. */ - private class PointObserver extends ContentObserver { - public PointObserver() { - super(contentHandler); + /** + * Observer when the track points table is updated. + * + * @author Jimmy Shih + */ + private class TrackPointsTableObserver extends ContentObserver { + + public TrackPointsTableObserver() { + super(handler); } @Override public void onChange(boolean selfChange) { - listener.notifyPointsUpdated(); + dataSourceListener.notifyTrackPointsTableUpdated(); } } - /** Listener for when preferences change. */ - private class HubSharedPreferenceListener implements OnSharedPreferenceChangeListener { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - listener.notifyPreferenceChanged(key); - } - } - - /** Listener for the current location (independent from track data). */ - private class CurrentLocationListener implements - LocationListener { - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - if (!LocationManager.GPS_PROVIDER.equals(provider)) return; - - listener.notifyLocationProviderAvailable(status == LocationProvider.AVAILABLE); - } + /** + * Listener for location changes. + * + * @author Jimmy Shih + */ + private class CurrentLocationListener implements LocationListener { @Override - public void onProviderEnabled(String provider) { - if (!LocationManager.GPS_PROVIDER.equals(provider)) return; - - listener.notifyLocationProviderEnabled(true); + public void onLocationChanged(Location location) { + dataSourceListener.notifyLocationChanged(location); } @Override public void onProviderDisabled(String provider) { - if (!LocationManager.GPS_PROVIDER.equals(provider)) return; - - listener.notifyLocationProviderEnabled(false); + if (!LocationManager.GPS_PROVIDER.equals(provider)) { + return; + } + dataSourceListener.notifyLocationProviderEnabled(false); } @Override - public void onLocationChanged(Location location) { - listener.notifyLocationChanged(location); + public void onProviderEnabled(String provider) { + if (!LocationManager.GPS_PROVIDER.equals(provider)) { + return; + } + dataSourceListener.notifyLocationProviderEnabled(true); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + if (!LocationManager.GPS_PROVIDER.equals(provider)) { + return; + } + dataSourceListener.notifyLocationProviderAvailable(status == LocationProvider.AVAILABLE); } } - /** Listener for compass readings. */ - private class CompassListener implements - SensorEventListener { - @Override - public void onSensorChanged(SensorEvent event) { - listener.notifyHeadingChanged(event.values[0]); - } + /** + * Listener for compass changes. + * + * @author Jimmy Shih + */ + private class CompassListener implements SensorEventListener { @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // Do nothing } + + @Override + public void onSensorChanged(SensorEvent event) { + dataSourceListener.notifyHeadingChanged(event.values[0]); + } } - /** Wrapper for registering internal listeners. */ - private final DataSourcesWrapper dataSources; + /** + * Listener for preference changes. + * + * @author Jimmy Shih + */ + private class PreferenceListener implements OnSharedPreferenceChangeListener { - // Internal listeners (to receive data from the system) - private final Set registeredListeners = - EnumSet.noneOf(ListenerDataType.class); - private final Handler contentHandler; - private final ContentObserver pointObserver; - private final ContentObserver waypointObserver; - private final ContentObserver trackObserver; - private final LocationListener locationListener; - private final OnSharedPreferenceChangeListener preferenceListener; - private final SensorEventListener compassListener; + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + dataSourceListener.notifyPreferenceChanged(key); + } + } - DataSourceManager(DataSourceListener listener, DataSourcesWrapper dataSources) { - this.listener = listener; - this.dataSources = dataSources; + private final DataSource dataSource; + private final DataSourceListener dataSourceListener; - contentHandler = new Handler(); - pointObserver = new PointObserver(); - waypointObserver = new WaypointObserver(); - trackObserver = new TrackObserver(); + // Registered listeners + private final Set registeredListeners = EnumSet.noneOf(TrackDataType.class); + + private final Handler handler; + private final TracksTableObserver tracksTableObserver; + private final WaypointsTableObserver waypointsTableObserver; + private final TrackPointsTableObserver trackPointsTableObserver; + private final CurrentLocationListener currentLocationListener; + private final CompassListener compassListener; + private final PreferenceListener preferenceListener; + public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) { + this.dataSource = dataSource; + this.dataSourceListener = dataSourceListener; + + handler = new Handler(); + tracksTableObserver = new TracksTableObserver(); + waypointsTableObserver = new WaypointsTableObserver(); + trackPointsTableObserver = new TrackPointsTableObserver(); + currentLocationListener = new CurrentLocationListener(); compassListener = new CompassListener(); - locationListener = new CurrentLocationListener(); - preferenceListener = new HubSharedPreferenceListener(); + preferenceListener = new PreferenceListener(); } - /** Updates the internal (sensor, position, etc) listeners. */ - void updateAllListeners(EnumSet externallyNeededListeners) { - EnumSet neededListeners = EnumSet.copyOf(externallyNeededListeners); + /** + * Updates listeners with data source. + * + * @param listeners the listeners + */ + public void updateListeners(EnumSet listeners) { + EnumSet neededListeners = EnumSet.copyOf(listeners); - // Special case - map sampled-out points type to points type since they - // correspond to the same internal listener. - if (neededListeners.contains(ListenerDataType.SAMPLED_OUT_POINT_UPDATES)) { - neededListeners.remove(ListenerDataType.SAMPLED_OUT_POINT_UPDATES); - neededListeners.add(ListenerDataType.POINT_UPDATES); + /* + * Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to + * the same internal listener + */ + if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS)) { + neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS); + neededListeners.add(TrackDataType.TRACK_POINTS_TABLE); } - Log.d(TAG, "Updating internal listeners to types " + neededListeners); + Log.d(TAG, "Updating listeners " + neededListeners); // Unnecessary = registered - needed - Set unnecessaryListeners = EnumSet.copyOf(registeredListeners); + Set unnecessaryListeners = EnumSet.copyOf(registeredListeners); unnecessaryListeners.removeAll(neededListeners); // Missing = needed - registered - Set missingListeners = EnumSet.copyOf(neededListeners); + Set missingListeners = EnumSet.copyOf(neededListeners); missingListeners.removeAll(registeredListeners); - // Remove all unnecessary listeners. - for (ListenerDataType type : unnecessaryListeners) { - unregisterListener(type); + // Remove unnecessary listeners + for (TrackDataType trackDataType : unnecessaryListeners) { + unregisterListener(trackDataType); } - // Add all missing listeners. - for (ListenerDataType type : missingListeners) { - registerListener(type); + // Add missing listeners + for (TrackDataType trackDataType : missingListeners) { + registerListener(trackDataType); } - // Now all needed types are registered. + // Update registered listeners registeredListeners.clear(); registeredListeners.addAll(neededListeners); } - private void registerListener(ListenerDataType type) { - switch (type) { - case COMPASS_UPDATES: { - // Listen to compass - Sensor compass = dataSources.getSensor(Sensor.TYPE_ORIENTATION); - if (compass != null) { - Log.d(TAG, "TrackDataHub: Now registering sensor listener."); - dataSources.registerSensorListener(compassListener, compass, SensorManager.SENSOR_DELAY_UI); - } + /** + * Registers a listener with data source. + * + * @param trackDataType the listener data type + */ + private void registerListener(TrackDataType trackDataType) { + switch (trackDataType) { + case SELECTED_TRACK: + // Do nothing break; - } - case LOCATION_UPDATES: - dataSources.requestLocationUpdates(locationListener); + case TRACKS_TABLE: + dataSource.registerContentObserver(TracksColumns.CONTENT_URI, tracksTableObserver); break; - case POINT_UPDATES: - dataSources.registerContentObserver( - TrackPointsColumns.CONTENT_URI, false, pointObserver); + case WAYPOINTS_TABLE: + dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver); break; - case TRACK_UPDATES: - dataSources.registerContentObserver(TracksColumns.CONTENT_URI, false, trackObserver); + case TRACK_POINTS_TABLE: + dataSource.registerContentObserver( + TrackPointsColumns.CONTENT_URI, trackPointsTableObserver); break; - case WAYPOINT_UPDATES: - dataSources.registerContentObserver( - WaypointsColumns.CONTENT_URI, false, waypointObserver); + case SAMPLED_OUT_TRACK_POINTS: + // Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES. break; - case DISPLAY_PREFERENCES: - dataSources.registerOnSharedPreferenceChangeListener(preferenceListener); + case LOCATION: + dataSource.registerLocationListener(currentLocationListener); + break; + case COMPASS: + dataSource.registerCompassListener(compassListener); + break; + case PREFERENCE: + dataSource.registerOnSharedPreferenceChangeListener(preferenceListener); + break; + default: break; - case SAMPLED_OUT_POINT_UPDATES: - throw new IllegalArgumentException("Should have been mapped to point updates"); } } - private void unregisterListener(ListenerDataType type) { - switch (type) { - case COMPASS_UPDATES: - dataSources.unregisterSensorListener(compassListener); + /** + * Unregisters a listener with data source. + * + * @param trackDataType listener data type + */ + private void unregisterListener(TrackDataType trackDataType) { + switch (trackDataType) { + case SELECTED_TRACK: + // Do nothing break; - case LOCATION_UPDATES: - dataSources.removeLocationUpdates(locationListener); + case TRACKS_TABLE: + dataSource.unregisterContentObserver(tracksTableObserver); break; - case POINT_UPDATES: - dataSources.unregisterContentObserver(pointObserver); + case WAYPOINTS_TABLE: + dataSource.unregisterContentObserver(waypointsTableObserver); break; - case TRACK_UPDATES: - dataSources.unregisterContentObserver(trackObserver); + case TRACK_POINTS_TABLE: + dataSource.unregisterContentObserver(trackPointsTableObserver); break; - case WAYPOINT_UPDATES: - dataSources.unregisterContentObserver(waypointObserver); + case SAMPLED_OUT_TRACK_POINTS: + // Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES. break; - case DISPLAY_PREFERENCES: - dataSources.unregisterOnSharedPreferenceChangeListener(preferenceListener); + case LOCATION: + dataSource.unregisterLocationListener(currentLocationListener); + break; + case COMPASS: + dataSource.unregisterCompassListener(compassListener); + break; + case PREFERENCE: + dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListener); + break; + default: break; - case SAMPLED_OUT_POINT_UPDATES: - throw new IllegalArgumentException("Should have been mapped to point updates"); } } - /** Unregisters all internal (sensor, position, etc.) listeners. */ - void unregisterAllListeners() { - dataSources.removeLocationUpdates(locationListener); - dataSources.unregisterSensorListener(compassListener); - dataSources.unregisterContentObserver(trackObserver); - dataSources.unregisterContentObserver(waypointObserver); - dataSources.unregisterContentObserver(pointObserver); - dataSources.unregisterOnSharedPreferenceChangeListener(preferenceListener); + /** + * Unregisters all listeners with data source. + */ + public void unregisterAllListeners() { + for (TrackDataType trackDataType : TrackDataType.values()) { + unregisterListener(trackDataType); + } } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/DataSourcesWrapper.java b/MyTracks/src/com/google/android/apps/mytracks/content/DataSourcesWrapper.java deleted file mode 100644 index a4dbf3544..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/content/DataSourcesWrapper.java +++ /dev/null @@ -1,54 +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.content; - -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.database.ContentObserver; -import android.hardware.Sensor; -import android.hardware.SensorEventListener; -import android.location.Location; -import android.location.LocationListener; -import android.net.Uri; - -/** - * Interface for abstracting registration of external data source listeners. - * - * @author Rodrigo Damazio - */ -interface DataSourcesWrapper { - // Preferences - void registerOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener); - void unregisterOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener); - - // Content provider - void registerContentObserver(Uri contentUri, boolean descendents, - ContentObserver observer); - void unregisterContentObserver(ContentObserver observer); - - // Sensors - Sensor getSensor(int type); - void registerSensorListener(SensorEventListener listener, - Sensor sensor, int sensorDelay); - void unregisterSensorListener(SensorEventListener listener); - - // Location - boolean isLocationProviderEnabled(String provider); - void requestLocationUpdates(LocationListener listener); - void removeLocationUpdates(LocationListener listener); - Location getLastKnownLocation(); -} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/DataSourcesWrapperImpl.java b/MyTracks/src/com/google/android/apps/mytracks/content/DataSourcesWrapperImpl.java deleted file mode 100644 index 9bc57f08c..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/content/DataSourcesWrapperImpl.java +++ /dev/null @@ -1,166 +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.content; - -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 com.google.android.apps.mytracks.Constants; -import com.google.android.maps.mytracks.R; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.database.ContentObserver; -import android.hardware.Sensor; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.location.LocationProvider; -import android.net.Uri; -import android.util.Log; -import android.widget.Toast; - -/** - * Real implementation of the data sources, which talks to system services. - * - * @author Rodrigo Damazio - */ -class DataSourcesWrapperImpl implements DataSourcesWrapper { - // System services - private final SensorManager sensorManager; - private final LocationManager locationManager; - private final ContentResolver contentResolver; - private final SharedPreferences sharedPreferences; - private final Context context; - - DataSourcesWrapperImpl(Context context, SharedPreferences sharedPreferences) { - this.context = context; - this.sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - this.contentResolver = context.getContentResolver(); - this.sharedPreferences = sharedPreferences; - } - - @Override - public void registerOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener) { - sharedPreferences.registerOnSharedPreferenceChangeListener(listener); - } - - @Override - public void unregisterOnSharedPreferenceChangeListener( - OnSharedPreferenceChangeListener listener) { - sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener); - } - - @Override - public void registerContentObserver(Uri contentUri, boolean descendents, - ContentObserver observer) { - contentResolver.registerContentObserver(contentUri, descendents, observer); - } - - @Override - public void unregisterContentObserver(ContentObserver observer) { - contentResolver.unregisterContentObserver(observer); - } - - @Override - public Sensor getSensor(int type) { - return sensorManager.getDefaultSensor(type); - } - - @Override - public void registerSensorListener(SensorEventListener listener, - Sensor sensor, int sensorDelay) { - sensorManager.registerListener(listener, sensor, sensorDelay); - } - - @Override - public void unregisterSensorListener(SensorEventListener listener) { - sensorManager.unregisterListener(listener); - } - - @Override - public boolean isLocationProviderEnabled(String provider) { - return locationManager.isProviderEnabled(provider); - } - - @Override - public void requestLocationUpdates(LocationListener listener) { - // Check if the provider exists. - LocationProvider gpsProvider = locationManager.getProvider(LocationManager.GPS_PROVIDER); - if (gpsProvider == null) { - listener.onProviderDisabled(LocationManager.GPS_PROVIDER); - locationManager.removeUpdates(listener); - return; - } - - // Listen to GPS location. - String providerName = gpsProvider.getName(); - Log.d(Constants.TAG, "TrackDataHub: Using location provider " + providerName); - locationManager.requestLocationUpdates(providerName, - 0 /*minTime*/, 0 /*minDist*/, listener); - - // Give an initial update on provider state. - if (locationManager.isProviderEnabled(providerName)) { - listener.onProviderEnabled(providerName); - } else { - listener.onProviderDisabled(providerName); - } - - // Listen to network location - try { - locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, - 1000 * 60 * 5 /*minTime*/, 0 /*minDist*/, listener); - } catch (RuntimeException e) { - // If anything at all goes wrong with getting a cell location do not - // abort. Cell location is not essential to this app. - Log.w(Constants.TAG, "Could not register network location listener.", e); - } - } - - @Override - public Location getLastKnownLocation() { - // TODO: Let's look at more advanced algorithms to determine the best - // current location. - - Location loc = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); - final long now = System.currentTimeMillis(); - if (loc == null || loc.getTime() < now - MAX_LOCATION_AGE_MS) { - // We don't have a recent GPS fix, just use cell towers if available - loc = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - - int toastResId = R.string.my_location_approximate_location; - if (loc == null || loc.getTime() < now - MAX_NETWORK_AGE_MS) { - // We don't have a recent cell tower location, let the user know: - toastResId = R.string.my_location_no_location; - } - - // Let the user know we have only an approximate location: - Toast.makeText(context, toastResId, Toast.LENGTH_LONG).show(); - } - return loc; - } - - @Override - public void removeLocationUpdates(LocationListener listener) { - locationManager.removeUpdates(listener); - } -} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/ListenerState.java b/MyTracks/src/com/google/android/apps/mytracks/content/ListenerState.java new file mode 100644 index 000000000..e1e3598a7 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/content/ListenerState.java @@ -0,0 +1,105 @@ +/* + * 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.content; + +import java.util.EnumSet; + +/** + * State for a registered {@link TrackDataListener}. + * + * @author Jimmy Shih + */ +public class ListenerState { + + private TrackDataListener trackDataListener; + private EnumSet trackDataTypes; + + private long lastTrackId; + private long lastPointId; + private int lastSamplingFrequency; + private int numberOfLoadedPoints; + + public ListenerState( + TrackDataListener trackDataListener, EnumSet trackDataTypes) { + this.trackDataListener = trackDataListener; + this.trackDataTypes = trackDataTypes; + } + + /** + * Gets the {@link TrackDataListener}. + */ + public TrackDataListener getTrackDataListener() { + return trackDataListener; + } + + /** + * Gets the track data types. + */ + public EnumSet getTrackDataTypes() { + return trackDataTypes; + } + + /** + * Gets the last track id. + */ + public long getLastTrackId() { + return lastTrackId; + } + + /** + * Gets the last point id. + */ + public long getLastPointId() { + return lastPointId; + } + + /** + * Gets the last sampling frequency. + */ + public int getLastSamplingFrequency() { + return lastSamplingFrequency; + } + + /** + * Get the number of loaded points. + */ + public int getNumberOfLoadedPoints() { + return numberOfLoadedPoints; + } + + /** + * Sets the listener state. + * + * @param trackId track id + * @param pointId point id + * @param samplingFrequency sampling frequency + * @param loadedPoints number of loaded points + */ + public void setState(long trackId, long pointId, int samplingFrequency, int loadedPoints) { + lastTrackId = trackId; + lastPointId = pointId; + lastSamplingFrequency = samplingFrequency; + numberOfLoadedPoints = loadedPoints; + } + + /** + * Resets state. + */ + public void resetState() { + setState(0L, 0L, 0, 0); + } +} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java index fc5b8b7cd..52c8c3c4a 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataHub.java @@ -21,18 +21,14 @@ import static com.google.android.apps.mytracks.Constants.MAX_NETWORK_AGE_MS; import static com.google.android.apps.mytracks.Constants.TAG; import static com.google.android.apps.mytracks.Constants.TARGET_DISPLAYED_TRACK_POINTS; -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; import com.google.android.apps.mytracks.content.TrackDataListener.ProviderState; -import com.google.android.apps.mytracks.content.TrackDataListeners.ListenerRegistration; import com.google.android.apps.mytracks.util.LocationUtils; import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.maps.mytracks.R; import android.content.Context; -import android.content.SharedPreferences; import android.database.Cursor; import android.hardware.GeomagneticField; import android.location.Location; @@ -60,54 +56,23 @@ public class TrackDataHub { // Overridable constants private final int targetNumPoints; - /** Types of data that we can expose. */ - public static enum ListenerDataType { - /** Listen to when the selected track changes. */ - SELECTED_TRACK_CHANGED, - - /** Listen to when the tracks change. */ - TRACK_UPDATES, - - /** Listen to when the waypoints change. */ - WAYPOINT_UPDATES, - - /** Listen to when the current track points change. */ - POINT_UPDATES, - - /** - * Listen to sampled-out points. - * Listening to this without listening to {@link #POINT_UPDATES} - * makes no sense and may yield unexpected results. - */ - SAMPLED_OUT_POINT_UPDATES, - - /** Listen to updates to the current location. */ - LOCATION_UPDATES, - - /** Listen to updates to the current heading. */ - COMPASS_UPDATES, - - /** Listens to changes in display preferences. */ - DISPLAY_PREFERENCES; - } - /** Listener which receives events from the system. */ private class HubDataSourceListener implements DataSourceListener { @Override - public void notifyTrackUpdated() { - TrackDataHub.this.notifyTrackUpdated(getListenersFor(ListenerDataType.TRACK_UPDATES)); + public void notifyTracksTableUpdated() { + TrackDataHub.this.notifyTrackUpdated(getListenersFor(TrackDataType.TRACKS_TABLE)); } @Override - public void notifyWaypointUpdated() { - TrackDataHub.this.notifyWaypointUpdated(getListenersFor(ListenerDataType.WAYPOINT_UPDATES)); + public void notifyWaypointsTableUpdated() { + TrackDataHub.this.notifyWaypointUpdated(getListenersFor(TrackDataType.WAYPOINTS_TABLE)); } @Override - public void notifyPointsUpdated() { + public void notifyTrackPointsTableUpdated() { TrackDataHub.this.notifyPointsUpdated(true, 0, 0, - getListenersFor(ListenerDataType.POINT_UPDATES), - getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES)); + getListenersFor(TrackDataType.TRACK_POINTS_TABLE), + getListenersFor(TrackDataType.SAMPLED_OUT_TRACK_POINTS)); } @Override @@ -130,21 +95,20 @@ public class TrackDataHub { @Override public void notifyLocationChanged(Location loc) { TrackDataHub.this.notifyLocationChanged(loc, - getListenersFor(ListenerDataType.LOCATION_UPDATES)); + getListenersFor(TrackDataType.LOCATION)); } @Override public void notifyHeadingChanged(float heading) { lastSeenMagneticHeading = heading; maybeUpdateDeclination(); - TrackDataHub.this.notifyHeadingChanged(getListenersFor(ListenerDataType.COMPASS_UPDATES)); + TrackDataHub.this.notifyHeadingChanged(getListenersFor(TrackDataType.COMPASS)); } } // Application services private final Context context; private final MyTracksProviderUtils providerUtils; - private final SharedPreferences preferences; // Get content notifications on the main thread, send listener callbacks in another. // This ensures listener calls are serialized. @@ -152,12 +116,9 @@ public class TrackDataHub { private Handler listenerHandler; /** Manager for external listeners (those from activities). */ - private final TrackDataListeners dataListeners; + private final TrackDataManager trackDataManager; - /** Wrapper for interacting with system data managers. */ - private DataSourcesWrapper dataSources; - - /** Manager for system data listener registrations. */ + private DataSource dataSource; private DataSourceManager dataSourceManager; /** Condensed listener for system data listener events. */ @@ -193,24 +154,19 @@ public class TrackDataHub { * Builds a new {@link TrackDataHub} instance. */ public synchronized static TrackDataHub newInstance(Context context) { - SharedPreferences preferences = context.getSharedPreferences( - Constants.SETTINGS_NAME, Context.MODE_PRIVATE); MyTracksProviderUtils providerUtils = MyTracksProviderUtils.Factory.get(context); - return new TrackDataHub(context, - new TrackDataListeners(), - preferences, providerUtils, - TARGET_DISPLAYED_TRACK_POINTS); + return new TrackDataHub( + context, new TrackDataManager(), providerUtils, TARGET_DISPLAYED_TRACK_POINTS); } /** * Injection constructor. */ // @VisibleForTesting - TrackDataHub(Context ctx, TrackDataListeners listeners, SharedPreferences preferences, - MyTracksProviderUtils providerUtils, int targetNumPoints) { + TrackDataHub(Context ctx, TrackDataManager trackDataManager, MyTracksProviderUtils providerUtils, + int targetNumPoints) { this.context = ctx; - this.dataListeners = listeners; - this.preferences = preferences; + this.trackDataManager = trackDataManager; this.providerUtils = providerUtils; this.targetNumPoints = targetNumPoints; this.locationFactory = new DoubleBufferedLocationFactory(); @@ -233,12 +189,12 @@ public class TrackDataHub { listenerHandlerThread = new HandlerThread("trackDataContentThread"); listenerHandlerThread.start(); listenerHandler = new Handler(listenerHandlerThread.getLooper()); - dataSources = newDataSources(); - dataSourceManager = new DataSourceManager(dataSourceListener, dataSources); + dataSource = newDataSource(); + dataSourceManager = new DataSourceManager(dataSource, dataSourceListener); // This may or may not register internal listeners, depending on whether // we already had external listeners. - dataSourceManager.updateAllListeners(getNeededListenerTypes()); + dataSourceManager.updateListeners(getNeededListenerTypes()); loadSharedPreferences(); // If there were listeners already registered, make sure they become up-to-date. @@ -246,8 +202,8 @@ public class TrackDataHub { } // @VisibleForTesting - protected DataSourcesWrapper newDataSources() { - return new DataSourcesWrapperImpl(context, preferences); + protected DataSource newDataSource() { + return new DataSource(context); } /** @@ -267,7 +223,7 @@ public class TrackDataHub { started = false; - dataSources = null; + dataSource = null; dataSourceManager = null; listenerHandlerThread = null; listenerHandler = null; @@ -343,9 +299,9 @@ public class TrackDataHub { } Log.i(TAG, "Forcing location update"); - Location loc = dataSources.getLastKnownLocation(); + Location loc = dataSource.getLastKnownLocation(); if (loc != null) { - notifyLocationChanged(loc, getListenersFor(ListenerDataType.LOCATION_UPDATES)); + notifyLocationChanged(loc, getListenersFor(TrackDataType.LOCATION)); } } @@ -415,30 +371,31 @@ public class TrackDataHub { } public void registerTrackDataListener( - TrackDataListener listener, EnumSet dataTypes) { - synchronized (dataListeners) { - ListenerRegistration registration = - dataListeners.registerTrackDataListener(listener, dataTypes); + TrackDataListener listener, EnumSet dataTypes) { + synchronized (trackDataManager) { + ListenerState listenerState = trackDataManager.registerListener(listener, dataTypes); // Don't load any data or start internal listeners if start() hasn't been // called. When it is called, we'll do both things. - if (!isStarted()) return; + if (!isStarted()) { + return; + } - loadNewDataForListener(registration); + loadNewDataForListener(listenerState); - dataSourceManager.updateAllListeners(getNeededListenerTypes()); + dataSourceManager.updateListeners(getNeededListenerTypes()); } } public void unregisterTrackDataListener(TrackDataListener listener) { - synchronized (dataListeners) { - dataListeners.unregisterTrackDataListener(listener); + synchronized (trackDataManager) { + trackDataManager.unregisterListener(listener); // Don't load any data or start internal listeners if start() hasn't been // called. When it is called, we'll do both things. if (!isStarted()) return; - dataSourceManager.updateAllListeners(getNeededListenerTypes()); + dataSourceManager.updateListeners(getNeededListenerTypes()); } } @@ -446,33 +403,32 @@ public class TrackDataHub { * Reloads all track data received so far into the specified listeners. */ public void reloadDataForListener(TrackDataListener listener) { - ListenerRegistration registration; - synchronized (dataListeners) { - registration = dataListeners.getRegistration(listener); - registration.resetState(); - loadNewDataForListener(registration); + synchronized (trackDataManager) { + ListenerState listenerState = trackDataManager.getListenerState(listener); + listenerState.resetState(); + loadNewDataForListener(listenerState); } } /** * Reloads all track data received so far into the specified listeners. * - * Assumes it's called from a block that synchronizes on {@link #dataListeners}. + * Assumes it's called from a block that synchronizes on {@link #trackDataManager}. */ - private void loadNewDataForListener(final ListenerRegistration registration) { + private void loadNewDataForListener(final ListenerState listenerState) { if (!isStarted()) { Log.w(TAG, "Not started, not reloading"); return; } - if (registration == null) { - Log.w(TAG, "Not reloading for null registration"); + if (listenerState == null) { + Log.w(TAG, "Not reloading for null listener state"); return; } // If a listener happens to be added after this method but before the Runnable below is // executed, it will have triggered a separate call to load data only up to the point this // listener got to. This is ensured by being synchronized on listeners. - final boolean isOnlyListener = (dataListeners.getNumListeners() == 1); + final boolean isOnlyListener = (trackDataManager.getNumberOfListeners() == 1); runInListenerThread(new Runnable() { @SuppressWarnings("unchecked") @@ -480,30 +436,31 @@ public class TrackDataHub { public void run() { // Reload everything if either it's a different track, or the track has been resampled // (this also covers the case of a new registration). - boolean reloadAll = registration.lastTrackId != selectedTrackId || - registration.lastSamplingFrequency != lastSamplingFrequency; - Log.d(TAG, "Doing a " + (reloadAll ? "full" : "partial") + " reload for " + registration); + boolean reloadAll = listenerState.getLastTrackId() != selectedTrackId || + listenerState.getLastSamplingFrequency() != lastSamplingFrequency; + Log.d(TAG, "Doing a " + (reloadAll ? "full" : "partial") + " reload for " + listenerState); - TrackDataListener listener = registration.listener; + TrackDataListener listener = listenerState.getTrackDataListener(); Set listenerSet = Collections.singleton(listener); - if (registration.isInterestedIn(ListenerDataType.DISPLAY_PREFERENCES)) { + EnumSet trackDataTypes = listenerState.getTrackDataTypes(); + + if (trackDataTypes.contains(TrackDataType.PREFERENCE)) { reloadAll |= listener.onUnitsChanged(metricUnits); reloadAll |= listener.onReportSpeedChanged(reportSpeed); } - if (reloadAll && registration.isInterestedIn(ListenerDataType.SELECTED_TRACK_CHANGED)) { + if (reloadAll && trackDataTypes.contains(TrackDataType.SELECTED_TRACK)) { notifySelectedTrackChanged(selectedTrackId, listenerSet); } - if (registration.isInterestedIn(ListenerDataType.TRACK_UPDATES)) { + if (trackDataTypes.contains(TrackDataType.TRACKS_TABLE)) { notifyTrackUpdated(listenerSet); } - boolean interestedInPoints = - registration.isInterestedIn(ListenerDataType.POINT_UPDATES); - boolean interestedInSampledOutPoints = - registration.isInterestedIn(ListenerDataType.SAMPLED_OUT_POINT_UPDATES); + boolean interestedInPoints = trackDataTypes.contains(TrackDataType.TRACK_POINTS_TABLE); + boolean interestedInSampledOutPoints = trackDataTypes.contains( + TrackDataType.SAMPLED_OUT_TRACK_POINTS); if (interestedInPoints || interestedInSampledOutPoints) { long minPointId = 0; int previousNumPoints = 0; @@ -513,8 +470,8 @@ public class TrackDataHub { notifyPointsCleared(listenerSet); } else { // Send only new points - minPointId = registration.lastPointId + 1; - previousNumPoints = registration.numLoadedPoints; + minPointId = listenerState.getLastPointId() + 1; + previousNumPoints = listenerState.getNumberOfLoadedPoints(); } // If this is the only listener we have registered, keep the state that we serve to it as @@ -530,11 +487,11 @@ public class TrackDataHub { interestedInSampledOutPoints ? listenerSet : Collections.EMPTY_SET); } - if (registration.isInterestedIn(ListenerDataType.WAYPOINT_UPDATES)) { + if (trackDataTypes.contains(TrackDataType.WAYPOINTS_TABLE)) { notifyWaypointUpdated(listenerSet); } - if (registration.isInterestedIn(ListenerDataType.LOCATION_UPDATES)) { + if (trackDataTypes.contains(TrackDataType.LOCATION)) { if (lastSeenLocation != null) { notifyLocationChanged(lastSeenLocation, true, listenerSet); } else { @@ -542,7 +499,7 @@ public class TrackDataHub { } } - if (registration.isInterestedIn(ListenerDataType.COMPASS_UPDATES)) { + if (trackDataTypes.contains(TrackDataType.COMPASS)) { notifyHeadingChanged(listenerSet); } } @@ -557,8 +514,8 @@ public class TrackDataHub { Log.w(TAG, "Not started, not reloading"); return; } - synchronized (dataListeners) { - if (!dataListeners.hasListeners()) { + synchronized (trackDataManager) { + if (trackDataManager.getNumberOfListeners() == 0) { Log.d(TAG, "No listeners, not reloading"); return; } @@ -569,33 +526,33 @@ public class TrackDataHub { public void run() { // Ignore the return values here, we're already sending the full data set anyway for (TrackDataListener listener : - getListenersFor(ListenerDataType.DISPLAY_PREFERENCES)) { + getListenersFor(TrackDataType.PREFERENCE)) { listener.onUnitsChanged(metricUnits); listener.onReportSpeedChanged(reportSpeed); } notifySelectedTrackChanged(selectedTrackId, - getListenersFor(ListenerDataType.SELECTED_TRACK_CHANGED)); + getListenersFor(TrackDataType.SELECTED_TRACK)); - notifyTrackUpdated(getListenersFor(ListenerDataType.TRACK_UPDATES)); + notifyTrackUpdated(getListenersFor(TrackDataType.TRACKS_TABLE)); Set pointListeners = - getListenersFor(ListenerDataType.POINT_UPDATES); + getListenersFor(TrackDataType.TRACK_POINTS_TABLE); Set sampledOutPointListeners = - getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES); + getListenersFor(TrackDataType.SAMPLED_OUT_TRACK_POINTS); notifyPointsCleared(pointListeners); notifyPointsUpdated(true, 0, 0, pointListeners, sampledOutPointListeners); - notifyWaypointUpdated(getListenersFor(ListenerDataType.WAYPOINT_UPDATES)); + notifyWaypointUpdated(getListenersFor(TrackDataType.WAYPOINTS_TABLE)); if (lastSeenLocation != null) { notifyLocationChanged(lastSeenLocation, true, - getListenersFor(ListenerDataType.LOCATION_UPDATES)); + getListenersFor(TrackDataType.LOCATION)); } else { notifyFixType(); } - notifyHeadingChanged(getListenersFor(ListenerDataType.COMPASS_UPDATES)); + notifyHeadingChanged(getListenersFor(TrackDataType.COMPASS)); } }); } @@ -630,12 +587,12 @@ public class TrackDataHub { @Override public void run() { Set displayListeners = - getListenersFor(ListenerDataType.DISPLAY_PREFERENCES); + getListenersFor(TrackDataType.PREFERENCE); for (TrackDataListener listener : displayListeners) { // TODO: Do the reloading just once for all interested listeners if (listener.onReportSpeedChanged(reportSpeed)) { - synchronized (dataListeners) { + synchronized (trackDataManager) { reloadDataForListener(listener); } } @@ -651,11 +608,11 @@ public class TrackDataHub { runInListenerThread(new Runnable() { @Override public void run() { - Set displayListeners = getListenersFor(ListenerDataType.DISPLAY_PREFERENCES); + Set displayListeners = getListenersFor(TrackDataType.PREFERENCE); for (TrackDataListener listener : displayListeners) { if (listener.onUnitsChanged(metricUnits)) { - synchronized (dataListeners) { + synchronized (trackDataManager) { reloadDataForListener(listener); } } @@ -683,7 +640,7 @@ public class TrackDataHub { // Notify to everyone. Log.d(TAG, "Notifying fix type: " + state); for (TrackDataListener listener : - getListenersFor(ListenerDataType.LOCATION_UPDATES)) { + getListenersFor(TrackDataType.LOCATION)) { listener.onProviderStateChange(state); } } @@ -945,9 +902,9 @@ public class TrackDataHub { Log.i(TAG, "Resampling point set after " + numLoadedPoints + " points."); resetState(); - synchronized (dataListeners) { - sampledListeners = getListenersFor(ListenerDataType.POINT_UPDATES); - sampledOutListeners = getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES); + synchronized (trackDataManager) { + sampledListeners = getListenersFor(TrackDataType.TRACK_POINTS_TABLE); + sampledOutListeners = getListenersFor(TrackDataType.SAMPLED_OUT_TRACK_POINTS); } maxPointId = -1; minPointId = 0; @@ -1030,12 +987,10 @@ public class TrackDataHub { listener.onNewTrackPointsDone(); // Update the listener state - ListenerRegistration registration = dataListeners.getRegistration(listener); - if (registration != null) { - registration.lastTrackId = currentSelectedTrackId; - registration.lastPointId = localLastSeenLocationId; - registration.lastSamplingFrequency = pointSamplingFrequency; - registration.numLoadedPoints = localNumLoadedPoints; + ListenerState listenerState = trackDataManager.getListenerState(listener); + if (listenerState != null) { + listenerState.setState(currentSelectedTrackId, localLastSeenLocationId, + pointSamplingFrequency, localNumLoadedPoints); } } } @@ -1089,17 +1044,17 @@ public class TrackDataHub { listenerHandler.post(runnable); } - private Set getListenersFor(ListenerDataType type) { - synchronized (dataListeners) { - return dataListeners.getListenersFor(type); + private Set getListenersFor(TrackDataType type) { + synchronized (trackDataManager) { + return trackDataManager.getListeners(type); } } - private EnumSet getNeededListenerTypes() { - EnumSet neededTypes = dataListeners.getAllRegisteredTypes(); + private EnumSet getNeededListenerTypes() { + EnumSet neededTypes = trackDataManager.getRegisteredTrackDataTypes(); // We always want preference updates. - neededTypes.add(ListenerDataType.DISPLAY_PREFERENCES); + neededTypes.add(TrackDataType.PREFERENCE); return neededTypes; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataListeners.java b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataListeners.java deleted file mode 100644 index 7a85b9eef..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataListeners.java +++ /dev/null @@ -1,159 +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.content; - -import static com.google.android.apps.mytracks.Constants.TAG; - -import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType; - -import android.util.Log; - -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -/** - * Manager for the external data listeners and their listening types. - * - * @author Rodrigo Damazio - */ -class TrackDataListeners { - - /** Internal representation of a listener's registration. */ - static class ListenerRegistration { - final TrackDataListener listener; - final EnumSet types; - - // State that was last notified to the listener, for resuming after a pause. - long lastTrackId; - long lastPointId; - int lastSamplingFrequency; - int numLoadedPoints; - - public ListenerRegistration(TrackDataListener listener, - EnumSet types) { - this.listener = listener; - this.types = types; - } - - public boolean isInterestedIn(ListenerDataType type) { - return types.contains(type); - } - - public void resetState() { - lastTrackId = 0L; - lastPointId = 0L; - lastSamplingFrequency = 0; - numLoadedPoints = 0; - } - - @Override - public String toString() { - return "ListenerRegistration [listener=" + listener + ", types=" + types - + ", lastTrackId=" + lastTrackId + ", lastPointId=" + lastPointId - + ", lastSamplingFrequency=" + lastSamplingFrequency - + ", numLoadedPoints=" + numLoadedPoints + "]"; - } - } - - /** Map of external listener to its registration details. */ - private final Map registeredListeners = - new HashMap(); - - /** Map of data type to external listeners interested in it. */ - private final Map> listenerSetsPerType = - new EnumMap>(ListenerDataType.class); - - public TrackDataListeners() { - // Create sets for all data types at startup. - for (ListenerDataType type : ListenerDataType.values()) { - listenerSetsPerType.put(type, new LinkedHashSet()); - } - } - - /** - * Registers a listener to send data to. - * It is ok to call this method before {@link TrackDataHub#start}, and in that case - * the data will only be passed to listeners when {@link TrackDataHub#start} is called. - * - * @param listener the listener to register - * @param dataTypes the type of data that the listener is interested in - */ - public ListenerRegistration registerTrackDataListener(final TrackDataListener listener, EnumSet dataTypes) { - Log.d(TAG, "Registered track data listener: " + listener); - if (registeredListeners.containsKey(listener)) { - throw new IllegalStateException("Listener already registered"); - } - - ListenerRegistration registration = new ListenerRegistration(listener, dataTypes); - registeredListeners.put(listener, registration); - - for (ListenerDataType type : dataTypes) { - // This is guaranteed not to be null. - Set typeSet = listenerSetsPerType.get(type); - typeSet.add(listener); - } - - return registration; - } - - /** - * Unregisters a listener to send data to. - * - * @param listener the listener to unregister - */ - public void unregisterTrackDataListener(TrackDataListener listener) { - Log.d(TAG, "Unregistered track data listener: " + listener); - // Remove and keep the corresponding registration. - ListenerRegistration match = registeredListeners.remove(listener); - if (match == null) { - Log.w(TAG, "Tried to unregister listener which is not registered."); - return; - } - - // Remove it from the per-type sets - for (ListenerDataType type : match.types) { - listenerSetsPerType.get(type).remove(listener); - } - } - - public ListenerRegistration getRegistration(TrackDataListener listener) { - return registeredListeners.get(listener); - } - - public Set getListenersFor(ListenerDataType type) { - return listenerSetsPerType.get(type); - } - - public EnumSet getAllRegisteredTypes() { - EnumSet listeners = EnumSet.noneOf(ListenerDataType.class); - for (ListenerRegistration registration : this.registeredListeners.values()) { - listeners.addAll(registration.types); - } - return listeners; - } - - public boolean hasListeners() { - return !registeredListeners.isEmpty(); - } - - public int getNumListeners() { - return registeredListeners.size(); - } -} diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataManager.java b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataManager.java new file mode 100644 index 000000000..9c3ededd7 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataManager.java @@ -0,0 +1,127 @@ +/* + * 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.content; + +import static com.google.android.apps.mytracks.Constants.TAG; + +import android.util.Log; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * Manages register/unregister {@link TrackDataListener} and keeping the state + * for each registered listener. + * + * @author Rodrigo Damazio + */ +public class TrackDataManager { + + // Map of listener to its state + private final Map + listenerToStateMap = new HashMap(); + + // Map of track data type to listeners + private final Map> + typeToListenersMap = new EnumMap>(TrackDataType.class); + + public TrackDataManager() { + for (TrackDataType trackDataType : TrackDataType.values()) { + typeToListenersMap.put(trackDataType, new LinkedHashSet()); + } + } + + /** + * Registers a listener. + * + * @param listener the listener + * @param trackDataTypes the track data types the listener is interested + */ + public ListenerState registerListener( + TrackDataListener listener, EnumSet trackDataTypes) { + if (listenerToStateMap.containsKey(listener)) { + throw new IllegalStateException("Listener is already registered"); + } + + ListenerState listenerState = new ListenerState(listener, trackDataTypes); + listenerToStateMap.put(listener, listenerState); + + for (TrackDataType trackDataType : trackDataTypes) { + typeToListenersMap.get(trackDataType).add(listener); + } + + return listenerState; + } + + /** + * Unregisters a listener. + * + * @param listener the listener + */ + public void unregisterListener(TrackDataListener listener) { + ListenerState removed = listenerToStateMap.remove(listener); + if (removed == null) { + Log.w(TAG, "Tried to unregister a listener that is not registered."); + return; + } + + // Remove the listener from the typeToListenersMap + for (TrackDataType trackDataType : removed.getTrackDataTypes()) { + typeToListenersMap.get(trackDataType).remove(listener); + } + } + + /** + * Gets the number of {@link TrackDataListener}. + */ + public int getNumberOfListeners() { + return listenerToStateMap.size(); + } + + /** + * Gets the track listener state. + * + * @param listener the listener + */ + public ListenerState getListenerState(TrackDataListener listener) { + return listenerToStateMap.get(listener); + } + + /** + * Gets the listeners for a {@link TrackDataType}. + * + * @param type the type + */ + public Set getListeners(TrackDataType type) { + return typeToListenersMap.get(type); + } + + /** + * Gets all the registered {@link TrackDataType}. + */ + public EnumSet getRegisteredTrackDataTypes() { + EnumSet types = EnumSet.noneOf(TrackDataType.class); + for (ListenerState registration : this.listenerToStateMap.values()) { + types.addAll(registration.getTrackDataTypes()); + } + return types; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataType.java b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataType.java new file mode 100644 index 000000000..319b5bbc0 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/content/TrackDataType.java @@ -0,0 +1,34 @@ +/* + * 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.content; + +/** + * Types of track data. + * + * @author Jimmy Shih + */ +public enum TrackDataType { + + SELECTED_TRACK, // selected track changes + TRACKS_TABLE, // tracks table changes + WAYPOINTS_TABLE, // waypoints table changes + TRACK_POINTS_TABLE, // track points table changes + SAMPLED_OUT_TRACK_POINTS, // sampled-out track points changes + LOCATION, // location changes + COMPASS, // compass changes + PREFERENCE; // preference changes +} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java b/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java index be9c1ff28..e1dab8720 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java +++ b/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java @@ -19,12 +19,12 @@ package com.google.android.apps.mytracks.fragments; import com.google.android.apps.mytracks.ChartView; import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.MyTracksApplication; +import com.google.android.apps.mytracks.content.TrackDataType; import com.google.android.apps.mytracks.content.MyTracksLocation; import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor.SensorDataSet; import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.TrackDataHub; -import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType; import com.google.android.apps.mytracks.content.TrackDataListener; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.stats.DoubleBuffer; @@ -360,12 +360,12 @@ public class ChartFragment extends Fragment implements TrackDataListener { private synchronized void resumeTrackDataHub() { trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub(); trackDataHub.registerTrackDataListener(this, EnumSet.of( - ListenerDataType.SELECTED_TRACK_CHANGED, - ListenerDataType.TRACK_UPDATES, - ListenerDataType.WAYPOINT_UPDATES, - ListenerDataType.POINT_UPDATES, - ListenerDataType.SAMPLED_OUT_POINT_UPDATES, - ListenerDataType.DISPLAY_PREFERENCES)); + TrackDataType.SELECTED_TRACK, + TrackDataType.TRACKS_TABLE, + TrackDataType.WAYPOINTS_TABLE, + TrackDataType.TRACK_POINTS_TABLE, + TrackDataType.SAMPLED_OUT_TRACK_POINTS, + TrackDataType.PREFERENCE)); } /** diff --git a/MyTracks/src/com/google/android/apps/mytracks/fragments/MapFragment.java b/MyTracks/src/com/google/android/apps/mytracks/fragments/MapFragment.java index d0196a512..5a5e5c72c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/fragments/MapFragment.java +++ b/MyTracks/src/com/google/android/apps/mytracks/fragments/MapFragment.java @@ -19,11 +19,11 @@ package com.google.android.apps.mytracks.fragments; import com.google.android.apps.mytracks.MapOverlay; import com.google.android.apps.mytracks.MyTracksApplication; import com.google.android.apps.mytracks.TrackDetailActivity; +import com.google.android.apps.mytracks.content.TrackDataType; import com.google.android.apps.mytracks.content.MyTracksProviderUtils; import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory; import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.TrackDataHub; -import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType; import com.google.android.apps.mytracks.content.TrackDataListener; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.stats.TripStatistics; @@ -420,11 +420,11 @@ public class MapFragment extends Fragment private synchronized void resumeTrackDataHub() { trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub(); trackDataHub.registerTrackDataListener(this, EnumSet.of( - ListenerDataType.SELECTED_TRACK_CHANGED, - ListenerDataType.WAYPOINT_UPDATES, - ListenerDataType.POINT_UPDATES, - ListenerDataType.LOCATION_UPDATES, - ListenerDataType.COMPASS_UPDATES)); + TrackDataType.SELECTED_TRACK, + TrackDataType.WAYPOINTS_TABLE, + TrackDataType.TRACK_POINTS_TABLE, + TrackDataType.LOCATION, + TrackDataType.COMPASS)); } /** diff --git a/MyTracks/src/com/google/android/apps/mytracks/fragments/StatsFragment.java b/MyTracks/src/com/google/android/apps/mytracks/fragments/StatsFragment.java index 8a00c3c2e..0eaf01a89 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/fragments/StatsFragment.java +++ b/MyTracks/src/com/google/android/apps/mytracks/fragments/StatsFragment.java @@ -17,9 +17,9 @@ package com.google.android.apps.mytracks.fragments; import com.google.android.apps.mytracks.MyTracksApplication; +import com.google.android.apps.mytracks.content.TrackDataType; import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.TrackDataHub; -import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType; import com.google.android.apps.mytracks.content.TrackDataListener; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.stats.TripStatistics; @@ -257,10 +257,10 @@ public class StatsFragment extends Fragment implements TrackDataListener { private synchronized void resumeTrackDataHub() { trackDataHub = ((MyTracksApplication) getActivity().getApplication()).getTrackDataHub(); trackDataHub.registerTrackDataListener(this, EnumSet.of( - ListenerDataType.SELECTED_TRACK_CHANGED, - ListenerDataType.TRACK_UPDATES, - ListenerDataType.LOCATION_UPDATES, - ListenerDataType.DISPLAY_PREFERENCES)); + TrackDataType.SELECTED_TRACK, + TrackDataType.TRACKS_TABLE, + TrackDataType.LOCATION, + TrackDataType.PREFERENCE)); } /** diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/content/TrackDataHubTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/content/TrackDataHubTest.java index da068091d..e6ab88ef0 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/content/TrackDataHubTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/content/TrackDataHubTest.java @@ -15,23 +15,21 @@ */ package com.google.android.apps.mytracks.content; -import static com.google.android.testing.mocking.AndroidMock.anyInt; import static com.google.android.testing.mocking.AndroidMock.capture; import static com.google.android.testing.mocking.AndroidMock.eq; import static com.google.android.testing.mocking.AndroidMock.expect; import static com.google.android.testing.mocking.AndroidMock.isA; import static com.google.android.testing.mocking.AndroidMock.leq; -import static com.google.android.testing.mocking.AndroidMock.same; import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationFactory; import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator; -import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType; import com.google.android.apps.mytracks.content.TrackDataListener.ProviderState; import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext; import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.maps.mytracks.R; import com.google.android.testing.mocking.AndroidMock; +import com.google.android.testing.mocking.UsesMocks; import android.content.Context; import android.content.SharedPreferences; @@ -69,8 +67,8 @@ public class TrackDataHubTest extends AndroidTestCase { private MyTracksProviderUtils providerUtils; private TrackDataHub hub; - private TrackDataListeners listeners; - private DataSourcesWrapper dataSources; + private TrackDataManager trackDataManager; + private DataSource dataSource; private SharedPreferences sharedPreferences; private TrackDataListener listener1; private TrackDataListener listener2; @@ -79,6 +77,7 @@ public class TrackDataHubTest extends AndroidTestCase { private MockContext context; private float declination; + @UsesMocks({MyTracksProviderUtils.class, DataSource.class, TrackDataListener.class}) @Override protected void setUp() throws Exception { super.setUp(); @@ -90,13 +89,13 @@ public class TrackDataHubTest extends AndroidTestCase { sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE); providerUtils = AndroidMock.createMock("providerUtils", MyTracksProviderUtils.class); - dataSources = AndroidMock.createNiceMock("dataSources", DataSourcesWrapper.class); + dataSource = AndroidMock.createMock("dataSource", DataSource.class, context); - listeners = new TrackDataListeners(); - hub = new TrackDataHub(context, listeners, sharedPreferences, providerUtils, TARGET_POINTS) { + trackDataManager = new TrackDataManager(); + hub = new TrackDataHub(context, trackDataManager, providerUtils, TARGET_POINTS) { @Override - protected DataSourcesWrapper newDataSources() { - return dataSources; + protected DataSource newDataSource() { + return dataSource; } @Override @@ -119,18 +118,18 @@ public class TrackDataHubTest extends AndroidTestCase { @Override protected void tearDown() throws Exception { - AndroidMock.reset(dataSources); + AndroidMock.reset(dataSource); // Expect everything to be unregistered. if (preferenceListenerCapture.hasCaptured()) { - dataSources.unregisterOnSharedPreferenceChangeListener(preferenceListenerCapture.getValue()); + dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListenerCapture.getValue()); } - dataSources.removeLocationUpdates(isA(LocationListener.class)); - dataSources.unregisterSensorListener(isA(SensorEventListener.class)); - dataSources.unregisterContentObserver(isA(ContentObserver.class)); + dataSource.unregisterLocationListener(isA(LocationListener.class)); + dataSource.unregisterCompassListener(isA(SensorEventListener.class)); + dataSource.unregisterContentObserver(isA(ContentObserver.class)); AndroidMock.expectLastCall().times(3); - AndroidMock.replay(dataSources); + AndroidMock.replay(dataSource); hub.stop(); hub = null; @@ -143,8 +142,7 @@ public class TrackDataHubTest extends AndroidTestCase { Track track = new Track(); expect(providerUtils.getTrack(TRACK_ID)).andStubReturn(track); expectStart(); - dataSources.registerContentObserver( - eq(TracksColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver(eq(TracksColumns.CONTENT_URI), capture(observerCapture)); // Expect the initial loading. // Both listeners (registered before and after start) should get the same data. @@ -153,9 +151,9 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.TRACK_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.TRACKS_TABLE)); hub.start(); - hub.registerTrackDataListener(listener2, EnumSet.of(ListenerDataType.TRACK_UPDATES)); + hub.registerTrackDataListener(listener2, EnumSet.of(TrackDataType.TRACKS_TABLE)); verifyAndReset(); @@ -185,7 +183,7 @@ public class TrackDataHubTest extends AndroidTestCase { verifyAndReset(); // Unregister the other, expect internal unregistration - dataSources.unregisterContentObserver(observer); + dataSource.unregisterContentObserver(observer); replay(); @@ -321,8 +319,7 @@ public class TrackDataHubTest extends AndroidTestCase { .andReturn(wpt2); expectStart(); - dataSources.registerContentObserver( - eq(WaypointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_URI), capture(observerCapture)); // Expect the initial loading. // Both listeners (registered before and after start) should get the same data. @@ -337,9 +334,9 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.WAYPOINT_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE)); hub.start(); - hub.registerTrackDataListener(listener2, EnumSet.of(ListenerDataType.WAYPOINT_UPDATES)); + hub.registerTrackDataListener(listener2, EnumSet.of(TrackDataType.WAYPOINTS_TABLE)); verifyAndReset(); @@ -398,7 +395,7 @@ public class TrackDataHubTest extends AndroidTestCase { verifyAndReset(); // Unregister the other, expect internal unregistration - dataSources.unregisterContentObserver(observer); + dataSource.unregisterContentObserver(observer); replay(); @@ -412,8 +409,8 @@ public class TrackDataHubTest extends AndroidTestCase { Capture observerCapture = new Capture(); expectStart(); - dataSources.registerContentObserver( - eq(TrackPointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver( + eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture)); FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5); expect(providerUtils.getLocationIterator( @@ -428,7 +425,7 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.start(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.POINT_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); verifyAndReset(); @@ -446,7 +443,7 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.start(); - hub.registerTrackDataListener(listener2, EnumSet.of(ListenerDataType.POINT_UPDATES)); + hub.registerTrackDataListener(listener2, EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); verifyAndReset(); @@ -497,8 +494,8 @@ public class TrackDataHubTest extends AndroidTestCase { Capture observerCapture = new Capture(); expectStart(); - dataSources.registerContentObserver( - eq(TrackPointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver( + eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture)); FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5); expect(providerUtils.getLocationIterator( @@ -513,13 +510,13 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.start(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.POINT_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); verifyAndReset(); // Unregister ContentObserver observer = observerCapture.getValue(); - dataSources.unregisterContentObserver(observer); + dataSource.unregisterContentObserver(observer); replay(); @@ -528,8 +525,8 @@ public class TrackDataHubTest extends AndroidTestCase { verifyAndReset(); // Register again, except only points since unregistered. - dataSources.registerContentObserver( - eq(TrackPointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver( + eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture)); locationIterator = new FixedSizeLocationIterator(1, 10, 5); expect(providerUtils.getLocationIterator( @@ -543,7 +540,7 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.POINT_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); verifyAndReset(); } @@ -552,8 +549,8 @@ public class TrackDataHubTest extends AndroidTestCase { Capture observerCapture = new Capture(); expectStart(); - dataSources.registerContentObserver( - eq(TrackPointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver( + eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture)); FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5); expect(providerUtils.getLocationIterator( @@ -568,13 +565,13 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.start(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.POINT_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); verifyAndReset(); // Unregister ContentObserver observer = observerCapture.getValue(); - dataSources.unregisterContentObserver(observer); + dataSource.unregisterContentObserver(observer); replay(); @@ -583,8 +580,8 @@ public class TrackDataHubTest extends AndroidTestCase { verifyAndReset(); // Register again after track changed, expect all points. - dataSources.registerContentObserver( - eq(TrackPointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver( + eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture)); locationIterator = new FixedSizeLocationIterator(1, 10); expect(providerUtils.getLocationIterator( @@ -599,7 +596,7 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.loadTrack(TRACK_ID + 1); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.POINT_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); verifyAndReset(); } @@ -608,8 +605,8 @@ public class TrackDataHubTest extends AndroidTestCase { Capture observerCapture = new Capture(); expectStart(); - dataSources.registerContentObserver( - eq(TrackPointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver( + eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture)); FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 200, 4, 25, 71, 120); expect(providerUtils.getLocationIterator( @@ -627,9 +624,9 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.registerTrackDataListener(listener1, - EnumSet.of(ListenerDataType.POINT_UPDATES)); + EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); hub.registerTrackDataListener(listener2, - EnumSet.of(ListenerDataType.POINT_UPDATES, ListenerDataType.SAMPLED_OUT_POINT_UPDATES)); + EnumSet.of(TrackDataType.TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS)); hub.start(); verifyAndReset(); @@ -639,8 +636,8 @@ public class TrackDataHubTest extends AndroidTestCase { Capture observerCapture = new Capture(); expectStart(); - dataSources.registerContentObserver( - eq(TrackPointsColumns.CONTENT_URI), eq(false), capture(observerCapture)); + dataSource.registerContentObserver( + eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture)); // Deliver 30 points (no sampling happens) FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 30, 5); @@ -656,7 +653,7 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.start(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.POINT_UPDATES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.TRACK_POINTS_TABLE)); verifyAndReset(); @@ -702,16 +699,14 @@ public class TrackDataHubTest extends AndroidTestCase { public void testCompassListen() throws Exception { AndroidMock.resetToDefault(listener1); - Sensor compass = newSensor(); - expect(dataSources.getSensor(Sensor.TYPE_ORIENTATION)).andReturn(compass); + expectStart(); Capture listenerCapture = new Capture(); - dataSources.registerSensorListener(capture(listenerCapture), same(compass), anyInt()); + dataSource.registerCompassListener(capture(listenerCapture)); Capture locationListenerCapture = new Capture(); - dataSources.requestLocationUpdates(capture(locationListenerCapture)); + dataSource.registerLocationListener(capture(locationListenerCapture)); SensorEvent event = newSensorEvent(); - event.sensor = compass; // First, get a dummy heading update. listener1.onCurrentHeadingChanged(0.0); @@ -726,7 +721,7 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); hub.registerTrackDataListener(listener1, - EnumSet.of(ListenerDataType.COMPASS_UPDATES, ListenerDataType.LOCATION_UPDATES)); + EnumSet.of(TrackDataType.COMPASS, TrackDataType.LOCATION)); hub.start(); SensorEventListener sensorListener = listenerCapture.getValue(); @@ -786,9 +781,7 @@ public class TrackDataHubTest extends AndroidTestCase { PreferencesUtils.setBoolean(context, R.string.report_speed_key, true); PreferencesUtils.setBoolean(context, R.string.metric_units_key, true); - Capture listenerCapture = - new Capture(); - dataSources.registerOnSharedPreferenceChangeListener(capture(listenerCapture)); + expectStart(); expect(listener1.onUnitsChanged(true)).andReturn(false); expect(listener2.onUnitsChanged(true)).andReturn(false); @@ -797,9 +790,9 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); - hub.registerTrackDataListener(listener1, EnumSet.of(ListenerDataType.DISPLAY_PREFERENCES)); + hub.registerTrackDataListener(listener1, EnumSet.of(TrackDataType.PREFERENCE)); hub.start(); - hub.registerTrackDataListener(listener2, EnumSet.of(ListenerDataType.DISPLAY_PREFERENCES)); + hub.registerTrackDataListener(listener2, EnumSet.of(TrackDataType.PREFERENCE)); verifyAndReset(); @@ -809,12 +802,12 @@ public class TrackDataHubTest extends AndroidTestCase { replay(); PreferencesUtils.setBoolean(context, R.string.report_speed_key, false); - OnSharedPreferenceChangeListener listener = listenerCapture.getValue(); + OnSharedPreferenceChangeListener listener = preferenceListenerCapture.getValue(); listener.onSharedPreferenceChanged( sharedPreferences, PreferencesUtils.getKey(context, R.string.report_speed_key)); - AndroidMock.verify(dataSources, providerUtils, listener1, listener2); - AndroidMock.reset(dataSources, providerUtils, listener1, listener2); + AndroidMock.verify(dataSource, providerUtils, listener1, listener2); + AndroidMock.reset(dataSource, providerUtils, listener1, listener2); expect(listener1.onUnitsChanged(false)).andReturn(false); expect(listener2.onUnitsChanged(false)).andReturn(false); @@ -829,15 +822,15 @@ public class TrackDataHubTest extends AndroidTestCase { } private void expectStart() { - dataSources.registerOnSharedPreferenceChangeListener(capture(preferenceListenerCapture)); + dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceListenerCapture)); } private void replay() { - AndroidMock.replay(dataSources, providerUtils, listener1, listener2); + AndroidMock.replay(dataSource, providerUtils, listener1, listener2); } private void verifyAndReset() { - AndroidMock.verify(listener1, listener2, dataSources, providerUtils); - AndroidMock.reset(listener1, listener2, dataSources, providerUtils); + AndroidMock.verify(listener1, listener2, dataSource, providerUtils); + AndroidMock.reset(listener1, listener2, dataSource, providerUtils); } }