forked from upstream-mirrors/OpenTracks
Merge heads
This commit is contained in:
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="41"
|
||||
android:versionCode="42"
|
||||
android:versionName="2.0"
|
||||
package="com.google.android.maps.mytracks" >
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -34,6 +34,7 @@ limitations under the License.
|
||||
style="@style/ListEmptyTextView" >
|
||||
</TextView>
|
||||
<ImageButton
|
||||
android:background="@color/transparent"
|
||||
android:contentDescription="@string/menu_record_track"
|
||||
android:id="@+id/track_list_record_button"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@drawable/app_widget_background"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingTop="5dp" >
|
||||
<ImageButton
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/my_tracks_app_name"
|
||||
android:id="@+id/appwidget_button"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:src="@drawable/app_widget_button_disabled" />
|
||||
android:padding="@dimen/widget_margin" >
|
||||
<LinearLayout
|
||||
android:clickable="true"
|
||||
android:id="@+id/appwidget_track_statistics"
|
||||
android:background="@drawable/app_widget_background"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="16dp" >
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal" >
|
||||
<ImageButton
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/my_tracks_app_name"
|
||||
android:id="@+id/track_widget_button"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/app_widget_button_disabled" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal" >
|
||||
<TextView
|
||||
android:id="@+id/appwidget_distance_text"
|
||||
style="@style/TrackWidgetValue" />
|
||||
<TextView
|
||||
android:text="@string/stats_total_distance"
|
||||
style="@style/TextSmall" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:background="@color/appwidget_hr"
|
||||
android:layout_height="1dp"
|
||||
android:layout_width="fill_parent" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal" >
|
||||
<TextView
|
||||
android:id="@+id/appwidget_time_text"
|
||||
style="@style/TrackWidgetValue" />
|
||||
<TextView
|
||||
android:text="@string/stats_moving_time"
|
||||
style="@style/TextSmall" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:background="@color/appwidget_hr"
|
||||
android:layout_height="1dp"
|
||||
android:layout_width="fill_parent" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal" >
|
||||
<TextView
|
||||
android:id="@+id/appwidget_speed_text"
|
||||
style="@style/TrackWidgetValue" />
|
||||
<TextView
|
||||
android:text="@string/stats_speed"
|
||||
style="@style/TextSmall" />
|
||||
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" >
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal" >
|
||||
<TextView
|
||||
android:id="@+id/track_widget_distance_value"
|
||||
style="@style/TrackWidgetValue" />
|
||||
<TextView
|
||||
android:text="@string/stats_total_distance"
|
||||
style="@style/TrackWidgetLabel" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:background="@color/track_widget_line"
|
||||
android:layout_height="1dp"
|
||||
android:layout_width="fill_parent" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal" >
|
||||
<TextView
|
||||
android:id="@+id/track_widget_time_value"
|
||||
style="@style/TrackWidgetValue" />
|
||||
<TextView
|
||||
android:id="@+id/track_widget_time_label"
|
||||
style="@style/TrackWidgetLabel" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:background="@color/track_widget_line"
|
||||
android:layout_height="1dp"
|
||||
android:layout_width="fill_parent" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="horizontal" >
|
||||
<TextView
|
||||
android:id="@+id/track_widget_speed_value"
|
||||
style="@style/TrackWidgetValue" />
|
||||
<TextView
|
||||
android:id="@+id/track_widget_speed_label"
|
||||
style="@style/TrackWidgetLabel" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -36,7 +36,7 @@ limitations under the License.
|
||||
android:id="@+id/track_list_import"
|
||||
android:title="@string/menu_import"/>
|
||||
<item
|
||||
android:icon="@drawable/menu_save_all"
|
||||
android:icon="@drawable/menu_save"
|
||||
android:id="@+id/track_list_save_all"
|
||||
android:title="@string/menu_save_all">
|
||||
<menu>
|
||||
@@ -47,7 +47,7 @@ limitations under the License.
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
android:icon="@drawable/menu_delete_all"
|
||||
android:icon="@drawable/menu_delete"
|
||||
android:id="@+id/track_list_delete_all"
|
||||
android:title="@string/menu_delete_all"/>
|
||||
<item
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2012 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.
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="widget_margin">0dp</dimen>
|
||||
</resources>
|
||||
@@ -19,46 +19,45 @@ limitations under the License.
|
||||
<color name="blue">#FF0000FF</color>
|
||||
<color name="gray">#FFAAAAAA</color>
|
||||
<color name="red">#FFFF0000</color>
|
||||
<color name="transparent">#00000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
<!-- holo_orange_dark -->
|
||||
<color name="warning_background">#FFFF8800</color>
|
||||
|
||||
<!-- Chart colors by series. -->
|
||||
<!-- Green and a transparent green -->
|
||||
|
||||
<!-- elevation chart colors, green and transparent green -->
|
||||
<color name="elevation_border">#FF339933</color>
|
||||
<color name="elevation_fill">#40B3CFB3</color>
|
||||
|
||||
<!-- Blue and a transparent blue -->
|
||||
<!-- speed chart colors, blue and transparent blue -->
|
||||
<color name="speed_border">#FF0066FF</color>
|
||||
<color name="speed_fill">#40C8E0FF</color>
|
||||
|
||||
<!-- Blue and a transparent blue -->
|
||||
<!-- pace chart colors, blue and transparent blue -->
|
||||
<color name="pace_border">#FF0066FF</color>
|
||||
<color name="pace_fill">#40C8E0FF</color>
|
||||
|
||||
<!-- Red and a transparent red -->
|
||||
<color name="heartrate_border">#FFCC3300</color>
|
||||
<color name="heartrate_fill">#40E88A70</color>
|
||||
|
||||
<!-- Pink and a transparent pink -->
|
||||
<!-- heart rate chart colors, red and transparent red -->
|
||||
<color name="heart_rate_border">#FFCC3300</color>
|
||||
<color name="heart_rate_fill">#40E88A70</color>
|
||||
|
||||
<!-- cadence chart colors, pink and transparent pink -->
|
||||
<color name="cadence_border">#FFF128F3</color>
|
||||
<color name="cadence_fill">#40F128F3</color>
|
||||
|
||||
<!-- Purple and a transparent purple -->
|
||||
<!-- power chart colors, purple and transparent purple -->
|
||||
<color name="power_border">#FF401465</color>
|
||||
<color name="power_fill">#40401465</color>
|
||||
|
||||
<!-- AppWidget colors -->
|
||||
<color name="appwidget_hr">#99999999</color>
|
||||
|
||||
<!-- Colors for dynamic track drawing -->
|
||||
<!-- Green -->
|
||||
<!-- track widget line -->
|
||||
<color name="track_widget_line">#99999999</color>
|
||||
|
||||
<!-- dynamic track slow path, green -->
|
||||
<color name="slow_path">#F774DF00</color>
|
||||
|
||||
<!-- Blue -->
|
||||
|
||||
<!-- dynamic track normal path, blue -->
|
||||
<color name="normal_path">#F75858FA</color>
|
||||
|
||||
<!-- Red -->
|
||||
|
||||
<!-- dynamic track fast path, red -->
|
||||
<color name="fast_path">#F7DF0101</color>
|
||||
</resources>
|
||||
</resources>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2012 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.
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="widget_margin">8dp</dimen>
|
||||
</resources>
|
||||
@@ -157,8 +157,13 @@ limitations under the License.
|
||||
</style>
|
||||
<!-- Theme selector -->
|
||||
<style name="ThemeSelector" parent="android:Theme" />
|
||||
<!-- The style for the track widget value -->
|
||||
<style name="TrackWidgetValue" parent="@style/TextSmall">
|
||||
<!-- The style for the track widget label -->
|
||||
<style name="TrackWidgetLabel" parent="@style/TextSmall">
|
||||
<item name="android:width">100dp</item>
|
||||
</style>
|
||||
<!-- The style for the track widget value -->
|
||||
<style name="TrackWidgetValue" parent="@style/TextSmall">
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_width">0dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -29,8 +29,8 @@ limitations under the License.
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="@string/stats_use_total_time_key"
|
||||
android:summaryOff="@string/stats_total_time"
|
||||
android:summaryOn="@string/stats_moving_time"
|
||||
android:summaryOff="@string/stats_moving_time"
|
||||
android:summaryOn="@string/stats_total_time"
|
||||
android:title="@string/settings_stats_time_title" />
|
||||
<PreferenceCategory android:title="@string/settings_show" >
|
||||
<CheckBoxPreference
|
||||
|
||||
@@ -14,9 +14,8 @@ 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.
|
||||
-->
|
||||
<appwidget-provider
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/track_widget"
|
||||
android:minHeight="72dp"
|
||||
android:minWidth="294dp"
|
||||
android:minHeight="110dp"
|
||||
android:minWidth="250dp"
|
||||
android:updatePeriodMillis="0" />
|
||||
|
||||
@@ -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();
|
||||
|
||||
+26
-24
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user