diff --git a/MyTracks/res/layout/track_widget.xml b/MyTracks/res/layout/track_widget.xml index c43096b4c..d6e33c462 100644 --- a/MyTracks/res/layout/track_widget.xml +++ b/MyTracks/res/layout/track_widget.xml @@ -14,71 +14,72 @@ 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. --> - - + android:padding="@dimen/widget_margin" > + android:layout_width="fill_parent" + android:orientation="horizontal" > + - - - - - - - - - - - - + android:clickable="true" + android:id="@+id/track_widget_statistics" + android:layout_height="fill_parent" + android:layout_weight="1" + android:layout_width="0dp" + android:orientation="vertical" + android:paddingLeft="8dp" > + + + + + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/MyTracks/res/values-v14/dimens.xml b/MyTracks/res/values-v14/dimens.xml new file mode 100644 index 000000000..344b6ad05 --- /dev/null +++ b/MyTracks/res/values-v14/dimens.xml @@ -0,0 +1,19 @@ + + + + 0dp + \ No newline at end of file diff --git a/MyTracks/res/values/colors.xml b/MyTracks/res/values/colors.xml index 7776ac17a..c2935ec0b 100644 --- a/MyTracks/res/values/colors.xml +++ b/MyTracks/res/values/colors.xml @@ -49,8 +49,8 @@ limitations under the License. #FF401465 #40401465 - - #99999999 + + #99999999 #F774DF00 diff --git a/MyTracks/res/values/dimens.xml b/MyTracks/res/values/dimens.xml new file mode 100644 index 000000000..11c778950 --- /dev/null +++ b/MyTracks/res/values/dimens.xml @@ -0,0 +1,19 @@ + + + + 8dp + \ No newline at end of file diff --git a/MyTracks/res/values/styles.xml b/MyTracks/res/values/styles.xml index cd6dae0d8..04f9c5ae7 100644 --- a/MyTracks/res/values/styles.xml +++ b/MyTracks/res/values/styles.xml @@ -157,8 +157,13 @@ limitations under the License. + + \ No newline at end of file diff --git a/MyTracks/res/xml/stats_settings.xml b/MyTracks/res/xml/stats_settings.xml index 9186c4a09..42c628a51 100644 --- a/MyTracks/res/xml/stats_settings.xml +++ b/MyTracks/res/xml/stats_settings.xml @@ -29,8 +29,8 @@ limitations under the License. - diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/StatsUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/StatsUtils.java index 32e49af82..07ebe5634 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/StatsUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/StatsUtils.java @@ -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)); } /** diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java index 4046d7f41..3ac00583f 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/StringUtils.java @@ -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); } } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java b/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java index c92f92984..d02a5eb11 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java +++ b/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java @@ -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); } }