forked from upstream-mirrors/OpenTracks
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bafc7d4a1 | |||
| be58b48d20 | |||
| 303abac980 | |||
| d1a459efc2 | |||
| 96069e6f09 | |||
| 301a721329 | |||
| e8c6801a32 | |||
| 355f3ef614 | |||
| eaa6083586 | |||
| a0c5d02c51 | |||
| 76f9f5fc82 | |||
| 8e2598e93b |
@@ -0,0 +1,10 @@
|
||||
v3.2.9: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Option to keep screen on while recording
|
||||
- Markers: removed split markers completely
|
||||
- Voice announcement now work without READ_PHONE_STATE permission
|
||||
|
||||
Bugfixes:
|
||||
- Statistics: heart rate was not shown
|
||||
- Statistics: time was briefly reset to 0s
|
||||
-21
@@ -151,27 +151,6 @@ public class TrackRecordingServiceTestLooper {
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, Integer.parseInt(context.getResources().getString(R.string.split_frequency_default)));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyByDistance() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, -1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyByTime() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, 1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDefault() throws TimeoutException {
|
||||
|
||||
@@ -16,8 +16,8 @@ limitations under the License.
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.dennisguse.opentracks"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="3162"
|
||||
android:versionName="v3.2.8"> <!-- For FDroid. -->
|
||||
android:versionCode="3175"
|
||||
android:versionName="v3.2.9"> <!-- For FDroid. -->
|
||||
<!-- Permission to initialize services -->
|
||||
<application
|
||||
android:name=".Startup"
|
||||
@@ -145,34 +145,17 @@ limitations under the License.
|
||||
android:name="android.hardware.location.gps"
|
||||
android:required="true" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:name="android.hardware.android.hardware.bluetooth_le"
|
||||
android:required="false" />
|
||||
<!-- Permissions to access sensors -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.faketouch"
|
||||
android:required="false" />
|
||||
<!-- Permission to read phone state so voice announcement will not interrupt ongoing calls -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.sensor.compass"
|
||||
android:required="false" />
|
||||
<!-- Permission to read/write external storage -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony.gsm"
|
||||
android:required="false" />
|
||||
<!-- Required features -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
<!-- Optional features -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen.multitouch"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
@@ -180,9 +163,6 @@ limitations under the License.
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
</manifest>
|
||||
|
||||
@@ -71,8 +71,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
waypointSection = findViewById(R.id.marker_edit_waypoint_section);
|
||||
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
||||
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type);
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||
this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
|
||||
waypointMarkerType.setAdapter(adapter);
|
||||
waypointDescription = findViewById(R.id.marker_edit_waypoint_description);
|
||||
|
||||
|
||||
@@ -39,13 +39,13 @@ import androidx.loader.content.Loader;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointsColumns;
|
||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
@@ -152,7 +152,6 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
int typeIndex = cursor.getColumnIndex(WaypointsColumns.TYPE);
|
||||
int nameIndex = cursor.getColumnIndex(WaypointsColumns.NAME);
|
||||
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
||||
int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY);
|
||||
@@ -161,12 +160,11 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
int latitudeIndex = cursor.getColumnIndex(WaypointsColumns.LATITUDE);
|
||||
int longitudeIndex = cursor.getColumnIndex(WaypointsColumns.LONGITUDE);
|
||||
|
||||
boolean statistics = WaypointType.values()[cursor.getInt(typeIndex)] == WaypointType.STATISTICS;
|
||||
int iconId = statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin;
|
||||
int iconId = MarkerUtils.ICON_ID;
|
||||
String name = cursor.getString(nameIndex);
|
||||
long time = cursor.getLong(timeIndex);
|
||||
String category = statistics ? null : cursor.getString(categoryIndex);
|
||||
String description = statistics ? null : cursor.getString(descriptionIndex);
|
||||
String category = cursor.getString(categoryIndex);
|
||||
String description = cursor.getString(descriptionIndex);
|
||||
String photoUrl = cursor.getString(photoUrlIndex);
|
||||
double latitude = cursor.getDouble(latitudeIndex);
|
||||
double longitude = cursor.getDouble(longitudeIndex);
|
||||
|
||||
@@ -46,7 +46,6 @@ import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
||||
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||
@@ -55,6 +54,7 @@ import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
@@ -393,11 +393,9 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
}
|
||||
}
|
||||
|
||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||
|
||||
resultMap.put(IS_RECORDING_FIELD, false);
|
||||
resultMap.put(IS_PAUSED_FIELD, true);
|
||||
resultMap.put(ICON_ID_FIELD, statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin);
|
||||
resultMap.put(ICON_ID_FIELD, MarkerUtils.ICON_ID);
|
||||
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_marker);
|
||||
resultMap.put(NAME_FIELD, waypoint.getName());
|
||||
// Display the marker's track name in the total time field
|
||||
@@ -405,8 +403,8 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
resultMap.put(TOTAL_DISTANCE_FIELD, null);
|
||||
resultMap.put(MARKER_COUNT_FIELD, 0);
|
||||
resultMap.put(START_TIME_FIELD, waypoint.getLocation().getTime());
|
||||
resultMap.put(CATEGORY_FIELD, statistics ? null : waypoint.getCategory());
|
||||
resultMap.put(DESCRIPTION_FIELD, statistics ? null : waypoint.getDescription());
|
||||
resultMap.put(CATEGORY_FIELD, waypoint.getCategory());
|
||||
resultMap.put(DESCRIPTION_FIELD, waypoint.getDescription());
|
||||
resultMap.put(PHOTO_URL_FIELD, waypoint.getPhotoUrl());
|
||||
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId());
|
||||
resultMap.put(MARKER_ID_FIELD, waypoint.getId());
|
||||
|
||||
@@ -55,7 +55,6 @@ import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StatsUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
@@ -127,16 +126,24 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.recording_track_id_key, key)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackDetailActivity.this);
|
||||
setLockscreenPolicy();
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackDetailActivity.this);
|
||||
setLockscreenPolicy();
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.stats_show_on_lockscreen_while_recording_key, key)) {
|
||||
setLockscreenPolicy();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
|
||||
if (key == null) return;
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@@ -227,11 +234,13 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
setLockscreenPolicy();
|
||||
setScreenOnPolicy();
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
|
||||
private void setLockscreenPolicy() {
|
||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackDetailActivity.this);
|
||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackDetailActivity.this)
|
||||
&& PreferencesUtils.isRecording(this);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(showOnLockScreen);
|
||||
@@ -242,6 +251,17 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
}
|
||||
}
|
||||
|
||||
private void setScreenOnPolicy() {
|
||||
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackDetailActivity.this)
|
||||
&& PreferencesUtils.isRecording(this);
|
||||
|
||||
if (keepScreenOn) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
@@ -413,22 +433,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
return trackDataHub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track id.
|
||||
*/
|
||||
// TODO Seems to be no used.
|
||||
@Deprecated
|
||||
public long getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
|
||||
private void setBluetoothHeartRateSensorName(String sensorName) {
|
||||
StatsUtils.setBluetoothHeartRateSensorName(this, sensorName, isRecording());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the data in the intent.
|
||||
*/
|
||||
private void handleIntent(Intent intent) {
|
||||
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
long markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||
|
||||
@@ -43,6 +43,7 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.stats.ExtremityMonitor;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
@@ -263,12 +264,9 @@ public class ChartView extends View {
|
||||
pointer = context.getResources().getDrawable(R.drawable.ic_logo_color_24dp);
|
||||
pointer.setBounds(0, 0, pointer.getIntrinsicWidth(), pointer.getIntrinsicHeight());
|
||||
|
||||
Drawable statisticsMarker = getResources().getDrawable(R.drawable.ic_marker_yellow_pushpin);
|
||||
markerWidth = statisticsMarker.getIntrinsicWidth();
|
||||
markerHeight = statisticsMarker.getIntrinsicHeight();
|
||||
statisticsMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
Drawable waypointMarker = getResources().getDrawable(R.drawable.ic_marker_blue_pushpin);
|
||||
Drawable waypointMarker = MarkerUtils.getDefaultPhoto(context);
|
||||
markerWidth = waypointMarker.getIntrinsicWidth();
|
||||
markerHeight = waypointMarker.getIntrinsicHeight();
|
||||
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
scroller = new Scroller(context);
|
||||
|
||||
@@ -466,12 +466,8 @@ public class TrackDataHub implements DataSourceListener {
|
||||
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
|
||||
}
|
||||
|
||||
if (!LocationUtils.isValidLocation(location)) {
|
||||
// TODO: also include the last valid point before a split
|
||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||
trackDataListener.onSegmentSplit(location);
|
||||
includeNextPoint = true;
|
||||
}
|
||||
if (!LocationUtils.isValidLocation(location)) { //This can be split markers (not anymore supported feature)
|
||||
includeNextPoint = true;
|
||||
} else {
|
||||
// Also include the last point if the selected track is not recording.
|
||||
if (includeNextPoint || (localNumLoadedPoints % samplingFrequency == 0) || (locationId == lastTrackPointId && !isSelectedTrackRecording())) {
|
||||
|
||||
@@ -51,14 +51,9 @@ public interface TrackDataListener {
|
||||
*/
|
||||
void onSampledOutTrackPoint(Location location);
|
||||
|
||||
/**
|
||||
* Called when an invalid track point representing a segment split is read.
|
||||
*/
|
||||
void onSegmentSplit(Location location);
|
||||
|
||||
/**
|
||||
* Called when finish sending new track points.
|
||||
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(Location)}, {@link #onSampledOutTrackPoint(Location)} and {@link #onSegmentSplit(Location)}.
|
||||
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(Location)} and {@link #onSampledOutTrackPoint(Location)}.
|
||||
*/
|
||||
void onNewTrackPointsDone();
|
||||
|
||||
|
||||
@@ -198,13 +198,6 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit(Location location) {
|
||||
if (isResumed()) {
|
||||
fillDataPoint(location, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPointsDone() {
|
||||
if (isResumed()) {
|
||||
|
||||
@@ -41,12 +41,10 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import de.dennisguse.opentracks.MarkerEditActivity;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.StatsUtils;
|
||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
@@ -62,7 +60,7 @@ public class MarkerDetailFragment extends Fragment {
|
||||
private static final long HIDE_TEXT_DELAY = 4 * UnitConversions.ONE_SECOND;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private Handler handler;
|
||||
private ImageView photo;
|
||||
private ImageView photoView;
|
||||
private ImageView textGradient;
|
||||
private LinearLayout waypointInfo;
|
||||
private Waypoint waypoint;
|
||||
@@ -119,11 +117,11 @@ public class MarkerDetailFragment extends Fragment {
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.marker_detail_fragment, container, false);
|
||||
|
||||
photo = view.findViewById(R.id.marker_detail_waypoint_photo);
|
||||
photoView = view.findViewById(R.id.marker_detail_waypoint_photo);
|
||||
textGradient = view.findViewById(R.id.marker_detail_waypoint_text_gradient);
|
||||
waypointInfo = view.findViewById(R.id.marker_detail_waypoint_info);
|
||||
|
||||
photo.setOnClickListener(new View.OnClickListener() {
|
||||
photoView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handler.removeCallbacks(hideText);
|
||||
@@ -156,11 +154,8 @@ public class MarkerDetailFragment extends Fragment {
|
||||
@Override
|
||||
public void setMenuVisibility(boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
/*
|
||||
* View pager caches the neighboring fragments in the resumed state. If
|
||||
* becoming visible from the resumed state, update the UI to display the
|
||||
* text above the image.
|
||||
*/
|
||||
// View pager caches the neighboring fragments in the resumed state.
|
||||
// If becoming visible from the resumed state, update the UI to display the text above the image.
|
||||
if (isResumed()) {
|
||||
if (menuVisible) {
|
||||
updateUi();
|
||||
@@ -214,47 +209,25 @@ public class MarkerDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI.
|
||||
*/
|
||||
private void updateUi() {
|
||||
View waypointView = getView().findViewById(R.id.marker_detail_waypoint);
|
||||
View statisticsView = getView().findViewById(R.id.marker_detail_statistics);
|
||||
|
||||
boolean isWaypoint = waypoint.getType() == WaypointType.WAYPOINT;
|
||||
waypointView.setVisibility(isWaypoint ? View.VISIBLE : View.GONE);
|
||||
statisticsView.setVisibility(isWaypoint ? View.GONE : View.VISIBLE);
|
||||
|
||||
if (isWaypoint) {
|
||||
boolean hasPhoto = waypoint.hasPhoto();
|
||||
photo.setVisibility(hasPhoto ? View.VISIBLE : View.GONE);
|
||||
textGradient.setVisibility(hasPhoto ? View.VISIBLE : View.GONE);
|
||||
waypointInfo.setVisibility(View.VISIBLE);
|
||||
|
||||
if (hasPhoto) {
|
||||
handler.removeCallbacks(hideText);
|
||||
photo.setImageURI(waypoint.getPhotoURI());
|
||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
||||
}
|
||||
|
||||
setName(R.id.marker_detail_waypoint_name, hasPhoto);
|
||||
|
||||
TextView category = getView().findViewById(R.id.marker_detail_waypoint_category);
|
||||
ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(waypoint.getCategory()), hasPhoto);
|
||||
|
||||
TextView description = getView().findViewById(R.id.marker_detail_waypoint_description);
|
||||
ListItemUtils.setTextView(getActivity(), description, waypoint.getDescription(), hasPhoto);
|
||||
|
||||
setLocation(R.id.marker_detail_waypoint_location, hasPhoto);
|
||||
boolean hasPhoto = waypoint.hasPhoto();
|
||||
if (hasPhoto) {
|
||||
handler.removeCallbacks(hideText);
|
||||
photoView.setImageURI(waypoint.getPhotoURI());
|
||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
||||
} else {
|
||||
setName(R.id.marker_detail_statistics_name, false);
|
||||
|
||||
setLocation(R.id.marker_detail_statistics_location, false);
|
||||
|
||||
Track track = contentProviderUtils.getTrack(waypoint.getTrackId());
|
||||
StatsUtils.setTripStatisticsValues(getActivity(), waypoint.getTripStatistics(), null);
|
||||
StatsUtils.setLocationValues(getActivity(), waypoint.getLocation(), false);
|
||||
photoView.setImageResource(MarkerUtils.ICON_ID);
|
||||
}
|
||||
|
||||
setName(R.id.marker_detail_waypoint_name, hasPhoto);
|
||||
|
||||
TextView category = getView().findViewById(R.id.marker_detail_waypoint_category);
|
||||
ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(waypoint.getCategory()), hasPhoto);
|
||||
|
||||
TextView description = getView().findViewById(R.id.marker_detail_waypoint_description);
|
||||
ListItemUtils.setTextView(getActivity(), description, waypoint.getDescription(), hasPhoto);
|
||||
|
||||
setLocation(R.id.marker_detail_waypoint_location, hasPhoto);
|
||||
}
|
||||
|
||||
private void setName(int resId, boolean addShadow) {
|
||||
|
||||
@@ -20,16 +20,18 @@ import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
@@ -43,9 +45,11 @@ import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.StatsUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
@@ -59,6 +63,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
public static final String STATS_FRAGMENT_TAG = StatsFragment.class.getSimpleName();
|
||||
|
||||
private static final long UI_UPDATE_INTERVAL = UnitConversions.ONE_SECOND;
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
private Handler handlerUpdateUI;
|
||||
|
||||
@@ -67,18 +73,6 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
private Location lastLocation = null;
|
||||
private TripStatistics lastTripStatistics = null;
|
||||
|
||||
private final Runnable updateUIeachSecond = new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed() && isSelectedTrackRecording()) {
|
||||
if (!isSelectedTrackPaused() && lastTripStatistics != null) {
|
||||
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime());
|
||||
updateSensorDataUI();
|
||||
}
|
||||
|
||||
handlerUpdateUI.postDelayed(this, UnitConversions.ONE_SECOND);
|
||||
}
|
||||
}
|
||||
};
|
||||
private String category = "";
|
||||
private int recordingGpsAccuracy;
|
||||
|
||||
@@ -87,6 +81,111 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
return inflater.inflate(R.layout.stats, container, false);
|
||||
}
|
||||
|
||||
/* Views */
|
||||
private View sensorContainerView;
|
||||
private TextView heartRateValueView;
|
||||
private TextView heartRateSensorView;
|
||||
|
||||
private TextView totalTimeValueView;
|
||||
private final Runnable updateUIeachSecond = new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed() && isSelectedTrackRecording()) {
|
||||
if (!isSelectedTrackPaused() && lastTripStatistics != null) {
|
||||
updateTotalTime();
|
||||
updateSensorDataUI();
|
||||
}
|
||||
|
||||
handlerUpdateUI.postDelayed(this, UI_UPDATE_INTERVAL);
|
||||
}
|
||||
}
|
||||
};
|
||||
private TextView distanceValue;
|
||||
private TextView distanceUnit;
|
||||
private View activityLabel;
|
||||
private Spinner activitySpinner;
|
||||
private TextView movingTimeValue;
|
||||
private TextView speedAvgLabel;
|
||||
private TextView speedAvgValue;
|
||||
private TextView speedAvgUnit;
|
||||
private TextView speedMaxLabel;
|
||||
private TextView speedMaxValue;
|
||||
private TextView speedMaxUnit;
|
||||
private TextView speedMovingLabel;
|
||||
private TextView speedMovingValue;
|
||||
private TextView speedMovingUnit;
|
||||
private View gradeElevationSeparator;
|
||||
private View gradeElevationContainer;
|
||||
private TextView elevationMinValue;
|
||||
private TextView elevationMinUnit;
|
||||
private TextView elevationMaxValue;
|
||||
private TextView elevationMaxUnit;
|
||||
private View speedContainer;
|
||||
private TextView speedLabel;
|
||||
private TextView speedValue;
|
||||
private TextView speedUnit;
|
||||
private View elevationContainer;
|
||||
private TextView elevationValue;
|
||||
private TextView elevationUnit;
|
||||
private View coordinateSeparator;
|
||||
private View coordinateContainer;
|
||||
private TextView latitudeValue;
|
||||
private TextView longitudeValue;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
sensorContainerView = view.findViewById(R.id.stats_sensor_container);
|
||||
heartRateValueView = view.findViewById(R.id.stats_sensor_heart_rate_value);
|
||||
heartRateSensorView = view.findViewById(R.id.stats_sensor_heart_rate_sensor_value);
|
||||
|
||||
totalTimeValueView = view.findViewById(R.id.stats_total_time_value);
|
||||
|
||||
distanceValue = view.findViewById(R.id.stats_distance_value);
|
||||
distanceUnit = view.findViewById(R.id.stats_distance_unit);
|
||||
|
||||
activityLabel = view.findViewById(R.id.stats_activity_type_label);
|
||||
activitySpinner = view.findViewById(R.id.stats_activity_type_icon);
|
||||
|
||||
movingTimeValue = view.findViewById(R.id.stats_moving_time_value);
|
||||
|
||||
speedAvgLabel = view.findViewById(R.id.stats_average_speed_label);
|
||||
speedAvgValue = view.findViewById(R.id.stats_average_speed_value);
|
||||
speedAvgUnit = view.findViewById(R.id.stats_average_speed_unit);
|
||||
|
||||
speedMaxLabel = view.findViewById(R.id.stats_max_speed_label);
|
||||
speedMaxValue = view.findViewById(R.id.stats_max_speed_value);
|
||||
speedMaxUnit = view.findViewById(R.id.stats_max_speed_unit);
|
||||
|
||||
speedMovingLabel = view.findViewById(R.id.stats_moving_speed_label);
|
||||
speedMovingValue = view.findViewById(R.id.stats_moving_speed_value);
|
||||
speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit);
|
||||
|
||||
gradeElevationSeparator = view.findViewById(R.id.stats_elevation_separator);
|
||||
gradeElevationContainer = view.findViewById(R.id.stats_elevation_container);
|
||||
|
||||
elevationMinValue = view.findViewById(R.id.stats_elevation_min_value);
|
||||
elevationMinUnit = view.findViewById(R.id.stats_elevation_min_unit);
|
||||
|
||||
elevationMaxValue = view.findViewById(R.id.stats_elevation_max_value);
|
||||
elevationMaxUnit = view.findViewById(R.id.stats_elevation_max_unit);
|
||||
|
||||
speedContainer = view.findViewById(R.id.stats_speed);
|
||||
speedLabel = view.findViewById(R.id.stats_speed_label);
|
||||
speedValue = view.findViewById(R.id.stats_speed_value);
|
||||
speedUnit = view.findViewById(R.id.stats_speed_unit);
|
||||
|
||||
elevationContainer = view.findViewById(R.id.stats_elevation);
|
||||
elevationValue = view.findViewById(R.id.stats_elevation_current_value);
|
||||
elevationUnit = view.findViewById(R.id.stats_elevation_current_unit);
|
||||
|
||||
coordinateSeparator = view.findViewById(R.id.stats_coordinate_separator);
|
||||
coordinateContainer = view.findViewById(R.id.stats_coordinate_container);
|
||||
|
||||
latitudeValue = view.findViewById(R.id.stats_latitude_value);
|
||||
longitudeValue = view.findViewById(R.id.stats_longitude_value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
@@ -125,10 +224,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(getContext(), null);
|
||||
trackRecordingServiceConnection.startConnection(getContext());
|
||||
|
||||
updateUi(getActivity());
|
||||
if (isSelectedTrackRecording()) {
|
||||
handlerUpdateUI.post(updateUIeachSecond);
|
||||
}
|
||||
handlerUpdateUI.post(updateUIeachSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,6 +241,60 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
trackRecordingServiceConnection = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
sensorContainerView = null;
|
||||
heartRateValueView = null;
|
||||
heartRateSensorView = null;
|
||||
|
||||
totalTimeValueView = null;
|
||||
|
||||
distanceValue = null;
|
||||
distanceUnit = null;
|
||||
|
||||
activityLabel = null;
|
||||
activitySpinner = null;
|
||||
|
||||
movingTimeValue = null;
|
||||
|
||||
speedAvgLabel = null;
|
||||
speedAvgValue = null;
|
||||
speedAvgUnit = null;
|
||||
|
||||
speedMaxLabel = null;
|
||||
speedMaxValue = null;
|
||||
speedMaxUnit = null;
|
||||
|
||||
speedMovingLabel = null;
|
||||
speedMovingValue = null;
|
||||
speedMovingUnit = null;
|
||||
|
||||
gradeElevationSeparator = null;
|
||||
gradeElevationContainer = null;
|
||||
|
||||
elevationMinValue = null;
|
||||
elevationMinUnit = null;
|
||||
|
||||
elevationMaxValue = null;
|
||||
elevationMaxUnit = null;
|
||||
|
||||
speedContainer = null;
|
||||
speedLabel = null;
|
||||
speedValue = null;
|
||||
speedUnit = null;
|
||||
|
||||
elevationContainer = null;
|
||||
elevationValue = null;
|
||||
elevationUnit = null;
|
||||
|
||||
coordinateSeparator = null;
|
||||
coordinateContainer = null;
|
||||
|
||||
latitudeValue = null;
|
||||
longitudeValue = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackUpdated(final Track track) {
|
||||
if (isResumed()) {
|
||||
@@ -154,7 +304,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
if (isResumed()) {
|
||||
lastTripStatistics = track != null ? track.getTripStatistics() : null;
|
||||
category = track != null ? track.getCategory() : "";
|
||||
updateUi(getActivity());
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -176,11 +326,6 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
lastLocation = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit(Location location) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPointsDone() {
|
||||
if (isResumed()) {
|
||||
@@ -200,7 +345,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
lastLocation = null;
|
||||
}
|
||||
}
|
||||
StatsUtils.setLocationValues(getActivity(), lastLocation, isSelectedTrackRecording());
|
||||
setLocationValues();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -229,7 +374,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
updateUi(getActivity());
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -244,7 +389,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
updateUi(getActivity());
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -300,6 +445,12 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
return trackDataHub != null && trackDataHub.isSelectedTrackPaused();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
updateStats();
|
||||
setLocationValues();
|
||||
updateSensorDataUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to fetch most recent {@link SensorDataSet} {@link de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager}.
|
||||
*/
|
||||
@@ -314,16 +465,178 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
sensorDataSet = trackRecordingService.getSensorData();
|
||||
}
|
||||
|
||||
StatsUtils.setSensorData(getActivity(), sensorDataSet, isSelectedTrackRecording());
|
||||
setHeartRateSensorData(sensorDataSet, isSelectedTrackRecording());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI.
|
||||
*/
|
||||
private void updateUi(FragmentActivity activity) {
|
||||
String trackIconValue = TrackIconUtils.getIconValue(activity, category);
|
||||
StatsUtils.setTripStatisticsValues(activity, lastTripStatistics, trackIconValue);
|
||||
StatsUtils.setLocationValues(activity, lastLocation, isSelectedTrackRecording());
|
||||
updateSensorDataUI();
|
||||
private void setHeartRateSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
|
||||
// heart rate
|
||||
int isVisible = View.VISIBLE;
|
||||
if (!isRecording || PreferencesUtils.isBluetoothHeartRateSensorAddressDefault(getContext())) {
|
||||
isVisible = View.INVISIBLE;
|
||||
}
|
||||
sensorContainerView.setVisibility(isVisible);
|
||||
|
||||
if (isRecording) {
|
||||
String heartRate = getContext().getString(R.string.value_unknown);
|
||||
String sensorName = getContext().getString(R.string.value_unknown);
|
||||
if (sensorDataSet != null && sensorDataSet.isRecent(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS)) {
|
||||
sensorName = sensorDataSet.getSensorName();
|
||||
if (sensorDataSet.hasHeartRate()) {
|
||||
heartRate = StringUtils.formatDecimal(sensorDataSet.getHeartRate(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
heartRateSensorView.setText(sensorName);
|
||||
heartRateValueView.setText(heartRate);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStats() {
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
|
||||
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext());
|
||||
boolean isRecording = isSelectedTrackRecording();
|
||||
|
||||
// Set total distance
|
||||
{
|
||||
double totalDistance = lastTripStatistics == null ? Double.NaN : lastTripStatistics.getTotalDistance();
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, metricUnits);
|
||||
|
||||
distanceValue.setText(parts.first);
|
||||
distanceUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set activity type
|
||||
{
|
||||
activityLabel.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
|
||||
|
||||
activitySpinner.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
|
||||
activitySpinner.setEnabled(isRecording);
|
||||
if (trackIconValue != null) {
|
||||
TrackIconUtils.setIconSpinner(activitySpinner, trackIconValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Set time
|
||||
if (lastTripStatistics != null) {
|
||||
movingTimeValue.setText(StringUtils.formatElapsedTime(lastTripStatistics.getMovingTime()));
|
||||
updateTotalTime();
|
||||
}
|
||||
|
||||
// Set average speed/pace
|
||||
{
|
||||
double speed = lastTripStatistics != null ? lastTripStatistics.getAverageSpeed() : Double.NaN;
|
||||
speedAvgLabel.setText(reportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, metricUnits, reportSpeed);
|
||||
speedAvgValue.setText(parts.first);
|
||||
speedAvgUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set max speed/pace
|
||||
{
|
||||
double speed = lastTripStatistics == null ? Double.NaN : lastTripStatistics.getMaxSpeed();
|
||||
|
||||
speedMaxLabel.setText(reportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, metricUnits, reportSpeed);
|
||||
speedMaxValue.setText(parts.first);
|
||||
speedMaxUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set moving speed/pace
|
||||
{
|
||||
double speed = lastTripStatistics != null ? lastTripStatistics.getAverageMovingSpeed() : Double.NaN;
|
||||
|
||||
speedMovingLabel.setText(reportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, metricUnits, reportSpeed);
|
||||
speedMovingValue.setText(parts.first);
|
||||
speedMovingUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
|
||||
// Set elevation
|
||||
{
|
||||
boolean showElevation = PreferencesUtils.isShowStatsGradeElevation(getContext());
|
||||
gradeElevationSeparator.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
gradeElevationContainer.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showElevation) {
|
||||
{
|
||||
double elevation = lastTripStatistics == null ? Double.NaN : lastTripStatistics.getMinElevation();
|
||||
Pair<String, String> parts = StringUtils.formatElevation(getContext(), elevation, metricUnits);
|
||||
|
||||
|
||||
elevationMinValue.setText(parts.first);
|
||||
elevationMinUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
{
|
||||
double elevation = lastTripStatistics == null ? Double.NaN : lastTripStatistics.getMaxElevation();
|
||||
Pair<String, String> parts = StringUtils.formatElevation(getContext(), elevation, metricUnits);
|
||||
|
||||
|
||||
elevationMaxValue.setText(parts.first);
|
||||
elevationMaxUnit.setText(parts.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTotalTime() {
|
||||
long totalTime;
|
||||
if (isSelectedTrackRecording()) {
|
||||
totalTime = System.currentTimeMillis() - lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime();
|
||||
} else {
|
||||
totalTime = lastTripStatistics.getTotalTime();
|
||||
}
|
||||
totalTimeValueView.setText(StringUtils.formatElapsedTime(totalTime));
|
||||
}
|
||||
|
||||
private void setLocationValues() {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext());
|
||||
boolean isRecording = isSelectedTrackRecording();
|
||||
|
||||
// Set speed/pace
|
||||
speedContainer.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||
if (isRecording) {
|
||||
speedLabel.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||
|
||||
double speed = lastLocation != null && lastLocation.hasSpeed() ? lastLocation.getSpeed() : Double.NaN;
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, metricUnits, reportSpeed);
|
||||
speedValue.setText(parts.first);
|
||||
speedUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set elevation
|
||||
boolean showGradeElevation = isRecording && PreferencesUtils.isShowStatsGradeElevation(getContext());
|
||||
elevationContainer.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showGradeElevation) {
|
||||
double altitude = lastLocation != null && lastLocation.hasAltitude() ? lastLocation.getAltitude() : Double.NaN;
|
||||
Pair<String, String> parts = StringUtils.formatElevation(getContext(), altitude, metricUnits);
|
||||
|
||||
elevationValue.setText(parts.first);
|
||||
elevationUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set coordinate
|
||||
boolean showCoordinate = isRecording && PreferencesUtils.isStatsShowCoordinate(getContext());
|
||||
|
||||
coordinateSeparator.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
coordinateContainer.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
if (showCoordinate) {
|
||||
double latitude = lastLocation != null ? lastLocation.getLatitude() : Double.NaN;
|
||||
String latitudeText = Double.isNaN(latitude) || Double.isInfinite(latitude) ? getContext().getString(R.string.value_unknown) : StringUtils.formatCoordinate(latitude);
|
||||
latitudeValue.setText(latitudeText);
|
||||
|
||||
double longitude = lastLocation != null ? lastLocation.getLongitude() : Double.NaN;
|
||||
String longitudeText = Double.isNaN(longitude) || Double.isInfinite(longitude) ? getContext().getString(R.string.value_unknown) : StringUtils.formatCoordinate(longitude);
|
||||
longitudeValue.setText(longitudeText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
|
||||
import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor;
|
||||
import de.dennisguse.opentracks.services.tasks.SplitPeriodicTaskFactory;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.stats.TripStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
@@ -95,7 +94,6 @@ public class TrackRecordingService extends Service {
|
||||
private Handler handler;
|
||||
private LocationManagerConnector locationManagerConnector;
|
||||
private PeriodicTaskExecutor voiceExecutor;
|
||||
private PeriodicTaskExecutor splitExecutor;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private TrackRecordingServiceNotificationManager notificationManager;
|
||||
private long recordingTrackId;
|
||||
@@ -126,14 +124,10 @@ public class TrackRecordingService extends Service {
|
||||
if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
voiceExecutor.setMetricUnits(metricUnits);
|
||||
splitExecutor.setMetricUnits(metricUnits);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.voice_frequency_key, key)) {
|
||||
voiceExecutor.setTaskFrequency(PreferencesUtils.getVoiceFrequency(context));
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.split_frequency_key, key)) {
|
||||
splitExecutor.setTaskFrequency(PreferencesUtils.getSplitFrequency(context));
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.min_recording_interval_key, key)) {
|
||||
int minRecordingInterval = PreferencesUtils.getMinRecordingInterval(context);
|
||||
if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptBatteryLife(context)) {
|
||||
@@ -219,7 +213,6 @@ public class TrackRecordingService extends Service {
|
||||
handler = new Handler();
|
||||
locationManagerConnector = new LocationManagerConnector(this, handler.getLooper());
|
||||
voiceExecutor = new PeriodicTaskExecutor(this, new AnnouncementPeriodicTaskFactory());
|
||||
splitExecutor = new PeriodicTaskExecutor(this, new SplitPeriodicTaskFactory());
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
||||
@@ -272,12 +265,6 @@ public class TrackRecordingService extends Service {
|
||||
// unregister sharedPreferences before shutting down splitExecutor and voiceExecutor
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
try {
|
||||
splitExecutor.shutdown();
|
||||
} finally {
|
||||
splitExecutor = null;
|
||||
}
|
||||
|
||||
try {
|
||||
voiceExecutor.shutdown();
|
||||
} finally {
|
||||
@@ -566,7 +553,6 @@ public class TrackRecordingService extends Service {
|
||||
|
||||
// Restore periodic tasks
|
||||
voiceExecutor.restore();
|
||||
splitExecutor.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -653,7 +639,6 @@ public class TrackRecordingService extends Service {
|
||||
private void endRecording(boolean trackStopped, long trackId) {
|
||||
// Shutdown periodic tasks
|
||||
voiceExecutor.shutdown();
|
||||
splitExecutor.shutdown();
|
||||
|
||||
// Update instance variables
|
||||
if (remoteSensorManager != null) {
|
||||
@@ -840,7 +825,6 @@ public class TrackRecordingService extends Service {
|
||||
Log.w(TAG, "SQLiteException", e);
|
||||
}
|
||||
voiceExecutor.update();
|
||||
splitExecutor.update();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-5
@@ -86,11 +86,11 @@ public class BluetoothRemoteSensorManager {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (bluetoothConnectionManager != null && PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_heart_rate_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(context);
|
||||
if (address.equals(PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT)) {
|
||||
if (PreferencesUtils.isBluetoothHeartRateSensorAddressDefault(context)) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(context);
|
||||
if (bluetoothConnectionManager.isSameBluetoothDevice(address)) {
|
||||
return;
|
||||
}
|
||||
@@ -145,12 +145,11 @@ public class BluetoothRemoteSensorManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// String address = PreferencesUtils.getString(context, R.string.settings_sensor_bluetooth_heart_rate_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(context);
|
||||
if (PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(address)) {
|
||||
if (PreferencesUtils.isBluetoothHeartRateSensorAddressDefault(context)) {
|
||||
Log.w(TAG, "No bluetooth address.");
|
||||
return;
|
||||
}
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(context);
|
||||
Log.i(TAG, "Connecting to bluetooth address: " + address);
|
||||
|
||||
BluetoothDevice device;
|
||||
|
||||
+50
-92
@@ -21,12 +21,8 @@ import android.media.AudioManager;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||
import android.speech.tts.UtteranceProgressListener;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
@@ -34,7 +30,6 @@ import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TTSUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
/**
|
||||
@@ -47,17 +42,49 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
/**
|
||||
* The rate at which announcements are spoken.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final float TTS_SPEECH_RATE = 0.9f;
|
||||
private static final float TTS_SPEECH_RATE = 0.9f;
|
||||
|
||||
private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
|
||||
private final AudioManager audioManager;
|
||||
|
||||
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
|
||||
@Override
|
||||
public void onAudioFocusChange(int focusChange) {
|
||||
Log.d(TAG, "Audio focus changed to " + focusChange);
|
||||
|
||||
boolean stop = false;
|
||||
switch (focusChange) {
|
||||
case AudioManager.AUDIOFOCUS_GAIN:
|
||||
stop = false;
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS:
|
||||
stop = true;
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
||||
stop = true;
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stop && tts != null && tts.isSpeaking()) {
|
||||
tts.stop();
|
||||
Log.i(TAG, "Aborting current tts due to focus change " + focusChange);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() {
|
||||
@Override
|
||||
public void onStart(String utteranceId) {
|
||||
int result = audioManager.requestAudioFocus(audioFocusChangeListener, TextToSpeech.Engine.DEFAULT_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.w(TAG, "Failed to request audio focus.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,32 +97,16 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
|
||||
@Override
|
||||
public void onError(String utteranceId) {
|
||||
Log.e(TAG, "An error occurred for utteranceId " + utteranceId);
|
||||
}
|
||||
};
|
||||
|
||||
private TextToSpeech tts;
|
||||
// Response from TTS after its initialization
|
||||
private int initStatus = TextToSpeech.ERROR;
|
||||
private int ttsInitStatus = TextToSpeech.ERROR;
|
||||
|
||||
private boolean ttsReady = false;
|
||||
|
||||
// True if speech is allowed
|
||||
private boolean speechAllowed;
|
||||
|
||||
/**
|
||||
* Listener which updates {@link #speechAllowed} when the phone state changes.
|
||||
*/
|
||||
private final PhoneStateListener phoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onCallStateChanged(int state, String incomingNumber) {
|
||||
speechAllowed = state == TelephonyManager.CALL_STATE_IDLE;
|
||||
if (!speechAllowed && tts != null && tts.isSpeaking()) {
|
||||
// If we're already speaking, stop it.
|
||||
tts.stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AnnouncementPeriodicTask(Context context) {
|
||||
this.context = context;
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
@@ -103,16 +114,17 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
Log.d(TAG, "Start");
|
||||
|
||||
if (tts == null) {
|
||||
tts = newTextToSpeech(context, new OnInitListener() {
|
||||
tts = new TextToSpeech(context, new OnInitListener() {
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
initStatus = status;
|
||||
Log.i(TAG, "TextToSpeech initialized with status " + status);
|
||||
ttsInitStatus = status;
|
||||
}
|
||||
});
|
||||
}
|
||||
speechAllowed = true;
|
||||
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,8 +141,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void announce(TripStatistics tripStatistics) {
|
||||
private void announce(TripStatistics tripStatistics) {
|
||||
if (tripStatistics == null) {
|
||||
Log.e(TAG, "TripStatistics is null.");
|
||||
return;
|
||||
@@ -138,7 +149,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
|
||||
synchronized (this) {
|
||||
if (!ttsReady) {
|
||||
ttsReady = initStatus == TextToSpeech.SUCCESS;
|
||||
ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
|
||||
if (ttsReady) {
|
||||
onTtsReady();
|
||||
}
|
||||
@@ -149,7 +160,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
}
|
||||
}
|
||||
|
||||
if (!speechAllowed) {
|
||||
if (audioManager.getMode() == AudioManager.MODE_IN_CALL || audioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
|
||||
Log.i(TAG, "Speech is not allowed at this time.");
|
||||
return;
|
||||
}
|
||||
@@ -158,21 +169,18 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_NONE);
|
||||
if (tts != null) {
|
||||
tts.shutdown();
|
||||
tts = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when TTS is ready.
|
||||
*/
|
||||
private void onTtsReady() {
|
||||
Locale locale = Locale.getDefault();
|
||||
int languageAvailability = tts.isLanguageAvailable(locale);
|
||||
if (languageAvailability == TextToSpeech.LANG_MISSING_DATA
|
||||
|| languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||
if (languageAvailability == TextToSpeech.LANG_MISSING_DATA || languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||
Log.w(TAG, "Default locale not available, use English.");
|
||||
locale = Locale.ENGLISH;
|
||||
/*
|
||||
@@ -188,42 +196,12 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
tts.setOnUtteranceProgressListener(utteranceListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaks the announcement.
|
||||
*
|
||||
* @param announcement the announcement
|
||||
*/
|
||||
private void speakAnnouncement(String announcement) {
|
||||
int result = audioManager.requestAudioFocus(null, TTSUtils.getTTSStream(), AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.w(TAG, "Failed to request audio focus.");
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't care about the utterance id. It is supplied here to force
|
||||
* onUtteranceCompleted to be called.
|
||||
*/
|
||||
// We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called.
|
||||
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TextToSpeech}.
|
||||
*
|
||||
* @param aContext a context
|
||||
* @param onInitListener an on init listener
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected TextToSpeech newTextToSpeech(Context aContext, OnInitListener onInitListener) {
|
||||
return new TextToSpeech(aContext, onInitListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the announcement.
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected String getAnnouncement(TripStatistics tripStatistics) {
|
||||
private String getAnnouncement(TripStatistics tripStatistics) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(context);
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
@@ -256,27 +234,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
getAnnounceTime(tripStatistics.getMovingTime()), rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to phone state.
|
||||
*
|
||||
* @param listener the listener
|
||||
* @param events the interested events
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void listenToPhoneState(PhoneStateListener listener, int events) {
|
||||
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (telephony != null) {
|
||||
telephony.listen(listener, events);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the announce time.
|
||||
*
|
||||
* @param time the time
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String getAnnounceTime(long time) {
|
||||
private String getAnnounceTime(long time) {
|
||||
int[] parts = StringUtils.getTimeParts(time);
|
||||
String seconds = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceSeconds, parts[0], parts[0]);
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 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 de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
|
||||
/**
|
||||
* A simple task to insert statistics markers periodically.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SplitPeriodicTask implements PeriodicTask {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TrackRecordingService trackRecordingService) {
|
||||
trackRecordingService.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* A {@link PeriodicTaskFactory} for {@link SplitPeriodicTask}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SplitPeriodicTaskFactory implements PeriodicTaskFactory {
|
||||
|
||||
@Override
|
||||
public PeriodicTask create(Context context) {
|
||||
return new SplitPeriodicTask();
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.BluetoothUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Preference to select a discoverable Bluetooth LE device.
|
||||
@@ -33,6 +34,8 @@ public class BluetoothLePreference extends DialogPreference {
|
||||
|
||||
private final static String TAG = BluetoothLePreference.class.getSimpleName();
|
||||
|
||||
private final static int DEVICE_NONE_RESOURCEID = R.string.value_none;
|
||||
|
||||
public BluetoothLePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
@@ -75,6 +78,10 @@ public class BluetoothLePreference extends DialogPreference {
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
if (getValue() == null || PreferencesUtils.isBluetoothHeartRateSensorAddressDefault(getContext(), getValue())) {
|
||||
return getContext().getString(DEVICE_NONE_RESOURCEID);
|
||||
}
|
||||
|
||||
return getValue();
|
||||
}
|
||||
|
||||
@@ -142,7 +149,7 @@ public class BluetoothLePreference extends DialogPreference {
|
||||
}
|
||||
|
||||
String deviceNone = getContext().getString(R.string.sensor_type_value_none);
|
||||
listAdapter.add(getContext().getString(R.string.value_none), deviceNone);
|
||||
listAdapter.add(getContext().getString(DEVICE_NONE_RESOURCEID), deviceNone);
|
||||
selectedEntryIndex = 0;
|
||||
|
||||
BluetoothLePreference preference = (BluetoothLePreference) getPreference();
|
||||
|
||||
@@ -167,9 +167,6 @@ public class SettingsActivity extends AppCompatActivity implements ChooseActivit
|
||||
ListPreference voiceFrequency = findPreference(getString(R.string.voice_frequency_key));
|
||||
voiceFrequency.setEntries(StringUtils.getFrequencyOptions(getActivity(), metricUnits));
|
||||
|
||||
ListPreference splitFrequency = findPreference(getString(R.string.split_frequency_key));
|
||||
splitFrequency.setEntries(StringUtils.getFrequencyOptions(getActivity(), metricUnits));
|
||||
|
||||
ListPreference minRecordingInterval = findPreference(getString(R.string.min_recording_interval_key));
|
||||
minRecordingInterval.setEntries(PreferenceHelper.getMinRecordingIntervalEntries(getActivity()));
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public class MarkerUtils {
|
||||
|
||||
public static final int ICON_ID = R.drawable.ic_marker_blue_pushpin;
|
||||
|
||||
private MarkerUtils() {
|
||||
}
|
||||
|
||||
public static Drawable getDefaultPhoto(@NonNull Context context) {
|
||||
return context.getResources().getDrawable(ICON_ID);
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,6 @@ import de.dennisguse.opentracks.R;
|
||||
*/
|
||||
public class PreferencesUtils {
|
||||
|
||||
public static final String BLUETOOTH_SENSOR_DEFAULT = "";
|
||||
|
||||
private PreferencesUtils() {
|
||||
}
|
||||
|
||||
@@ -251,12 +249,20 @@ public class PreferencesUtils {
|
||||
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, RECORDING_TRACK_PAUSED);
|
||||
}
|
||||
|
||||
public static String getBluetoothHeartRateSensorAddressNone(Context context) {
|
||||
return context.getString(R.string.sensor_type_value_none);
|
||||
}
|
||||
|
||||
public static boolean isBluetoothHeartRateSensorAddressDefault(Context context) {
|
||||
return PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(getBluetoothHeartRateSensorAddress(context));
|
||||
return getBluetoothHeartRateSensorAddressNone(context).equals(getBluetoothHeartRateSensorAddress(context));
|
||||
}
|
||||
|
||||
public static boolean isBluetoothHeartRateSensorAddressDefault(Context context, String currentValue) {
|
||||
return getBluetoothHeartRateSensorAddressNone(context).equals(currentValue);
|
||||
}
|
||||
|
||||
public static String getBluetoothHeartRateSensorAddress(Context context) {
|
||||
return PreferencesUtils.getString(context, R.string.settings_sensor_bluetooth_heart_rate_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
return PreferencesUtils.getString(context, R.string.settings_sensor_bluetooth_heart_rate_key, getBluetoothHeartRateSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static boolean shouldChartShowCadence(Context context) {
|
||||
@@ -289,6 +295,11 @@ public class PreferencesUtils {
|
||||
return getBoolean(context, R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldKeepScreenOn(Context context) {
|
||||
final boolean DEFAULT = context.getResources().getBoolean(R.bool.stats_keep_screen_on_while_recording_default);
|
||||
return getBoolean(context, R.string.stats_keep_screen_on_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean isShowStatsGradeElevation(Context context) {
|
||||
final boolean STATS_SHOW_GRADE_ELEVATION = context.getResources().getBoolean(R.bool.stats_show_grade_elevation_default);
|
||||
return PreferencesUtils.getBoolean(context, R.string.stats_show_grade_elevation_key, STATS_SHOW_GRADE_ELEVATION);
|
||||
@@ -304,11 +315,6 @@ public class PreferencesUtils {
|
||||
return PreferencesUtils.getInt(context, R.string.voice_frequency_key, VOICE_FREQUENCY_DEFAULT);
|
||||
}
|
||||
|
||||
public static int getSplitFrequency(Context context) {
|
||||
final int SPLIT_FREQUENCY_DEFAULT = Integer.parseInt(context.getResources().getString(R.string.split_frequency_default));
|
||||
return PreferencesUtils.getInt(context, R.string.split_frequency_key, SPLIT_FREQUENCY_DEFAULT);
|
||||
}
|
||||
|
||||
public static int getRecordingDistanceInterval(Context context) {
|
||||
final int RECORDING_DISTANCE_INTERVAL = Integer.parseInt(context.getResources().getString(R.string.recording_distance_interval_default));
|
||||
return PreferencesUtils.getInt(context, R.string.recording_distance_interval_key, getRecordingDistanceIntervalDefault(context));
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.location.Location;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
/**
|
||||
* Utilities for updating the statistics UI labels and values.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class StatsUtils {
|
||||
|
||||
private static final String GRADE_PERCENTAGE = "%";
|
||||
private static final String GRADE_FORMAT = "%1$d";
|
||||
|
||||
private StatsUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location values.
|
||||
*
|
||||
* @param activity the activity for finding views
|
||||
* @param location the location
|
||||
* @param isRecording true if recording
|
||||
*/
|
||||
public static void setLocationValues(Activity activity, Location location, boolean isRecording) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(activity);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(activity);
|
||||
|
||||
// Set speed/pace
|
||||
View speedContainer = activity.findViewById(R.id.stats_speed);
|
||||
speedContainer.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||
if (isRecording) {
|
||||
TextView speedLabel = activity.findViewById(R.id.stats_speed_label);
|
||||
speedLabel.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||
|
||||
double speed = location != null && location.hasSpeed() ? location.getSpeed() : Double.NaN;
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(activity, speed, metricUnits, reportSpeed);
|
||||
|
||||
TextView speedValue = activity.findViewById(R.id.stats_speed_value);
|
||||
speedValue.setText(parts.first);
|
||||
|
||||
TextView speedUnit = activity.findViewById(R.id.stats_speed_unit);
|
||||
speedUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set elevation
|
||||
boolean showGradeElevation = isRecording && PreferencesUtils.isShowStatsGradeElevation(activity);
|
||||
View elevationContainer = activity.findViewById(R.id.stats_elevation);
|
||||
elevationContainer.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showGradeElevation) {
|
||||
double altitude = location != null && location.hasAltitude() ? location.getAltitude() : Double.NaN;
|
||||
Pair<String, String> parts = StringUtils.formatElevation(activity, altitude, metricUnits);
|
||||
|
||||
TextView elevationValue = activity.findViewById(R.id.stats_elevation_current_value);
|
||||
elevationValue.setText(parts.first);
|
||||
TextView elevationUnit = activity.findViewById(R.id.stats_elevation_current_unit);
|
||||
elevationUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set coordinate
|
||||
boolean showCoordinate = isRecording && PreferencesUtils.isStatsShowCoordinate(activity);
|
||||
|
||||
View coordinateSeparator = activity.findViewById(R.id.stats_coordinate_separator);
|
||||
coordinateSeparator.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
|
||||
View coordinateContainer = activity.findViewById(R.id.stats_coordinate_container);
|
||||
coordinateContainer.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
|
||||
if (showCoordinate) {
|
||||
double latitude = location != null ? location.getLatitude() : Double.NaN;
|
||||
String latitudeText = Double.isNaN(latitude) || Double.isInfinite(latitude) ? activity.getString(R.string.value_unknown) : StringUtils.formatCoordinate(latitude);
|
||||
TextView latitudeValue = activity.findViewById(R.id.stats_latitude_value);
|
||||
latitudeValue.setText(latitudeText);
|
||||
|
||||
double longitude = location != null ? location.getLongitude() : Double.NaN;
|
||||
String longitudeText = Double.isNaN(longitude) || Double.isInfinite(longitude) ? activity.getString(R.string.value_unknown) : StringUtils.formatCoordinate(longitude);
|
||||
TextView longitudeValue = activity.findViewById(R.id.stats_longitude_value);
|
||||
longitudeValue.setText(longitudeText);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setSensorData(Activity activity, SensorDataSet sensorDataSet, boolean isRecording) {
|
||||
// heart rate
|
||||
int isVisible = View.VISIBLE;
|
||||
if (!isRecording || PreferencesUtils.isBluetoothHeartRateSensorAddressDefault(activity)) {
|
||||
isVisible = View.INVISIBLE;
|
||||
}
|
||||
activity.findViewById(R.id.stats_sensor_container).setVisibility(isVisible);
|
||||
|
||||
if (isRecording) {
|
||||
TextView heartRateValue = activity.findViewById(R.id.stats_sensor_heart_rate_value);
|
||||
TextView heartRateSensor = activity.findViewById(R.id.stats_sensor_heart_rate_sensor_value);
|
||||
String heartRate = activity.getString(R.string.value_unknown);
|
||||
String sensor = activity.getString(R.string.value_unknown);
|
||||
if (sensorDataSet != null && sensorDataSet.isRecent(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS)) {
|
||||
sensor = sensorDataSet.getSensorName();
|
||||
if (sensorDataSet.hasHeartRate()) {
|
||||
heartRate = StringUtils.formatDecimal(sensorDataSet.getHeartRate(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
setBluetoothHeartRateSensorName(activity, sensor, isRecording);
|
||||
heartRateSensor.setText(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setBluetoothHeartRateSensorName(Activity activity, String sensorName, boolean isRecording) {
|
||||
if (isRecording) {
|
||||
TextView heartRateSensor = activity.findViewById(R.id.stats_sensor_heart_rate_sensor_value);
|
||||
heartRateSensor.setText(sensorName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total time value.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param totalTime the total time
|
||||
*/
|
||||
public static void setTotalTimeValue(Activity activity, long totalTime) {
|
||||
TextView totalTimeValue = activity.findViewById(R.id.stats_total_time_value);
|
||||
totalTimeValue.setText(StringUtils.formatElapsedTime(totalTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trip statistics values.
|
||||
*
|
||||
* @param activity the activity for finding views
|
||||
* @param tripStatistics the trip statistics
|
||||
* @param trackIconValue the track icon value or null to hide the track icon spinner
|
||||
*/
|
||||
public static void setTripStatisticsValues(Activity activity, TripStatistics tripStatistics, String trackIconValue) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(activity);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(activity);
|
||||
boolean isRecording = PreferencesUtils.isRecording(activity);
|
||||
|
||||
// Set total distance
|
||||
{
|
||||
double totalDistance = tripStatistics == null ? Double.NaN : tripStatistics.getTotalDistance();
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(activity, totalDistance, metricUnits);
|
||||
|
||||
TextView distanceValue = activity.findViewById(R.id.stats_distance_value);
|
||||
distanceValue.setText(parts.first);
|
||||
|
||||
TextView distanceUnit = activity.findViewById(R.id.stats_distance_unit);
|
||||
distanceUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set activity type
|
||||
{
|
||||
activity.findViewById(R.id.stats_activity_type_label).setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
|
||||
|
||||
Spinner spinner = activity.findViewById(R.id.stats_activity_type_icon);
|
||||
spinner.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
|
||||
if (trackIconValue != null) {
|
||||
TrackIconUtils.setIconSpinner(spinner, trackIconValue);
|
||||
}
|
||||
spinner.setEnabled(isRecording);
|
||||
}
|
||||
|
||||
// Set time
|
||||
if (tripStatistics != null) {
|
||||
setTotalTimeValue(activity, tripStatistics.getTotalTime());
|
||||
|
||||
TextView movingTimeValue = activity.findViewById(R.id.stats_moving_time_value);
|
||||
movingTimeValue.setText(StringUtils.formatElapsedTime(tripStatistics.getMovingTime()));
|
||||
}
|
||||
|
||||
// Set average speed/pace
|
||||
{
|
||||
double speed = tripStatistics != null ? tripStatistics.getAverageSpeed() : Double.NaN;
|
||||
TextView speedLabel = activity.findViewById(R.id.stats_average_speed_label);
|
||||
speedLabel.setText(reportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(activity, speed, metricUnits, reportSpeed);
|
||||
TextView speedValue = activity.findViewById(R.id.stats_average_speed_value);
|
||||
speedValue.setText(parts.first);
|
||||
TextView speedUnit = activity.findViewById(R.id.stats_average_speed_unit);
|
||||
speedUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set max speed/pace
|
||||
{
|
||||
double speed = tripStatistics == null ? Double.NaN : tripStatistics.getMaxSpeed();
|
||||
|
||||
TextView speedLabel = activity.findViewById(R.id.stats_max_speed_label);
|
||||
speedLabel.setText(reportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(activity, speed, metricUnits, reportSpeed);
|
||||
TextView speedValue = activity.findViewById(R.id.stats_max_speed_value);
|
||||
speedValue.setText(parts.first);
|
||||
TextView speedUnit = activity.findViewById(R.id.stats_max_speed_unit);
|
||||
speedUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set average speed/pace
|
||||
{
|
||||
double speed = tripStatistics != null ? tripStatistics.getAverageSpeed() : Double.NaN;
|
||||
|
||||
TextView speedLabel = activity.findViewById(R.id.stats_average_speed_label);
|
||||
speedLabel.setText(reportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(activity, speed, metricUnits, reportSpeed);
|
||||
TextView speedValue = activity.findViewById(R.id.stats_average_speed_value);
|
||||
speedValue.setText(parts.first);
|
||||
TextView speedUnit = activity.findViewById(R.id.stats_average_speed_unit);
|
||||
speedUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
// Set moving speed/pace
|
||||
{
|
||||
double speed = tripStatistics != null ? tripStatistics.getAverageMovingSpeed() : Double.NaN;
|
||||
|
||||
TextView speedLabel = activity.findViewById(R.id.stats_moving_speed_label);
|
||||
speedLabel.setText(reportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(activity, speed, metricUnits, reportSpeed);
|
||||
TextView speedValue = activity.findViewById(R.id.stats_moving_speed_value);
|
||||
speedValue.setText(parts.first);
|
||||
TextView speedUnit = activity.findViewById(R.id.stats_moving_speed_unit);
|
||||
speedUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
|
||||
// Set elevation
|
||||
{
|
||||
boolean showElevation = PreferencesUtils.isShowStatsGradeElevation(activity);
|
||||
View gradeElevationSeparator = activity.findViewById(R.id.stats_elevation_separator);
|
||||
gradeElevationSeparator.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
View gradeElevationContainer = activity.findViewById(R.id.stats_elevation_container);
|
||||
gradeElevationContainer.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showElevation) {
|
||||
{
|
||||
double elevation = tripStatistics == null ? Double.NaN : tripStatistics.getMinElevation();
|
||||
Pair<String, String> parts = StringUtils.formatElevation(activity, elevation, metricUnits);
|
||||
|
||||
TextView elevationValue = activity.findViewById(R.id.stats_elevation_min_value);
|
||||
elevationValue.setText(parts.first);
|
||||
TextView elevationUnit = activity.findViewById(R.id.stats_elevation_min_unit);
|
||||
elevationUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
{
|
||||
double elevation = tripStatistics == null ? Double.NaN : tripStatistics.getMaxElevation();
|
||||
Pair<String, String> parts = StringUtils.formatElevation(activity, elevation, metricUnits);
|
||||
|
||||
TextView elevationValue = activity.findViewById(R.id.stats_elevation_max_value);
|
||||
elevationValue.setText(parts.first);
|
||||
TextView elevationUnit = activity.findViewById(R.id.stats_elevation_max_unit);
|
||||
elevationUnit.setText(parts.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ public class StringUtils {
|
||||
/**
|
||||
* Format a decimal number while removing trailing zeros of the decimal part (if present).
|
||||
*/
|
||||
static String formatDecimal(double value, int decimalPlaces) {
|
||||
public static String formatDecimal(double value, int decimalPlaces) {
|
||||
if (decimalPlaces < 1) {
|
||||
return Long.toString(Math.round(value));
|
||||
}
|
||||
@@ -158,7 +158,7 @@ public class StringUtils {
|
||||
* @param metricUnits true to use metric unit
|
||||
* @return the formatted distance (or null) and it's unit as {@link Pair}
|
||||
*/
|
||||
static Pair<String, String> getDistanceParts(Context context, double distance, boolean metricUnits) {
|
||||
public static Pair<String, String> getDistanceParts(Context context, double distance, boolean metricUnits) {
|
||||
if (Double.isNaN(distance) || Double.isInfinite(distance)) {
|
||||
return new Pair<>(null, context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet));
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
@@ -18,7 +18,6 @@ limitations under the License.
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/marker_detail_waypoint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@@ -69,33 +68,4 @@ limitations under the License.
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/marker_detail_statistics"
|
||||
style="@style/StatsScrollView">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marker_detail_statistics_name"
|
||||
style="@style/TextMedium"
|
||||
android:paddingTop="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/marker_detail_statistics_location"
|
||||
style="@style/TextSmall" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextSmall"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:text="@string/marker_detail_statistics_title" />
|
||||
|
||||
<include layout="@layout/stats_content" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
@@ -41,7 +41,7 @@ limitations under the License.
|
||||
<EditText
|
||||
android:id="@+id/marker_edit_statistics_name"
|
||||
style="@style/EditNameMarker"
|
||||
android:drawableLeft="@drawable/ic_marker_yellow_pushpin"
|
||||
android:drawableLeft="@drawable/ic_marker_blue_pushpin"
|
||||
android:hint="@string/generic_name"
|
||||
android:imeOptions="actionDone"
|
||||
android:importantForAutofill="no" />
|
||||
|
||||
@@ -363,7 +363,6 @@ limitations under the License.
|
||||
<LinearLayout style="@style/StatsLargeItemContainer">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_sensor_heart_rate_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/sensor_state_heart_rate" />
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
<string name="stats_show_on_lockscreen_while_recording_key" translatable="false">trackdetail_show_on_lockscreen_while_recording</string>
|
||||
<bool name="stats_show_on_lockscreen_while_recording_default" translatable="false">false</bool>
|
||||
|
||||
<string name="stats_keep_screen_on_while_recording_key" translatable="false">trackdetail_keep_screen_on_while_recording</string>
|
||||
<bool name="stats_keep_screen_on_while_recording_default" translatable="false">false</bool>
|
||||
|
||||
<string name="settings_reset_key" translatable="false">settingsReset</string>
|
||||
|
||||
<string name="settings_sensor_bluetooth_heart_rate_key" translatable="false">bluetoothSensor</string>
|
||||
@@ -114,28 +117,6 @@
|
||||
<item>@string/auto_resume_track_timeout_always</item>
|
||||
</string-array>
|
||||
|
||||
<string name="split_frequency_key" translatable="false">splitFrequency</string>
|
||||
<string name="split_frequency_default" translatable="false">@string/frequency_off</string>
|
||||
<string name="voice_frequency_key" translatable="false">voiceFrequency</string>
|
||||
<string name="voice_frequency_default" translatable="false">@string/frequency_off</string>
|
||||
<string name="frequency_off" translatable="false">0</string>
|
||||
<string-array name="frequency_values">
|
||||
<item>@string/frequency_off</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>5</item>
|
||||
<item>10</item>
|
||||
<item>15</item>
|
||||
<item>30</item>
|
||||
<item>60</item>
|
||||
<item>-1</item>
|
||||
<item>-5</item>
|
||||
<item>-10</item>
|
||||
<item>-25</item>
|
||||
<item>-50</item>
|
||||
<item>-100</item>
|
||||
</string-array>
|
||||
|
||||
<string name="recording_distance_interval_key" translatable="false">recordingDistanceInterval</string>
|
||||
<string name="recording_distance_interval_default" translatable="false">10</string>
|
||||
<string-array name="recording_distance_interval_values">
|
||||
|
||||
@@ -15,4 +15,26 @@
|
||||
<item>@string/settings_chart_by_distance</item>
|
||||
<item>@string/settings_chart_by_time</item>
|
||||
</string-array>
|
||||
|
||||
<string name="split_frequency_key" translatable="false">splitFrequency</string>
|
||||
<string name="split_frequency_default" translatable="false">@string/frequency_off</string>
|
||||
<string name="voice_frequency_key" translatable="false">voiceFrequency</string>
|
||||
<string name="voice_frequency_default" translatable="false">@string/frequency_off</string>
|
||||
<string name="frequency_off" translatable="false">0</string>
|
||||
<string-array name="frequency_values">
|
||||
<item>@string/frequency_off</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>5</item>
|
||||
<item>10</item>
|
||||
<item>15</item>
|
||||
<item>30</item>
|
||||
<item>60</item>
|
||||
<item>-1</item>
|
||||
<item>-5</item>
|
||||
<item>-10</item>
|
||||
<item>-25</item>
|
||||
<item>-50</item>
|
||||
<item>-100</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -1194,6 +1194,13 @@ limitations under the License.
|
||||
While recording, show statistics without unlocking the device.
|
||||
</string>
|
||||
|
||||
<string name="settings_recording_keep_screen_on_while_recording_title" translation_description="In the 'Settings' page, the option to change the 'show on lockscreen' behavior while recording.">
|
||||
Keep screen on
|
||||
</string>
|
||||
<string name="settings_recording_keep_screen_on_while_recording_summary" translation_description="In the 'Settings' page, the option to change the 'show on lockscreen' behavior while recording.">
|
||||
While recording, keep the screen on.
|
||||
</string>
|
||||
|
||||
<string name="settings_recording" translation_description="In the 'Settings' page, the option to change the recording settings.
|
||||
|
||||
Character limit: ~26 (1 line)">
|
||||
|
||||
@@ -74,18 +74,17 @@ limitations under the License.
|
||||
android:key="@string/stats_show_on_lockscreen_while_recording_key"
|
||||
android:summary="@string/settings_recording_show_on_lockscreen_while_recording_summary"
|
||||
android:title="@string/settings_recording_show_on_lockscreen_while_recording_title" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="@bool/stats_keep_screen_on_while_recording_default"
|
||||
android:key="@string/stats_keep_screen_on_while_recording_key"
|
||||
android:summary="@string/settings_recording_keep_screen_on_while_recording_summary"
|
||||
android:title="@string/settings_recording_keep_screen_on_while_recording_title" />
|
||||
<ListPreference
|
||||
android:defaultValue="@string/voice_frequency_default"
|
||||
android:entryValues="@array/frequency_values"
|
||||
android:key="@string/voice_frequency_key"
|
||||
android:title="@string/menu_voice_frequency"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<ListPreference
|
||||
android:defaultValue="@string/split_frequency_default"
|
||||
android:entryValues="@array/frequency_values"
|
||||
android:key="@string/split_frequency_key"
|
||||
android:title="@string/menu_split_frequency"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<ListPreference
|
||||
android:defaultValue="@string/track_name_default"
|
||||
android:entries="@array/track_name_options"
|
||||
|
||||
Reference in New Issue
Block a user