Compare commits

..

10 Commits

Author SHA1 Message Date
Dennis Guse d1f3791f97 Release v3.7.4 2020-05-27 08:21:37 +02:00
Román 490b95a001 Merge branch 'split_trackdetail#171' 2020-05-25 13:28:36 +02:00
Román 12e6f03098 Recorded/Recording nested layouts converted to constraint layout. Fixes #15. 2020-05-25 13:06:36 +02:00
Román d86f42e2e9 Animation between recording and recorded activities. Update PlantUML with new changes. 2020-05-25 13:06:17 +02:00
Román 360d59d3de Split detail and record to simplify the code. Fixes #171. 2020-05-25 13:06:17 +02:00
Dennis Guse b617420290 Export: do not show progress of exported TrackPoints.
Only the overall number of tracks is shown now (similar to import).
2020-05-24 10:58:43 +02:00
Dennis Guse 53b267150b Export/Import: TrackFileFormat is now selected via settings only. 2020-05-24 10:58:43 +02:00
Dennis Guse 0dfd55ed52 Settings: added default sharing track file format.
Fixes #225.
2020-05-24 10:58:43 +02:00
Román daabd5381d Links in help and about are clickable. Also added scroll in about activity. Fixes #232. 2020-05-22 16:01:33 +02:00
Dennis Guse a842129e37 Bugfix: wheel circumference was not stored in correct preference key. 2020-05-13 21:46:48 +02:00
42 changed files with 1789 additions and 918 deletions
+4 -3
View File
@@ -59,9 +59,9 @@ android {
applicationName: "@string/app_name"
]
versionCode 3386
versionCode 3396
//versionCode getVersionCode() //Useful for debugging.
versionName "v3.7.3"
versionName "v3.7.4"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
@@ -100,7 +100,7 @@ android {
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.documentfile:documentfile:1.0.1'
androidTestImplementation 'androidx.test:core:1.2.0'
@@ -114,4 +114,5 @@ dependencies {
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'org.mockito:mockito-core:3.0.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
}
+23 -12
View File
@@ -9,23 +9,34 @@ from every activity showing a menu.
endlegend
[*] --> TrackListActivity
TrackListActivity --> TrackDetailActivity : Start recording
TrackListActivity --> TrackRecordingActivity : Start recording
TrackListActivity --> SearchListActivity
TrackListActivity --> MarkerListActivity : Show all markers
TrackListActivity --> TrackDetailActivity : Show track
TrackListActivity --> TrackRecordedActivity : Show track
TrackListActivity --> TrackDeleteActivity
SearchListActivity --> MarkerDetailActivity : Search result
SearchListActivity --> TrackDetailActivity : Search result
SearchListActivity --> TrackRecordedActivity : Search result
TrackDetailActivity --> TrackDeleteActivity
TrackDetailActivity --> TrackEditActivity
TrackDetailActivity --> MarkerListActivity
TrackDetailActivity --> MarkerEditActivity : Add marker
TrackDetailActivity --> CameraIntent : Add marker (with photo)
TrackDetailActivity --> ShareFileIntent
TrackDetailActivity --> DashboardIntent : Database cursor
TrackDetailActivity --> TrackOnMapIntent : KMZ
TrackRecordedActivity --> TrackDeleteActivity
TrackRecordedActivity --> TrackEditActivity
TrackRecordedActivity --> MarkerListActivity
TrackRecordedActivity --> MarkerEditActivity : Add marker
TrackRecordedActivity --> CameraIntent : Add marker (with photo)
TrackRecordedActivity --> ShareFileIntent
TrackRecordedActivity --> DashboardIntent : Database cursor
TrackRecordedActivity --> TrackOnMapIntent : KMZ
TrackRecordedActivity --> TrackRecordingActivity
TrackRecordedActivity --> SettingsActivity
TrackRecordedActivity --> TrackListActivity : Back button
TrackRecordingActivity --> TrackOnMapIntent
TrackRecordingActivity --> MarkerEditActivity : Add marker
TrackRecordingActivity --> MarkerListActivity
TrackRecordingActivity --> TrackEditActivity
TrackRecordingActivity --> SettingsActivity
TrackRecordingActivity --> TrackRecordedActivity : Stop recording
TrackRecordingActivity --> TrackListActivity : Back button
MarkerListActivity --> MarkerDetailActivity
@@ -37,4 +48,4 @@ SettingsActivity --> HelpActivity
SettingsActivity --> ExportActivity
SettingsActivity --> ImportActivity
@enduml
@enduml
@@ -0,0 +1,12 @@
v3.7.4: OpenTracks
Changes:
- Sharing/export format configurable
Bugfix:
- Wheel diameter not stored correctly (for speed sensor)
- Help: links are clickable
Developer:
- Split TrackDetailActivity
- StatsFragement successor uses ConstraintLayout
@@ -97,7 +97,7 @@ public class ExportImportTest {
Track track = contentProviderUtils.getTrack(trackId);
TrackFileFormat trackFileFormat = TrackFileFormat.KML_WITH_TRACKDETAIL;
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track}, null);
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
// when
// 1. export
@@ -169,7 +169,7 @@ public class ExportImportTest {
Track track = contentProviderUtils.getTrack(trackId);
TrackFileFormat trackFileFormat = TrackFileFormat.GPX;
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track}, null);
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
// when
// 1. export
@@ -0,0 +1,62 @@
package de.dennisguse.opentracks.util;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
@RunWith(AndroidJUnit4.class)
public class PreferencesUtilsTest {
private final Context context = ApplicationProvider.getApplicationContext();
@Test
public void ExportTrackFileFormat_ok() {
// given
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
editor.putString(context.getString(R.string.export_trackfileformat_key), TrackFileFormat.KMZ_WITH_TRACKDETAIL.name());
editor.commit();
// when
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
}
@Test
public void ExportTrackFileFormat_invalid() {
// given
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
editor.putString(context.getString(R.string.export_trackfileformat_key), "invalid");
editor.commit();
// when
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
}
@Test
public void ExportTrackFileFormat_noValue() {
// given
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
editor.clear();
editor.commit();
// when
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
}
}
+5 -1
View File
@@ -79,7 +79,11 @@ limitations under the License.
</activity>
<activity
android:name=".TrackDetailActivity"
android:name=".TrackRecordedActivity"
android:screenOrientation="userPortrait" />
<activity
android:name=".TrackRecordingActivity"
android:screenOrientation="userPortrait" />
<activity android:name=".TrackEditActivity" />
@@ -2,11 +2,13 @@ package de.dennisguse.opentracks;
import android.os.Bundle;
import android.view.Menu;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
import de.dennisguse.opentracks.util.SystemUtils;
import de.dennisguse.opentracks.util.ViewUtils;
public class AboutActivity extends AbstractActivity {
@@ -27,6 +29,8 @@ public class AboutActivity extends AbstractActivity {
TextView textURL = findViewById(R.id.about_app_url);
textURL.setText(getString(R.string.about_url, getString(R.string.app_web_url)));
ViewUtils.makeClickableLinks((ViewGroup) findViewById(android.R.id.content));
}
protected int getLayoutResId() {
@@ -26,7 +26,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An abstract class for the following common tasks across
* {@link TrackListActivity}, {@link TrackDetailActivity}, and
* {@link TrackListActivity}, {@link TrackRecordedActivity}, and
* {@link SearchListActivity}:
* <p>
* - share track <br>
@@ -1,7 +1,20 @@
package de.dennisguse.opentracks;
import android.os.Bundle;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import de.dennisguse.opentracks.util.ViewUtils;
public class HelpActivity extends AbstractActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.makeClickableLinks((ViewGroup) findViewById(android.R.id.content));
}
@Override
protected int getLayoutResId() {
return R.layout.help;
@@ -214,11 +214,11 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
Map<String, Object> item = arrayAdapter.getItem(position);
Long trackId = (Long) item.get(TRACK_ID_FIELD);
Long markerId = (Long) item.get(MARKER_ID_FIELD);
Intent intent = IntentUtils.newIntent(SearchListActivity.this, TrackDetailActivity.class);
Intent intent = IntentUtils.newIntent(SearchListActivity.this, TrackRecordedActivity.class);
if (markerId != null) {
intent = intent.putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerId);
intent = intent.putExtra(TrackRecordedActivity.EXTRA_MARKER_ID, markerId);
} else {
intent = intent.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId);
intent = intent.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
}
startActivity(intent);
}
@@ -0,0 +1,10 @@
package de.dennisguse.opentracks;
import de.dennisguse.opentracks.content.TrackDataHub;
/**
* Interface for communication between activities that use {@link de.dennisguse.opentracks.content.TrackDataHub} and their fragments that need thi data hub.
*/
public interface TrackActivityDataHubInterface {
TrackDataHub getTrackDataHub();
}
@@ -205,8 +205,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
if (!PreferencesUtils.isRecording(recordingTrackId)) {
// Not recording -> Recording
updateMenuItems(false, true);
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackDetailActivity.class)
.putExtra(TrackDetailActivity.EXTRA_RECORDING_REQUEST, true);
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
startActivity(newIntent);
} else if (recordingTrackPaused) {
// Paused -> Resume
@@ -251,9 +250,17 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
listView.setEmptyView(findViewById(R.id.track_list_empty_view));
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackDetailActivity.class)
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, id);
public void onItemClick(AdapterView<?> parent, View view, int position, long trackId) {
Intent newIntent;
if (trackId == recordingTrackId) {
// Is recording -> open record activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
} else {
// Not recording -> open detail activity.
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
}
startActivity(newIntent);
}
});
@@ -0,0 +1,276 @@
/*
* Copyright 2008 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;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.ChartFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.util.TrackUtils;
/**
* An activity to show the track detail, record a new track or resumes an existing one.
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
public class TrackRecordedActivity extends AbstractListActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface {
public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_MARKER_ID = "marker_id";
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
// The following are set in onCreate.
private ContentProviderUtils contentProviderUtils;
private TrackDataHub trackDataHub;
private ViewPager pager;
// From intent.
private long trackId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
contentProviderUtils = new ContentProviderUtils(this);
handleIntent(getIntent());
trackDataHub = new TrackDataHub(this);
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager(), 1) {
@Override
public int getCount() {
return 3;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StatisticsRecordedFragment();
case 1:
return ChartFragment.newInstance(false);
case 2:
return ChartFragment.newInstance(true);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.track_detail_stats_tab);
case 1:
return getString(R.string.settings_chart_by_time);
case 2:
return getString(R.string.settings_chart_by_distance);
}
return "Unknown Tab";
}
};
pager = findViewById(R.id.track_detail_activity_view_pager);
pager.setAdapter(adapter);
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
tabs.setupWithViewPager(pager);
if (savedInstanceState != null) {
pager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
}
}
@Override
protected void onStart() {
super.onStart();
trackDataHub.start();
}
@Override
protected void onResume() {
super.onResume();
// Update UI
this.invalidateOptionsMenu();
if (trackId != -1L) {
trackDataHub.loadTrack(trackId);
}
}
@Override
protected void onStop() {
super.onStop();
trackDataHub.stop();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
}
@Override
protected int getLayoutResId() {
return R.layout.track_detail;
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleIntent(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_detail, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.track_detail_markers).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.findItem(R.id.track_detail_resume_track).setVisible(!PreferencesUtils.isRecording(this));
Track track = contentProviderUtils.getTrack(trackId);
setTitle(track != null ? track.getName() : "");
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent intent;
switch (item.getItemId()) {
case R.id.track_detail_share:
intent = IntentUtils.newShareFileIntent(this, new long[]{trackId});
intent = Intent.createChooser(intent, null);
startActivity(intent);
return true;
case R.id.track_detail_menu_show_on_map:
IntentUtils.showTrackOnMap(this, new long[]{trackId});
return true;
case R.id.track_detail_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
.putExtra(MarkerListActivity.EXTRA_TRACK_ID, trackId);
startActivity(intent);
return true;
case R.id.track_detail_edit:
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
startActivity(intent);
return true;
case R.id.track_detail_delete:
deleteTracks(new long[]{trackId});
return true;
case R.id.track_detail_resume_track:
Intent newIntent = IntentUtils.newIntent(TrackRecordedActivity.this, TrackRecordingActivity.class)
.putExtra(TrackRecordingActivity.EXTRA_TRACK_ID, trackId);
startActivity(newIntent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
finish();
return true;
case R.id.track_detail_settings:
intent = IntentUtils.newIntent(this, SettingsActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
// Not needed.
return null;
}
@Override
protected void onDeleted() {
runOnUiThread(new Runnable() {
@Override
public void run() {
finish();
}
});
}
/**
* Gets the {@link TrackDataHub}.
*/
@Override
public TrackDataHub getTrackDataHub() {
return trackDataHub;
}
private void handleIntent(Intent intent) {
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
long markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
if (markerId != -1L) {
// Use the trackId from the marker
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
if (waypoint == null) {
finish();
return;
}
trackId = waypoint.getTrackId();
}
if (trackId == -1L) {
Log.e(TAG, "TrackDetailActivity needs EXTRA_TRACK_ID.");
finish();
return;
}
}
public void chooseActivityType(String category) {
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), category);
}
@Override
public void onChooseActivityTypeDone(String iconValue) {
Track track = contentProviderUtils.getTrack(trackId);
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
}
}
@@ -1,21 +1,6 @@
/*
* Copyright 2008 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;
import android.app.ActivityOptions;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -38,12 +23,10 @@ import com.google.android.material.tabs.TabLayout;
import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.ChartFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.StatsFragment;
import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.settings.SettingsActivity;
@@ -58,13 +41,11 @@ import de.dennisguse.opentracks.util.TrackUtils;
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
public class TrackDetailActivity extends AbstractListActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface {
public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_MARKER_ID = "marker_id";
public static final String EXTRA_RECORDING_REQUEST = "request_recording";
private static final String TAG = TrackDetailActivity.class.getSimpleName();
private static final String TAG = TrackRecordingActivity.class.getSimpleName();
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
@@ -79,8 +60,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
// From intent
private long trackId;
private boolean isRecordingRequest = false;
// Preferences
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused;
@@ -92,7 +71,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
runOnUiThread(new Runnable() {
@Override
public void run() {
trackController.update(isRecording(), recordingTrackPaused);
trackController.update(true, recordingTrackPaused);
}
});
@@ -102,9 +81,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
Log.d(TAG, "could not get TrackRecordingService");
return;
}
if (!isRecordingRequest) {
return;
}
// Starts or resumes a track.
int msg;
@@ -121,12 +97,10 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
}
// A recording track is on.
Toast.makeText(TrackDetailActivity.this, msg, Toast.LENGTH_SHORT).show();
Toast.makeText(TrackRecordingActivity.this, msg, Toast.LENGTH_SHORT).show();
trackDataHub.loadTrack(trackId);
trackController.update(true, false);
trackController.onResume(true, recordingTrackPaused);
isRecordingRequest = false;
}
}
};
@@ -134,23 +108,23 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackDetailActivity.this);
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackRecordingActivity.this);
setLockscreenPolicy();
setScreenOnPolicy();
}
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackDetailActivity.this);
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackRecordingActivity.this);
setLockscreenPolicy();
setScreenOnPolicy();
}
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.stats_show_on_lockscreen_while_recording_key, key)) {
if (PreferencesUtils.isKey(TrackRecordingActivity.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)) {
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
setScreenOnPolicy();
}
@@ -159,9 +133,8 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
runOnUiThread(new Runnable() {
@Override
public void run() {
TrackDetailActivity.this.invalidateOptionsMenu();
boolean isRecording = isRecording();
trackController.update(isRecording, recordingTrackPaused);
TrackRecordingActivity.this.invalidateOptionsMenu();
trackController.update(true, recordingTrackPaused);
}
});
}
@@ -169,8 +142,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
private MenuItem insertMarkerMenuItem;
private MenuItem markerListMenuItem;
private MenuItem shareMenuItem;
private MenuItem resumeMenuItem;
private final OnClickListener recordListener = new OnClickListener() {
@Override
@@ -192,8 +163,13 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
private final OnClickListener stopListener = new OnClickListener() {
@Override
public void onClick(View v) {
trackRecordingServiceConnection.stopRecording(TrackDetailActivity.this, true);
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this, true);
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
startActivity(newIntent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
updateMenuItems(true);
finish();
}
};
@@ -220,12 +196,14 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StatisticsRecordingFragment();
case 1:
return ChartFragment.newInstance(false);
case 2:
return ChartFragment.newInstance(true);
default: //0
return new StatsFragment();
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@@ -261,7 +239,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
}
private void setLockscreenPolicy() {
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackDetailActivity.this)
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackRecordingActivity.this)
&& PreferencesUtils.isRecording(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
@@ -274,7 +252,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
}
private void setScreenOnPolicy() {
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackDetailActivity.this)
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackRecordingActivity.this)
&& PreferencesUtils.isRecording(this);
if (keepScreenOn) {
@@ -304,10 +282,10 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
if (trackId != -1L) {
trackDataHub.loadTrack(trackId);
trackController.onResume(isRecording(), recordingTrackPaused);
} else {
startRecording();
trackController.onResume(true, recordingTrackPaused);
}
startRecording();
}
@Override
@@ -332,7 +310,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
@Override
protected int getLayoutResId() {
return R.layout.track_detail;
return R.layout.track_record;
}
@Override
@@ -344,12 +322,10 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_detail, menu);
getMenuInflater().inflate(R.menu.track_record, menu);
insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker);
shareMenuItem = menu.findItem(R.id.track_detail_share);
markerListMenuItem = menu.findItem(R.id.track_detail_markers);
resumeMenuItem = menu.findItem(R.id.track_detail_resume_track);
return super.onCreateOptionsMenu(menu);
}
@@ -370,11 +346,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId);
startActivity(intent);
return true;
case R.id.track_detail_share:
intent = IntentUtils.newShareFileIntent(this, new long[]{trackId});
intent = Intent.createChooser(intent, null);
startActivity(intent);
return true;
case R.id.track_detail_menu_show_on_map:
IntentUtils.showTrackOnMap(this, new long[]{trackId});
return true;
@@ -388,13 +359,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
startActivity(intent);
return true;
case R.id.track_detail_delete:
deleteTracks(new long[]{trackId});
return true;
case R.id.track_detail_resume_track:
isRecordingRequest = true;
startRecording();
return true;
case R.id.track_detail_settings:
intent = IntentUtils.newIntent(this, SettingsActivity.class);
startActivity(intent);
@@ -404,58 +368,25 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
}
}
@Override
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
return trackRecordingServiceConnection;
}
@Override
protected void onDeleted() {
runOnUiThread(new Runnable() {
@Override
public void run() {
finish();
}
});
}
/**
* Gets the {@link TrackDataHub}.
*/
@Override
public TrackDataHub getTrackDataHub() {
return trackDataHub;
}
private void handleIntent(Intent intent) {
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
isRecordingRequest = intent.getBooleanExtra(EXTRA_RECORDING_REQUEST, false);
long markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
if (markerId != -1L) {
// Use the trackId from the marker
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
if (waypoint == null) {
finish();
return;
}
trackId = waypoint.getTrackId();
if (trackId == -1L) {
finish();
return;
}
}
if (trackId == -1L) {
return;
}
Track track = contentProviderUtils.getTrack(trackId);
if (track == null) {
// Use the last track if markerId is not set
if (markerId == -1L) {
track = contentProviderUtils.getLastTrack();
if (track != null) {
trackId = track.getId();
return;
}
track = contentProviderUtils.getLastTrack();
if (track != null) {
trackId = track.getId();
return;
}
finish();
}
@@ -465,20 +396,9 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
* Updates the menu items.
*/
private void updateMenuItems(boolean isPaused) {
insertMarkerMenuItem.setVisible(isRecording() && !isPaused);
shareMenuItem.setVisible(!isRecording());
markerListMenuItem.setShowAsAction(isRecording() ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM);
String title;
if (isRecording()) {
title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
resumeMenuItem.setVisible(false);
} else {
Track track = contentProviderUtils.getTrack(trackId);
title = track != null ? track.getName() : "";
// Only visible if there aren't other tracks that is recording.
resumeMenuItem.setVisible(recordingTrackId == -1L);
}
setTitle(title);
insertMarkerMenuItem.setVisible(!isPaused);
markerListMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
setTitle(getString(isPaused ? R.string.generic_paused : R.string.generic_recording));
}
public void chooseActivityType(String category) {
@@ -492,10 +412,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
}
private boolean isRecording() {
return trackId == recordingTrackId;
}
private void startRecording() {
trackRecordingServiceConnection.startAndBind(this);
@@ -209,7 +209,7 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
}
final TrackExporter trackExporter = getTrackFileFormat(uri).newTrackExporter(getContext(), tracks, null);
final TrackExporter trackExporter = getTrackFileFormat(uri).newTrackExporter(getContext(), tracks);
PipeDataWriter pipeDataWriter = new PipeDataWriter<String>() {
@Override
@@ -32,7 +32,7 @@ import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackDetailActivity;
import de.dennisguse.opentracks.TrackActivityDataHubInterface;
import de.dennisguse.opentracks.chart.ChartPoint;
import de.dennisguse.opentracks.chart.ChartView;
import de.dennisguse.opentracks.content.TrackDataHub;
@@ -286,7 +286,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/
private synchronized void resumeTrackDataHub() {
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
trackDataHub = ((TrackActivityDataHubInterface) getActivity()).getTrackDataHub();
trackDataHub.registerTrackDataListener(this, true, true, true, true);
}
@@ -1,126 +0,0 @@
/*
* Copyright 2013 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.fragments;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import java.util.Locale;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
/**
* A DialogFragment to select a file type, gpx, kml, etc.
*
* @author Jimmy Shih
*/
public class FileTypeDialogFragment extends DialogFragment {
private static final String FILE_TYPE_DIALOG_TAG = "fileType";
private static final String KEY_TITLE_ID = "titleId";
private static final String KEY_OPTION_ID = "optionId";
private FileTypeCaller caller;
public static void showDialog(FragmentManager fragmentManager, int titleId, int optionId) {
Bundle bundle = new Bundle();
bundle.putInt(KEY_TITLE_ID, titleId);
bundle.putInt(KEY_OPTION_ID, optionId);
FileTypeDialogFragment fileTypeDialogFragment = new FileTypeDialogFragment();
fileTypeDialogFragment.setArguments(bundle);
fileTypeDialogFragment.show(fragmentManager, FILE_TYPE_DIALOG_TAG);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
caller = (FileTypeCaller) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + FileTypeCaller.class.getSimpleName());
}
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
int optionId = getArguments().getInt(KEY_OPTION_ID);
final int titleId = getArguments().getInt(KEY_TITLE_ID);
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
String[] choices = new String[trackFileFormats.length];
for (int i = 0; i < choices.length; i++) {
TrackFileFormat trackFileFormat = trackFileFormats[i];
String trackFileFormatUpperCase = trackFileFormat.getExtension().toUpperCase(Locale.US); //ASCII upper case
int photoMessageId = trackFileFormat.includesPhotos() ? R.string.export_with_photos : R.string.export_without_photos;
String trackFileFormatDisplayName = String.format("%s (%s)", trackFileFormatUpperCase, getString(photoMessageId));
choices[i] = getString(optionId, trackFileFormatDisplayName);
}
return new AlertDialog.Builder(getActivity())
.setNegativeButton(R.string.generic_cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onDismissed();
}
})
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
caller.onFileTypeDone(trackFileFormats[position]);
}
})
.setSingleChoiceItems(choices, 0, null)
.setTitle(titleId)
.create();
}
@Override
public void onCancel(@NonNull DialogInterface dialog) {
onDismissed();
}
private void onDismissed() {
dismiss();
caller.onDismissed();
}
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface FileTypeCaller {
/**
* Called when file type selection is done.
*/
void onFileTypeDone(TrackFileFormat trackFileFormat);
void onDismissed();
}
}
@@ -0,0 +1,310 @@
/*
* Copyright 2008 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.fragments;
import android.content.SharedPreferences;
import android.os.Bundle;
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 de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackRecordedActivity;
import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
/**
* A fragment to display track statistics to the user.
*
* @author Sandor Dornbush
* @author Rodrigo Damazio
*/
public class StatisticsRecordedFragment extends Fragment implements TrackDataListener {
private TrackStatistics trackStatistics = null;
private String category = "";
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
updateUI();
}
}
});
}
}
}
};
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.statistics_recorded, container, false);
}
/* Views */
private TextView totalTimeValueView;
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;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
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);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Spinner activityTypeIcon = getView().findViewById(R.id.stats_activity_type_icon);
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getActivity(), ""));
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
((TrackRecordedActivity) getActivity()).chooseActivityType(category);
}
return true;
}
});
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
((TrackRecordedActivity) getActivity()).chooseActivityType(category);
}
return true;
}
});
}
@Override
public void onResume() {
super.onResume();
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
TrackDataHub trackDataHub = ((TrackRecordedActivity) getActivity()).getTrackDataHub();
trackDataHub.registerTrackDataListener(this, true, false, true, true);
}
@Override
public void onPause() {
super.onPause();
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
TrackDataHub trackDataHub = ((TrackRecordedActivity) getActivity()).getTrackDataHub();
trackDataHub.unregisterTrackDataListener(this);
}
@Override
public void onDestroyView() {
super.onDestroyView();
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;
}
@Override
public void onTrackUpdated(final Track track) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
trackStatistics = track != null ? track.getTrackStatistics() : null;
category = track != null ? track.getCategory() : "";
updateUI();
}
}
});
}
}
@Override
public void clearTrackPoints() {
// We don't care.
}
@Override
public void onSampledInTrackPoint(TrackPoint trackPoint) {
// We don't care.
}
@Override
public void onSampledOutTrackPoint(TrackPoint trackPoint) {
// We don't care.
}
@Override
public void onNewTrackPointsDone() {
// We don't care.
}
@Override
public void clearWaypoints() {
// We don't care.
}
@Override
public void onNewWaypoint(Waypoint wpt) {
// We don't care.
}
@Override
public void onNewWaypointsDone() {
// We don't care.
}
private void updateUI() {
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
// Set total distance
{
double totalDistance = trackStatistics == null ? Double.NaN : trackStatistics.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(false);
if (trackIconValue != null) {
TrackIconUtils.setIconSpinner(activitySpinner, trackIconValue);
}
}
// Set time and start datetime
if (trackStatistics != null) {
movingTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getMovingTime()));
totalTimeValueView.setText(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
}
// Set average speed/pace
{
double speed = trackStatistics != null ? trackStatistics.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 = trackStatistics == null ? Double.NaN : trackStatistics.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 = trackStatistics != null ? trackStatistics.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);
}
}
}
@@ -1,19 +1,3 @@
/*
* Copyright 2008 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.fragments;
import android.content.SharedPreferences;
@@ -31,10 +15,11 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.fragment.app.Fragment;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackDetailActivity;
import de.dennisguse.opentracks.TrackRecordingActivity;
import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Track;
@@ -59,16 +44,17 @@ import de.dennisguse.opentracks.util.UnitConversions;
* @author Sandor Dornbush
* @author Rodrigo Damazio
*/
public class StatsFragment extends Fragment implements TrackDataListener {
public class StatisticsRecordingFragment extends Fragment implements TrackDataListener {
private static final String TAG = StatsFragment.class.getSimpleName();
private static final String TAG = StatisticsRecordingFragment.class.getSimpleName();
private static final long UI_UPDATE_INTERVAL = UnitConversions.ONE_SECOND_MS;
private TrackDataHub trackDataHub;
private Handler handlerUpdateUI;
private final TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
//TODO Initialize immediately and remove in onDestroy()
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private TrackPoint lastTrackPoint = null;
private TrackStatistics lastTrackStatistics = null;
@@ -97,6 +83,9 @@ public class StatsFragment extends Fragment implements TrackDataListener {
if (PreferencesUtils.getRecordingTrackId(getContext()) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
// A recording track id has been set -> Resumes track and starts timer.
resumeTrackDataHub();
if (trackRecordingServiceConnection == null) {
trackRecordingServiceConnection = new TrackRecordingServiceConnection(null);
}
trackRecordingServiceConnection.startConnection(getContext());
handlerUpdateUI.post(updateUIeachSecond);
@@ -107,14 +96,15 @@ public class StatsFragment extends Fragment implements TrackDataListener {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.stats, container, false);
return inflater.inflate(R.layout.statistics_recording, container, false);
}
/* Views */
private View heartRateContainer;
private View sensorHorizontalLine;
private Group heartRateGroup;
private TextView heartRateValueView;
private TextView heartRateSensorView;
private View cadenceContainer;
private Group cadenceGroup;
private TextView cadenceValueView;
private TextView cadenceSensorView;
@@ -145,16 +135,13 @@ public class StatsFragment extends Fragment implements TrackDataListener {
private TextView speedMovingLabel;
private TextView speedMovingValue;
private TextView speedMovingUnit;
private View elevationSeparator;
private View elevationContainer;
private View speedContainer;
private Group elevationGroup;
private TextView speedLabel;
private TextView speedValue;
private TextView speedUnit;
private TextView elevationValue;
private TextView elevationUnit;
private View coordinateSeparator;
private View coordinateContainer;
private Group coordinateGroup;
private TextView latitudeValue;
private TextView longitudeValue;
@@ -162,11 +149,13 @@ public class StatsFragment extends Fragment implements TrackDataListener {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
heartRateContainer = view.findViewById(R.id.stats_sensor_heart_rate_container);
sensorHorizontalLine = view.findViewById(R.id.stats_sensor_horizontal_line);
heartRateGroup = view.findViewById(R.id.stats_sensor_heart_rate_group);
heartRateValueView = view.findViewById(R.id.stats_sensor_heart_rate_value);
heartRateSensorView = view.findViewById(R.id.stats_sensor_heart_rate_sensor_value);
cadenceContainer = view.findViewById(R.id.stats_sensor_cadence_container);
cadenceGroup = view.findViewById(R.id.stats_sensor_cadence_group);
cadenceValueView = view.findViewById(R.id.stats_sensor_cadence_value);
cadenceSensorView = view.findViewById(R.id.stats_sensor_cadence_sensor_value);
@@ -192,10 +181,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
speedMovingValue = view.findViewById(R.id.stats_moving_speed_value);
speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit);
elevationSeparator = view.findViewById(R.id.stats_elevation_separator);
elevationContainer = view.findViewById(R.id.stats_elevation_container);
elevationGroup = view.findViewById(R.id.stats_elevation_current_group);
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);
@@ -203,8 +190,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
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);
coordinateGroup = view.findViewById(R.id.stats_coordinate_group);
latitudeValue = view.findViewById(R.id.stats_latitude_value);
longitudeValue = view.findViewById(R.id.stats_longitude_value);
@@ -224,7 +210,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
((TrackDetailActivity) getActivity()).chooseActivityType(category);
((TrackRecordingActivity) getActivity()).chooseActivityType(category);
}
return true;
}
@@ -233,7 +219,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
((TrackDetailActivity) getActivity()).chooseActivityType(category);
((TrackRecordingActivity) getActivity()).chooseActivityType(category);
}
return true;
}
@@ -246,6 +232,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
resumeTrackDataHub();
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(null);
trackRecordingServiceConnection.startConnection(getContext());
handlerUpdateUI.post(updateUIeachSecond);
@@ -263,16 +250,21 @@ public class StatsFragment extends Fragment implements TrackDataListener {
@Override
public void onStop() {
super.onStop();
trackRecordingServiceConnection.unbind(getContext());
if (trackRecordingServiceConnection != null) {
trackRecordingServiceConnection.unbind(getContext());
}
trackRecordingServiceConnection = null;
}
@Override
public void onDestroyView() {
super.onDestroyView();
heartRateContainer = null;
sensorHorizontalLine = null;
heartRateGroup = null;
heartRateValueView = null;
heartRateSensorView = null;
cadenceContainer = null;
cadenceGroup = null;
cadenceValueView = null;
cadenceSensorView = null;
@@ -298,10 +290,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
speedMovingValue = null;
speedMovingUnit = null;
elevationSeparator = null;
elevationContainer = null;
elevationGroup = null;
speedContainer = null;
speedLabel = null;
speedValue = null;
speedUnit = null;
@@ -309,8 +299,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
elevationValue = null;
elevationUnit = null;
coordinateSeparator = null;
coordinateContainer = null;
coordinateGroup = null;
latitudeValue = null;
longitudeValue = null;
@@ -393,7 +382,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/
private synchronized void resumeTrackDataHub() {
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
trackDataHub = ((TrackRecordingActivity) getActivity()).getTrackDataHub();
trackDataHub.registerTrackDataListener(this, true, false, true, true);
}
@@ -441,57 +430,65 @@ public class StatsFragment extends Fragment implements TrackDataListener {
sensorDataSet = trackRecordingService.getSensorData();
}
setHeartRateSensorData(sensorDataSet, isSelectedTrackRecording());
setCadenceSensorData(sensorDataSet, isSelectedTrackRecording());
setHeartRateSensorData(sensorDataSet);
setCadenceSensorData(sensorDataSet);
setSpeedSensorData(sensorDataSet, isSelectedTrackRecording());
}
private void setHeartRateSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
private void setHeartRateSensorData(SensorDataSet sensorDataSet) {
int isVisible = View.VISIBLE;
if (!isRecording || PreferencesUtils.isBluetoothHeartRateSensorAddressNone(getContext())) {
isVisible = View.INVISIBLE;
if (PreferencesUtils.isBluetoothHeartRateSensorAddressNone(getContext())) {
isVisible = View.GONE;
}
heartRateContainer.setVisibility(isVisible);
heartRateGroup.setVisibility(isVisible);
setVisibilitySensorHorizontalLine();
if (isRecording) {
String sensorValue = getContext().getString(R.string.value_unknown);
String sensorName = getContext().getString(R.string.value_unknown);
if (sensorDataSet != null && sensorDataSet.getHeartRate() != null) {
SensorDataHeartRate data = sensorDataSet.getHeartRate();
String sensorValue = getContext().getString(R.string.value_unknown);
String sensorName = getContext().getString(R.string.value_unknown);
if (sensorDataSet != null && sensorDataSet.getHeartRate() != null) {
SensorDataHeartRate data = sensorDataSet.getHeartRate();
sensorName = data.getSensorName();
if (data.hasHeartRate_bpm() && data.isRecent()) {
sensorValue = StringUtils.formatDecimal(data.getHeartRate_bpm(), 0);
}
sensorName = data.getSensorName();
if (data.hasHeartRate_bpm() && data.isRecent()) {
sensorValue = StringUtils.formatDecimal(data.getHeartRate_bpm(), 0);
}
heartRateSensorView.setText(sensorName);
heartRateValueView.setText(sensorValue);
}
heartRateSensorView.setText(sensorName);
heartRateValueView.setText(sensorValue);
}
private void setCadenceSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
private void setCadenceSensorData(SensorDataSet sensorDataSet) {
int isVisible = View.VISIBLE;
if (!isRecording || PreferencesUtils.isBluetoothCyclingCadenceSensorAddressNone(getContext())) {
isVisible = View.INVISIBLE;
if (PreferencesUtils.isBluetoothCyclingCadenceSensorAddressNone(getContext())) {
isVisible = View.GONE;
}
cadenceContainer.setVisibility(isVisible);
cadenceGroup.setVisibility(isVisible);
setVisibilitySensorHorizontalLine();
if (isRecording) {
String sensorValue = getContext().getString(R.string.value_unknown);
String sensorName = getContext().getString(R.string.value_unknown);
if (sensorDataSet != null && sensorDataSet.getCyclingCadence() != null) {
SensorDataCycling.Cadence data = sensorDataSet.getCyclingCadence();
sensorName = data.getSensorName();
String sensorValue = getContext().getString(R.string.value_unknown);
String sensorName = getContext().getString(R.string.value_unknown);
if (sensorDataSet != null && sensorDataSet.getCyclingCadence() != null) {
SensorDataCycling.Cadence data = sensorDataSet.getCyclingCadence();
sensorName = data.getSensorName();
if (data.hasCadence_rpm() && data.isRecent()) {
sensorValue = StringUtils.formatDecimal(data.getCadence_rpm(), 0);
}
if (data.hasCadence_rpm() && data.isRecent()) {
sensorValue = StringUtils.formatDecimal(data.getCadence_rpm(), 0);
}
cadenceSensorView.setText(sensorName);
cadenceValueView.setText(sensorValue);
}
cadenceSensorView.setText(sensorName);
cadenceValueView.setText(sensorValue);
}
/**
* If cadence and hear rate groups are invisible then sensor horizontal line hast to be invisible too.
*/
private void setVisibilitySensorHorizontalLine() {
if (cadenceGroup.getVisibility() != View.VISIBLE && heartRateGroup.getVisibility() != View.VISIBLE) {
sensorHorizontalLine.setVisibility(View.GONE);
}
}
private void setSpeedSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
@@ -574,7 +571,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
// Make elevation visible?
{
boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext());
elevationSeparator.setVisibility(showElevation ? View.VISIBLE : View.GONE);
elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE);
}
}
@@ -603,18 +600,14 @@ public class StatsFragment extends Fragment implements TrackDataListener {
private void setLocationValues() {
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
boolean isRecording = isSelectedTrackRecording();
// Set speed/pace
speedContainer.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
if (isRecording) {
double speed = lastTrackPoint != null && lastTrackPoint.hasSpeed() ? lastTrackPoint.getSpeed() : Double.NaN;
setSpeed(speed);
}
double speed = lastTrackPoint != null && lastTrackPoint.hasSpeed() ? lastTrackPoint.getSpeed() : Double.NaN;
setSpeed(speed);
// Set elevation
boolean showElevation = isRecording && PreferencesUtils.isShowStatsElevation(getContext());
elevationContainer.setVisibility(showElevation ? View.VISIBLE : View.GONE);
boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext());
elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE);
if (showElevation) {
double altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? lastTrackPoint.getAltitude() : Double.NaN;
@@ -625,10 +618,9 @@ public class StatsFragment extends Fragment implements TrackDataListener {
}
// Set coordinate
boolean showCoordinate = isRecording && PreferencesUtils.isStatsShowCoordinate(getContext());
boolean showCoordinate = PreferencesUtils.isStatsShowCoordinate(getContext());
coordinateSeparator.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
coordinateContainer.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
coordinateGroup.setVisibility(showCoordinate ? View.VISIBLE : View.GONE);
if (showCoordinate) {
double latitude = lastTrackPoint != null ? lastTrackPoint.getLatitude() : Double.NaN;
String latitudeText = Double.isNaN(latitude) || Double.isInfinite(latitude) ? getContext().getString(R.string.value_unknown) : StringUtils.formatCoordinate(latitude);
@@ -12,7 +12,6 @@ import de.dennisguse.opentracks.io.file.exporter.GpxTrackWriter;
import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter;
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.io.file.exporter.TrackExporterListener;
import de.dennisguse.opentracks.io.file.exporter.TrackWriter;
/**
@@ -74,8 +73,8 @@ public enum TrackFileFormat {
return new KmlTrackWriter(context, multiple, false, false, exportPhotos);
}
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, trackExporterListener, exportPhotos);
public TrackExporter newTrackExporter(Context context, Track[] tracks) {
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, exportPhotos);
}
@Override
@@ -101,8 +100,8 @@ public enum TrackFileFormat {
return new KmlTrackWriter(context, multiple, true, false, exportPhotos);
}
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, trackExporterListener, exportPhotos);
public TrackExporter newTrackExporter(Context context, Track[] tracks) {
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, exportPhotos);
}
@Override
@@ -134,8 +133,8 @@ public enum TrackFileFormat {
return MIME_KMZ;
}
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, trackExporterListener, exportPhotos);
public TrackExporter newTrackExporter(Context context, Track[] tracks) {
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, exportPhotos);
}
public String getExtension() {
@@ -163,8 +162,8 @@ public enum TrackFileFormat {
return MIME_KMZ;
}
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
return newKmzTrackExporter(context, newTrackWriter(context, tracks.length > 1), tracks, trackExporterListener, exportPhotos);
public TrackExporter newTrackExporter(Context context, Track[] tracks) {
return newKmzTrackExporter(context, newTrackWriter(context, tracks.length > 1), tracks, exportPhotos);
}
public String getExtension() {
@@ -197,10 +196,10 @@ public enum TrackFileFormat {
private static final String MIME_KML = "application/vnd.google-earth.kml+xml";
private static TrackExporter newKmzTrackExporter(Context context, TrackWriter trackWriter, Track[] tracks, TrackExporterListener trackExporterListener, boolean exportPhotos) {
private static TrackExporter newKmzTrackExporter(Context context, TrackWriter trackWriter, Track[] tracks, boolean exportPhotos) {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, trackWriter, tracks, trackExporterListener);
FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, trackWriter, tracks);
return new KmzTrackExporter(contentProviderUtils, fileTrackExporter, tracks, exportPhotos);
}
@@ -210,10 +209,10 @@ public enum TrackFileFormat {
*/
public abstract String getMimeType();
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
public TrackExporter newTrackExporter(Context context, Track[] tracks) {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
TrackWriter trackWriter = newTrackWriter(context, tracks.length > 1);
return new FileTrackExporter(contentProviderUtils, trackWriter, tracks, trackExporterListener);
return new FileTrackExporter(contentProviderUtils, trackWriter, tracks);
}
/**
@@ -30,17 +30,17 @@ import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.FragmentActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.DialogUtils;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An activity for saving tracks to the external storage.
*
* @author Rodrigo Damazio
*/
public class ExportActivity extends FragmentActivity implements FileTypeDialogFragment.FileTypeCaller {
public class ExportActivity extends FragmentActivity {
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
@@ -55,8 +55,6 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
private int processedTrackCount;
private int totalTrackCount;
private Uri directoryUri;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -71,36 +69,24 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
directoryUri = resultData.getData();
fileTypeDialogStart();
Uri directoryUri = resultData.getData();
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
DocumentFile exportDirectory = pickedDirectory.findFile(FileUtils.EXPORT_DIR);
if (exportDirectory == null) {
exportDirectory = pickedDirectory.createDirectory(FileUtils.EXPORT_DIR);
}
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(this);
directoryDisplayName = FileUtils.getPath(exportDirectory);
exportAsyncTask = new ExportAsyncTask(this, trackFileFormat, exportDirectory);
exportAsyncTask.execute();
} else {
finish();
}
}
}
private void fileTypeDialogStart() {
FileTypeDialogFragment.showDialog(getSupportFragmentManager(), R.string.export_all_title, R.string.export_all_option);
}
@Override
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
DocumentFile exportDirectory = pickedDirectory.findFile(FileUtils.EXPORT_DIR);
if (exportDirectory == null) {
exportDirectory = pickedDirectory.createDirectory(FileUtils.EXPORT_DIR);
}
directoryDisplayName = FileUtils.getPath(exportDirectory);
exportAsyncTask = new ExportAsyncTask(this, trackFileFormat, exportDirectory);
exportAsyncTask.execute();
}
@Override
public void onDismissed() {
finish();
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
@@ -112,7 +98,7 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
public void onCancel(DialogInterface dialog) {
exportAsyncTask.cancel(true);
dialog.dismiss();
onDismissed();
finish();
}
}, directoryDisplayName);
return progressDialog;
@@ -138,13 +124,13 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
onDismissed();
finish();
}
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
dialog.dismiss();
onDismissed();
finish();
}
}).setTitle(titleId);
return builder.create();
@@ -16,7 +16,6 @@
package de.dennisguse.opentracks.io.file.exporter;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.PowerManager.WakeLock;
@@ -45,13 +44,10 @@ class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = ExportAsyncTask.class.getSimpleName();
private final TrackFileFormat trackFileFormat;
private final DocumentFile directory;
private final Context context;
private final ContentProviderUtils contentProviderUtils;
private ExportActivity exportActivity;
private WakeLock wakeLock;
private boolean completed = false;
private int processedTrackCount = 0;
private int totalTrackCount = 0;
@@ -66,8 +62,7 @@ class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
this.exportActivity = exportActivity;
this.trackFileFormat = trackFileFormat;
this.directory = directory;
context = exportActivity.getApplicationContext();
contentProviderUtils = new ContentProviderUtils(context);
contentProviderUtils = new ContentProviderUtils(exportActivity);
}
@Override
@@ -99,7 +94,6 @@ class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPostExecute(Boolean result) {
completed = true;
if (exportActivity != null) {
exportActivity.onAsyncTaskCompleted(processedTrackCount, totalTrackCount);
}
@@ -107,7 +101,6 @@ class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onCancelled() {
completed = true;
if (exportActivity != null) {
exportActivity.onAsyncTaskCompleted(processedTrackCount, totalTrackCount);
}
@@ -119,17 +112,7 @@ class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
* @param track the track
*/
private Boolean exportTrack(Track track) {
TrackExporterListener trackExporterListener = new TrackExporterListener() {
@Override
public void onProgressUpdate(int number, int max) {
//Update the progress dialog once every 500 points.
if (number % 500 == 0) {
publishProgress(number, max);
}
}
};
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track}, trackExporterListener);
TrackExporter trackExporter = trackFileFormat.newTrackExporter(exportActivity, new Track[]{track});
String fileName = track.getId() + "." + trackFileFormat.getExtension();
@@ -139,8 +122,8 @@ class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
file = directory.createFile(trackFileFormat.getMimeType(), fileName);
}
try (OutputStream outputStream = context.getContentResolver().openOutputStream(file.getUri())) {
if (trackExporter.writeTrack(context, outputStream)) {
try (OutputStream outputStream = exportActivity.getContentResolver().openOutputStream(file.getUri())) {
if (trackExporter.writeTrack(exportActivity, outputStream)) {
return true;
} else {
if (!file.delete()) {
@@ -167,6 +150,7 @@ class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
return false;
}
totalTrackCount = cursor.getCount();
for (int i = 0; i < totalTrackCount; i++) {
if (isCancelled()) {
return false;
@@ -44,7 +44,6 @@ public class FileTrackExporter implements TrackExporter {
private final ContentProviderUtils contentProviderUtils;
private final Track[] tracks;
private final TrackWriter trackWriter;
private final TrackExporterListener trackExporterListener;
/**
* Constructor.
@@ -52,13 +51,11 @@ public class FileTrackExporter implements TrackExporter {
* @param contentProviderUtils the content provider utils
* @param trackWriter the track writer
* @param tracks the tracks
* @param trackExporterListener the track export listener
*/
public FileTrackExporter(ContentProviderUtils contentProviderUtils, TrackWriter trackWriter, Track[] tracks, TrackExporterListener trackExporterListener) {
public FileTrackExporter(ContentProviderUtils contentProviderUtils, TrackWriter trackWriter, Track[] tracks) {
this.contentProviderUtils = contentProviderUtils;
this.tracks = tracks;
this.trackWriter = trackWriter;
this.trackExporterListener = trackExporterListener;
}
@Override
@@ -158,9 +155,6 @@ public class FileTrackExporter implements TrackExporter {
// Write the current trackPoint
trackWriter.writeTrackPoint(trackPoint);
if (trackExporterListener != null) {
trackExporterListener.onProgressUpdate(locationNumber, trackPointIterator.getCount());
}
} else {
if (wroteSegment) {
trackWriter.writeCloseSegment();
@@ -1,33 +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.io.file.exporter;
/**
* Listener for {@link TrackExporter} progress.
*
* @author Jimmy Shih
*/
public interface TrackExporterListener {
/**
* Called to update progress.
*
* @param number the number of {@link de.dennisguse.opentracks.content.data.TrackPoint}s written
* @param max the maximum number of {@link de.dennisguse.opentracks.content.data.TrackPoint}s in a {@link de.dennisguse.opentracks.content.data.Track}, for calculation of
* completion percentage
*/
void onProgressUpdate(int number, int max);
}
@@ -30,17 +30,17 @@ import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.FragmentActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.DialogUtils;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An activity to import files from the external storage.
*
* @author Rodrigo Damazio
*/
public class ImportActivity extends FragmentActivity implements FileTypeDialogFragment.FileTypeCaller {
public class ImportActivity extends FragmentActivity {
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
@@ -55,8 +55,6 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
private int importedTrackCount;
private int totalTrackCount;
private Uri directoryUri;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -72,34 +70,20 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
directoryUri = resultData.getData();
fileTypeDialogStart();
Uri directoryUri = resultData.getData();
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
directoryDisplayName = FileUtils.getPath(pickedDirectory);
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(this);
importAsyncTask = new ImportAsyncTask(this, trackFileFormat, pickedDirectory);
importAsyncTask.execute();
} else {
finish();
}
}
}
private void fileTypeDialogStart() {
FileTypeDialogFragment.showDialog(getSupportFragmentManager(), R.string.import_selection_title, R.string.import_selection_option);
}
@Override
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
directoryDisplayName = FileUtils.getPath(pickedDirectory);
importAsyncTask = new ImportAsyncTask(this, trackFileFormat, pickedDirectory);
importAsyncTask.execute();
}
@Override
public void onDismissed() {
finish();
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
@@ -110,7 +94,7 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
public void onCancel(DialogInterface dialog) {
importAsyncTask.cancel(true);
dialog.dismiss();
onDismissed();
finish();
}
}, directoryDisplayName);
return progressDialog;
@@ -140,13 +124,13 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
@Override
public void onCancel(DialogInterface dialogInterface) {
dialogInterface.dismiss();
onDismissed();
finish();
}
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
dialogInterface.dismiss();
onDismissed();
finish();
}
}).setTitle(titleId).create();
default:
@@ -41,8 +41,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackDetailActivity;
import de.dennisguse.opentracks.TrackListActivity;
import de.dennisguse.opentracks.TrackRecordingActivity;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
@@ -743,10 +743,10 @@ public class TrackRecordingService extends Service {
private void showNotification(boolean isGpsStarted) {
if (isRecording()) {
Intent intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, recordingTrackId);
Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class)
.putExtra(TrackRecordingActivity.EXTRA_TRACK_ID, recordingTrackId);
PendingIntent pendingIntent = TaskStackBuilder.create(this)
.addParentStack(TrackDetailActivity.class)
.addParentStack(TrackRecordingActivity.class)
.addNextIntent(intent)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
@@ -11,8 +11,11 @@ import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import java.util.Locale;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeHeartRatePreference;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeSensorPreference;
@@ -89,6 +92,8 @@ public class SettingsActivity extends AppCompatActivity implements ChooseActivit
setPreferencesFromResource(R.xml.settings, rootKey);
}
setExportTrackFileFormatOptions();
}
@Override
@@ -172,5 +177,24 @@ public class SettingsActivity extends AppCompatActivity implements ChooseActivit
HackUtils.invalidatePreference(statsRatePreferences);
}
private void setExportTrackFileFormatOptions() {
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
String[] entries = new String[trackFileFormats.length];
String[] entryValues = new String[trackFileFormats.length];
for (int i = 0; i < entries.length; i++) {
TrackFileFormat trackFileFormat = trackFileFormats[i];
String trackFileFormatUpperCase = trackFileFormat.getExtension().toUpperCase(Locale.US); //ASCII upper case
int photoMessageId = trackFileFormat.includesPhotos() ? R.string.export_with_photos : R.string.export_without_photos;
entries[i] = String.format("%s (%s)", trackFileFormatUpperCase, getString(photoMessageId));
entryValues[i] = trackFileFormat.name();
}
ListPreference listPreference = findPreference(getString(R.string.export_trackfileformat_key));
listPreference.setEntries(entries);
listPreference.setEntryValues(entryValues);
}
}
}
@@ -92,9 +92,10 @@ public class IntentUtils {
Track track = contentProviderUtils.getTrack(trackId);
if (track == null) {
Log.e(TAG, "TrackId " + trackId + " could not be resolved.");
continue;
}
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(new long[]{trackId}, track.getName(), TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES);
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(new long[]{trackId}, track.getName(), PreferencesUtils.getExportTrackFileFormat(context));
uris.add(uriAndMime.first);
mime = uriAndMime.second;
}
@@ -24,6 +24,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceManager;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
/**
* Utilities to access preferences stored in {@link SharedPreferences}.
@@ -320,6 +321,16 @@ public class PreferencesUtils {
return getInt(context, R.string.recording_gps_accuracy_key, RECORDING_GPS_ACCURACY);
}
public static TrackFileFormat getExportTrackFileFormat(Context context) {
final String TRACKFILEFORMAT_NAME_DEFAULT = getString(context, R.string.export_trackfileformat_default, null);
String trackFileFormatName = getString(context, R.string.export_trackfileformat_key, TRACKFILEFORMAT_NAME_DEFAULT);
try {
return TrackFileFormat.valueOf(trackFileFormatName);
} catch (Exception e) {
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
}
}
public static boolean isRecording(Context context) {
long recordingTrackId = getRecordingTrackId(context);
return isRecording(recordingTrackId);
@@ -0,0 +1,30 @@
package de.dennisguse.opentracks.util;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class ViewUtils {
private ViewUtils() {
}
/**
* Traverses all childs of {@link View} recursively and makes links within {@link TextView}s clickable.
*/
public static void makeClickableLinks(ViewGroup view) {
if (view == null) {
return;
}
for (int i = 0; i < view.getChildCount(); i++) {
final View child = view.getChildAt(i);
if (child instanceof ViewGroup) {
makeClickableLinks((ViewGroup) child);
} else if (child instanceof TextView) {
((TextView) child).setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
}
+78 -65
View File
@@ -6,82 +6,95 @@
<include layout="@layout/toolbar" />
<ImageView
android:id="@+id/imageView"
<ScrollView
android:layout_width="match_parent"
android:layout_height="239dp"
android:contentDescription="@string/app_name"
android:scaleType="fitCenter"
android:src="@drawable/ic_logo_color_24dp" />
android:layout_height="wrap_content">
<TextView
style="@style/TextLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/app_name"
android:textSize="24sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<View style="@style/StatsHorizontalLine" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="239dp"
android:contentDescription="@string/app_name"
android:scaleType="fitCenter"
android:src="@drawable/ic_logo_color_24dp" />
<TextView
android:id="@+id/about_text_description"
style="@style/TextMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="12dp" />
<TextView
style="@style/TextLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/app_name"
android:textSize="24sp" />
<View style="@style/StatsHorizontalLine" />
<View style="@style/StatsHorizontalLine" />
<TextView
android:id="@+id/about_text_version_name"
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="6dp" />
<TextView
android:id="@+id/about_text_description"
style="@style/TextMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="12dp" />
<TextView
android:id="@+id/about_text_version_code"
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="6dp" />
<View style="@style/StatsHorizontalLine" />
<TextView
android:id="@+id/about_app_url"
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:gravity="center"
android:padding="6dp" />
<TextView
android:id="@+id/about_text_version_name"
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="6dp" />
<TextView
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:gravity="center"
android:padding="6dp"
android:text="@string/about_history" />
<TextView
android:id="@+id/about_text_version_code"
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="6dp" />
<View style="@style/StatsHorizontalLine" />
<TextView
android:id="@+id/about_app_url"
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:gravity="center"
android:padding="6dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="12dp"
android:text="@string/about_libraries" />
<TextView
style="@style/TextSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="6dp"
android:linksClickable="true"
android:text="@string/about_history" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:gravity="center"
android:text="@string/third_party_libraries" />
<View style="@style/StatsHorizontalLine" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="12dp"
android:text="@string/about_libraries" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:linksClickable="true"
android:text="@string/third_party_libraries" />
</LinearLayout>
</ScrollView>
</LinearLayout>
+213
View File
@@ -0,0 +1,213 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/StatsScrollView">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Guidelines -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="8dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_end="8dp" />
<!-- Distance -->
<TextView
android:id="@+id/stats_distance_label"
style="@style/StatsLargeLabel"
android:text="@string/stats_distance"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/stats_distance_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/stats_distance_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_distance_label"
tools:text="100" />
<TextView
android:id="@+id/stats_distance_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_distance_value"
app:layout_constraintLeft_toRightOf="@+id/stats_distance_value"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
tools:text="km" />
<!-- Activity Type -->
<TextView
android:id="@+id/stats_activity_type_label"
style="@style/StatsLargeLabel"
android:text="@string/track_edit_activity_type_hint"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="@+id/stats_activity_type_icon"
style="@style/StatsLargeValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintBottom_toBottomOf="@+id/stats_distance_value"
app:layout_constraintTop_toTopOf="@+id/stats_distance_value" />
<!-- Total time -->
<TextView
android:id="@+id/stats_total_time_label"
style="@style/StatsLargeLabel"
android:text="@string/stats_total_time"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_distance_value" />
<TextView
android:id="@+id/stats_total_time_value"
style="@style/StatsLargeValue"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_total_time_label"
tools:text="00:00:00" />
<!-- Moving time -->
<TextView
android:id="@+id/stats_moving_time_label"
style="@style/StatsLargeLabel"
android:text="@string/stats_moving_time"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toTopOf="@+id/stats_total_time_label"
app:layout_constraintBottom_toBottomOf="@+id/stats_total_time_label" />
<TextView
android:id="@+id/stats_moving_time_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_moving_time_label"
tools:text="00:00:00" />
<!-- Horizontal line -->
<View
android:id="@+id/stats_speed_horizontal_line"
style="@style/StatsHorizontalLine"
android:layout_marginBottom="8dp"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_moving_time_value" />
<!-- Average speed/pace -->
<TextView
android:id="@+id/stats_average_speed_label"
style="@style/StatsSmallLabel"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@id/stats_speed_horizontal_line"
tools:text="Avg. Speed" />
<TextView
android:id="@+id/stats_average_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/stats_average_speed_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_average_speed_label"
tools:text="30.00" />
<TextView
android:id="@+id/stats_average_speed_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_average_speed_value"
app:layout_constraintLeft_toRightOf="@+id/stats_average_speed_value"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
tools:text="km/h" />
<!-- Speed max -->
<TextView
android:id="@+id/stats_max_speed_label"
style="@style/StatsSmallLabel"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@id/stats_average_speed_value"
tools:text="Max. Speed" />
<TextView
android:id="@+id/stats_max_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/stats_max_speed_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_max_speed_label"
tools:text="30.00" />
<TextView
android:id="@+id/stats_max_speed_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_max_speed_value"
app:layout_constraintLeft_toRightOf="@+id/stats_max_speed_value"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
tools:text="km/h" />
<!-- Moving speed max -->
<TextView
android:id="@+id/stats_moving_speed_label"
style="@style/StatsSmallLabel"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@id/stats_average_speed_value"
tools:text="Max. Moving Speed" />
<TextView
android:id="@+id/stats_moving_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/stats_moving_speed_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_moving_speed_label"
tools:text="30.00" />
<TextView
android:id="@+id/stats_moving_speed_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_moving_speed_value"
app:layout_constraintLeft_toRightOf="@+id/stats_moving_speed_value"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
tools:text="km/h" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
@@ -0,0 +1,430 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/StatsScrollView">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Guidelines -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="8dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_end="8dp" />
<!-- Distance -->
<TextView
android:id="@+id/stats_distance_label"
style="@style/StatsLargeLabel"
android:text="@string/stats_distance"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/stats_distance_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/stats_distance_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_distance_label"
tools:text="100" />
<TextView
android:id="@+id/stats_distance_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_distance_value"
app:layout_constraintLeft_toRightOf="@+id/stats_distance_value"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
tools:text="km" />
<!-- Activity Type -->
<TextView
android:id="@+id/stats_activity_type_label"
style="@style/StatsLargeLabel"
android:text="@string/track_edit_activity_type_hint"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="@+id/stats_activity_type_icon"
style="@style/StatsLargeValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintBottom_toBottomOf="@+id/stats_distance_value"
app:layout_constraintTop_toTopOf="@+id/stats_distance_value" />
<!-- Total time -->
<TextView
android:id="@+id/stats_total_time_label"
style="@style/StatsLargeLabel"
android:text="@string/stats_total_time"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_distance_value" />
<TextView
android:id="@+id/stats_total_time_value"
style="@style/StatsLargeValue"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_total_time_label"
tools:text="00:00:00" />
<!-- Moving time -->
<TextView
android:id="@+id/stats_moving_time_label"
style="@style/StatsLargeLabel"
android:text="@string/stats_moving_time"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toTopOf="@+id/stats_total_time_label"
app:layout_constraintBottom_toBottomOf="@+id/stats_total_time_label" />
<TextView
android:id="@+id/stats_moving_time_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_moving_time_label"
tools:text="00:00:00" />
<!-- Horizontal line -->
<View
android:id="@+id/stats_speed_horizontal_line"
style="@style/StatsHorizontalLine"
android:layout_marginBottom="8dp"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_moving_time_value" />
<!-- Speed/pace -->
<TextView
android:id="@+id/stats_speed_label"
style="@style/StatsSmallLabel"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@id/stats_speed_horizontal_line"
tools:text="Speed" />
<TextView
android:id="@+id/stats_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/stats_speed_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_speed_label"
tools:text="30.00" />
<TextView
android:id="@+id/stats_speed_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_speed_value"
app:layout_constraintLeft_toRightOf="@+id/stats_speed_value"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
tools:text="km/h" />
<!-- Average speed/pace -->
<TextView
android:id="@+id/stats_average_speed_label"
style="@style/StatsSmallLabel"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@id/stats_speed_horizontal_line"
tools:text="Avg. Speed" />
<TextView
android:id="@+id/stats_average_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/stats_average_speed_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_average_speed_label"
tools:text="30.00" />
<TextView
android:id="@+id/stats_average_speed_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_average_speed_value"
app:layout_constraintLeft_toRightOf="@+id/stats_average_speed_value"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
tools:text="km/h" />
<!-- Speed max -->
<TextView
android:id="@+id/stats_max_speed_label"
style="@style/StatsSmallLabel"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@id/stats_average_speed_value"
tools:text="Max. Speed" />
<TextView
android:id="@+id/stats_max_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/stats_max_speed_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_max_speed_label"
tools:text="30.00" />
<TextView
android:id="@+id/stats_max_speed_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_max_speed_value"
app:layout_constraintLeft_toRightOf="@+id/stats_max_speed_value"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
tools:text="km/h" />
<!-- Moving speed max -->
<TextView
android:id="@+id/stats_moving_speed_label"
style="@style/StatsSmallLabel"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@id/stats_average_speed_value"
tools:text="Max. Moving Speed" />
<TextView
android:id="@+id/stats_moving_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/stats_moving_speed_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_moving_speed_label"
tools:text="30.00" />
<TextView
android:id="@+id/stats_moving_speed_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_moving_speed_value"
app:layout_constraintLeft_toRightOf="@+id/stats_moving_speed_value"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
tools:text="km/h" />
<!-- Horizontal line -->
<View
android:id="@+id/stats_elevation_horizontal_line"
style="@style/StatsHorizontalLine"
android:layout_marginBottom="8dp"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_moving_speed_value" />
<!-- Current elevation -->
<androidx.constraintlayout.widget.Group
android:id="@+id/stats_elevation_current_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="stats_elevation_current_label,stats_elevation_current_value,stats_elevation_current_unit,stats_elevation_horizontal_line" />
<TextView
android:id="@+id/stats_elevation_current_label"
style="@style/StatsSmallLabel"
android:text="@string/stats_elevation"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@id/stats_elevation_horizontal_line" />
<TextView
android:id="@+id/stats_elevation_current_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/stats_elevation_current_unit"
app:layout_constraintTop_toBottomOf="@+id/stats_elevation_current_label"
tools:text="100" />
<TextView
android:id="@+id/stats_elevation_current_unit"
style="@style/StatsUnit"
app:layout_constraintBottom_toBottomOf="@+id/stats_elevation_current_value"
app:layout_constraintLeft_toRightOf="@+id/stats_elevation_current_value"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
tools:text="m" />
<!-- Coordinate group -->
<androidx.constraintlayout.widget.Group
android:id="@+id/stats_coordinate_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="stats_latitude_label,stats_latitude_value,stats_longitude_label,stats_longitude_value,stats_coordinate_horizontal_line" />
<!-- Horizontal line -->
<View
android:id="@+id/stats_coordinate_horizontal_line"
style="@style/StatsHorizontalLine"
android:layout_marginBottom="8dp"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_elevation_current_value" />
<!-- Latitude -->
<TextView
android:id="@+id/stats_latitude_label"
style="@style/StatsSmallLabel"
android:text="@string/stats_latitude"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@id/stats_coordinate_horizontal_line" />
<TextView
android:id="@+id/stats_latitude_value"
style="@style/StatsSmallValue"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_latitude_label"
tools:text="0.834937848" />
<!-- Longitude -->
<TextView
android:id="@+id/stats_longitude_label"
style="@style/StatsSmallLabel"
android:text="@string/stats_longitude"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@id/stats_coordinate_horizontal_line" />
<TextView
android:id="@+id/stats_longitude_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_longitude_label"
tools:text="0.834937848" />
<!-- Horizontal line -->
<View
android:id="@+id/stats_sensor_horizontal_line"
style="@style/StatsHorizontalLine"
android:layout_marginBottom="8dp"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_latitude_value" />
<!-- Heart rate -->
<androidx.constraintlayout.widget.Group
android:id="@+id/stats_sensor_heart_rate_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="stats_sensor_heart_rate_label,stats_sensor_heart_rate_sensor_value,stats_sensor_heart_rate_value,stats_sensor_heart_rate_unit" />
<TextView
android:id="@+id/stats_sensor_heart_rate_label"
style="@style/StatsLargeLabel"
android:text="@string/sensor_state_heart_rate"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@id/stats_sensor_horizontal_line" />
<TextView
android:id="@+id/stats_sensor_heart_rate_sensor_value"
style="@style/StatsTinyValue"
android:text="@string/value_unknown"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_sensor_heart_rate_label"
tools:text="90" />
<TextView
android:id="@+id/stats_sensor_heart_rate_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_sensor_heart_rate_sensor_value"
tools:text="90" />
<TextView
android:id="@+id/stats_sensor_heart_rate_unit"
style="@style/StatsUnit"
android:text="@string/sensor_unit_beats_per_minute"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
app:layout_constraintTop_toBottomOf="@+id/stats_sensor_heart_rate_value" />
<!-- Cadence -->
<androidx.constraintlayout.widget.Group
android:id="@+id/stats_sensor_cadence_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="stats_sensor_cadence_label,stats_sensor_cadence_sensor_value,stats_sensor_cadence_value,stats_sensor_cadence_unit" />
<TextView
android:id="@+id/stats_sensor_cadence_label"
style="@style/StatsLargeLabel"
android:text="@string/sensor_state_cadence"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@id/stats_sensor_horizontal_line" />
<TextView
android:id="@+id/stats_sensor_cadence_sensor_value"
style="@style/StatsTinyValue"
android:text="@string/value_unknown"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_sensor_cadence_label"
tools:text="90" />
<TextView
android:id="@+id/stats_sensor_cadence_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_sensor_cadence_sensor_value"
tools:text="90" />
<TextView
android:id="@+id/stats_sensor_cadence_unit"
style="@style/StatsUnit"
android:text="@string/sensor_unit_rounds_per_minute"
app:layout_constraintLeft_toRightOf="@+id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/stats_sensor_cadence_value" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
-332
View File
@@ -1,332 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/StatsScrollView">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Distance and activity type -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Distance -->
<LinearLayout style="@style/StatsLargeItemContainer">
<TextView
style="@style/StatsLargeLabel"
android:text="@string/stats_distance" />
<LinearLayout style="@style/StatsLargeValueContainer">
<TextView
android:id="@+id/stats_distance_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown" />
<TextView
android:id="@+id/stats_distance_unit"
style="@style/StatsUnit" />
</LinearLayout>
</LinearLayout>
<!-- Activity type -->
<include
android:id="@+id/stats_activity_type"
layout="@layout/stats_activity_type" />
</LinearLayout>
<!-- Total time and moving time -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Total time -->
<LinearLayout style="@style/StatsLargeItemContainer">
<TextView
style="@style/StatsLargeLabel"
android:text="@string/stats_total_time" />
<TextView
android:id="@+id/stats_total_time_value"
style="@style/StatsLargeValue" />
</LinearLayout>
<!-- Moving time -->
<LinearLayout style="@style/StatsLargeItemContainer">
<TextView
style="@style/StatsLargeLabel"
android:text="@string/stats_moving_time" />
<TextView
android:id="@+id/stats_moving_time_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown" />
</LinearLayout>
</LinearLayout>
<!-- Horizontal line -->
<View style="@style/StatsHorizontalLine" />
<!-- Speed/pace and average -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Speed/pace -->
<LinearLayout
android:id="@+id/stats_speed"
style="@style/StatsSmallItemContainer">
<TextView
android:id="@+id/stats_speed_label"
style="@style/StatsSmallLabel" />
<LinearLayout style="@style/StatsSmallValueContainer">
<TextView
android:id="@+id/stats_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown" />
<TextView
android:id="@+id/stats_speed_unit"
style="@style/StatsUnit" />
</LinearLayout>
</LinearLayout>
<!-- Average -->
<LinearLayout style="@style/StatsSmallItemContainer">
<TextView
android:id="@+id/stats_average_speed_label"
style="@style/StatsSmallLabel" />
<LinearLayout style="@style/StatsSmallValueContainer">
<TextView
android:id="@+id/stats_average_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown" />
<TextView
android:id="@+id/stats_average_speed_unit"
style="@style/StatsUnit" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- Speed max and moving -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Speed max -->
<LinearLayout style="@style/StatsSmallItemContainer">
<TextView
android:id="@+id/stats_max_speed_label"
style="@style/StatsSmallLabel" />
<LinearLayout style="@style/StatsSmallValueContainer">
<TextView
android:id="@+id/stats_max_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown" />
<TextView
android:id="@+id/stats_max_speed_unit"
style="@style/StatsUnit" />
</LinearLayout>
</LinearLayout>
<!-- Speed moving -->
<LinearLayout style="@style/StatsSmallItemContainer">
<TextView
android:id="@+id/stats_moving_speed_label"
style="@style/StatsSmallLabel" />
<LinearLayout style="@style/StatsSmallValueContainer">
<TextView
android:id="@+id/stats_moving_speed_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown" />
<TextView
android:id="@+id/stats_moving_speed_unit"
style="@style/StatsUnit" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/stats_elevation_separator"
style="@style/StatsHorizontalLine" />
<LinearLayout
android:id="@+id/stats_elevation_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout style="@style/StatsSmallGroupContainer">
<!-- This is a placeholder -->
</LinearLayout>
<!-- Elevation -->
<LinearLayout style="@style/StatsSmallGroupContainer">
<TextView
style="@style/StatsSmallLabel"
android:text="@string/stats_elevation" />
<TableLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<!-- Current elevation -->
<LinearLayout
style="@style/StatsSmallValueContainer"
android:layout_gravity="start|bottom">
<TextView
android:id="@+id/stats_elevation_current_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown" />
<TextView
android:id="@+id/stats_elevation_current_unit"
style="@style/StatsUnit" />
</LinearLayout>
</TableLayout>
</LinearLayout>
</LinearLayout>
<!-- Horizontal line -->
<View
android:id="@+id/stats_coordinate_separator"
style="@style/StatsHorizontalLine" />
<!-- Latitude and longitude -->
<LinearLayout
android:id="@+id/stats_coordinate_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout style="@style/StatsSmallItemContainer">
<TextView
style="@style/StatsSmallLabel"
android:text="@string/stats_latitude" />
<TextView
android:id="@+id/stats_latitude_value"
style="@style/StatsSmallValue" />
</LinearLayout>
<LinearLayout style="@style/StatsSmallItemContainer">
<TextView
style="@style/StatsSmallLabel"
android:text="@string/stats_longitude" />
<TextView
android:id="@+id/stats_longitude_value"
style="@style/StatsSmallValue"
android:value="@string/value_unknown" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/stats_sensor_separator"
style="@style/StatsHorizontalLine" />
<!-- Sensor data -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/stats_sensor_heart_rate_container"
style="@style/StatsLargeItemContainer">
<TextView
style="@style/StatsLargeLabel"
android:text="@string/sensor_state_heart_rate" />
<TextView
android:id="@+id/stats_sensor_heart_rate_sensor_value"
style="@style/StatsTinyValue"
android:text="@string/value_unknown" />
<LinearLayout style="@style/StatsLargeValueContainer">
<TextView
android:id="@+id/stats_sensor_heart_rate_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown" />
<TextView
style="@style/StatsUnit"
android:text="@string/sensor_unit_beats_per_minute" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/stats_sensor_cadence_container"
style="@style/StatsLargeItemContainer">
<TextView
style="@style/StatsLargeLabel"
android:text="@string/sensor_state_cadence" />
<TextView
android:id="@+id/stats_sensor_cadence_sensor_value"
style="@style/StatsTinyValue"
android:text="@string/value_unknown" />
<LinearLayout style="@style/StatsLargeValueContainer">
<TextView
android:id="@+id/stats_sensor_cadence_value"
style="@style/StatsLargeValue"
android:value="@string/value_unknown" />
<TextView
style="@style/StatsUnit"
android:text="@string/sensor_unit_rounds_per_minute" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
-2
View File
@@ -32,6 +32,4 @@ limitations under the License.
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include layout="@layout/track_controller" />
</LinearLayout>
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/track_detail_activity_tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/holo_orange_dark" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/track_detail_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include layout="@layout/track_controller" />
</LinearLayout>
-6
View File
@@ -15,12 +15,6 @@ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/track_detail_insert_marker"
android:icon="@drawable/ic_marker_add_24dp"
android:orderInCategory="1"
app:showAsAction="ifRoom"
android:title="@string/menu_insert_marker" />
<item
android:id="@+id/track_detail_menu_show_on_map"
android:icon="@drawable/ic_map_24dp"
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2011 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/track_detail_insert_marker"
android:icon="@drawable/ic_marker_add_24dp"
android:orderInCategory="1"
app:showAsAction="ifRoom"
android:title="@string/menu_insert_marker" />
<item
android:id="@+id/track_detail_menu_show_on_map"
android:icon="@drawable/ic_map_24dp"
app:showAsAction="ifRoom"
android:title="@string/menu_show_on_map" />
<item
android:id="@+id/track_detail_markers"
android:icon="@drawable/ic_marker_show_24dp"
android:orderInCategory="5"
app:showAsAction="ifRoom"
android:title="@string/menu_markers" />
<item
android:id="@+id/track_detail_edit"
android:icon="@drawable/ic_edit_24dp"
android:orderInCategory="9"
android:title="@string/menu_edit" />
<item
android:id="@+id/track_detail_settings"
android:icon="@drawable/ic_settings_24dp"
android:orderInCategory="13"
android:title="@string/menu_settings" />
</menu>
+4
View File
@@ -220,5 +220,9 @@
<item>@string/settings_recording_track_name_number_option</item>
</string-array>
<string name="export_trackfileformat_key" translatable="false">exportTrackFileFormat</string>
<!-- See TrackFileFormat -->
<string name="export_trackfileformat_default" translatable="false">KMZ_WITH_TRACKDETAIL_AND_SENSORDATA</string>
<string name="settings_recording_key" translatable="false">settingsRecordingKey</string>
</resources>
+3
View File
@@ -236,6 +236,8 @@ limitations under the License.
<string name="generic_success_title">Success</string>
<string name="generic_tracks">Tracks</string>
<string name="generic_yes">Yes</string>
<string name="generic_today">Today</string>
<string name="generic_yesterday">Yesterday</string>
<!-- Gps -->
<string name="gps_location_access">&#8216;Location access&#8217;</string>
<string name="gps_disabled">GPS not enabled. Tap here to go to %1$s. Press the back button to return.</string>
@@ -333,6 +335,7 @@ limitations under the License.
<string name="settings_show">Show</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Advanced</string>
<string name="settings_default_trackfileformat">Export/sharing file format</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">By distance</string>
<string name="settings_chart_by_time">By time</string>
+5 -2
View File
@@ -117,8 +117,7 @@ limitations under the License.
<style name="StatsHorizontalLine">
<item name="android:background">@color/stats_separator</item>
<item name="android:layout_height">1dp</item>
<item name="android:layout_marginLeft">8dp</item>
<item name="android:layout_marginRight">8dp</item>
<item name="android:layout_margin">8dp</item>
<item name="android:layout_width">match_parent</item>
</style>
<!-- stats large item container -->
@@ -140,6 +139,9 @@ limitations under the License.
<item name="android:textAllCaps">true</item>
<item name="android:textColor">@color/stats_large_label</item>
<item name="android:textStyle">bold</item>
<item name="android:paddingLeft">4dp</item>
<item name="android:paddingRight">4dp</item>
<item name="android:layout_marginTop">16dp</item>
</style>
<style name="StatsLargeValue">
@@ -179,6 +181,7 @@ limitations under the License.
<item name="android:singleLine">false</item>
<item name="android:textAllCaps">true</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_marginTop">16dp</item>
</style>
<!-- stats small value -->
<style name="StatsSmallValue" parent="@style/TextLarge">
+6 -1
View File
@@ -127,7 +127,7 @@ limitations under the License.
android:defaultValue="@string/settings_sensor_bluetooth_cycling_speed_wheel_circumference_default"
android:entries="@array/wheel_circumference_options"
android:entryValues="@array/wheel_circumference_values"
android:key="settings_sensor_bluetooth_cycling_speed_wheel_circumference_key"
android:key="@string/settings_sensor_bluetooth_cycling_speed_wheel_circumference_key"
android:title="@string/settings_sensor_wheel_circumference"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
@@ -143,6 +143,11 @@ limitations under the License.
android:targetClass="de.dennisguse.opentracks.io.file.exporter.ExportActivity"
android:targetPackage="@string/applicationId" />
</Preference>
<ListPreference
android:defaultValue="@string/export_trackfileformat_default"
android:key="@string/export_trackfileformat_key"
android:title="@string/settings_default_trackfileformat"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/settings_reset">