Merge heads

This commit is contained in:
youtaol
2012-06-04 13:20:38 +08:00
parent cad1a105ca
commit 953bd8b908
22 changed files with 325 additions and 306 deletions
@@ -160,8 +160,8 @@ public class ChartView extends View {
new int[] {25, 50 },
R.string.description_sensor_heart_rate,
R.string.description_sensor_heart_rate,
R.color.heartrate_fill,
R.color.heartrate_border);
R.color.heart_rate_fill,
R.color.heart_rate_border);
series[CADENCE_SERIES] = new ChartValueSeries(context,
0,
Integer.MAX_VALUE,
@@ -33,8 +33,8 @@ public abstract class SensorManager {
private static final String TAG = SensorManager.class.getSimpleName();
private static final long MAX_SENSOR_DATE_SET_AGE = 5000;
private static final long MAX_SENSOR_STATE_AGE = 30000;
private static final int RETRY_PERIOD = 30000;
private static final long MAX_SENSOR_STATE_AGE = 20000;
private static final int RETRY_PERIOD = 20000;
private SensorState sensorState = SensorState.NONE;
private long sensorStateTimestamp = System.currentTimeMillis();
@@ -126,28 +126,39 @@ public class AntSensorManager extends SensorManager {
}
@Override
protected void setUpChannel() {
protected synchronized void setUpChannel() {
tearDownChannel();
if (AntInterface.hasAntSupport(context)) {
context.registerReceiver(statusReceiver, statusIntentFilter);
if (!antInterface.initService(context, serviceListener)) {
AntInterface.goToMarket(context);
} else {
setSensorState(Sensor.SensorState.CONNECTING);
serviceConnected = antInterface.isServiceConnected();
if (serviceConnected) {
try {
hasClaimedInterface = antInterface.hasClaimedInterface();
if (hasClaimedInterface) {
enableDataMessage(true);
}
} catch (AntInterfaceException e) {
handleAntError();
}
}
handleServiceConnected();
}
}
}
/**
* Handles service connected. Needs to be synchronized.
*/
private synchronized void handleServiceConnected() {
serviceConnected = antInterface.isServiceConnected();
if (serviceConnected) {
try {
hasClaimedInterface = antInterface.hasClaimedInterface();
if (hasClaimedInterface) {
enableDataMessage(true);
} else {
// Need to claim the ant interface if it is available
hasClaimedInterface = antInterface.claimInterface();
}
} catch (AntInterfaceException e) {
handleAntError();
}
}
}
@Override
protected void tearDownChannel() {
try {
@@ -161,6 +172,7 @@ public class AntSensorManager extends SensorManager {
if (hasClaimedInterface) {
antInterface.releaseInterface();
}
hasClaimedInterface = false;
antInterface.stopRequestForceClaimInterface();
} catch (AntServiceNotConnectedException e) {
// Can safely ignore
@@ -168,6 +180,7 @@ public class AntSensorManager extends SensorManager {
Log.w(TAG, "Exception in tearDonwChannel.", e);
}
antInterface.releaseService();
serviceConnected = false;
}
}
@@ -190,18 +203,7 @@ public class AntSensorManager extends SensorManager {
private AntInterface.ServiceListener serviceListener = new AntInterface.ServiceListener() {
@Override
public void onServiceConnected() {
serviceConnected = true;
try {
hasClaimedInterface = antInterface.hasClaimedInterface();
if (hasClaimedInterface) {
enableDataMessage(true);
} else {
// Need to claim the ant interface if it is available
hasClaimedInterface = antInterface.claimInterface();
}
} catch (AntInterfaceException e) {
handleAntError();
}
handleServiceConnected();
}
@Override
@@ -220,31 +220,7 @@ public class StatsUtils {
private static void setSpeedValue(
Activity activity, int id, double speed, boolean metricUnits, boolean reportSpeed) {
TextView textView = (TextView) activity.findViewById(id);
String value;
if (Double.isNaN(speed) || Double.isInfinite(speed)) {
value = activity.getString(R.string.value_unknown);
} else {
speed *= UnitConversions.MS_TO_KMH;
if (metricUnits) {
if (reportSpeed) {
value = activity.getString(R.string.value_float_kilometer_hour, speed);
} else {
// convert from hours to minutes
double pace = speed == 0 ? 0.0 : 60.0 / speed;
value = activity.getString(R.string.value_float_minute_kilometer, pace);
}
} else {
speed *= UnitConversions.KM_TO_MI;
if (reportSpeed) {
value = activity.getString(R.string.value_float_mile_hour, speed);
} else {
// convert from hours to minutes
double pace = speed == 0 ? 0.0 : 60.0 / speed;
value = activity.getString(R.string.value_float_minute_mile, pace);
}
}
}
textView.setText(value);
textView.setText(StringUtils.formatSpeed(activity, speed, metricUnits, reportSpeed));
}
/**
@@ -258,19 +234,7 @@ public class StatsUtils {
private static void setDistanceValue(
Activity activity, int id, double distance, boolean metricUnits) {
TextView textView = (TextView) activity.findViewById(id);
String value;
if (Double.isNaN(distance) || Double.isInfinite(distance)) {
value = activity.getString(R.string.value_unknown);
} else {
distance *= UnitConversions.M_TO_KM;
if (metricUnits) {
value = activity.getString(R.string.value_float_kilometer, distance);
} else {
distance *= UnitConversions.KM_TO_MI;
value = activity.getString(R.string.value_float_mile, distance);
}
}
textView.setText(value);
textView.setText(StringUtils.formatDistance(activity, distance, metricUnits));
}
/**
@@ -108,23 +108,26 @@ public class StringUtils {
/**
* Formats the distance.
*
*
* @param context the context
* @param distance the distance in meters
* @param metric true to use metric. False to use imperial
* @param metricUnits true to use metric units. False to use imperial units
*/
public static String formatDistance(Context context, double distance, boolean metric) {
if (metric) {
if (distance > 2000.0) {
public static String formatDistance(Context context, double distance, boolean metricUnits) {
if (Double.isNaN(distance) || Double.isInfinite(distance)) {
return context.getString(R.string.value_unknown);
}
if (metricUnits) {
if (distance > 1000.0) {
distance *= UnitConversions.M_TO_KM;
return context.getString(R.string.value_float_kilometer, distance);
} else {
return context.getString(R.string.value_float_meter, distance);
return context.getString(R.string.value_float_meter, distance);
}
} else {
if (distance * UnitConversions.M_TO_MI > 2) {
if (distance * UnitConversions.M_TO_MI > 1) {
distance *= UnitConversions.M_TO_MI;
return context.getString(R.string.value_float_mile, distance);
return context.getString(R.string.value_float_mile, distance);
} else {
distance *= UnitConversions.M_TO_FT;
return context.getString(R.string.value_float_feet, distance);
@@ -137,29 +140,31 @@ public class StringUtils {
*
* @param context the context
* @param speed the speed in meters per second
* @param metric true to use metric. False to use imperial
* @param metricUnits true to use metric units. False to use imperial units
* @param reportSpeed true to report as speed. False to report as pace
*/
public static String formatSpeed(
Context context, double speed, boolean metric, boolean reportSpeed) {
Context context, double speed, boolean metricUnits, boolean reportSpeed) {
if (Double.isNaN(speed) || Double.isInfinite(speed)) {
return context.getString(R.string.value_unknown);
}
if (metric) {
speed = speed * UnitConversions.MS_TO_KMH;
speed *= UnitConversions.MS_TO_KMH;
if (metricUnits) {
if (reportSpeed) {
return context.getString(R.string.value_float_kilometer_hour, speed);
} else {
double paceInMinute = speed == 0 ? 0.0 : 60 / speed;
return context.getString(R.string.value_float_minute_kilometer, paceInMinute);
// convert from hours to minutes
double pace = speed == 0 ? 0.0 : 60.0 / speed;
return context.getString(R.string.value_float_minute_kilometer, pace);
}
} else {
speed = speed * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI;
speed *= UnitConversions.KM_TO_MI;
if (reportSpeed) {
return context.getString(R.string.value_float_mile_hour, speed);
} else {
double paceInMinute = speed == 0 ? 0.0 : 60 / speed;
return context.getString(R.string.value_float_minute_mile, paceInMinute);
// convert from hours to minutes
double pace = speed == 0 ? 0.0 : 60.0 / speed;
return context.getString(R.string.value_float_minute_mile, pace);
}
}
}
@@ -17,7 +17,6 @@
package com.google.android.apps.mytracks.widgets;
import static com.google.android.apps.mytracks.Constants.SETTINGS_NAME;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.TrackDetailActivity;
import com.google.android.apps.mytracks.TrackListActivity;
@@ -41,66 +40,77 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.ContentObserver;
import android.os.Handler;
import android.util.Log;
import android.widget.RemoteViews;
/**
* An AppWidgetProvider for displaying key track statistics (distance, time,
* speed) from the current or most recent track.
*
* A track widget to start/stop recording, launch My Tracks, and display track
* statistics (distance, time, speed) for the selected track or the last track.
*
* @author Sandor Dornbush
* @author Paul R. Saxman
*/
public class TrackWidgetProvider
extends AppWidgetProvider
public class TrackWidgetProvider extends AppWidgetProvider
implements OnSharedPreferenceChangeListener {
class TrackObserver extends ContentObserver {
/**
* Observer for track content.
*
* @author Jimmy Shih
*/
private class TrackObserver extends ContentObserver {
public TrackObserver() {
super(contentHandler);
super(handler);
}
@Override
public void onChange(boolean selfChange) {
updateTrack(null);
}
}
private final Handler contentHandler;
private MyTracksProviderUtils providerUtils;
private final Handler handler;
private Context context;
private String unknown;
private TrackObserver trackObserver;
private MyTracksProviderUtils myTracksProviderUtils;
private String unknown;
private String trackStartedBroadcastAction;
private String trackStoppedBroadcastAction;
private SharedPreferences sharedPreferences;
private long selectedTrackId;
private boolean metricUnits;
private boolean reportSpeed;
private long selectedTrackId;
private SharedPreferences sharedPreferences;
private String TRACK_STARTED_ACTION;
private String TRACK_STOPPED_ACTION;
private boolean useTotalTime;
public TrackWidgetProvider() {
super();
contentHandler = new Handler();
selectedTrackId = PreferencesUtils.SELECTED_TRACK_ID_DEFAULT;
handler = new Handler();
}
private void initialize(Context aContext) {
if (this.context != null) {
return;
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key == null || PreferencesUtils.getKey(context, R.string.metric_units_key).equals(key)) {
metricUnits = PreferencesUtils.getBoolean(
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
}
if (key == null || PreferencesUtils.getKey(context, R.string.report_speed_key).equals(key)) {
reportSpeed = PreferencesUtils.getBoolean(
context, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
}
if (key == null
|| PreferencesUtils.getKey(context, R.string.stats_use_total_time_key).equals(key)) {
useTotalTime = PreferencesUtils.getBoolean(context, R.string.stats_use_total_time_key,
PreferencesUtils.STATS_USE_TOTAL_TIME_DEFAULT);
}
if (key == null
|| PreferencesUtils.getKey(context, R.string.selected_track_id_key).equals(key)) {
selectedTrackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
}
if (key != null) {
updateTrack(null);
}
this.context = aContext;
trackObserver = new TrackObserver();
providerUtils = MyTracksProviderUtils.Factory.get(context);
unknown = context.getString(R.string.value_unknown);
sharedPreferences = context.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE);
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(sharedPreferences, null);
context.getContentResolver().registerContentObserver(
TracksColumns.CONTENT_URI, true, trackObserver);
TRACK_STARTED_ACTION = context.getString(R.string.track_started_broadcast_action);
TRACK_STOPPED_ACTION = context.getString(R.string.track_stopped_broadcast_action);
}
@Override
@@ -111,43 +121,62 @@ public class TrackWidgetProvider
selectedTrackId = intent.getLongExtra(
context.getString(R.string.track_id_broadcast_extra), selectedTrackId);
String action = intent.getAction();
Log.d(TAG,
"TrackWidgetProvider.onReceive: trackId=" + selectedTrackId + ", action=" + action);
if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
|| TRACK_STARTED_ACTION.equals(action)
|| TRACK_STOPPED_ACTION.equals(action)) {
|| trackStartedBroadcastAction.equals(action)
|| trackStoppedBroadcastAction.equals(action)) {
updateTrack(action);
}
}
@Override
public void onDisabled(Context aContext) {
if (trackObserver != null) {
aContext.getContentResolver().unregisterContentObserver(trackObserver);
}
if (sharedPreferences != null) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
}
if (trackObserver != null) {
aContext.getContentResolver().unregisterContentObserver(trackObserver);
}
}
private void updateTrack(String action) {
Track track = null;
if (selectedTrackId != PreferencesUtils.SELECTED_TRACK_ID_DEFAULT) {
Log.d(TAG, "TrackWidgetProvider.updateTrack: Retrieving specified track.");
track = providerUtils.getTrack(selectedTrackId);
} else {
Log.d(TAG, "TrackWidgetProvider.updateTrack: Attempting to retrieve previous track.");
// TODO we should really read the pref.
track = providerUtils.getLastTrack();
/**
* Initializes the widget.
*
* @param aContext a context
*/
private void initialize(Context aContext) {
if (context != null) {
// Already initialized
return;
}
context = aContext;
trackObserver = new TrackObserver();
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
unknown = context.getString(R.string.value_unknown);
trackStartedBroadcastAction = context.getString(R.string.track_started_broadcast_action);
trackStoppedBroadcastAction = context.getString(R.string.track_stopped_broadcast_action);
sharedPreferences = context.getSharedPreferences(SETTINGS_NAME, Context.MODE_PRIVATE);
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(sharedPreferences, null);
context.getContentResolver()
.registerContentObserver(TracksColumns.CONTENT_URI, true, trackObserver);
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName widget = new ComponentName(context, TrackWidgetProvider.class);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.track_widget);
/**
* Updates the widget.
*
* @param action the action
*/
private void updateTrack(String action) {
Track track = selectedTrackId != PreferencesUtils.SELECTED_TRACK_ID_DEFAULT ? myTracksProviderUtils
.getTrack(selectedTrackId)
: myTracksProviderUtils.getLastTrack();
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.track_widget);
// Make all of the stats open the mytracks activity.
Intent intent;
if (track != null) {
intent = IntentUtils.newIntent(context, TrackDetailActivity.class)
@@ -156,92 +185,68 @@ public class TrackWidgetProvider
intent = IntentUtils.newIntent(context, TrackListActivity.class);
}
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.appwidget_track_statistics, pendingIntent);
remoteViews.setOnClickPendingIntent(R.id.track_widget_statistics, pendingIntent);
if (action != null) {
updateViewButton(views, action);
updateButton(remoteViews, action);
}
updateViewTrackStatistics(views, track);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(widget);
updateStatistics(remoteViews, track);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
new ComponentName(context, TrackWidgetProvider.class));
for (int appWidgetId : appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId, views);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
/**
* Update the widget's button with the appropriate intent and icon.
*
* @param views The RemoteViews containing the button
* @param action The action broadcast from the track service
* Updates button to start/stop recording.
*
* @param remoteViews the remote views
* @param action the action
*/
private void updateViewButton(RemoteViews views, String action) {
if (TRACK_STARTED_ACTION.equals(action)) {
// If a new track is started by this appwidget or elsewhere,
// toggle the button to active and have it disable the track if pressed.
setButtonIntent(views, R.string.track_action_end, R.drawable.app_widget_button_enabled);
private void updateButton(RemoteViews remoteViews, String action) {
int trackAction = trackStartedBroadcastAction.equals(action) ? R.string.track_action_end
: R.string.track_action_start;
int icon = trackStartedBroadcastAction.equals(action) ? R.drawable.app_widget_button_enabled
: R.drawable.app_widget_button_disabled;
Intent intent = new Intent(context, ControlRecordingService.class).setAction(
context.getString(trackAction));
PendingIntent pendingIntent = PendingIntent.getService(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.track_widget_button, pendingIntent);
remoteViews.setImageViewResource(R.id.track_widget_button, icon);
}
/**
* Updates statistics.
*
* @param remoteViews the remote views
* @param track the track
*/
private void updateStatistics(RemoteViews remoteViews, Track track) {
TripStatistics tripStatistics = track == null ? null : track.getStatistics();
String distance = tripStatistics == null ? unknown
: StringUtils.formatDistance(context, tripStatistics.getTotalDistance(), metricUnits);
int timeLabelId = useTotalTime ? R.string.stats_total_time : R.string.stats_moving_time;
String time = tripStatistics == null ? unknown : StringUtils.formatElapsedTime(
useTotalTime ? tripStatistics.getTotalTime()
: tripStatistics.getMovingTime());
int speedLabelId;
if (useTotalTime) {
speedLabelId = reportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace;
} else {
// If a track is stopped by this appwidget or elsewhere,
// toggle the button to inactive and have it start a new track if pressed.
setButtonIntent(views, R.string.track_action_start, R.drawable.app_widget_button_disabled);
}
}
/**
* Set up the main widget button.
*
* @param views The widget views
* @param action The resource id of the action to fire when the button is pressed
* @param icon The resource id of the icon to show for the button
*/
private void setButtonIntent(RemoteViews views, int action, int icon) {
Intent intent = new Intent(context, ControlRecordingService.class)
.setAction(context.getString(action));
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.appwidget_button, pendingIntent);
views.setImageViewResource(R.id.appwidget_button, icon);
}
/**
* Update the specified widget's view with the distance, time, and speed of
* the specified track.
*
* @param views The RemoteViews to update with statistics
* @param track The track to extract statistics from.
*/
protected void updateViewTrackStatistics(RemoteViews views, Track track) {
if (track == null) {
views.setTextViewText(R.id.appwidget_distance_text, unknown);
views.setTextViewText(R.id.appwidget_time_text, unknown);
views.setTextViewText(R.id.appwidget_speed_text, unknown);
return;
}
TripStatistics stats = track.getStatistics();
String distance = StringUtils.formatDistance(context, stats.getTotalDistance(), metricUnits);
String time = StringUtils.formatElapsedTime(stats.getMovingTime());
String speed = StringUtils.formatSpeed(
context, stats.getAverageMovingSpeed(), metricUnits, reportSpeed);
views.setTextViewText(R.id.appwidget_distance_text, distance);
views.setTextViewText(R.id.appwidget_time_text, time);
views.setTextViewText(R.id.appwidget_speed_text, speed);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key == null || PreferencesUtils.getKey(context, R.string.metric_units_key).equals(key)) {
metricUnits = PreferencesUtils.getBoolean(
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
}
if (key == null || PreferencesUtils.getKey(context, R.string.report_speed_key).equals(key)) {
reportSpeed = PreferencesUtils.getBoolean(
context, R.string.report_speed_key, PreferencesUtils.REPORT_SPEED_DEFAULT);
}
if (key == null
|| PreferencesUtils.getKey(context, R.string.selected_track_id_key).equals(key)) {
selectedTrackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
Log.d(TAG, "TrackWidgetProvider setting selecting track from preference: " + selectedTrackId);
speedLabelId = reportSpeed ? R.string.stats_average_moving_speed
: R.string.stats_average_moving_pace;
}
String speed = tripStatistics == null ? unknown : StringUtils.formatSpeed(
context, useTotalTime ? tripStatistics.getAverageSpeed()
: tripStatistics.getAverageMovingSpeed(), metricUnits, reportSpeed);
remoteViews.setTextViewText(R.id.track_widget_distance_value, distance);
remoteViews.setTextViewText(R.id.track_widget_time_label, context.getString(timeLabelId));
remoteViews.setTextViewText(R.id.track_widget_time_value, time);
remoteViews.setTextViewText(R.id.track_widget_speed_label, context.getString(speedLabelId));
remoteViews.setTextViewText(R.id.track_widget_speed_value, speed);
}
}