diff --git a/build.gradle b/build.gradle index bcda3d8af..5bae6876b 100644 --- a/build.gradle +++ b/build.gradle @@ -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' } diff --git a/doc/activities.txt b/doc/activities.txt index 2bf9bdab9..ed5441481 100644 --- a/doc/activities.txt +++ b/doc/activities.txt @@ -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 \ No newline at end of file +@enduml diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 026854e4f..15d669c6a 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -79,7 +79,11 @@ limitations under the License. + + diff --git a/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java b/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java index 27d064836..b81a42e7f 100644 --- a/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/AbstractListActivity.java @@ -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}: *

* - share track
diff --git a/src/main/java/de/dennisguse/opentracks/SearchListActivity.java b/src/main/java/de/dennisguse/opentracks/SearchListActivity.java index 6dc35c35c..71679be72 100644 --- a/src/main/java/de/dennisguse/opentracks/SearchListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/SearchListActivity.java @@ -214,11 +214,11 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa Map 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); } diff --git a/src/main/java/de/dennisguse/opentracks/TrackActivityDataHubInterface.java b/src/main/java/de/dennisguse/opentracks/TrackActivityDataHubInterface.java new file mode 100644 index 000000000..d5de39eed --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/TrackActivityDataHubInterface.java @@ -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(); +} diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java index 6db013db6..30a1bfb9d 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java @@ -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); } }); diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java new file mode 100644 index 000000000..901a8981a --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java @@ -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); + } +} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/TrackDetailActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java similarity index 70% rename from src/main/java/de/dennisguse/opentracks/TrackDetailActivity.java rename to src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java index 836e42b06..8df8041f2 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackDetailActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java @@ -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); diff --git a/src/main/java/de/dennisguse/opentracks/fragments/ChartFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/ChartFragment.java index 4a54b3382..03af17e78 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/ChartFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/ChartFragment.java @@ -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); } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java new file mode 100644 index 000000000..0f50feed0 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java @@ -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 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 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 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 parts = StringUtils.getSpeedParts(getContext(), speed, metricUnits, reportSpeed); + speedMovingValue.setText(parts.first); + speedMovingUnit.setText(parts.second); + } + } +} diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatsFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java similarity index 79% rename from src/main/java/de/dennisguse/opentracks/fragments/StatsFragment.java rename to src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java index 8b9c3452f..2a1170d39 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatsFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java @@ -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); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index 0085fcd03..1c1d3d0bd 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -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); diff --git a/src/main/res/layout/statistics_recorded.xml b/src/main/res/layout/statistics_recorded.xml new file mode 100644 index 000000000..0be5a6019 --- /dev/null +++ b/src/main/res/layout/statistics_recorded.xml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/statistics_recording.xml b/src/main/res/layout/statistics_recording.xml new file mode 100644 index 000000000..aabd699cd --- /dev/null +++ b/src/main/res/layout/statistics_recording.xml @@ -0,0 +1,430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/stats.xml b/src/main/res/layout/stats.xml deleted file mode 100644 index dcd753d99..000000000 --- a/src/main/res/layout/stats.xml +++ /dev/null @@ -1,332 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/res/layout/track_detail.xml b/src/main/res/layout/track_detail.xml index da16e02b7..1490bfdf0 100644 --- a/src/main/res/layout/track_detail.xml +++ b/src/main/res/layout/track_detail.xml @@ -32,6 +32,4 @@ limitations under the License. android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> - - \ No newline at end of file diff --git a/src/main/res/layout/track_record.xml b/src/main/res/layout/track_record.xml new file mode 100644 index 000000000..1715d9bff --- /dev/null +++ b/src/main/res/layout/track_record.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/menu/track_detail.xml b/src/main/res/menu/track_detail.xml index 3fd5d24a1..0f88e6fb2 100644 --- a/src/main/res/menu/track_detail.xml +++ b/src/main/res/menu/track_detail.xml @@ -15,12 +15,6 @@ limitations under the License. -->

- + + + + + + + \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 834ce5f2a..69aea1a61 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -236,6 +236,8 @@ limitations under the License. Success Tracks Yes + Today + Yesterday ‘Location access’ GPS not enabled. Tap here to go to %1$s. Press the back button to return. diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 326cbb156..a1a9bc2ce 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -117,8 +117,7 @@ limitations under the License. @@ -140,6 +139,9 @@ limitations under the License. true @color/stats_large_label bold + 4dp + 4dp + 16dp