Clean up DataSource, DataSourceManager, and DataSourceListener.

This commit is contained in:
Jimmy Shih
2012-07-11 10:10:42 -07:00
parent 80470ef4f2
commit e3229dc933
7 changed files with 521 additions and 434 deletions
@@ -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);
}
}
@@ -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);
}
@@ -1,3 +1,19 @@
/*
* 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;
@@ -10,7 +26,6 @@ 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,154 +38,178 @@ 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<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;
@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<ListenerDataType> registeredListeners = EnumSet.noneOf(ListenerDataType.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<ListenerDataType> externallyNeededListeners) {
EnumSet<ListenerDataType> neededListeners = EnumSet.copyOf(externallyNeededListeners);
/**
* Updates listeners with data source.
*
* @param listeners the listeners
*/
public void updateListeners(EnumSet<ListenerDataType> listeners) {
EnumSet<ListenerDataType> neededListeners = EnumSet.copyOf(listeners);
// Special case - map sampled-out points type to points type since they
// correspond to the same internal listener.
/*
* Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES 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);
Log.d(TAG, "Updating listeners " + neededListeners);
// Unnecessary = registered - needed
Set<ListenerDataType> unnecessaryListeners = EnumSet.copyOf(registeredListeners);
@@ -180,86 +219,100 @@ class DataSourceManager {
Set<ListenerDataType> missingListeners = EnumSet.copyOf(neededListeners);
missingListeners.removeAll(registeredListeners);
// Remove all unnecessary listeners.
// Remove unnecessary listeners
for (ListenerDataType type : unnecessaryListeners) {
unregisterListener(type);
}
// Add all missing listeners.
// Add missing listeners
for (ListenerDataType type : missingListeners) {
registerListener(type);
}
// Now all needed types are registered.
// Update registered listeners
registeredListeners.clear();
registeredListeners.addAll(neededListeners);
}
/**
* Registers a listener with data source.
*
* @param type the listener data type
*/
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);
case SELECTED_TRACK_CHANGED:
// Do nothing
break;
case TRACK_UPDATES:
dataSources.registerContentObserver(TracksColumns.CONTENT_URI, false, trackObserver);
dataSource.registerContentObserver(TracksColumns.CONTENT_URI, tracksTableObserver);
break;
case WAYPOINT_UPDATES:
dataSources.registerContentObserver(
WaypointsColumns.CONTENT_URI, false, waypointObserver);
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
break;
case DISPLAY_PREFERENCES:
dataSources.registerOnSharedPreferenceChangeListener(preferenceListener);
case POINT_UPDATES:
dataSource.registerContentObserver(
TrackPointsColumns.CONTENT_URI, trackPointsTableObserver);
break;
case SAMPLED_OUT_POINT_UPDATES:
throw new IllegalArgumentException("Should have been mapped to point updates");
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
break;
case LOCATION_UPDATES:
dataSource.registerLocationListener(currentLocationListener);
break;
case COMPASS_UPDATES:
dataSource.registerCompassListener(compassListener);
break;
case DISPLAY_PREFERENCES:
dataSource.registerOnSharedPreferenceChangeListener(preferenceListener);
break;
default:
break;
}
}
/**
* Unregisters a listener with data source.
*
* @param type listener data type
*/
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);
case SELECTED_TRACK_CHANGED:
// Do nothing
break;
case TRACK_UPDATES:
dataSources.unregisterContentObserver(trackObserver);
dataSource.unregisterContentObserver(tracksTableObserver);
break;
case WAYPOINT_UPDATES:
dataSources.unregisterContentObserver(waypointObserver);
dataSource.unregisterContentObserver(waypointsTableObserver);
break;
case DISPLAY_PREFERENCES:
dataSources.unregisterOnSharedPreferenceChangeListener(preferenceListener);
case POINT_UPDATES:
dataSource.unregisterContentObserver(trackPointsTableObserver);
break;
case SAMPLED_OUT_POINT_UPDATES:
throw new IllegalArgumentException("Should have been mapped to point updates");
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
break;
case LOCATION_UPDATES:
dataSource.unregisterLocationListener(currentLocationListener);
break;
case COMPASS_UPDATES:
dataSource.unregisterCompassListener(compassListener);
break;
case DISPLAY_PREFERENCES:
dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListener);
break;
default:
break;
}
}
/** 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 (ListenerDataType type : ListenerDataType.values()) {
unregisterListener(type);
}
}
}
@@ -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();
}
@@ -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);
}
}
@@ -21,8 +21,6 @@ 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;
@@ -32,7 +30,6 @@ 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;
@@ -94,17 +91,17 @@ public class TrackDataHub {
/** Listener which receives events from the system. */
private class HubDataSourceListener implements DataSourceListener {
@Override
public void notifyTrackUpdated() {
public void notifyTracksTableUpdated() {
TrackDataHub.this.notifyTrackUpdated(getListenersFor(ListenerDataType.TRACK_UPDATES));
}
@Override
public void notifyWaypointUpdated() {
public void notifyWaypointsTableUpdated() {
TrackDataHub.this.notifyWaypointUpdated(getListenersFor(ListenerDataType.WAYPOINT_UPDATES));
}
@Override
public void notifyPointsUpdated() {
public void notifyTrackPointsTableUpdated() {
TrackDataHub.this.notifyPointsUpdated(true, 0, 0,
getListenersFor(ListenerDataType.POINT_UPDATES),
getListenersFor(ListenerDataType.SAMPLED_OUT_POINT_UPDATES));
@@ -144,7 +141,6 @@ public class TrackDataHub {
// 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.
@@ -154,8 +150,7 @@ public class TrackDataHub {
/** Manager for external listeners (those from activities). */
private final TrackDataListeners dataListeners;
/** Wrapper for interacting with system data managers. */
private DataSourcesWrapper dataSources;
private DataSource dataSource;
/** Manager for system data listener registrations. */
private DataSourceManager dataSourceManager;
@@ -193,24 +188,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 TrackDataListeners(), providerUtils, TARGET_DISPLAYED_TRACK_POINTS);
}
/**
* Injection constructor.
*/
// @VisibleForTesting
TrackDataHub(Context ctx, TrackDataListeners listeners, SharedPreferences preferences,
MyTracksProviderUtils providerUtils, int targetNumPoints) {
TrackDataHub(Context ctx, TrackDataListeners listeners, MyTracksProviderUtils providerUtils,
int targetNumPoints) {
this.context = ctx;
this.dataListeners = listeners;
this.preferences = preferences;
this.providerUtils = providerUtils;
this.targetNumPoints = targetNumPoints;
this.locationFactory = new DoubleBufferedLocationFactory();
@@ -233,12 +223,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 +236,8 @@ public class TrackDataHub {
}
// @VisibleForTesting
protected DataSourcesWrapper newDataSources() {
return new DataSourcesWrapperImpl(context, preferences);
protected DataSource newDataSource() {
return new DataSource(context);
}
/**
@@ -267,7 +257,7 @@ public class TrackDataHub {
started = false;
dataSources = null;
dataSource = null;
dataSourceManager = null;
listenerHandlerThread = null;
listenerHandler = null;
@@ -343,7 +333,7 @@ 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));
}
@@ -426,7 +416,7 @@ public class TrackDataHub {
loadNewDataForListener(registration);
dataSourceManager.updateAllListeners(getNeededListenerTypes());
dataSourceManager.updateListeners(getNeededListenerTypes());
}
}
@@ -438,7 +428,7 @@ public class TrackDataHub {
// called. When it is called, we'll do both things.
if (!isStarted()) return;
dataSourceManager.updateAllListeners(getNeededListenerTypes());
dataSourceManager.updateListeners(getNeededListenerTypes());
}
}