forked from upstream-mirrors/OpenTracks
Further splitting the data hub.
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
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;
|
||||
import android.location.LocationProvider;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* External data source manager, which converts system-level events into My Tracks data events.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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 notifyLocationChanged(Location loc);
|
||||
void notifyHeadingChanged(float heading);
|
||||
}
|
||||
|
||||
private final DataSourceListener listener;
|
||||
|
||||
/** Observer for when the tracks table is updated. */
|
||||
private class TrackObserver extends ContentObserver {
|
||||
public TrackObserver() {
|
||||
super(contentHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
listener.notifyTrackUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/** Observer for when the waypoints table is updated. */
|
||||
private class WaypointObserver extends ContentObserver {
|
||||
public WaypointObserver() {
|
||||
super(contentHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
listener.notifyWaypointUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/** Observer for when the points table is updated. */
|
||||
private class PointObserver extends ContentObserver {
|
||||
public PointObserver() {
|
||||
super(contentHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
listener.notifyPointsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/** 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.notifyLocationProviderEnabled(status == LocationProvider.AVAILABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
if (!LocationManager.GPS_PROVIDER.equals(provider)) return;
|
||||
|
||||
listener.notifyLocationProviderEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
if (!LocationManager.GPS_PROVIDER.equals(provider)) return;
|
||||
|
||||
listener.notifyLocationProviderEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
listener.notifyLocationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
/** Listener for compass readings. */
|
||||
private class CompassListener implements
|
||||
SensorEventListener {
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
listener.notifyHeadingChanged(event.values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/** Wrapper for registering internal listeners. */
|
||||
private final DataSourcesWrapper dataSources;
|
||||
|
||||
// Internal listeners (to receive data from the system)
|
||||
private final Set<ListenerDataType> 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;
|
||||
|
||||
DataSourceManager(DataSourceListener listener, DataSourcesWrapper dataSources) {
|
||||
this.listener = listener;
|
||||
this.dataSources = dataSources;
|
||||
|
||||
contentHandler = new Handler();
|
||||
pointObserver = new PointObserver();
|
||||
waypointObserver = new WaypointObserver();
|
||||
trackObserver = new TrackObserver();
|
||||
|
||||
compassListener = new CompassListener();
|
||||
locationListener = new CurrentLocationListener();
|
||||
preferenceListener = new HubSharedPreferenceListener();
|
||||
}
|
||||
|
||||
/** Updates the internal (sensor, position, etc) listeners. */
|
||||
void updateAllListeners(EnumSet<ListenerDataType> externallyNeededListeners) {
|
||||
EnumSet<ListenerDataType> neededListeners = EnumSet.copyOf(externallyNeededListeners);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Updating internal listeners to types " + neededListeners);
|
||||
|
||||
// Unnecessary = registered - needed
|
||||
Set<ListenerDataType> unnecessaryListeners = EnumSet.copyOf(registeredListeners);
|
||||
unnecessaryListeners.removeAll(neededListeners);
|
||||
|
||||
// Missing = needed - registered
|
||||
Set<ListenerDataType> missingListeners = EnumSet.copyOf(neededListeners);
|
||||
missingListeners.removeAll(registeredListeners);
|
||||
|
||||
// Remove all unnecessary listeners.
|
||||
for (ListenerDataType type : unnecessaryListeners) {
|
||||
unregisterListener(type);
|
||||
}
|
||||
|
||||
// Add all missing listeners.
|
||||
for (ListenerDataType type : missingListeners) {
|
||||
registerListener(type);
|
||||
}
|
||||
|
||||
// Now all needed types are registered.
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LOCATION_UPDATES:
|
||||
dataSources.requestLocationUpdates(locationListener);
|
||||
break;
|
||||
case POINT_UPDATES:
|
||||
dataSources.registerContentObserver(
|
||||
TrackPointsColumns.CONTENT_URI, false, pointObserver);
|
||||
break;
|
||||
case TRACK_UPDATES:
|
||||
dataSources.registerContentObserver(TracksColumns.CONTENT_URI, false, trackObserver);
|
||||
break;
|
||||
case WAYPOINT_UPDATES:
|
||||
dataSources.registerContentObserver(
|
||||
WaypointsColumns.CONTENT_URI, false, waypointObserver);
|
||||
break;
|
||||
case DISPLAY_PREFERENCES:
|
||||
dataSources.registerOnSharedPreferenceChangeListener(preferenceListener);
|
||||
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);
|
||||
break;
|
||||
case LOCATION_UPDATES:
|
||||
dataSources.removeLocationUpdates(locationListener);
|
||||
break;
|
||||
case POINT_UPDATES:
|
||||
dataSources.unregisterContentObserver(pointObserver);
|
||||
break;
|
||||
case TRACK_UPDATES:
|
||||
dataSources.unregisterContentObserver(trackObserver);
|
||||
break;
|
||||
case WAYPOINT_UPDATES:
|
||||
dataSources.unregisterContentObserver(waypointObserver);
|
||||
break;
|
||||
case DISPLAY_PREFERENCES:
|
||||
dataSources.unregisterOnSharedPreferenceChangeListener(preferenceListener);
|
||||
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);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -24,11 +24,11 @@ import android.location.LocationListener;
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* Interface for abstracting external data sources.
|
||||
* Interface for abstracting registration of external data source listeners.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
interface TrackDataSources {
|
||||
interface DataSourcesWrapper {
|
||||
// Preferences
|
||||
void registerOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener listener);
|
||||
+2
-2
@@ -42,7 +42,7 @@ import android.widget.Toast;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
class TrackDataSourcesImpl implements TrackDataSources {
|
||||
class DataSourcesWrapperImpl implements DataSourcesWrapper {
|
||||
// System services
|
||||
private final SensorManager sensorManager;
|
||||
private final LocationManager locationManager;
|
||||
@@ -50,7 +50,7 @@ class TrackDataSourcesImpl implements TrackDataSources {
|
||||
private final SharedPreferences sharedPreferences;
|
||||
private final Context context;
|
||||
|
||||
TrackDataSourcesImpl(Context context, SharedPreferences sharedPreferences) {
|
||||
DataSourcesWrapperImpl(Context context, SharedPreferences sharedPreferences) {
|
||||
this.context = context;
|
||||
this.sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
@@ -19,44 +19,27 @@ import static com.google.android.apps.mytracks.Constants.MAX_LOCATION_AGE_MS;
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.DataSourceManager.DataSourceListener;
|
||||
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.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.content.TrackDataListeners.ListenerRegistration;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.hardware.GeomagneticField;
|
||||
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;
|
||||
import android.location.LocationProvider;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -106,37 +89,72 @@ public class TrackDataHub {
|
||||
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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyWaypointUpdated() {
|
||||
TrackDataHub.this.notifyWaypointUpdated(getListenersFor(ListenerDataType.WAYPOINT_UPDATES));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPointsUpdated() {
|
||||
TrackDataHub.this.notifyPointsUpdated(true,
|
||||
getListenersFor(ListenerDataType.POINT_UPDATES),
|
||||
getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPreferenceChanged(String key) {
|
||||
TrackDataHub.this.notifyPreferenceChanged(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyLocationProviderEnabled(boolean enabled) {
|
||||
hasProviderEnabled = enabled;
|
||||
TrackDataHub.this.notifyFixType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyLocationChanged(Location loc) {
|
||||
TrackDataHub.this.notifyLocationChanged(loc,
|
||||
getListenersFor(ListenerDataType.LOCATION_UPDATES));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyHeadingChanged(float heading) {
|
||||
lastSeenMagneticHeading = heading;
|
||||
maybeUpdateDeclination();
|
||||
TrackDataHub.this.notifyHeadingChanged(getListenersFor(ListenerDataType.COMPASS_UPDATES));
|
||||
}
|
||||
}
|
||||
|
||||
// Application services
|
||||
private final Context context;
|
||||
private final TrackDataSources dataSources;
|
||||
private final MyTracksProviderUtils providerUtils;
|
||||
private final SharedPreferences preferences;
|
||||
|
||||
// Internal listeners (to receive data from the system)
|
||||
private final ContentObserver pointObserver;
|
||||
private final ContentObserver waypointObserver;
|
||||
private final ContentObserver trackObserver;
|
||||
private final LocationListener locationListener;
|
||||
private final OnSharedPreferenceChangeListener preferenceListener;
|
||||
private final SensorEventListener compassListener;
|
||||
|
||||
/** Set of internal listeners which are already registered. */
|
||||
private final Set<ListenerDataType> registeredInternalListeners =
|
||||
EnumSet.noneOf(ListenerDataType.class);
|
||||
|
||||
/** Map of external listener to its registration details. */
|
||||
private final Map<TrackDataListener, ListenerRegistration> registeredListeners =
|
||||
new HashMap<TrackDataListener, ListenerRegistration>();
|
||||
|
||||
/** Map of data type to external listeners interested in it. */
|
||||
private final Map<ListenerDataType, Set<TrackDataListener>> listenerSetsPerType =
|
||||
new EnumMap<ListenerDataType, Set<TrackDataListener>>(ListenerDataType.class);
|
||||
|
||||
// Get content notifications on the main thread, send listener callbacks in another.
|
||||
// This ensures listener calls are serialized.
|
||||
private final HandlerThread listenerHandlerThread;
|
||||
private final Handler listenerHandler;
|
||||
|
||||
/** Manager for external listeners (those from activities). */
|
||||
private final TrackDataListeners listeners;
|
||||
|
||||
/** Wrapper for interacting with system data managers. */
|
||||
private final DataSourcesWrapper dataSources;
|
||||
|
||||
/** Manager for system data listener registrations. */
|
||||
private final DataSourceManager dataSourceManager;
|
||||
|
||||
/** Condensed listener for system data listener events. */
|
||||
private final DataSourceListener dataSourceListener = new HubDataSourceListener();
|
||||
|
||||
/** Whether we've been started. */
|
||||
private boolean started;
|
||||
|
||||
@@ -163,158 +181,27 @@ public class TrackDataHub {
|
||||
private long lastSeenLocationId;
|
||||
private int numLoadedPoints;
|
||||
|
||||
/** Internal representation of a listener's registration. */
|
||||
private static class ListenerRegistration {
|
||||
final TrackDataListener listener;
|
||||
final EnumSet<ListenerDataType> types;
|
||||
// TODO: Add the last-notified point ID here, to allow pausing/resuming.
|
||||
|
||||
public ListenerRegistration(TrackDataListener listener,
|
||||
EnumSet<ListenerDataType> types) {
|
||||
this.listener = listener;
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
public boolean isInterestedIn(ListenerDataType type) {
|
||||
return types.contains(type);
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback for when the tracks table is updated. */
|
||||
private class TrackObserverCallback implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyTrackUpdated(getListenersFor(ListenerDataType.TRACK_UPDATES));
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback for when the waypoints table is updated. */
|
||||
private class WaypointObserverCallback implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyWaypointUpdated(getListenersFor(ListenerDataType.WAYPOINT_UPDATES));
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback for when the points table is updated. */
|
||||
private class PointObserverCallback implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyPointsUpdated(true,
|
||||
getListenersFor(ListenerDataType.POINT_UPDATES),
|
||||
getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES));
|
||||
}
|
||||
}
|
||||
|
||||
/** Listener for when preferences change. */
|
||||
private class HubSharedPreferenceListener implements OnSharedPreferenceChangeListener {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
notifyPreferenceChanged(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic content observer which will call a given {@link Runnable} in the
|
||||
* given handler if the content has changed and we're recording the selected
|
||||
* track.
|
||||
*/
|
||||
private class TrackContentObserver extends ContentObserver {
|
||||
private final Runnable callback;
|
||||
|
||||
public TrackContentObserver(Handler contentHandler, Runnable callback) {
|
||||
super(contentHandler);
|
||||
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
Log.v(TAG, "TrackContentObserver.onChange");
|
||||
|
||||
// We want to filter only updates from the selected track, but since
|
||||
// we can't see what the update is, we'll let two cases pass:
|
||||
// 1 - The point(s) was(ere) changed because it's a recording track
|
||||
// (and thus we care about it if the recording is the selected one)
|
||||
// 2 - The point(s) was(ere) changed because it's syncing a track
|
||||
// (and thus there will be no new points for the selected one)
|
||||
if (!isRecordingSelected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update can potentially be lengthy, put it in its own thread:
|
||||
runInListenerThread(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
hasProviderEnabled = (status == LocationProvider.AVAILABLE);
|
||||
notifyFixType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
if (!LocationManager.GPS_PROVIDER.equals(provider)) return;
|
||||
|
||||
hasProviderEnabled = true;
|
||||
notifyFixType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
if (!LocationManager.GPS_PROVIDER.equals(provider)) return;
|
||||
|
||||
hasProviderEnabled = false;
|
||||
notifyFixType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
notifyLocationChanged(location,
|
||||
getListenersFor(ListenerDataType.LOCATION_UPDATES));
|
||||
}
|
||||
}
|
||||
|
||||
/** Listener for compass readings. */
|
||||
private class CompassListener implements
|
||||
SensorEventListener {
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
lastSeenMagneticHeading = event.values[0];
|
||||
maybeUpdateDeclination();
|
||||
notifyHeadingChanged(getListenersFor(ListenerDataType.COMPASS_UPDATES));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public TrackDataHub(Context ctx, SharedPreferences preferences,
|
||||
MyTracksProviderUtils providerUtils) {
|
||||
this(ctx, new TrackDataSourcesImpl(ctx, preferences), preferences, providerUtils);
|
||||
this(ctx, new DataSourcesWrapperImpl(ctx, preferences), new TrackDataListeners(),
|
||||
preferences, providerUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection constructor.
|
||||
*/
|
||||
// @VisibleForTesting
|
||||
TrackDataHub(Context ctx, TrackDataSources dataSources, SharedPreferences preferences,
|
||||
MyTracksProviderUtils providerUtils) {
|
||||
TrackDataHub(Context ctx, DataSourcesWrapper dataSources, TrackDataListeners listeners,
|
||||
SharedPreferences preferences, MyTracksProviderUtils providerUtils) {
|
||||
this.context = ctx;
|
||||
this.dataSources = dataSources;
|
||||
this.listeners = listeners;
|
||||
this.preferences = preferences;
|
||||
this.providerUtils = providerUtils;
|
||||
this.dataSources = dataSources;
|
||||
this.dataSourceManager = new DataSourceManager(dataSourceListener, dataSources);
|
||||
|
||||
SELECTED_TRACK_KEY = context.getString(R.string.selected_track_key);
|
||||
RECORDING_TRACK_KEY = context.getString(R.string.recording_track_key);
|
||||
@@ -322,24 +209,9 @@ public class TrackDataHub {
|
||||
METRIC_UNITS_KEY = context.getString(R.string.metric_units_key);
|
||||
SPEED_REPORTING_KEY = context.getString(R.string.report_speed_key);
|
||||
|
||||
// Create sets for all data type at startup.
|
||||
for (ListenerDataType type : ListenerDataType.values()) {
|
||||
listenerSetsPerType.put(type, new LinkedHashSet<TrackDataListener>());
|
||||
}
|
||||
|
||||
listenerHandlerThread = new HandlerThread("trackDataContentThread");
|
||||
listenerHandlerThread.start();
|
||||
listenerHandler = new Handler(listenerHandlerThread.getLooper());
|
||||
|
||||
preferenceListener = new HubSharedPreferenceListener();
|
||||
|
||||
Handler contentHandler = new Handler();
|
||||
pointObserver = new TrackContentObserver(contentHandler, new PointObserverCallback());
|
||||
waypointObserver = new TrackContentObserver(contentHandler, new WaypointObserverCallback());
|
||||
trackObserver = new TrackContentObserver(contentHandler, new TrackObserverCallback());
|
||||
|
||||
compassListener = new CompassListener();
|
||||
locationListener = new CurrentLocationListener();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,12 +226,10 @@ public class TrackDataHub {
|
||||
}
|
||||
started = true;
|
||||
|
||||
dataSources.registerOnSharedPreferenceChangeListener(preferenceListener);
|
||||
loadSharedPreferences();
|
||||
|
||||
// This may or may not register internal listeners, depending on whether
|
||||
// we already had external listeners.
|
||||
updateInternalListeners();
|
||||
dataSourceManager.updateAllListeners(getNeededListenerTypes());
|
||||
loadSharedPreferences();
|
||||
|
||||
// If there were listeners already registered, make sure they become up-to-date.
|
||||
// TODO: This should really only send new data (in a start-stop-start cycle).
|
||||
@@ -386,10 +256,8 @@ public class TrackDataHub {
|
||||
return;
|
||||
}
|
||||
|
||||
dataSources.unregisterOnSharedPreferenceChangeListener(preferenceListener);
|
||||
|
||||
// Unregister internal listeners even if there are external listeners registered.
|
||||
unregisterInternalListeners();
|
||||
dataSourceManager.unregisterAllListeners();
|
||||
|
||||
started = false;
|
||||
}
|
||||
@@ -515,163 +383,38 @@ public class TrackDataHub {
|
||||
loadTrack(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to send data to.
|
||||
* It is ok to call this method before {@link start}, and in that case
|
||||
* the data will only be passed to listeners when {@link start} is called.
|
||||
*
|
||||
* @param listener the listener to register
|
||||
* @param dataTypes the type of data that the listener is interested in
|
||||
*/
|
||||
public void registerTrackDataListener(final TrackDataListener listener, EnumSet<ListenerDataType> dataTypes) {
|
||||
Log.d(TAG, "Registered track data listener: " + listener);
|
||||
ListenerRegistration registration = new ListenerRegistration(listener, dataTypes);
|
||||
synchronized (registeredListeners) {
|
||||
if (registeredListeners.get(listener) != null) {
|
||||
throw new IllegalStateException("Listener already registered");
|
||||
}
|
||||
registeredListeners.put(listener, registration);
|
||||
|
||||
for (ListenerDataType type : dataTypes) {
|
||||
// This is guaranteed not to be null.
|
||||
Set<TrackDataListener> typeSet = listenerSetsPerType.get(type);
|
||||
typeSet.add(listener);
|
||||
}
|
||||
public void registerTrackDataListener(
|
||||
TrackDataListener listener, EnumSet<ListenerDataType> dataTypes) {
|
||||
synchronized (listeners) {
|
||||
ListenerRegistration registration = listeners.registerTrackDataListener(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 (!started) return;
|
||||
|
||||
reloadDataForListener(registration);
|
||||
}
|
||||
|
||||
updateInternalListeners();
|
||||
dataSourceManager.updateAllListeners(getNeededListenerTypes());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
synchronized (registeredListeners) {
|
||||
// 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);
|
||||
}
|
||||
synchronized (listeners) {
|
||||
listeners.unregisterTrackDataListener(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 (!started) return;
|
||||
|
||||
dataSourceManager.updateAllListeners(getNeededListenerTypes());
|
||||
}
|
||||
updateInternalListeners();
|
||||
}
|
||||
|
||||
/** Updates the internal (sensor, position, etc) listeners. */
|
||||
private void updateInternalListeners() {
|
||||
synchronized (registeredListeners) {
|
||||
Set<ListenerDataType> registeredListeners = registeredInternalListeners;
|
||||
Set<ListenerDataType> neededListeners = EnumSet.noneOf(ListenerDataType.class);
|
||||
for (ListenerRegistration registration : this.registeredListeners.values()) {
|
||||
neededListeners.addAll(registration.types);
|
||||
}
|
||||
|
||||
// Unnecessary = registered - needed
|
||||
Set<ListenerDataType> unnecessaryListeners = EnumSet.copyOf(registeredListeners);
|
||||
unnecessaryListeners.removeAll(neededListeners);
|
||||
|
||||
// Missing = needed - registered
|
||||
Set<ListenerDataType> missingListeners = EnumSet.copyOf(neededListeners);
|
||||
missingListeners.removeAll(registeredListeners);
|
||||
|
||||
// Remove all unnecessary listeners.
|
||||
for (ListenerDataType type : unnecessaryListeners) {
|
||||
switch (type) {
|
||||
case COMPASS_UPDATES:
|
||||
dataSources.unregisterSensorListener(compassListener);
|
||||
break;
|
||||
case LOCATION_UPDATES:
|
||||
dataSources.removeLocationUpdates(locationListener);
|
||||
break;
|
||||
case POINT_UPDATES:
|
||||
case SAMPLED_OUT_POINT_UPDATES:
|
||||
// Special case - don't unregister if the other type is needed.
|
||||
if (!neededListeners.contains(ListenerDataType.POINT_UPDATES) &&
|
||||
!neededListeners.contains(ListenerDataType.SAMPLED_OUT_POINT_UPDATES)) {
|
||||
dataSources.unregisterContentObserver(pointObserver);
|
||||
}
|
||||
break;
|
||||
case TRACK_UPDATES:
|
||||
dataSources.unregisterContentObserver(trackObserver);
|
||||
break;
|
||||
case WAYPOINT_UPDATES:
|
||||
dataSources.unregisterContentObserver(waypointObserver);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add all missing listeners.
|
||||
for (ListenerDataType type : missingListeners) {
|
||||
switch (type) {
|
||||
case COMPASS_UPDATES: {
|
||||
// Listen to compass
|
||||
Sensor compass = dataSources.getSensor(Sensor.TYPE_ORIENTATION);
|
||||
if (compass != null) {
|
||||
Log.d(Constants.TAG, "TrackDataHub: Now registering sensor listener.");
|
||||
dataSources.registerSensorListener(compassListener, compass, SensorManager.SENSOR_DELAY_UI);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LOCATION_UPDATES:
|
||||
dataSources.requestLocationUpdates(locationListener);
|
||||
break;
|
||||
case POINT_UPDATES:
|
||||
case SAMPLED_OUT_POINT_UPDATES:
|
||||
// Special case - don't register if the other type was already registered.
|
||||
if (!registeredListeners.contains(ListenerDataType.POINT_UPDATES) &&
|
||||
!registeredListeners.contains(ListenerDataType.SAMPLED_OUT_POINT_UPDATES)) {
|
||||
dataSources.registerContentObserver(
|
||||
TrackPointsColumns.CONTENT_URI, false, pointObserver);
|
||||
}
|
||||
break;
|
||||
case TRACK_UPDATES:
|
||||
dataSources.registerContentObserver(TracksColumns.CONTENT_URI, false, trackObserver);
|
||||
break;
|
||||
case WAYPOINT_UPDATES:
|
||||
dataSources.registerContentObserver(
|
||||
WaypointsColumns.CONTENT_URI, false, waypointObserver);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now all needed types are registered.
|
||||
registeredInternalListeners.clear();
|
||||
registeredInternalListeners.addAll(neededListeners);
|
||||
} // synchronized
|
||||
}
|
||||
|
||||
/** Unregisters all internal (sensor, position, etc.) listeners. */
|
||||
private void unregisterInternalListeners() {
|
||||
dataSources.removeLocationUpdates(locationListener);
|
||||
dataSources.unregisterSensorListener(compassListener);
|
||||
dataSources.unregisterContentObserver(trackObserver);
|
||||
dataSources.unregisterContentObserver(waypointObserver);
|
||||
dataSources.unregisterContentObserver(pointObserver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads all track data received so far into the specified listeners.
|
||||
*/
|
||||
public void reloadDataForListener(TrackDataListener listener) {
|
||||
reloadDataForListener(registeredListeners.get(listener));
|
||||
reloadDataForListener(listeners.getRegistration(listener));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -738,7 +481,7 @@ public class TrackDataHub {
|
||||
* Reloads all track data received so far into the specified listeners.
|
||||
*/
|
||||
private void loadDataForAllListeners() {
|
||||
if (registeredListeners.isEmpty()) {
|
||||
if (!listeners.hasListeners()) {
|
||||
Log.d(TAG, "No listeners, not reloading");
|
||||
return;
|
||||
}
|
||||
@@ -751,7 +494,8 @@ public class TrackDataHub {
|
||||
@Override
|
||||
public void run() {
|
||||
// Ignore the return values here, we're already sending the full data set anyway
|
||||
for (TrackDataListener listener : getListenersFor(ListenerDataType.DISPLAY_PREFERENCES)) {
|
||||
for (TrackDataListener listener :
|
||||
getListenersFor(ListenerDataType.DISPLAY_PREFERENCES)) {
|
||||
listener.onUnitsChanged(useMetricUnits);
|
||||
listener.onReportSpeedChanged(reportSpeed);
|
||||
}
|
||||
@@ -809,12 +553,13 @@ public class TrackDataHub {
|
||||
runInListenerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Set<TrackDataListener> listeners = getListenersFor(ListenerDataType.DISPLAY_PREFERENCES);
|
||||
Set<TrackDataListener> displayListeners =
|
||||
getListenersFor(ListenerDataType.DISPLAY_PREFERENCES);
|
||||
|
||||
for (TrackDataListener listener : listeners) {
|
||||
for (TrackDataListener listener : displayListeners) {
|
||||
// TODO: Do the reloading just once for all interested listeners
|
||||
if (listener.onReportSpeedChanged(reportSpeed)) {
|
||||
reloadDataForListener(registeredListeners.get(listener));
|
||||
reloadDataForListener(listeners.getRegistration(listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -828,11 +573,11 @@ public class TrackDataHub {
|
||||
runInListenerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Set<TrackDataListener> listeners = getListenersFor(ListenerDataType.DISPLAY_PREFERENCES);
|
||||
Set<TrackDataListener> displayListeners = getListenersFor(ListenerDataType.DISPLAY_PREFERENCES);
|
||||
|
||||
for (TrackDataListener listener : listeners) {
|
||||
for (TrackDataListener listener : displayListeners) {
|
||||
if (listener.onUnitsChanged(useMetricUnits)) {
|
||||
reloadDataForListener(registeredListeners.get(listener));
|
||||
reloadDataForListener(listeners.getRegistration(listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -860,7 +605,8 @@ public class TrackDataHub {
|
||||
public void run() {
|
||||
// Notify to everyone.
|
||||
Log.d(TAG, "Notifying fix type: " + state);
|
||||
for (TrackDataListener listener : getListenersFor(ListenerDataType.LOCATION_UPDATES)) {
|
||||
for (TrackDataListener listener :
|
||||
getListenersFor(ListenerDataType.LOCATION_UPDATES)) {
|
||||
listener.onProviderStateChange(state);
|
||||
}
|
||||
}
|
||||
@@ -1094,8 +840,10 @@ public class TrackDataHub {
|
||||
firstSeenLocationId = -1;
|
||||
lastSeenLocationId = -1;
|
||||
numLoadedPoints = 0;
|
||||
sampledListeners = getListenersFor(ListenerDataType.POINT_UPDATES);
|
||||
sampledOutListeners = getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES);
|
||||
synchronized (listeners) {
|
||||
sampledListeners = getListenersFor(ListenerDataType.POINT_UPDATES);
|
||||
sampledOutListeners = getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES);
|
||||
}
|
||||
maxPointId = -1;
|
||||
minPointId = 0;
|
||||
keepState = true;
|
||||
@@ -1209,14 +957,23 @@ public class TrackDataHub {
|
||||
}
|
||||
}
|
||||
|
||||
private Set<TrackDataListener> getListenersFor(ListenerDataType type) {
|
||||
synchronized (registeredListeners) {
|
||||
return listenerSetsPerType.get(type);
|
||||
}
|
||||
}
|
||||
|
||||
// @VisibleForTesting
|
||||
protected void runInListenerThread(Runnable runnable) {
|
||||
listenerHandler.post(runnable);
|
||||
}
|
||||
|
||||
private Set<TrackDataListener> getListenersFor(ListenerDataType type) {
|
||||
synchronized (listeners) {
|
||||
return listeners.getListenersFor(type);
|
||||
}
|
||||
}
|
||||
|
||||
private EnumSet<ListenerDataType> getNeededListenerTypes() {
|
||||
EnumSet<ListenerDataType> neededTypes = listeners.getAllRegisteredTypes();
|
||||
|
||||
// We always want preference updates.
|
||||
neededTypes.add(ListenerDataType.DISPLAY_PREFERENCES);
|
||||
|
||||
return neededTypes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import android.location.Location;
|
||||
/**
|
||||
* Listener for track data, for both initial and incremental loading.
|
||||
*
|
||||
* TODO: Allow listening to just part of the data received here.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public interface TrackDataListener {
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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<ListenerDataType> types;
|
||||
// TODO: Add the last-notified point ID here, to allow pausing/resuming.
|
||||
|
||||
public ListenerRegistration(TrackDataListener listener,
|
||||
EnumSet<ListenerDataType> types) {
|
||||
this.listener = listener;
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
public boolean isInterestedIn(ListenerDataType type) {
|
||||
return types.contains(type);
|
||||
}
|
||||
}
|
||||
|
||||
/** Map of external listener to its registration details. */
|
||||
private final Map<TrackDataListener, ListenerRegistration> registeredListeners =
|
||||
new HashMap<TrackDataListener, ListenerRegistration>();
|
||||
|
||||
/** Map of data type to external listeners interested in it. */
|
||||
private final Map<ListenerDataType, Set<TrackDataListener>> listenerSetsPerType =
|
||||
new EnumMap<ListenerDataType, Set<TrackDataListener>>(ListenerDataType.class);
|
||||
|
||||
public TrackDataListeners() {
|
||||
// Create sets for all data types at startup.
|
||||
for (ListenerDataType type : ListenerDataType.values()) {
|
||||
listenerSetsPerType.put(type, new LinkedHashSet<TrackDataListener>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to send data to.
|
||||
* It is ok to call this method before {@link start}, and in that case
|
||||
* the data will only be passed to listeners when {@link 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<ListenerDataType> dataTypes) {
|
||||
Log.d(TAG, "Registered track data listener: " + listener);
|
||||
ListenerRegistration registration = new ListenerRegistration(listener, dataTypes);
|
||||
|
||||
if (registeredListeners.get(listener) != null) {
|
||||
throw new IllegalStateException("Listener already registered");
|
||||
}
|
||||
registeredListeners.put(listener, registration);
|
||||
|
||||
for (ListenerDataType type : dataTypes) {
|
||||
// This is guaranteed not to be null.
|
||||
Set<TrackDataListener> 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<TrackDataListener> getListenersFor(ListenerDataType type) {
|
||||
return listenerSetsPerType.get(type);
|
||||
}
|
||||
|
||||
public EnumSet<ListenerDataType> getAllRegisteredTypes() {
|
||||
EnumSet<ListenerDataType> listeners = EnumSet.noneOf(ListenerDataType.class);
|
||||
for (ListenerRegistration registration : this.registeredListeners.values()) {
|
||||
listeners.addAll(registration.types);
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
public boolean hasListeners() {
|
||||
return !registeredListeners.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||
import com.google.android.apps.mytracks.content.TrackDataSources;
|
||||
import com.google.android.apps.mytracks.content.DataSourcesWrapper;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
@@ -69,7 +69,8 @@ public class TrackDataHubTest extends AndroidTestCase {
|
||||
private static final long TRACK_ID = 42;
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
private TrackDataHub hub;
|
||||
private TrackDataSources dataSources;
|
||||
private TrackDataListeners listeners;
|
||||
private DataSourcesWrapper dataSources;
|
||||
private SharedPreferences prefs;
|
||||
private TrackDataListener listener1;
|
||||
private TrackDataListener listener2;
|
||||
@@ -89,9 +90,10 @@ public class TrackDataHubTest extends AndroidTestCase {
|
||||
|
||||
prefs = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
|
||||
providerUtils = AndroidMock.createMock("providerUtils", MyTracksProviderUtils.class);
|
||||
dataSources = AndroidMock.createNiceMock("dataSources", TrackDataSources.class);
|
||||
dataSources = AndroidMock.createNiceMock("dataSources", DataSourcesWrapper.class);
|
||||
|
||||
hub = new TrackDataHub(context, dataSources, prefs, providerUtils) {
|
||||
listeners = new TrackDataListeners();
|
||||
hub = new TrackDataHub(context, dataSources, listeners, prefs, providerUtils) {
|
||||
@Override
|
||||
protected void runInListenerThread(Runnable runnable) {
|
||||
// Run everything in the same thread.
|
||||
|
||||
Reference in New Issue
Block a user