Compare commits

..

24 Commits

Author SHA1 Message Date
Dennis Guse 9bd4ba489c Release: v3.2.2 2019-10-21 23:00:13 +02:00
Dennis Guse dd55b50dad Re-added actionbar (actually toolbar) to SettingsActivity. 2019-10-21 22:54:23 +02:00
Dennis Guse 1719d21a5b Bugfix: export now really includes sensor data (as double) and import is done as float. 2019-10-21 22:34:33 +02:00
Dennis Guse 75ef8d99d4 SensorDataSet: data unavailable is now NaN. 2019-10-21 22:34:28 +02:00
Dennis Guse ae06fd1680 Bugfix: export sensor data if available and allowed. 2019-10-20 18:39:31 +02:00
Dennis Guse 2b03f7dea8 Use LinearLayout for SearchActivity (show toolbar correctly). 2019-10-20 17:05:09 +02:00
Dennis Guse 7591b012b4 Extracted ShareActionProvider setup code into a separate class. 2019-10-20 16:55:28 +02:00
Dennis Guse 0e089f49f3 Re-added application logo to material design toolbar. 2019-10-19 12:27:11 +02:00
Dennis Guse 5ad1856b13 Removed context menu from ListActivities. 2019-10-17 21:32:55 +02:00
Dennis Guse 965dbbe829 Removed custom action bar style. 2019-10-17 21:26:31 +02:00
Dennis Guse 262c9886df Removed aggregated stats activity. 2019-10-17 21:22:07 +02:00
Dennis Guse ac5a9ea65d Use Androidx compat's toolbar instead of actionbars.
ListView's multiselect still uses actionbar.
2019-10-17 21:19:14 +02:00
Dennis Guse ed9b4c8f3d Add workaround for Compat's ShareActionProvider, which is not available when inflating in a AbsListView.MultiChoiceModeListener. 2019-10-17 18:33:45 +02:00
Dennis Guse 209aa6a8a5 Hide share action from SearchActivity as it is not (yet) functional. 2019-10-17 07:58:11 +02:00
Dennis Guse 81ee6ac1fa Do not use custom style for AlertDialogs, so PreferenceDialogs and AlertDialogs look the same. 2019-10-17 06:38:05 +02:00
Dennis Guse 024dd603cb Migration to Androidx PreferenceCompat. 2019-10-17 06:00:47 +02:00
Dennis Guse 50654c6baf Release v3.2.1 2019-10-15 18:36:40 +02:00
Dennis Guse 8c9bf60fc6 Update Settings if the units (metric/imperial) are changed. 2019-10-15 17:29:59 +02:00
Dennis Guse a83eb0b983 Added de-DE description. 2019-10-13 12:19:27 +02:00
Dennis Guse ab3cbfea0f Metadata: adjust to fastlane layout. 2019-10-13 12:19:27 +02:00
Dennis Guse c93e5ca1a6 Settings: moved to Androidx Compat and use one screen only. 2019-10-12 18:00:51 +02:00
Dennis Guse c2841092da Settings: moved setup into helper. 2019-09-19 22:30:28 +02:00
Dennis Guse 8b11e1d611 DialogFragments: do not save state. 2019-09-19 22:30:28 +02:00
Dennis Guse 6375a851b2 Moved ChooseActivityTypeDialog to be a real DialogFragment.
Note: This breaks RecordingsSettingsActivity.
2019-09-19 22:28:04 +02:00
84 changed files with 1232 additions and 1452 deletions
+2 -2
View File
@@ -8,5 +8,5 @@
4. Create changelog (`_version code_.txt`) 4. Create changelog (`_version code_.txt`)
5. Create commit with all changes 5. Create commit with all changes
6. Push commits 6. Push commits
7. Tag newly create commit with _version name_ 7. Tag newly create commit with _version name_ (e.g., v3.2.1)
8. Push the tagged to public repository 8. Push the tagged commit to public repository
+1
View File
@@ -83,6 +83,7 @@ android {
dependencies { dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
androidTestImplementation 'androidx.test:core:1.2.0' androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
@@ -0,0 +1,9 @@
Open Tracks – ein GPS-Tracker, der deine Daten für sich behält.
Erfasst deine Kilometer, ohne dass es jemand anderes tut.
Funktionen & Features:
* zeichnet Sport und andere Outdooraktivitäten auf
* unterstützt das Markieren interessanter Orte, inkl. Foto-Funktion
* zeichnet deine Herzfrequenz auf (unterstützt Bluetooth-LE-Sensoren)
* exportiert deine Daten als KML oder GPX
* funktioniert ganz ohne Internet
@@ -0,0 +1 @@
Open Tracks – ein GPS-Tracker, der deine Daten für sich behält.
@@ -1,4 +1,4 @@
v3.2: OpenTracks v3.2.0: OpenTracks
Changes: Changes:
- Removed Widget - Removed Widget
@@ -0,0 +1,7 @@
v3.2.1: OpenTracks
Changes:
- Settings are now on a single page
Developer:
- Settings moved to Androidx compat (uses fragments)
@@ -0,0 +1,13 @@
v3.2.2: OpenTracks
Changes:
- Removed aggregated stats
- Migrated to Material Design
Bugfix:
- Fixed export of sensor data
- Fixed import of incomplete sensor data
Developer:
- Replaced actionbar with toolbar
- Migrated to androidx appcompat (incl. Material Design)
@@ -3,6 +3,6 @@ OpenTracks is a sport tracking application that completely respects your privacy
Features: Features:
* Sport tracking: track your sport and outdoor activities * Sport tracking: track your sport and outdoor activities
* Photos and markers: mark interesting locations while tracking * Photos and markers: mark interesting locations while tracking
* Heart rate: supports bluetooth LE sensors * Heart rate: supports Bluetooth LE sensors
* Export data: Export tracks either as KML or GPX * Export data: export tracks either as KML or GPX
* No Internet access: Internet is not used * No Internet access: Internet is not used

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

@@ -0,0 +1 @@
OpenTracks
@@ -149,8 +149,7 @@ public class StringUtilsTest {
} }
/** /**
* Tests {@link StringUtils#getTime(String)} with fractional seconds and time * Tests {@link StringUtils#getTime(String)} with fractional seconds and time zone.
* zone.
*/ */
@Test @Test
public void testGetTime_fractionalAndTimezone() { public void testGetTime_fractionalAndTimezone() {
@@ -183,7 +182,7 @@ public class StringUtilsTest {
* Tests {@link StringUtils#getTimeParts(long)} with a positive number. * Tests {@link StringUtils#getTimeParts(long)} with a positive number.
*/ */
@Test @Test
public void testGetTimeParts_postive() { public void testGetTimeParts_positive() {
int[] parts = StringUtils.getTimeParts(61000); int[] parts = StringUtils.getTimeParts(61000);
Assert.assertEquals(1, parts[0]); Assert.assertEquals(1, parts[0]);
Assert.assertEquals(1, parts[1]); Assert.assertEquals(1, parts[1]);
+2 -11
View File
@@ -16,8 +16,8 @@ limitations under the License.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.dennisguse.opentracks" package="de.dennisguse.opentracks"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="3054" android:versionCode="3079"
android:versionName="v3.2.0"> <!-- For FDroid. --> android:versionName="v3.2.2"> <!-- For FDroid. -->
<!-- Permission to initialize services --> <!-- Permission to initialize services -->
<application <application
android:name=".Startup" android:name=".Startup"
@@ -30,10 +30,6 @@ limitations under the License.
<!-- Activities --> <!-- Activities -->
<activity android:name=".AboutActivity" /> <activity android:name=".AboutActivity" />
<activity
android:name=".AggregatedStatsActivity"
android:label="@string/menu_aggregated_statistics" />
<activity android:name=".DeleteActivity" /> <activity android:name=".DeleteActivity" />
<activity <activity
@@ -97,12 +93,8 @@ limitations under the License.
android:name=".io.file.exporter.ExportActivity" android:name=".io.file.exporter.ExportActivity"
android:theme="@style/ThemeCustomTransparent" /> android:theme="@style/ThemeCustomTransparent" />
<activity android:name=".settings.RecordingSettingsActivity" />
<activity android:name=".settings.SettingsActivity" /> <activity android:name=".settings.SettingsActivity" />
<activity android:name=".settings.StatsSettingsActivity" />
<!-- Services --> <!-- Services -->
<provider <provider
android:name=".content.ShareContentProvider" android:name=".content.ShareContentProvider"
@@ -128,7 +120,6 @@ limitations under the License.
</receiver> </receiver>
<!-- Providers --> <!-- Providers -->
<service <service
android:name=".services.TrackRecordingService" android:name=".services.TrackRecordingService"
android:enabled="true" android:enabled="true"
@@ -19,12 +19,13 @@ package de.dennisguse.opentracks;
import android.os.Bundle; import android.os.Bundle;
import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech;
import androidx.fragment.app.FragmentActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
/** /**
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public abstract class AbstractActivity extends FragmentActivity { public abstract class AbstractActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -34,6 +35,9 @@ public abstract class AbstractActivity extends FragmentActivity {
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM); setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
setContentView(getLayoutResId()); setContentView(getLayoutResId());
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
} }
/** /**
@@ -16,24 +16,7 @@
package de.dennisguse.opentracks; package de.dennisguse.opentracks;
import android.app.Activity;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.SearchView;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
@@ -58,115 +41,6 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
protected static final int GPS_REQUEST_CODE = 6; protected static final int GPS_REQUEST_CODE = 6;
private static final int DELETE_REQUEST_CODE = 3; private static final int DELETE_REQUEST_CODE = 3;
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// Do nothing
}
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
mode.invalidate();
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
mode.finish();
}
return true;
}
/**
* Gets the checked positions in a list view.
*
* @param list the list view
*/
private int[] getCheckedPositions(ListView list) {
SparseBooleanArray positions = list.getCheckedItemPositions();
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < positions.size(); i++) {
int key = positions.keyAt(i);
if (positions.valueAt(i)) {
arrayList.add(key);
}
}
int[] result = new int[arrayList.size()];
for (int i = 0; i < arrayList.size(); i++) {
result[i] = arrayList.get(i);
}
return result;
}
});
}
public static void configureSearchWidget(Activity activity, final MenuItem menuItem, final TrackController trackController) {
final SearchView searchView = (SearchView) menuItem.getActionView();
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
if (searchManager != null) {
//NOTE: for some reason activity.getComponentName() did not trigger the SearchListActivity
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(activity, SearchListActivity.class)));
} else {
Log.w(TAG, "Could not retrieve SearchManager.");
}
searchView.setQueryRefinementEnabled(true);
searchView.setSubmitButtonEnabled(true);
searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
// Hide and show trackController when searchable widget has focus/no focus
if (trackController != null) {
if (hasFocus) {
trackController.hide();
} else {
trackController.show();
}
}
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
menuItem.collapseActionView();
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
@Override
public boolean onSuggestionSelect(int position) {
return false;
}
@Override
public boolean onSuggestionClick(int position) {
menuItem.collapseActionView();
return false;
}
});
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DELETE_REQUEST_CODE) { if (requestCode == DELETE_REQUEST_CODE) {
@@ -182,8 +56,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
* @param trackIds the track ids * @param trackIds the track ids
*/ */
protected void deleteTracks(long[] trackIds) { protected void deleteTracks(long[] trackIds) {
ConfirmDeleteDialogFragment.newInstance(trackIds) ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds);
.show(getSupportFragmentManager(), ConfirmDeleteDialogFragment.CONFIRM_DELETE_DIALOG_TAG);
} }
@Override @Override
@@ -209,10 +82,9 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
} }
/** /**
* Gets the track recording service connection. For stopping the current * Gets the track recording service connection.
* recording if need to delete the current recording track. * For stopping the current recording if need to delete the current recording track.
*/ */
@NonNull
abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection(); abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection();
/** /**
@@ -1,62 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks;
import android.os.Bundle;
import java.util.List;
import de.dennisguse.opentracks.content.ContentProviderUtils;
import de.dennisguse.opentracks.content.Track;
import de.dennisguse.opentracks.stats.TripStatistics;
import de.dennisguse.opentracks.util.StatsUtils;
/**
* An activity to view aggregated stats from all recorded tracks.
*
* @author Fergus Nelson
*/
public class AggregatedStatsActivity extends AbstractActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatsUtils.setTripStatisticsValues(this, getTripStatistics(), null);
StatsUtils.setLocationValues(this, null, false);
}
@Override
protected int getLayoutResId() {
return R.layout.stats;
}
/**
* Gets the aggregated trip statistics for all the recorded tracks or null if
* there is no track.
*/
private TripStatistics getTripStatistics() {
List<Track> tracks = ContentProviderUtils.Factory.get(this).getAllTracks();
TripStatistics tripStatistics = null;
if (!tracks.isEmpty()) {
tripStatistics = new TripStatistics(tracks.iterator().next().getTripStatistics());
for (int i = 1; i < tracks.size(); i++) {
tripStatistics.merge(tracks.get(i).getTripStatistics());
}
}
return tripStatistics;
}
}
@@ -223,8 +223,8 @@ public class ChartView extends View {
} }
/** /**
* Sets chart by distance. It is expected that after changing this value, data * Sets chart by distance.
* will be reloaded. * It is expected that after changing this value, data will be reloaded.
* *
* @param value true for by distance, false for by time * @param value true for by distance, false for by time
*/ */
@@ -22,14 +22,11 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView; import android.widget.ListView;
@@ -46,6 +43,7 @@ import de.dennisguse.opentracks.content.Waypoint.WaypointType;
import de.dennisguse.opentracks.content.WaypointsColumns; import de.dennisguse.opentracks.content.WaypointsColumns;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.ListItemUtils; import de.dennisguse.opentracks.util.ListItemUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -175,7 +173,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
} }
}; };
listView.setAdapter(resourceCursorAdapter); listView.setAdapter(resourceCursorAdapter);
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback); ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() { LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
@NonNull @NonNull
@@ -234,7 +232,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
insertMarkerMenuItem = menu.findItem(R.id.marker_list_insert_marker); insertMarkerMenuItem = menu.findItem(R.id.marker_list_insert_marker);
searchMenuItem = menu.findItem(R.id.marker_list_search); searchMenuItem = menu.findItem(R.id.marker_list_search);
AbstractListActivity.configureSearchWidget(this, searchMenuItem, null); ActivityUtils.configureSearchWidget(this, searchMenuItem, null);
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@@ -256,24 +254,6 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.list_context_menu, menu);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
if (handleContextItem(item.getItemId(), new long[]{info.id})) {
return true;
}
return super.onContextItemSelected(item);
}
/** /**
* Handles a context item selection. * Handles a context item selection.
* *
@@ -300,8 +280,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
if (markerIds.length > 1 && markerIds.length == listView.getCount()) { if (markerIds.length > 1 && markerIds.length == listView.getCount()) {
markerIds = new long[]{-1L}; markerIds = new long[]{-1L};
} }
DeleteMarkerDialogFragment.newInstance(markerIds) DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), markerIds);
.show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
return true; return true;
case R.id.list_context_menu_select_all: case R.id.list_context_menu_select_all:
int size = listView.getCount(); int size = listView.getCount();
@@ -22,14 +22,11 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ListView; import android.widget.ListView;
@@ -55,10 +52,12 @@ import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.stats.TripStatistics; import de.dennisguse.opentracks.stats.TripStatistics;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.ListItemUtils; import de.dennisguse.opentracks.util.ListItemUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.ToolbarUtils;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
/** /**
@@ -109,28 +108,33 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() { private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@Override @Override
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) { public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
boolean isSingleSelection = positions.length == 1; boolean isSingleSelection = positions.length == 1;
boolean isSingleSelectionTrack; boolean isSingleSelectionTrack;
if (isSingleSelection) { if (isSingleSelection) {
Map<String, Object> item = arrayAdapter.getItem(positions[0]); Map<String, Object> item = arrayAdapter.getItem(positions[0]);
Long trackId = (Long) item.get(TRACK_ID_FIELD);
Track track = contentProviderUtils.getTrack(trackId);
isSingleSelectionTrack = item.get(MARKER_ID_FIELD) == null; isSingleSelectionTrack = item.get(MARKER_ID_FIELD) == null;
} else { } else {
isSingleSelectionTrack = false; isSingleSelectionTrack = false;
} }
// Not recording, one item, item is a track // Not recording, one item, item is a track
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection && isSingleSelectionTrack); MenuItem shareMenuItem = menu.findItem(R.id.list_context_menu_share);
//TODO Setup shareIntent. if (isSingleSelectionTrack) {
shareMenuItem.setVisible(!isRecording);
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
Long trackId = (Long) item.get(TRACK_ID_FIELD);
ToolbarUtils.setupShareActionProvider(SearchListActivity.this, shareMenuItem, new long[]{trackId});
}
// One item, item is a marker // One item, item is a marker
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection && !isSingleSelectionTrack); menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection && !isSingleSelectionTrack);
// One item, can be a track or a marker // One item, can be a track or a marker
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection); menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
// One item. If track, no restriction. // One item. If track, no restriction.
menu.findItem(R.id.list_context_menu_delete).setVisible(isSingleSelection && (isSingleSelectionTrack)); menu.findItem(R.id.list_context_menu_delete).setVisible(isSingleSelection && isSingleSelectionTrack);
// Disable select all, no action is available for multiple selection // Disable select all, no action is available for multiple selection
menu.findItem(R.id.list_context_menu_select_all).setVisible(false); menu.findItem(R.id.list_context_menu_select_all).setVisible(false);
} }
@@ -222,7 +226,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
startActivity(intent); startActivity(intent);
} }
}); });
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback); ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
handleIntent(getIntent()); handleIntent(getIntent());
} }
@@ -259,24 +263,6 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
handleIntent(intent); handleIntent(intent);
} }
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.list_context_menu, menu);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
if (handleContextItem(item.getItemId(), new int[]{info.position})) {
return true;
}
return super.onContextItemSelected(item);
}
@Override @Override
public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH) { if (keyCode == KeyEvent.KEYCODE_SEARCH) {
@@ -301,10 +287,6 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
Long markerId = (Long) item.get(MARKER_ID_FIELD); Long markerId = (Long) item.get(MARKER_ID_FIELD);
Intent intent; Intent intent;
switch (itemId) { switch (itemId) {
case R.id.list_context_menu_share:
//TODO
Log.e(TAG, "Not implemented");
return true;
case R.id.list_context_menu_show_on_map: case R.id.list_context_menu_show_on_map:
IntentUtils.showCoordinateOnMap(this, (double) item.get(MARKER_LATITUDE_FIELD), (double) item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + ""); IntentUtils.showCoordinateOnMap(this, (double) item.get(MARKER_LATITUDE_FIELD), (double) item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + "");
return true; return true;
@@ -323,7 +305,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
return true; return true;
case R.id.list_context_menu_delete: case R.id.list_context_menu_delete:
if (markerId != null) { if (markerId != null) {
DeleteMarkerDialogFragment.newInstance(new long[]{markerId}).show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG); DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), new long[]{markerId});
} else { } else {
deleteTracks(new long[]{trackId}); deleteTracks(new long[]{trackId});
} }
@@ -130,11 +130,11 @@ public class TrackController {
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable); handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
} }
void hide() { public void hide() {
containerView.setVisibility(View.GONE); containerView.setVisibility(View.GONE);
} }
void show() { public void show() {
containerView.setVisibility(View.VISIBLE); containerView.setVisibility(View.VISIBLE);
} }
@@ -31,7 +31,6 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ShareActionProvider;
import android.widget.TabHost; import android.widget.TabHost;
import android.widget.TabHost.TabSpec; import android.widget.TabHost.TabSpec;
import android.widget.Toast; import android.widget.Toast;
@@ -48,7 +47,6 @@ import de.dennisguse.opentracks.content.Waypoint.WaypointType;
import de.dennisguse.opentracks.content.WaypointCreationRequest; import de.dennisguse.opentracks.content.WaypointCreationRequest;
import de.dennisguse.opentracks.fragments.ChartFragment; import de.dennisguse.opentracks.fragments.ChartFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment; import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.FrequencyDialogFragment; import de.dennisguse.opentracks.fragments.FrequencyDialogFragment;
import de.dennisguse.opentracks.fragments.StatsFragment; import de.dennisguse.opentracks.fragments.StatsFragment;
@@ -57,6 +55,7 @@ import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.ToolbarUtils;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.util.TrackUtils; import de.dennisguse.opentracks.util.TrackUtils;
@@ -66,7 +65,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
public class TrackDetailActivity extends AbstractListActivity implements ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller { public class TrackDetailActivity extends AbstractListActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
public static final String EXTRA_TRACK_ID = "track_id"; public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_MARKER_ID = "marker_id"; public static final String EXTRA_MARKER_ID = "marker_id";
@@ -319,8 +318,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
insertPhotoMenuItem.setVisible(new Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(getPackageManager()) != null); insertPhotoMenuItem.setVisible(new Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(getPackageManager()) != null);
shareMenuItem = menu.findItem(R.id.track_detail_share); shareMenuItem = menu.findItem(R.id.track_detail_share);
ShareActionProvider shareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider(); ToolbarUtils.setupShareActionProviderCompat(this, shareMenuItem, new long[]{trackId});
shareActionProvider.setShareIntent(IntentUtils.newShareFileIntent(this, new long[]{trackId}));
voiceFrequencyMenuItem = menu.findItem(R.id.track_detail_voice_frequency); voiceFrequencyMenuItem = menu.findItem(R.id.track_detail_voice_frequency);
splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency); splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency);
@@ -361,12 +359,10 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.track_detail_voice_frequency: case R.id.track_detail_voice_frequency:
FrequencyDialogFragment.newInstance(R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.string.menu_voice_frequency) FrequencyDialogFragment.showDialog(getSupportFragmentManager(), R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.string.menu_voice_frequency);
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
return true; return true;
case R.id.track_detail_split_frequency: case R.id.track_detail_split_frequency:
FrequencyDialogFragment.newInstance(R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.string.menu_split_frequency) FrequencyDialogFragment.showDialog(getSupportFragmentManager(), R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.string.menu_split_frequency);
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
return true; return true;
case R.id.track_detail_edit: case R.id.track_detail_edit:
intent = IntentUtils.newIntent(this, TrackEditActivity.class) intent = IntentUtils.newIntent(this, TrackEditActivity.class)
@@ -395,6 +391,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
} }
return; return;
} }
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
} }
@@ -487,7 +484,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
} }
public void chooseActivityType(String category) { public void chooseActivityType(String category) {
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(), ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG); ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), category);
} }
@Override @Override
@@ -28,10 +28,11 @@ import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Spinner; import android.widget.Spinner;
import androidx.annotation.NonNull;
import de.dennisguse.opentracks.content.ContentProviderUtils; import de.dennisguse.opentracks.content.ContentProviderUtils;
import de.dennisguse.opentracks.content.Track; import de.dennisguse.opentracks.content.Track;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment; import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.util.TrackUtils; import de.dennisguse.opentracks.util.TrackUtils;
@@ -41,7 +42,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
* *
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
*/ */
public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeCaller { public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
public static final String EXTRA_TRACK_ID = "track_id"; public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_NEW_TRACK = "new_track"; public static final String EXTRA_NEW_TRACK = "new_track";
@@ -119,9 +120,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) { if (event.getAction() == MotionEvent.ACTION_UP) {
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show( ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
getSupportFragmentManager(),
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
} }
return true; return true;
} }
@@ -130,9 +129,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
@Override @Override
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show( ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
getSupportFragmentManager(),
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
} }
return true; return true;
} }
@@ -182,7 +179,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
} }
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putString(ICON_VALUE_KEY, iconValue); outState.putString(ICON_VALUE_KEY, iconValue);
} }
@@ -28,8 +28,6 @@ import android.location.LocationManager;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity; import android.view.Gravity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
@@ -37,14 +35,12 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView; import android.widget.ListView;
import android.widget.SearchView;
import android.widget.ShareActionProvider;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.widget.SearchView;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.cursoradapter.widget.ResourceCursorAdapter; import androidx.cursoradapter.widget.ResourceCursorAdapter;
import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager;
@@ -60,10 +56,12 @@ import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.services.ITrackRecordingService; import de.dennisguse.opentracks.services.ITrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.SettingsActivity; import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.ListItemUtils; import de.dennisguse.opentracks.util.ListItemUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.ToolbarUtils;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.util.TrackUtils; import de.dennisguse.opentracks.util.TrackUtils;
@@ -118,8 +116,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
boolean isSingleSelection = trackIds.length == 1; boolean isSingleSelection = trackIds.length == 1;
MenuItem shareMenuItem = menu.findItem(R.id.list_context_menu_share); MenuItem shareMenuItem = menu.findItem(R.id.list_context_menu_share);
ShareActionProvider shareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider(); ToolbarUtils.setupShareActionProvider(TrackListActivity.this, shareMenuItem, trackIds);
shareActionProvider.setShareIntent(trackIds.length == 0 ? null : IntentUtils.newShareFileIntent(TrackListActivity.this, trackIds));
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection); menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll); menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
@@ -148,7 +145,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
PreferencesUtils.getKey(TrackListActivity.this, R.string.recording_track_id_key))) { PreferencesUtils.getKey(TrackListActivity.this, R.string.recording_track_id_key))) {
recordingTrackId = PreferencesUtils.getLong( recordingTrackId = PreferencesUtils.getLong(
TrackListActivity.this, R.string.recording_track_id_key); TrackListActivity.this, R.string.recording_track_id_key);
if (key != null && recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) { if (key != null && PreferencesUtils.isRecording(recordingTrackId)) {
trackRecordingServiceConnection.startAndBind(); trackRecordingServiceConnection.startAndBind();
} }
} }
@@ -164,7 +161,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
public void run() { public void run() {
TrackListActivity.this.invalidateOptionsMenu(); TrackListActivity.this.invalidateOptionsMenu();
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks); LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
trackController.update(isRecording, recordingTrackPaused); trackController.update(isRecording, recordingTrackPaused);
} }
}); });
@@ -175,7 +172,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Menu items // Menu items
private MenuItem searchMenuItem; private MenuItem searchMenuItem;
private MenuItem startGpsMenuItem; private MenuItem startGpsMenuItem;
private MenuItem aggregatedStatisticsMenuItem;
private MenuItem deleteAllMenuItem; private MenuItem deleteAllMenuItem;
private final OnClickListener stopListener = new OnClickListener() { private final OnClickListener stopListener = new OnClickListener() {
@@ -200,7 +196,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
trackController.update(recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, recordingTrackPaused); trackController.update(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused);
} }
}); });
@@ -317,7 +313,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
} }
}; };
listView.setAdapter(resourceCursorAdapter); listView.setAdapter(resourceCursorAdapter);
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
LoaderManager.getInstance(this).initLoader(0, null, loaderCallbacks); LoaderManager.getInstance(this).initLoader(0, null, loaderCallbacks);
showStartupDialogs(); showStartupDialogs();
@@ -339,8 +336,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
// Update UI // Update UI
this.invalidateOptionsMenu(); this.invalidateOptionsMenu();
LoaderManager.getInstance(this).restartLoader(0, null, loaderCallbacks); LoaderManager.getInstance(this).restartLoader(0, null, loaderCallbacks);
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; trackController.onResume(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused);
trackController.onResume(isRecording, recordingTrackPaused);
} }
@Override @Override
@@ -380,10 +376,9 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
getMenuInflater().inflate(R.menu.track_list, menu); getMenuInflater().inflate(R.menu.track_list, menu);
searchMenuItem = menu.findItem(R.id.track_list_search); searchMenuItem = menu.findItem(R.id.track_list_search);
AbstractListActivity.configureSearchWidget(this, searchMenuItem, trackController); ActivityUtils.configureSearchWidget(this, searchMenuItem, trackController);
startGpsMenuItem = menu.findItem(R.id.track_list_start_gps); startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
aggregatedStatisticsMenuItem = menu.findItem(R.id.track_list_aggregated_statistics);
deleteAllMenuItem = menu.findItem(R.id.track_list_delete_all); deleteAllMenuItem = menu.findItem(R.id.track_list_delete_all);
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
@@ -392,13 +387,12 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
boolean isGpsStarted = TrackRecordingServiceConnection.isRecordingServiceRunning(this); boolean isGpsStarted = TrackRecordingServiceConnection.isRecordingServiceRunning(this);
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
updateMenuItems(isGpsStarted, isRecording); updateMenuItems(isGpsStarted, isRecording);
View searchView = searchMenuItem.getActionView(); SearchView searchView = (SearchView) searchMenuItem.getActionView();
if (searchView instanceof SearchView) { searchView.setQuery("", false);
((SearchView) searchView).setQuery("", false);
}
return super.onPrepareOptionsMenu(menu); return super.onPrepareOptionsMenu(menu);
} }
@@ -439,10 +433,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
intent = IntentUtils.newIntent(this, MarkerListActivity.class); intent = IntentUtils.newIntent(this, MarkerListActivity.class);
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.track_list_aggregated_statistics:
intent = IntentUtils.newIntent(this, AggregatedStatsActivity.class);
startActivity(intent);
return true;
case R.id.track_list_delete_all: case R.id.track_list_delete_all:
deleteTracks(new long[]{-1L}); deleteTracks(new long[]{-1L});
return true; return true;
@@ -454,24 +444,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.list_context_menu, menu);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
if (handleContextItem(item.getItemId(), new long[]{info.id})) {
return true;
}
return super.onContextItemSelected(item);
}
@Override @Override
public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH && searchMenuItem != null) { if (keyCode == KeyEvent.KEYCODE_SEARCH && searchMenuItem != null) {
@@ -531,9 +503,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
startGpsMenuItem.setIcon(isGpsStarted ? R.drawable.ic_gps_fixed_24dp : R.drawable.ic_gps_off_24dp); startGpsMenuItem.setIcon(isGpsStarted ? R.drawable.ic_gps_fixed_24dp : R.drawable.ic_gps_off_24dp);
} }
} }
if (aggregatedStatisticsMenuItem != null) {
aggregatedStatisticsMenuItem.setVisible(hasTrack);
}
if (deleteAllMenuItem != null) { if (deleteAllMenuItem != null) {
deleteAllMenuItem.setVisible(hasTrack && !isRecording); deleteAllMenuItem.setVisible(hasTrack && !isRecording);
} }
@@ -208,7 +208,7 @@ public class TrackDataHub implements DataSourceListener {
* Returns true if the selected track is recording. * Returns true if the selected track is recording.
*/ */
public boolean isSelectedTrackRecording() { public boolean isSelectedTrackRecording() {
return selectedTrackId == recordingTrackId && recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; return selectedTrackId == recordingTrackId && PreferencesUtils.isRecording(recordingTrackId);
} }
/** /**
@@ -2,7 +2,7 @@ package de.dennisguse.opentracks.content.sensor;
public final class SensorDataSet { public final class SensorDataSet {
public static final float DATA_UNAVAILABLE = Integer.MAX_VALUE; public static final float DATA_UNAVAILABLE = Float.NaN;
//TODO It might be necessary to consider: sensor if sensor was connected as well. //TODO It might be necessary to consider: sensor if sensor was connected as well.
public String sensorName; public String sensorName;
@@ -40,7 +40,7 @@ public final class SensorDataSet {
} }
public boolean hasHeartRate() { public boolean hasHeartRate() {
return heartRate != DATA_UNAVAILABLE; return !Float.isNaN(heartRate);
} }
public float getHeartRate() { public float getHeartRate() {
@@ -48,7 +48,7 @@ public final class SensorDataSet {
} }
public boolean hasCadence() { public boolean hasCadence() {
return cadence != DATA_UNAVAILABLE; return !Float.isNaN(cadence);
} }
public float getCadence() { public float getCadence() {
@@ -56,7 +56,7 @@ public final class SensorDataSet {
} }
public boolean hasPower() { public boolean hasPower() {
return power != DATA_UNAVAILABLE; return !Float.isNaN(power);
} }
public float getPower() { public float getPower() {
@@ -77,7 +77,7 @@ public final class SensorDataSet {
} }
public boolean hasBatteryLevel() { public boolean hasBatteryLevel() {
return batteryLevel != DATA_UNAVAILABLE; return !Float.isNaN(batteryLevel);
} }
public float getBatteryLevel() { public float getBatteryLevel() {
@@ -352,8 +352,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
/** /**
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can * Resumes the trackDataHub.
* be accessed by multiple threads. * Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/ */
private synchronized void resumeTrackDataHub() { private synchronized void resumeTrackDataHub() {
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub(); trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
@@ -363,8 +363,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
/** /**
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can * Pauses the trackDataHub.
* be accessed by multiple threads. * Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/ */
private synchronized void pauseTrackDataHub() { private synchronized void pauseTrackDataHub() {
trackDataHub.unregisterTrackDataListener(this); trackDataHub.unregisterTrackDataListener(this);
@@ -372,16 +372,16 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
/** /**
* Returns true if the selected track is recording. Needs to be synchronized * Returns true if the selected track is recording.
* because trackDataHub can be accessed by multiple threads. * Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/ */
private synchronized boolean isSelectedTrackRecording() { private synchronized boolean isSelectedTrackRecording() {
return trackDataHub != null && trackDataHub.isSelectedTrackRecording(); return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
} }
/** /**
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can * Reloads the trackDataHub.
* be accessed by multiple threads. * Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/ */
private synchronized void reloadTrackDataHub() { private synchronized void reloadTrackDataHub() {
if (trackDataHub != null) { if (trackDataHub != null) {
@@ -389,18 +389,12 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
} }
/**
* To zoom in.
*/
private void zoomIn() { private void zoomIn() {
chartView.zoomIn(); chartView.zoomIn();
zoomControls.setIsZoomInEnabled(chartView.canZoomIn()); zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut()); zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
} }
/**
* To zoom out.
*/
private void zoomOut() { private void zoomOut() {
chartView.zoomOut(); chartView.zoomOut();
zoomControls.setIsZoomInEnabled(chartView.canZoomIn()); zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
@@ -438,9 +432,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
double elevation = Double.NaN; double elevation = Double.NaN;
double speed = Double.NaN; double speed = Double.NaN;
double pace = Double.NaN; double pace = Double.NaN;
double heartRate = Double.NaN;
double cadence = Double.NaN;
double power = Double.NaN;
if (tripStatisticsUpdater != null) { if (tripStatisticsUpdater != null) {
tripStatisticsUpdater.addLocation(location, recordingDistanceInterval); tripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
@@ -466,6 +457,10 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
pace = speed == 0 ? 0.0 : 60.0 / speed; pace = speed == 0 ? 0.0 : 60.0 / speed;
} }
double heartRate = Double.NaN;
double cadence = Double.NaN;
double power = Double.NaN;
if (location instanceof SensorDataSetLocation && ((SensorDataSetLocation) location).getSensorDataSet() != null) { if (location instanceof SensorDataSetLocation && ((SensorDataSetLocation) location).getSensorDataSet() != null) {
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet(); SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
if (sensorDataSet.hasHeartRate()) { if (sensorDataSet.hasHeartRate()) {
@@ -479,6 +474,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
} }
//TODO: Is related to ChartView.ELEVATION_SERIES etc.
if (data != null) { if (data != null) {
data[0] = timeOrDistance; data[0] = timeOrDistance;
data[1] = elevation; data[1] = elevation;
@@ -1,34 +1,18 @@
/*
* 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; package de.dennisguse.opentracks.fragments;
import android.app.AlertDialog; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView; import android.widget.GridView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -38,56 +22,13 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
/** /**
* A DialogFragment to choose an activity type. * A DialogFragment to choose an activity type.
*
* @author apoorvn
*/ */
public class ChooseActivityTypeDialogFragment extends DialogFragment { public class ChooseActivityTypeDialogFragment extends DialogFragment implements AdapterView.OnItemClickListener {
public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType"; private static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
private static final String KEY_CATEGORY = "category";
private ChooseActivityTypeCaller caller;
public static ChooseActivityTypeDialogFragment newInstance(String category) { public static void showDialog(FragmentManager fragmentManager, String preselectedCategory) {
Bundle bundle = new Bundle(); new ChooseActivityTypeDialogFragment(preselectedCategory).show(fragmentManager, ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
bundle.putString(KEY_CATEGORY, category);
ChooseActivityTypeDialogFragment fragment = new ChooseActivityTypeDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
public static Dialog getDialog(final Context context, final String category, final ChooseActivityTypeCaller caller) {
View view = LayoutInflater.from(context).inflate(R.layout.choose_activity_type, null);
GridView gridView = view.findViewById(R.id.choose_activity_type_grid_view);
List<Integer> imageIds = new ArrayList<>();
for (String iconValue : TrackIconUtils.getAllIconValues()) {
imageIds.add(TrackIconUtils.getIconDrawable(iconValue));
}
final ChooseActivityTypeImageAdapter imageAdapter = new ChooseActivityTypeImageAdapter(imageIds);
gridView.setAdapter(imageAdapter);
final AlertDialog alertDialog = new AlertDialog.Builder(context).setTitle(R.string.track_edit_activity_type_hint).setView(view).create();
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
int position = getPosition(context, category);
if (position != -1) {
imageAdapter.setSelected(position);
imageAdapter.notifyDataSetChanged();
}
}
});
gridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
caller.onChooseActivityTypeDone(TrackIconUtils.getAllIconValues().get(position));
alertDialog.dismiss();
}
});
return alertDialog;
} }
private static int getPosition(Context context, String category) { private static int getPosition(Context context, String category) {
@@ -99,32 +40,79 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment {
return TrackIconUtils.getAllIconValues().indexOf(iconValue); return TrackIconUtils.getAllIconValues().indexOf(iconValue);
} }
@Override private String preselectedCategory;
public void onAttach(@NonNull Context context) {
super.onAttach(context); private ChooseActivityTypeCaller chooseActivityTypeCaller;
try {
caller = (ChooseActivityTypeCaller) context; private ChooseActivityTypeDialogFragment(String preselectedCategory) {
} catch (ClassCastException e) { this.preselectedCategory = preselectedCategory;
throw new ClassCastException(context + " must implement " + ChooseActivityTypeCaller.class.getSimpleName());
}
} }
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
return getDialog(getActivity(), getArguments().getString(KEY_CATEGORY), caller); Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setTitle(R.string.track_edit_activity_type_hint);
return dialog;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.choose_activity_type, container);
GridView gridView = view.findViewById(R.id.choose_activity_type_grid_view);
List<Integer> imageIds = new ArrayList<>();
for (String iconValue : TrackIconUtils.getAllIconValues()) {
imageIds.add(TrackIconUtils.getIconDrawable(iconValue));
}
final ChooseActivityTypeImageAdapter imageAdapter = new ChooseActivityTypeImageAdapter(imageIds);
gridView.setAdapter(imageAdapter);
int position = getPosition(getContext(), preselectedCategory);
if (position != -1) {
imageAdapter.setSelected(position);
imageAdapter.notifyDataSetChanged();
}
gridView.setOnItemClickListener(this);
return view;
} }
/** /**
* Interface for caller of this dialog fragment. * TODO: Only used by obsolete Androidx preferences. Remove when onAttach(Context) is called.
* */
* @author apoorvn @Override
public void onAttach(@NonNull Activity activity) {
super.onAttach(activity);
try {
chooseActivityTypeCaller = (ChooseActivityTypeCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity + " must implement " + ChooseActivityTypeCaller.class.getSimpleName());
}
}
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
chooseActivityTypeCaller = (ChooseActivityTypeCaller) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + ChooseActivityTypeCaller.class.getSimpleName());
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
chooseActivityTypeCaller.onChooseActivityTypeDone(TrackIconUtils.getAllIconValues().get(position));
dismiss();
}
/**
* Interface for chooseActivityTypeCaller of this dialog fragment.
*/ */
public interface ChooseActivityTypeCaller { public interface ChooseActivityTypeCaller {
/**
* Called when choose activity type is done.
*/
void onChooseActivityTypeDone(String iconValue); void onChooseActivityTypeDone(String iconValue);
} }
} }
@@ -23,6 +23,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.DialogUtils; import de.dennisguse.opentracks.util.DialogUtils;
@@ -34,22 +35,21 @@ import de.dennisguse.opentracks.util.DialogUtils;
*/ */
public class ConfirmDeleteDialogFragment extends DialogFragment { public class ConfirmDeleteDialogFragment extends DialogFragment {
public static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog"; private static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
private static final String KEY_TRACK_IDS = "trackIds";
private ConfirmDeleteCaller caller; private ConfirmDeleteCaller caller;
private long[] trackIds;
public ConfirmDeleteDialogFragment(long[] trackIds) {
this.trackIds = trackIds;
}
/** /**
* Create a new instance. * Create a new instance.
* *
* @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L * @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L
*/ */
public static ConfirmDeleteDialogFragment newInstance(long[] trackIds) { public static void showDialog(FragmentManager fragmentManager, long[] trackIds) {
Bundle bundle = new Bundle(); new ConfirmDeleteDialogFragment(trackIds).show(fragmentManager, CONFIRM_DELETE_DIALOG_TAG);
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
deleteTrackDialogFragment.setArguments(bundle);
return deleteTrackDialogFragment;
} }
@Override @Override
@@ -62,11 +62,9 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
} }
} }
@Override @Override
@NonNull @NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
int titleId; int titleId;
int messageId; int messageId;
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title; titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
@@ -24,6 +24,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.ContentProviderUtils; import de.dennisguse.opentracks.content.ContentProviderUtils;
@@ -37,17 +38,17 @@ import de.dennisguse.opentracks.util.DialogUtils;
*/ */
public class DeleteMarkerDialogFragment extends DialogFragment { public class DeleteMarkerDialogFragment extends DialogFragment {
public static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog"; private static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
private static final String KEY_MARKER_IDS = "markerIds"; private long[] markerIds;
private DeleteMarkerCaller caller; private DeleteMarkerCaller caller;
public static DeleteMarkerDialogFragment newInstance(long[] markerIds) { public DeleteMarkerDialogFragment(long[] markerIds) {
Bundle bundle = new Bundle(); this.markerIds = markerIds;
bundle.putLongArray(KEY_MARKER_IDS, markerIds); }
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment(); public static void showDialog(FragmentManager fragmentManager, long[] markerIds) {
deleteMarkerDialogFragment.setArguments(bundle); new DeleteMarkerDialogFragment(markerIds).show(fragmentManager, DELETE_MARKER_DIALOG_TAG);
return deleteMarkerDialogFragment;
} }
@Override @Override
@@ -64,7 +65,6 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
@NonNull @NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity fragmentActivity = getActivity(); final FragmentActivity fragmentActivity = getActivity();
final long[] markerIds = getArguments().getLongArray(KEY_MARKER_IDS);
int titleId; int titleId;
int messageId; int messageId;
if (markerIds.length == 1 && markerIds[0] == -1L) { if (markerIds.length == 1 && markerIds[0] == -1L) {
@@ -81,8 +81,7 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get( ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(fragmentActivity);
fragmentActivity);
for (long markerId : markerIds) { for (long markerId : markerIds) {
contentProviderUtils.deleteWaypoint(fragmentActivity, contentProviderUtils.deleteWaypoint(fragmentActivity,
markerId, new DescriptionGeneratorImpl(fragmentActivity)); markerId, new DescriptionGeneratorImpl(fragmentActivity));
@@ -25,6 +25,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import java.util.Locale; import java.util.Locale;
@@ -39,19 +40,19 @@ import de.dennisguse.opentracks.util.FileUtils;
*/ */
public class FileTypeDialogFragment extends DialogFragment { public class FileTypeDialogFragment extends DialogFragment {
public static final String FILE_TYPE_DIALOG_TAG = "fileType"; private static final String FILE_TYPE_DIALOG_TAG = "fileType";
private static final String KEY_TITLE_ID = "titleId"; private int titleId;
private static final String KEY_OPTION_ID = "optionId";
private FileTypeCaller caller; private FileTypeCaller caller;
private int optionId;
public static FileTypeDialogFragment newInstance(int titleId, int optionId) { public FileTypeDialogFragment(int titleId, int optionId) {
Bundle bundle = new Bundle(); this.titleId = titleId;
bundle.putInt(KEY_TITLE_ID, titleId); this.optionId = optionId;
bundle.putInt(KEY_OPTION_ID, optionId); }
FileTypeDialogFragment fileTypeDialogFragment = new FileTypeDialogFragment(); public static void showDialog(FragmentManager fragmentManager, int titleId, int optionId) {
fileTypeDialogFragment.setArguments(bundle); new FileTypeDialogFragment(titleId, optionId).show(fragmentManager, FILE_TYPE_DIALOG_TAG);
return fileTypeDialogFragment;
} }
@Override @Override
@@ -67,8 +68,6 @@ public class FileTypeDialogFragment extends DialogFragment {
@Override @Override
@NonNull @NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) { 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.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX}; final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
String[] choices = new String[trackFileFormats.length]; String[] choices = new String[trackFileFormats.length];
for (int i = 0; i < choices.length; i++) { for (int i = 0; i < choices.length; i++) {
@@ -96,7 +95,7 @@ public class FileTypeDialogFragment extends DialogFragment {
} }
@Override @Override
public void onCancel(DialogInterface dialog) { public void onCancel(@NonNull DialogInterface dialog) {
onDismissed(); onDismissed();
} }
@@ -24,6 +24,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -36,21 +37,19 @@ import de.dennisguse.opentracks.util.StringUtils;
*/ */
public class FrequencyDialogFragment extends DialogFragment { public class FrequencyDialogFragment extends DialogFragment {
public static final String FREQUENCY_DIALOG_TAG = "frequencyDialog"; private static final String FREQUENCY_DIALOG_TAG = "frequencyDialog";
private int preferenceId;
private int defaultValue;
private int titleId;
private static final String KEY_PREFERENCE_ID = "preferenceId"; public FrequencyDialogFragment(int preferenceId, int defaultValue, int titleId) {
private static final String KEY_DEFAULT_VALUE = "defaultValue"; this.preferenceId = preferenceId;
private static final String KEY_TITLE_ID = "titleId"; this.defaultValue = defaultValue;
this.titleId = titleId;
}
public static FrequencyDialogFragment newInstance(int preferenceId, int defaultValue, int titleId) { public static void showDialog(FragmentManager fragmentManager, int preferenceId, int defaultValue, int titleId) {
Bundle bundle = new Bundle(); new FrequencyDialogFragment(preferenceId, defaultValue, titleId).show(fragmentManager, FREQUENCY_DIALOG_TAG);
bundle.putInt(KEY_PREFERENCE_ID, preferenceId);
bundle.putInt(KEY_DEFAULT_VALUE, defaultValue);
bundle.putInt(KEY_TITLE_ID, titleId);
FrequencyDialogFragment frequencyDialogFragment = new FrequencyDialogFragment();
frequencyDialogFragment.setArguments(bundle);
return frequencyDialogFragment;
} }
@Override @Override
@@ -58,9 +57,6 @@ public class FrequencyDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
FragmentActivity fragmentActivity = getActivity(); FragmentActivity fragmentActivity = getActivity();
final int preferenceId = getArguments().getInt(KEY_PREFERENCE_ID);
int defaultValue = getArguments().getInt(KEY_DEFAULT_VALUE);
int titleId = getArguments().getInt(KEY_TITLE_ID);
int frequencyValue = PreferencesUtils.getInt(fragmentActivity, preferenceId, defaultValue); int frequencyValue = PreferencesUtils.getInt(fragmentActivity, preferenceId, defaultValue);
return new AlertDialog.Builder(fragmentActivity).setPositiveButton( return new AlertDialog.Builder(fragmentActivity).setPositiveButton(
@@ -192,8 +192,7 @@ public class MarkerDetailFragment extends Fragment {
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.marker_detail_delete: case R.id.marker_detail_delete:
DeleteMarkerDialogFragment.newInstance(new long[]{markerId}) DeleteMarkerDialogFragment.showDialog(getChildFragmentManager(), new long[]{markerId});
.show(getChildFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@@ -84,9 +84,7 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
} }
private void fileTypeDialogStart() { private void fileTypeDialogStart() {
FileTypeDialogFragment FileTypeDialogFragment.showDialog(getSupportFragmentManager(), R.string.export_all_title, R.string.export_all_option);
.newInstance(R.string.export_all_title, R.string.export_all_option)
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
} }
@Override @Override
@@ -99,7 +99,7 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
try { try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY); Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
boolean isRecording = PreferencesUtils.getLong(exportActivity, R.string.recording_track_id_key) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; boolean isRecording = PreferencesUtils.isRecording(exportActivity);
boolean isPaused = PreferencesUtils.getBoolean(exportActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT); boolean isPaused = PreferencesUtils.getBoolean(exportActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
// Get the wake lock if not recording or paused // Get the wake lock if not recording or paused
if (!isRecording || isPaused) { if (!isRecording || isPaused) {
@@ -49,9 +49,10 @@ public class KmlTrackWriter implements TrackWriter {
private static final String END_STYLE = "end"; private static final String END_STYLE = "end";
private static final String TRACK_STYLE = "track"; private static final String TRACK_STYLE = "track";
private static final String SCHEMA_ID = "schema"; private static final String SCHEMA_ID = "schema";
private static final String CADENCE = "cadence";
private static final String HEART_RATE = "heart_rate"; private static final String SENSOR_TYPE_CADENCE = "cadence";
private static final String POWER = "power"; private static final String SENSOR_TYPE_HEART_RATE = "heart_rate";
private static final String SENSOR_TYPE_POWER = "power";
private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png"; private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
private static final String STATISTICS_ICON = "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png"; private static final String STATISTICS_ICON = "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";
@@ -71,9 +72,6 @@ public class KmlTrackWriter implements TrackWriter {
private List<Float> powerList = new ArrayList<>(); private List<Float> powerList = new ArrayList<>();
private List<Float> cadenceList = new ArrayList<>(); private List<Float> cadenceList = new ArrayList<>();
private List<Float> heartRateList = new ArrayList<>(); private List<Float> heartRateList = new ArrayList<>();
private boolean hasPower;
private boolean hasCadence;
private boolean hasHeartRate;
private Location startLocation; private Location startLocation;
@@ -132,9 +130,9 @@ public class KmlTrackWriter implements TrackWriter {
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">"); printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
if (exportSensorData) { if (exportSensorData) {
writeSensorStyle(POWER, context.getString(R.string.description_sensor_power)); writeSensorStyle(SENSOR_TYPE_POWER, context.getString(R.string.description_sensor_power));
writeSensorStyle(CADENCE, context.getString(R.string.description_sensor_cadence)); writeSensorStyle(SENSOR_TYPE_CADENCE, context.getString(R.string.description_sensor_cadence));
writeSensorStyle(HEART_RATE, context.getString(R.string.description_sensor_heart_rate)); writeSensorStyle(SENSOR_TYPE_HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
} }
printWriter.println("</Schema>"); printWriter.println("</Schema>");
} }
@@ -235,9 +233,6 @@ public class KmlTrackWriter implements TrackWriter {
public void writeOpenSegment() { public void writeOpenSegment() {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("<gx:Track>"); printWriter.println("<gx:Track>");
hasPower = false;
hasCadence = false;
hasHeartRate = false;
powerList.clear(); powerList.clear();
cadenceList.clear(); cadenceList.clear();
heartRateList.clear(); heartRateList.clear();
@@ -250,14 +245,14 @@ public class KmlTrackWriter implements TrackWriter {
printWriter.println("<ExtendedData>"); printWriter.println("<ExtendedData>");
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">"); printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
if (exportSensorData) { if (exportSensorData) {
if (hasPower) { if (powerList.size() > 0) {
writeSensorData(powerList, POWER); writeSensorData(powerList, SENSOR_TYPE_POWER);
} }
if (hasCadence) { if (cadenceList.size() > 0) {
writeSensorData(cadenceList, CADENCE); writeSensorData(cadenceList, SENSOR_TYPE_CADENCE);
} }
if (hasHeartRate) { if (heartRateList.size() > 0) {
writeSensorData(heartRateList, HEART_RATE); writeSensorData(heartRateList, SENSOR_TYPE_HEART_RATE);
} }
} }
printWriter.println("</SchemaData>"); printWriter.println("</SchemaData>");
@@ -466,11 +461,11 @@ public class KmlTrackWriter implements TrackWriter {
* Writes a sensor style. * Writes a sensor style.
* *
* @param name the name of the sesnor * @param name the name of the sesnor
* @param displayName the sensor display name * @param sensorType the sensor display name
*/ */
private void writeSensorStyle(String name, String displayName) { private void writeSensorStyle(String name, String sensorType) {
printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"int\">"); printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"float\">");
printWriter.println("<displayName>" + StringUtils.formatCData(displayName) + "</displayName>"); printWriter.println("<displayName>" + StringUtils.formatCData(sensorType) + "</displayName>");
printWriter.println("</gx:SimpleArrayField>"); printWriter.println("</gx:SimpleArrayField>");
} }
} }
@@ -384,8 +384,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
* @param message the message * @param message the message
*/ */
protected String createErrorMessage(String message) { protected String createErrorMessage(String message) {
return String.format(Locale.US, "Parsing error at line: %d column: %d. %s", return String.format(Locale.US, "Parsing error at line: %d column: %d. %s", locator.getLineNumber(), locator.getColumnNumber(), message);
locator.getLineNumber(), locator.getColumnNumber(), message);
} }
/** /**
@@ -85,9 +85,7 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
} }
private void fileTypeDialogStart() { private void fileTypeDialogStart() {
FileTypeDialogFragment FileTypeDialogFragment.showDialog(getSupportFragmentManager(), R.string.import_selection_title, R.string.import_selection_option);
.newInstance(R.string.import_selection_title, R.string.import_selection_option)
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
} }
@Override @Override
@@ -103,7 +103,7 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
try { try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY); Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
// Get the wake lock if not recording or paused // Get the wake lock if not recording or paused
boolean isRecording = PreferencesUtils.getLong(importActivity, R.string.recording_track_id_key) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; boolean isRecording = PreferencesUtils.isRecording(importActivity);
boolean isPaused = PreferencesUtils.getBoolean(importActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT); boolean isPaused = PreferencesUtils.getBoolean(importActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
if (!isRecording || isPaused) { if (!isRecording || isPaused) {
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock); wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
@@ -19,6 +19,7 @@ package de.dennisguse.opentracks.io.file.importer;
import android.content.Context; import android.content.Context;
import android.location.Location; import android.location.Location;
import android.net.Uri; import android.net.Uri;
import android.util.Log;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -39,6 +40,8 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
*/ */
public class KmlFileTrackImporter extends AbstractFileTrackImporter { public class KmlFileTrackImporter extends AbstractFileTrackImporter {
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
private static final String CADENCE = "cadence"; private static final String CADENCE = "cadence";
private static final String HEART_RATE = "heart_rate"; private static final String HEART_RATE = "heart_rate";
private static final String POWER = "power"; private static final String POWER = "power";
@@ -65,11 +68,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
private static final String ATTRIBUTE_NAME = "name"; private static final String ATTRIBUTE_NAME = "name";
private boolean trackStarted = false; private boolean trackStarted = false;
private String sensorName; private String sensorType;
private ArrayList<Location> locationList; private ArrayList<Location> locationList;
private ArrayList<Integer> cadenceList; private ArrayList<Float> cadenceList;
private ArrayList<Integer> heartRateList; private ArrayList<Float> heartRateList;
private ArrayList<Integer> powerList; private ArrayList<Float> powerList;
/** /**
* Constructor. * Constructor.
@@ -229,20 +232,30 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
*/ */
private void onTrackSegmentEnd() { private void onTrackSegmentEnd() {
// Close a track segment by inserting the segment locations // Close a track segment by inserting the segment locations
boolean hasHeartRate = heartRateList.size() == locationList.size();
boolean hasCadence = cadenceList.size() == locationList.size();
boolean hasPower = powerList.size() == locationList.size();
for (int i = 0; i < locationList.size(); i++) { for (int i = 0; i < locationList.size(); i++) {
Location location = locationList.get(i); Location location = locationList.get(i);
if (!hasPower && !hasCadence && !hasHeartRate) { boolean hasSensorData = false;
float heartrate = SensorDataSet.DATA_UNAVAILABLE;
float cadence = SensorDataSet.DATA_UNAVAILABLE;
float power = SensorDataSet.DATA_UNAVAILABLE;
if (i < heartRateList.size()) {
heartrate = heartRateList.get(i);
hasSensorData = true;
}
if (i < cadenceList.size()) {
cadence = cadenceList.get(i);
hasSensorData = true;
}
if (i < powerList.size()) {
power = powerList.get(i);
hasSensorData = true;
}
if (!hasSensorData) {
insertTrackPoint(location); insertTrackPoint(location);
} else { } else {
float heartrate = hasHeartRate ? heartRateList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
float cadence = hasHeartRate ? cadenceList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
float power = hasHeartRate ? powerList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
SensorDataSetLocation sensorDataSetLocation = new SensorDataSetLocation(location, new SensorDataSet(heartrate, cadence, power, SensorDataSet.DATA_UNAVAILABLE, location.getTime())); SensorDataSetLocation sensorDataSetLocation = new SensorDataSetLocation(location, new SensorDataSet(heartrate, cadence, power, SensorDataSet.DATA_UNAVAILABLE, location.getTime()));
insertTrackPoint(sensorDataSetLocation); insertTrackPoint(sensorDataSetLocation);
} }
@@ -277,7 +290,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
* On sensor data start. gx:SimpleArrayData start tag. * On sensor data start. gx:SimpleArrayData start tag.
*/ */
private void onSensorDataStart(Attributes attributes) { private void onSensorDataStart(Attributes attributes) {
sensorName = attributes.getValue(ATTRIBUTE_NAME); sensorType = attributes.getValue(ATTRIBUTE_NAME);
} }
/** /**
@@ -291,18 +304,24 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
if (content.equals("")) { if (content.equals("")) {
return; return;
} }
int value; float value;
try { try {
value = Integer.parseInt(content); value = Float.valueOf(content);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e); throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
} }
if (POWER.equals(sensorName)) { switch (sensorType) {
case POWER:
powerList.add(value); powerList.add(value);
} else if (HEART_RATE.equals(sensorName)) { break;
case HEART_RATE:
heartRateList.add(value); heartRateList.add(value);
} else if (CADENCE.equals(sensorName)) { break;
case CADENCE:
cadenceList.add(value); cadenceList.add(value);
break;
default:
Log.w(TAG, "Data from sensor " + sensorType + " is not (yet) supported.");
} }
} }
} }
@@ -119,10 +119,9 @@ public class TrackRecordingService extends Service {
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.recording_track_id_key))) { if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.recording_track_id_key))) {
long trackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
// Only through the TrackRecordingService can one stop a recording and set the recordingTrackId to -1L. // Only through the TrackRecordingService can one stop a recording and set the recordingTrackId to -1L.
if (trackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) { if (PreferencesUtils.isRecording(context)) {
recordingTrackId = trackId; recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
} }
} }
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.recording_track_paused_key))) { if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.recording_track_paused_key))) {
@@ -310,7 +309,7 @@ public class TrackRecordingService extends Service {
* Returns true if the service is recording. * Returns true if the service is recording.
*/ */
public boolean isRecording() { public boolean isRecording() {
return recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; return PreferencesUtils.isRecording(recordingTrackId);
} }
/** /**
@@ -854,7 +853,7 @@ public class TrackRecordingService extends Service {
*/ */
private void insertLocation(Track track, Location location, Location lastValidTrackPoint) { private void insertLocation(Track track, Location location, Location lastValidTrackPoint) {
if (location == null) { if (location == null) {
Log.w(TAG, "Ignore insertLocation. loation is null."); Log.w(TAG, "Ignore insertLocation. location is null.");
return; return;
} }
// Do not insert if inserted already // Do not insert if inserted already
@@ -226,37 +226,14 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
} }
} }
/** private static void resetRecordingState(Context context) {
* Stops the recording. if (PreferencesUtils.isRecording(context)) {
* PreferencesUtils.setLong(context, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
* @param context the context
* @param showEditor true to show the editor
*/
public void stopRecording(@NonNull Context context, boolean showEditor) {
ITrackRecordingService trackRecordingService = getServiceIfBound();
if (trackRecordingService == null) {
resetRecordingState(context);
} else {
try {
if (showEditor) {
// Need to remember the recordingTrackId before calling endCurrentTrack() as endCurrentTrack() sets the value to -1L.
long recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
trackRecordingService.endCurrentTrack();
if (recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
Intent intent = IntentUtils.newIntent(context, TrackEditActivity.class)
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, recordingTrackId)
.putExtra(TrackEditActivity.EXTRA_NEW_TRACK, true);
context.startActivity(intent);
} }
} else { boolean recordingTrackPaused = PreferencesUtils.getBoolean(context, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
trackRecordingService.endCurrentTrack(); if (!recordingTrackPaused) {
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
} }
} catch (Exception e) {
//TODO What exception are we catching here? Should be removed...
Log.e(TAG, "Unable to stop recording.", e);
}
}
unbindAndStop();
} }
/** /**
@@ -284,14 +261,36 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
return -1L; return -1L;
} }
private static void resetRecordingState(Context context) { /**
* Stops the recording.
*
* @param context the context
* @param showEditor true to show the editor
*/
public void stopRecording(@NonNull Context context, boolean showEditor) {
ITrackRecordingService trackRecordingService = getServiceIfBound();
if (trackRecordingService == null) {
resetRecordingState(context);
} else {
try {
if (showEditor) {
// Need to remember the recordingTrackId before calling endCurrentTrack() as endCurrentTrack() sets the value to -1L.
long recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key); long recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
if (recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) { trackRecordingService.endCurrentTrack();
PreferencesUtils.setLong(context, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT); if (PreferencesUtils.isRecording(context)) {
Intent intent = IntentUtils.newIntent(context, TrackEditActivity.class)
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, recordingTrackId)
.putExtra(TrackEditActivity.EXTRA_NEW_TRACK, true);
context.startActivity(intent);
} }
boolean recordingTrackPaused = PreferencesUtils.getBoolean(context, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT); } else {
if (!recordingTrackPaused) { trackRecordingService.endCurrentTrack();
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT); }
} catch (Exception e) {
//TODO What exception are we catching here? Should be removed...
Log.e(TAG, "Unable to stop recording.", e);
} }
} }
unbindAndStop();
}
} }
@@ -17,8 +17,7 @@
package de.dennisguse.opentracks.settings; package de.dennisguse.opentracks.settings;
import android.content.Context; import android.content.Context;
import android.os.Parcelable; import android.os.Bundle;
import android.preference.DialogPreference;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
@@ -28,7 +27,11 @@ import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.Spinner; import android.widget.Spinner;
import androidx.preference.DialogPreference;
import androidx.preference.PreferenceDialogFragmentCompat;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
@@ -39,35 +42,59 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
*/ */
public class ActivityTypePreference extends DialogPreference { public class ActivityTypePreference extends DialogPreference {
private RecordingSettingsActivity recordingSettingsActivity; private SummaryProvider<DialogPreference> summaryProvider = new SummaryProvider<DialogPreference>() {
private AutoCompleteTextView textView; @Override
private Spinner spinner; public CharSequence provideSummary(DialogPreference preference) {
String defaultActivity = PreferencesUtils.getString(ActivityTypePreference.this.getContext(), R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
return defaultActivity != null && !defaultActivity.equals(PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT) ? defaultActivity : getContext().getString(R.string.value_unknown);
}
};
public ActivityTypePreference(Context context, AttributeSet attrs) { public ActivityTypePreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
setDialogLayoutResource(R.layout.activity_type_preference); setDialogLayoutResource(R.layout.preference_activity_type);
setPositiveButtonText(android.R.string.ok); setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel); setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null); setDialogIcon(null);
setPersistent(false); setPersistent(true);
//TODO: for some reason the summary is not updated; after changing it. Enable summaryprovider, when fixed.
// setSummaryProvider(summaryProvider);
} }
@Override @Override
protected View onCreateDialogView() { public int getDialogLayoutResource() {
View view = super.onCreateDialogView(); return R.layout.preference_activity_type;
}
public static class ActivityPreferenceDialog extends PreferenceDialogFragmentCompat {
private AutoCompleteTextView textView;
private Spinner spinner;
static ActivityPreferenceDialog newInstance(String preferenceKey) {
ActivityTypePreference.ActivityPreferenceDialog dialog = new ActivityTypePreference.ActivityPreferenceDialog();
final Bundle bundle = new Bundle(1);
bundle.putString(PreferenceDialogFragmentCompat.ARG_KEY, preferenceKey);
dialog.setArguments(bundle);
return dialog;
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
final Context context = getActivity();
textView = view.findViewById(R.id.activity_type_preference_text_view); textView = view.findViewById(R.id.activity_type_preference_text_view);
spinner = view.findViewById(R.id.activity_type_preference_spinner); String category = PreferencesUtils.getString(context, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
String category = PreferencesUtils.getString(
getContext(), R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
textView.setText(category); textView.setText(category);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getContext(), R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
textView.setAdapter(adapter); textView.setAdapter(adapter);
textView.setOnItemClickListener(new AdapterView.OnItemClickListener() { textView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) { public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
String iconValue = TrackIconUtils.getIconValue(getContext(), (String) textView.getAdapter().getItem(position)); String iconValue = TrackIconUtils.getIconValue(context, (String) textView.getAdapter().getItem(position));
TrackIconUtils.setIconSpinner(spinner, iconValue); TrackIconUtils.setIconSpinner(spinner, iconValue);
} }
}); });
@@ -75,19 +102,20 @@ public class ActivityTypePreference extends DialogPreference {
@Override @Override
public void onFocusChange(View v, boolean hasFocus) { public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) { if (!hasFocus) {
String iconValue = TrackIconUtils.getIconValue(getContext(), textView.getText().toString()); String iconValue = TrackIconUtils.getIconValue(context, textView.getText().toString());
TrackIconUtils.setIconSpinner(spinner, iconValue); TrackIconUtils.setIconSpinner(spinner, iconValue);
} }
} }
}); });
String iconValue = TrackIconUtils.getIconValue(getContext(), category); String iconValue = TrackIconUtils.getIconValue(context, category);
spinner.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getContext(), iconValue)); spinner = view.findViewById(R.id.activity_type_preference_spinner);
spinner.setAdapter(TrackIconUtils.getIconSpinnerAdapter(context, iconValue));
spinner.setOnTouchListener(new View.OnTouchListener() { spinner.setOnTouchListener(new View.OnTouchListener() {
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) { if (event.getAction() == MotionEvent.ACTION_UP) {
recordingSettingsActivity.showChooseActivityTypeDialog(); showIconSelectDialog();
} }
return true; return true;
} }
@@ -96,49 +124,37 @@ public class ActivityTypePreference extends DialogPreference {
@Override @Override
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
recordingSettingsActivity.showChooseActivityTypeDialog(); showIconSelectDialog();
} }
return true; return true;
} }
}); });
return view; }
private void showIconSelectDialog() {
String category = PreferencesUtils.getString(getActivity(), R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
ChooseActivityTypeDialogFragment.showDialog(getActivity().getSupportFragmentManager(), category);
} }
@Override @Override
protected void onDialogClosed(boolean positiveResult) { public void onDialogClosed(boolean positiveResult) {
if (positiveResult) { if (positiveResult) {
String value = textView.getText().toString(); String newDefaultActivity = textView.getText().toString();
if (callChangeListener(value)) { if (getPreference().callChangeListener(newDefaultActivity)) {
PreferencesUtils.setString(getContext(), R.string.default_activity_key, value); PreferencesUtils.setString(getActivity(), R.string.default_activity_key, newDefaultActivity);
} }
} }
} }
@Override
protected void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
String iconValue = TrackIconUtils.getIconValue(getContext(), textView.getText().toString());
TrackIconUtils.setIconSpinner(spinner, iconValue);
textView.clearFocus();
}
/**
* Sets the recording settings activity.
*
* @param activity the activity
*/
void setRecordingSettingsActivity(RecordingSettingsActivity activity) {
recordingSettingsActivity = activity;
}
/** /**
* Updates the value of the dialog. * Updates the value of the dialog.
* *
* @param iconValue the icon value * @param iconValue the icon value
*/ */
void updateValue(String iconValue) { public void updateUI(String iconValue) {
TrackIconUtils.setIconSpinner(spinner, iconValue); TrackIconUtils.setIconSpinner(spinner, iconValue);
textView.setText(recordingSettingsActivity.getString(TrackIconUtils.getIconActivityType(iconValue))); textView.setText(getActivity().getString(TrackIconUtils.getIconActivityType(iconValue)));
textView.clearFocus(); textView.clearFocus();
} }
} }
}
@@ -0,0 +1,213 @@
package de.dennisguse.opentracks.settings;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import androidx.preference.ListPreference;
import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.BluetoothDeviceUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.UnitConversions;
final class PreferenceHelper {
/**
* Sets the min recording interval options.
*
* @param options the options
* @param values the values
*/
static void setMinRecordingIntervalOptions(Context context, String[] options, String[] values) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
switch (value) {
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_BATTERY_LIFE:
options[i] = context.getString(R.string.value_adapt_battery_life);
break;
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_ACCURACY:
options[i] = context.getString(R.string.value_adapt_accuracy);
break;
case PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT:
options[i] = context.getString(R.string.value_smallest_recommended);
break;
default:
options[i] = value < 60 ? context.getString(R.string.value_integer_second, value) : context.getString(R.string.value_integer_minute, value / 60);
}
}
}
/**
* Sets the recording distance interval options.
*
* @param options the options
* @param values the values
* @param metricUnits true for metric units
*/
static void setRecordingDistanceIntervalOptions(Context context, String[] options, String[] values, boolean metricUnits) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
String displayValue;
if (metricUnits) {
displayValue = context.getString(R.string.value_integer_meter, value);
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
options[i] = context.getString(R.string.value_integer_meter_recommended, value);
} else {
options[i] = displayValue;
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
displayValue = context.getString(R.string.value_integer_feet, feet);
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
options[i] = context.getString(R.string.value_integer_feet_recommended, feet);
} else {
options[i] = displayValue;
}
}
}
}
/**
* Sets the max recording distance options.
*
* @param options the options
* @param values the values
* @param metricUnits true for metric units
*/
static void setMaxRecordingDistanceOptions(Context context, String[] options, String[] values, boolean metricUnits) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
String displayValue;
if (metricUnits) {
displayValue = context.getString(R.string.value_integer_meter, value);
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
options[i] = context.getString(R.string.value_integer_meter_recommended, value);
} else {
options[i] = displayValue;
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
if (feet < 2000) {
displayValue = context.getString(R.string.value_integer_feet, feet);
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
options[i] = context.getString(R.string.value_integer_feet_recommended, feet);
} else {
options[i] = displayValue;
}
} else {
double mile = feet * UnitConversions.FT_TO_MI;
displayValue = context.getString(R.string.value_float_mile, mile);
options[i] = displayValue;
}
}
}
}
/**
* Sets the recording gps accuracy options.
*
* @param options the options
* @param values the values
* @param metricUnits true for metric units
*/
static void setRecordingGpsAccuracyOptions(Context context, String[] options, String[] values, boolean metricUnits) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
String displayValue;
if (metricUnits) {
displayValue = context.getString(R.string.value_integer_meter, value);
switch (value) {
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
options[i] = context.getString(R.string.value_integer_meter_recommended, value);
break;
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
options[i] = context.getString(R.string.value_integer_meter_excellent_gps, value);
break;
case PreferencesUtils.RECORDING_GPS_ACCURACY_POOR:
options[i] = context.getString(R.string.value_integer_meter_poor_gps, value);
break;
default:
options[i] = displayValue;
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
if (feet < 2000) {
displayValue = context.getString(R.string.value_integer_feet, feet);
switch (value) {
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
options[i] = context.getString(R.string.value_integer_feet_recommended, feet);
break;
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
options[i] = context.getString(R.string.value_integer_feet_excellent_gps, feet);
break;
default:
options[i] = displayValue;
}
} else {
double mile = feet * UnitConversions.FT_TO_MI;
displayValue = context.getString(R.string.value_float_mile, mile);
if (value == PreferencesUtils.RECORDING_GPS_ACCURACY_POOR) {
options[i] = context.getString(R.string.value_float_mile_poor_gps, mile);
} else {
options[i] = displayValue;
}
}
}
}
}
/**
* Sets the auto resume track timeout options.
*
* @param options the options
* @param values the values
*/
static void setAutoResumeTrackTimeoutOptions(Context context, String[] options, String[] values) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
switch (value) {
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_NEVER:
options[i] = context.getString(R.string.value_never);
break;
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_ALWAYS:
options[i] = context.getString(R.string.value_always);
break;
default:
options[i] = context.getString(R.string.value_integer_minute, value);
}
}
}
/**
* Configures the bluetooth sensor.
*/
static void configureBluetoothSensorList(ListPreference preference) {
Context context = preference.getContext();
String value = PreferencesUtils.getString(context, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
List<String> devicesNameList = new ArrayList<>();
List<String> devicesAddressList = new ArrayList<>();
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter != null) {
BluetoothDeviceUtils.populateDeviceLists(bluetoothAdapter, devicesNameList, devicesAddressList);
}
// Was the previously configured device unpaired? Then forget it.
if (!devicesAddressList.contains(value)) {
value = PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT;
PreferencesUtils.setString(context, R.string.bluetooth_sensor_key, value);
}
devicesNameList.add(0, context.getString(R.string.value_none));
devicesAddressList.add(0, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
String[] values = devicesAddressList.toArray(new String[0]);
preference.setEntryValues(values);
String[] options = devicesNameList.toArray(new String[0]);
preference.setEntries(options);
}
}
@@ -1,303 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.settings;
import android.app.Dialog;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.UnitConversions;
/**
* An activity for accessing recording settings.
*
* @author Jimmy Shih
*/
public class RecordingSettingsActivity extends PreferenceActivity implements ChooseActivityTypeCaller {
private static final int DIALOG_CHOOSE_ACTIVITY = 0;
private ActivityTypePreference activityTypePreference;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
addPreferencesFromResource(R.xml.settings_recording);
boolean metricUnits = PreferencesUtils.isMetricUnits(this);
configFrequencyPreference(R.string.voice_frequency_key, metricUnits);
configFrequencyPreference(R.string.split_frequency_key, metricUnits);
configDefaultActivity();
configListPreference(R.string.min_recording_interval_key, R.array.min_recording_interval_values, metricUnits);
configListPreference(R.string.recording_distance_interval_key, R.array.recording_distance_interval_values, metricUnits);
configListPreference(R.string.max_recording_distance_key, R.array.max_recording_distance_values, metricUnits);
configListPreference(R.string.recording_gps_accuracy_key, R.array.recording_gps_accuracy_values, metricUnits);
configListPreference(R.string.auto_resume_track_timeout_key, R.array.auto_resume_track_timeout_values, metricUnits);
}
@Override
protected Dialog onCreateDialog(int id, Bundle bundle) {
if (id != DIALOG_CHOOSE_ACTIVITY) {
return null;
}
String category = PreferencesUtils.getString(
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
return ChooseActivityTypeDialogFragment.getDialog(this, category, this);
}
private void configFrequencyPreference(int key, boolean metricUnits) {
ListPreference preference = (ListPreference) findPreference(getString(key));
String[] options = StringUtils.getFrequencyOptions(this, metricUnits);
preference.setEntries(options);
}
private void configDefaultActivity() {
activityTypePreference = (ActivityTypePreference) findPreference(getString(R.string.default_activity_key));
String defaultActivity = PreferencesUtils.getString(
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
activityTypePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference pref, Object newValue) {
String stringValue = (String) newValue;
pref.setSummary(stringValue != null && !stringValue.equals(PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT) ? stringValue : getString(R.string.value_unknown));
return true;
}
});
activityTypePreference.setSummary(defaultActivity != null && !defaultActivity.equals(PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT) ? defaultActivity : getString(R.string.value_unknown));
activityTypePreference.setRecordingSettingsActivity(this);
}
private void configListPreference(int key, int valueArray, boolean metricUnits) {
ListPreference preference = (ListPreference) findPreference(getString(key));
@Deprecated //TODO Can we make values an int array?
String[] values = getResources().getStringArray(valueArray);
String[] options = new String[values.length];
switch (key) {
case R.string.min_recording_interval_key:
setMinRecordingIntervalOptions(options, values);
break;
case R.string.recording_distance_interval_key:
setRecordingDistanceIntervalOptions(options, values, metricUnits);
break;
case R.string.max_recording_distance_key:
setMaxRecordingDistanceOptions(options, values, metricUnits);
break;
case R.string.recording_gps_accuracy_key:
setRecordingGpsAccuracyOptions(options, values, metricUnits);
break;
case R.string.auto_resume_track_timeout_key:
setAutoResumeTrackTimeoutOptions(options, values);
break;
default:
return;
}
preference.setEntries(options);
}
/**
* Sets the min recording interval options.
*
* @param options the options
* @param values the values
*/
private void setMinRecordingIntervalOptions(String[] options, String[] values) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
switch (value) {
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_BATTERY_LIFE:
options[i] = getString(R.string.value_adapt_battery_life);
break;
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_ACCURACY:
options[i] = getString(R.string.value_adapt_accuracy);
break;
case PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT:
options[i] = getString(R.string.value_smallest_recommended);
break;
default:
options[i] = value < 60 ? getString(R.string.value_integer_second, value)
: getString(R.string.value_integer_minute, value / 60);
}
}
}
/**
* Sets the recording distance interval options.
*
* @param options the options
* @param values the values
* @param metricUnits true for metric units
*/
private void setRecordingDistanceIntervalOptions(String[] options, String[] values, boolean metricUnits) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
String displayValue;
if (metricUnits) {
displayValue = getString(R.string.value_integer_meter, value);
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
options[i] = getString(R.string.value_integer_meter_recommended, value);
} else {
options[i] = displayValue;
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
displayValue = getString(R.string.value_integer_feet, feet);
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
options[i] = getString(R.string.value_integer_feet_recommended, feet);
} else {
options[i] = displayValue;
}
}
}
}
/**
* Sets the max recording distance options.
*
* @param options the options
* @param values the values
* @param metricUnits true for metric units
*/
private void setMaxRecordingDistanceOptions(String[] options, String[] values, boolean metricUnits) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
String displayValue;
if (metricUnits) {
displayValue = getString(R.string.value_integer_meter, value);
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
options[i] = getString(R.string.value_integer_meter_recommended, value);
} else {
options[i] = displayValue;
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
if (feet < 2000) {
displayValue = getString(R.string.value_integer_feet, feet);
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
options[i] = getString(R.string.value_integer_feet_recommended, feet);
} else {
options[i] = displayValue;
}
} else {
double mile = feet * UnitConversions.FT_TO_MI;
displayValue = getString(R.string.value_float_mile, mile);
options[i] = displayValue;
}
}
}
}
/**
* Sets the recording gps accuracy options.
*
* @param options the options
* @param values the values
* @param metricUnits true for metric units
*/
private void setRecordingGpsAccuracyOptions(String[] options, String[] values, boolean metricUnits) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
String displayValue;
if (metricUnits) {
displayValue = getString(R.string.value_integer_meter, value);
switch (value) {
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
options[i] = getString(R.string.value_integer_meter_recommended, value);
break;
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
options[i] = getString(R.string.value_integer_meter_excellent_gps, value);
break;
case PreferencesUtils.RECORDING_GPS_ACCURACY_POOR:
options[i] = getString(R.string.value_integer_meter_poor_gps, value);
break;
default:
options[i] = displayValue;
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
if (feet < 2000) {
displayValue = getString(R.string.value_integer_feet, feet);
switch (value) {
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
options[i] = getString(R.string.value_integer_feet_recommended, feet);
break;
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
options[i] = getString(R.string.value_integer_feet_excellent_gps, feet);
break;
default:
options[i] = displayValue;
}
} else {
double mile = feet * UnitConversions.FT_TO_MI;
displayValue = getString(R.string.value_float_mile, mile);
if (value == PreferencesUtils.RECORDING_GPS_ACCURACY_POOR) {
options[i] = getString(R.string.value_float_mile_poor_gps, mile);
} else {
options[i] = displayValue;
}
}
}
}
}
/**
* Sets the auto resume track timeout options.
*
* @param options the options
* @param values the values
*/
private void setAutoResumeTrackTimeoutOptions(String[] options, String[] values) {
for (int i = 0; i < values.length; i++) {
int value = Integer.parseInt(values[i]);
switch (value) {
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_NEVER:
options[i] = getString(R.string.value_never);
break;
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_ALWAYS:
options[i] = getString(R.string.value_always);
break;
default:
options[i] = getString(R.string.value_integer_minute, value);
}
}
}
public void showChooseActivityTypeDialog() {
try {
removeDialog(DIALOG_CHOOSE_ACTIVITY);
} catch (Exception e) {
// Can safely ignore.
}
showDialog(DIALOG_CHOOSE_ACTIVITY);
}
@Override
public void onChooseActivityTypeDone(String iconValue) {
activityTypePreference.updateValue(iconValue);
}
}
@@ -1,11 +1,13 @@
package de.dennisguse.opentracks.settings; package de.dennisguse.opentracks.settings;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.os.Bundle;
import android.preference.DialogPreference;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.Toast; import android.widget.Toast;
import androidx.preference.DialogPreference;
import androidx.preference.PreferenceDialogFragmentCompat;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -15,13 +17,24 @@ public class ResetDialogPreference extends DialogPreference {
super(context, attrs); super(context, attrs);
} }
@Override public static class ResetPreferenceDialog extends PreferenceDialogFragmentCompat {
public void onClick(DialogInterface dialog, int which) {
super.onClick(dialog, which);
if (which == DialogInterface.BUTTON_POSITIVE) {
PreferencesUtils.resetPreferences(getContext(), true);
Toast.makeText(getContext(), R.string.settings_reset_done, Toast.LENGTH_SHORT).show(); static PreferenceDialogFragmentCompat newInstance(String preferenceKey) {
ResetPreferenceDialog dialog = new ResetPreferenceDialog();
final Bundle bundle = new Bundle(1);
bundle.putString(PreferenceDialogFragmentCompat.ARG_KEY, preferenceKey);
dialog.setArguments(bundle);
return dialog;
}
@Override
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
PreferencesUtils.resetPreferences(getActivity(), true);
Toast.makeText(getActivity(), R.string.settings_reset_done, Toast.LENGTH_SHORT).show();
}
} }
} }
} }
@@ -1,159 +1,182 @@
/*
* 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.settings; package de.dennisguse.opentracks.settings;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.provider.Settings;
import java.util.ArrayList; import androidx.appcompat.app.AppCompatActivity;
import java.util.List; import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.DialogFragment;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.BluetoothDeviceUtils; import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
/** public class SettingsActivity extends AppCompatActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
* An activity for accessing settings.
*
* @author Jimmy Shih
*/
public class SettingsActivity extends PreferenceActivity {
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; private PrefsFragment prefsFragment = new PrefsFragment();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings);
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(R.string.menu_settings);
setSupportActionBar(toolbar);
getSupportFragmentManager().beginTransaction().replace(R.id.settings_fragment, prefsFragment).commit();
}
@Override
public void onChooseActivityTypeDone(String iconValue) {
prefsFragment.setDefaultActivity(iconValue);
}
public static class PrefsFragment extends PreferenceFragmentCompat {
/* /*
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class. * Note that sharedPreferenceChangeListener cannot be an anonymous inner class.
* Anonymous inner class will get garbage collected. * Anonymous inner class will get garbage collected.
*/ */
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key != null && key.equals(PreferencesUtils.getKey(SettingsActivity.this, R.string.recording_track_id_key))) { getActivity().runOnUiThread(new Runnable() {
recordingTrackId = PreferencesUtils.getLong(SettingsActivity.this, R.string.recording_track_id_key);
}
//TODO Should only be called if something meaningful happens.
runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
updateUI(); updateUI();
} }
}); });
if (key == null || key.equals(PreferencesUtils.getKey(getActivity(), R.string.stats_units_key))) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
updateUnits();
}
});
}
} }
}; };
// Used to forward update from ChooseActivityTypeDialogFragment; TODO Could be replaced with LiveData.
private ActivityTypePreference.ActivityPreferenceDialog activityPreferenceDialog;
@Override @Override
protected void onCreate(Bundle bundle) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
super.onCreate(bundle); setPreferencesFromResource(R.xml.settings, rootKey);
updateUnits();
addPreferencesFromResource(R.xml.settings);
configPreference(R.string.settings_stats_key, StatsSettingsActivity.class);
configPreference(R.string.settings_recording_key, RecordingSettingsActivity.class);
findPreference(getString(R.string.settings_sensor_bluetooth_pairing_key))
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
startActivity(settingsIntent);
return true;
}
});
} }
@Override @Override
protected void onResume() { public void onResume() {
super.onResume(); super.onResume();
PreferencesUtils.getSharedPreferences(this).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); PreferencesUtils.getSharedPreferences(getActivity()).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
updateUI(); updateUI();
configBluetoothSensor();
} }
@Override @Override
protected void onPause() { public void onPause() {
super.onPause(); super.onPause();
PreferencesUtils.getSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); PreferencesUtils.getSharedPreferences(getActivity()).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
} }
/** private void configFrequencyPreference(int key, boolean metricUnits) {
* Configures a preference by starting a new activity when it is clicked. ListPreference preference = findPreference(getString(key));
*
* @param key the preference key
* @param cl the class to start the new activity
*/
private void configPreference(int key, final Class<?> cl) {
Preference preference = findPreference(getString(key));
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference pref) {
Intent intent = IntentUtils.newIntent(SettingsActivity.this, cl);
startActivity(intent);
return true;
}
});
}
/** String[] options = StringUtils.getFrequencyOptions(getActivity(), metricUnits);
* Configures the bluetooth sensor.
*/
private void configBluetoothSensor() {
ListPreference preference = (ListPreference) findPreference(getString(R.string.bluetooth_sensor_key));
String value = PreferencesUtils.getString(this, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
List<String> devicesNameList = new ArrayList<>();
List<String> devicesAddressList = new ArrayList<>();
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter != null) {
BluetoothDeviceUtils.populateDeviceLists(bluetoothAdapter, devicesNameList, devicesAddressList);
}
// Was the previously configured device unpaired? Then forget it.
if (!devicesAddressList.contains(value)) {
value = PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT;
PreferencesUtils.setString(this, R.string.bluetooth_sensor_key, value);
}
devicesNameList.add(0, getString(R.string.value_none));
devicesAddressList.add(0, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
String[] values = devicesAddressList.toArray(new String[0]);
preference.setEntryValues(values);
String[] options = devicesNameList.toArray(new String[0]);
preference.setEntries(options); preference.setEntries(options);
} }
private void configListPreference(int key, int valueArray, boolean metricUnits) {
//TODO Can we make values an int array?
String[] values = getResources().getStringArray(valueArray);
final String[] options = new String[values.length];
switch (key) {
case R.string.min_recording_interval_key:
PreferenceHelper.setMinRecordingIntervalOptions(getActivity(), options, values);
break;
case R.string.recording_distance_interval_key:
PreferenceHelper.setRecordingDistanceIntervalOptions(getActivity(), options, values, metricUnits);
break;
case R.string.max_recording_distance_key:
PreferenceHelper.setMaxRecordingDistanceOptions(getActivity(), options, values, metricUnits);
break;
case R.string.recording_gps_accuracy_key:
PreferenceHelper.setRecordingGpsAccuracyOptions(getActivity(), options, values, metricUnits);
break;
case R.string.auto_resume_track_timeout_key:
PreferenceHelper.setAutoResumeTrackTimeoutOptions(getActivity(), options, values);
break;
default:
return;
}
final ListPreference listPreference = findPreference(getString(key));
listPreference.setEntries(options);
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
DialogFragment dialogFragment = null;
if (preference instanceof ResetDialogPreference) {
dialogFragment = ResetDialogPreference.ResetPreferenceDialog.newInstance(preference.getKey());
} else if (preference instanceof ActivityTypePreference) {
activityPreferenceDialog = ActivityTypePreference.ActivityPreferenceDialog.newInstance(preference.getKey());
dialogFragment = activityPreferenceDialog;
}
if (dialogFragment != null) {
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getFragmentManager(), getClass().getSimpleName());
return;
}
super.onDisplayPreferenceDialog(preference);
}
public void setDefaultActivity(String iconValue) {
if (activityPreferenceDialog != null) {
activityPreferenceDialog.updateUI(iconValue);
}
}
private void updateUI() { private void updateUI() {
//TODO Remove the following if recordingTrackId is replaced by direct communication rather than via preferences. final boolean isRecording = PreferencesUtils.isRecording(getActivity());
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
Preference resetPreference = findPreference(getString(R.string.settings_reset_key)); Preference resetPreference = findPreference(getString(R.string.settings_reset_key));
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : ""); resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
resetPreference.setEnabled(!isRecording); resetPreference.setEnabled(!isRecording);
Preference speedCheckBoxPreference = findPreference(getString(R.string.chart_show_speed_key)); Preference speedCheckBoxPreference = findPreference(getString(R.string.chart_show_speed_key));
speedCheckBoxPreference.setTitle(PreferencesUtils.isReportSpeed(this) ? R.string.stats_speed : R.string.stats_pace); speedCheckBoxPreference.setTitle(PreferencesUtils.isReportSpeed(getActivity()) ? R.string.stats_speed : R.string.stats_pace);
ListPreference bluetoothPreference = findPreference(getString(R.string.bluetooth_sensor_key));
PreferenceHelper.configureBluetoothSensorList(bluetoothPreference);
}
private void updateUnits() {
boolean metricUnits = PreferencesUtils.isMetricUnits(getActivity());
configFrequencyPreference(R.string.voice_frequency_key, metricUnits);
configFrequencyPreference(R.string.split_frequency_key, metricUnits);
configListPreference(R.string.min_recording_interval_key, R.array.min_recording_interval_values, metricUnits);
configListPreference(R.string.recording_distance_interval_key, R.array.recording_distance_interval_values, metricUnits);
configListPreference(R.string.max_recording_distance_key, R.array.max_recording_distance_values, metricUnits);
configListPreference(R.string.recording_gps_accuracy_key, R.array.recording_gps_accuracy_values, metricUnits);
configListPreference(R.string.auto_resume_track_timeout_key, R.array.auto_resume_track_timeout_values, metricUnits);
configListPreference(R.string.auto_resume_track_timeout_key, R.array.auto_resume_track_timeout_values, metricUnits);
final ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
String[] options = getResources().getStringArray(metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
statsRatePreferences.setEntries(options);
//TODO This is a hack!!! Need to manually updated the summary as otherwise it will only be updated after scrolling down and up again (bring the object out of view).
//Check if this is still needed after upgraded PreferenceFragment.
statsRatePreferences.setSummary(statsRatePreferences.getEntry());
}
} }
} }
@@ -1,78 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.settings;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An activity for accessing stats settings.
*
* @author Jimmy Shih
*/
public class StatsSettingsActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
addPreferencesFromResource(R.xml.settings_statistics);
configUnitsListPreference();
boolean metricUnits = PreferencesUtils.STATS_UNITS_DEFAULT.equals(PreferencesUtils.getString(this, R.string.stats_units_key, ""));
configRateListPreference(metricUnits);
}
/**
* Configures the preferred units list preference.
*/
private void configUnitsListPreference() {
ListPreference listPreference = (ListPreference) findPreference(getString(R.string.stats_units_key));
final OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference pref, Object newValue) {
boolean metricUnits = PreferencesUtils.STATS_UNITS_DEFAULT.equals(newValue);
configRateListPreference(metricUnits);
return true;
}
};
listPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference pref, Object newValue) {
return listener.onPreferenceChange(pref, newValue);
}
});
}
/**
* Configures the preferred rate list preference.
*
* @param metricUnits true if metric units
*/
private void configRateListPreference(boolean metricUnits) {
ListPreference listPreference = (ListPreference) findPreference(getString(R.string.stats_rate_key));
String[] options = getResources().getStringArray(metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
listPreference.setEntries(options);
}
}
@@ -0,0 +1,137 @@
package de.dennisguse.opentracks.util;
import android.app.Activity;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
import androidx.appcompat.widget.SearchView;
import java.util.ArrayList;
import de.dennisguse.opentracks.ContextualActionModeCallback;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.SearchListActivity;
import de.dennisguse.opentracks.TrackController;
public class ActivityUtils {
private static final String TAG = ActivityUtils.class.getSimpleName();
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// Do nothing
}
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
mode.invalidate();
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
mode.finish();
}
return true;
}
/**
* Gets the checked positions in a list view.
*
* @param list the list view
*/
private int[] getCheckedPositions(ListView list) {
SparseBooleanArray positions = list.getCheckedItemPositions();
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < positions.size(); i++) {
int key = positions.keyAt(i);
if (positions.valueAt(i)) {
arrayList.add(key);
}
}
int[] result = new int[arrayList.size()];
for (int i = 0; i < arrayList.size(); i++) {
result[i] = arrayList.get(i);
}
return result;
}
});
}
public static void configureSearchWidget(Activity activity, final MenuItem menuItem, final TrackController trackController) {
final SearchView searchView = (SearchView) menuItem.getActionView();
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
if (searchManager != null) {
//NOTE: for some reason activity.getComponentName() did not trigger the SearchListActivity
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(activity, SearchListActivity.class)));
} else {
Log.w(TAG, "Could not retrieve SearchManager.");
}
searchView.setQueryRefinementEnabled(true);
searchView.setSubmitButtonEnabled(true);
searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
// Hide and show trackController when searchable widget has focus/no focus
if (trackController != null) {
if (hasFocus) {
trackController.hide();
} else {
trackController.show();
}
}
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
menuItem.collapseActionView();
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
@Override
public boolean onSuggestionSelect(int position) {
return false;
}
@Override
public boolean onSuggestionClick(int position) {
menuItem.collapseActionView();
return false;
}
});
}
}
@@ -19,7 +19,8 @@ package de.dennisguse.opentracks.util;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
import androidx.preference.PreferenceManager;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
@@ -252,9 +253,16 @@ public class PreferencesUtils {
return CHART_X_AXIS_DEFAULT.equals(getString(context, R.string.chart_x_axis_key, CHART_X_AXIS_DEFAULT)); return CHART_X_AXIS_DEFAULT.equals(getString(context, R.string.chart_x_axis_key, CHART_X_AXIS_DEFAULT));
} }
public static boolean isRecording(Context context) {
long recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
return recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
}
public static boolean isRecording(long recordingTrackId) {
return recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
}
public static void resetPreferences(Context context, boolean readAgain) { public static void resetPreferences(Context context, boolean readAgain) {
PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain); PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_recording, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_statistics, readAgain);
} }
} }
@@ -0,0 +1,32 @@
package de.dennisguse.opentracks.util;
import android.content.Context;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.ShareActionProvider;
import androidx.core.internal.view.SupportMenuItem;
import androidx.core.view.MenuItemCompat;
public class ToolbarUtils {
private ToolbarUtils() {
}
//TODO Compat is not working as the AbsListView.MultiChoiceModeListener; instantiating it manually using the non-compat is a workaround.
@Deprecated
public static void setupShareActionProvider(@NonNull Context context, @NonNull MenuItem shareMenuItem, @NonNull long[] trackIds) {
android.widget.ShareActionProvider shareActionProvider = new android.widget.ShareActionProvider(context);
shareActionProvider.setShareIntent(trackIds.length == 0 ? null : IntentUtils.newShareFileIntent(context, trackIds));
shareMenuItem.setActionProvider(shareActionProvider);
}
/**
* @param shareMenuItem Should be a {@link SupportMenuItem}.
*/
public static void setupShareActionProviderCompat(@NonNull Context context, @NonNull MenuItem shareMenuItem, @NonNull long[] trackIds) {
ShareActionProvider shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareMenuItem);
shareActionProvider.setShareIntent(trackIds.length == 0 ? null : IntentUtils.newShareFileIntent(context, trackIds));
MenuItemCompat.setActionProvider(shareMenuItem, shareActionProvider);
}
}
@@ -62,7 +62,7 @@ public class TrackIconUtils {
private static final LinkedHashMap<String, Pair<Integer, Integer>> MAP = new LinkedHashMap<>(); private static final LinkedHashMap<String, Pair<Integer, Integer>> MAP = new LinkedHashMap<>();
static { static {
//Reflects order in ChooseActivityTypeDialogFragment //Reflects order in ChooseActivityTypeDialogFragmentActivity
MAP.put(UNKNOWN, new Pair<>(R.string.activity_type_unknown, ACTIVITY_UNKNOWN_LOGO)); MAP.put(UNKNOWN, new Pair<>(R.string.activity_type_unknown, ACTIVITY_UNKNOWN_LOGO));
MAP.put(RUN, new Pair<>(R.string.activity_type_running, R.drawable.ic_activity_run_24dp)); MAP.put(RUN, new Pair<>(R.string.activity_type_running, R.drawable.ic_activity_run_24dp));
MAP.put(WALK, new Pair<>(R.string.activity_type_walking, R.drawable.ic_activity_walk_24dp)); MAP.put(WALK, new Pair<>(R.string.activity_type_walking, R.drawable.ic_activity_walk_24dp));
Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

+4 -1
View File
@@ -16,7 +16,10 @@ limitations under the License.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:baselineAligned="false"> android:baselineAligned="false"
android:orientation="horizontal">
<include layout="@layout/toolbar" />
<include layout="@layout/track_controller" /> <include layout="@layout/track_controller" />
+2
View File
@@ -4,6 +4,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/toolbar" />
<ImageView <ImageView
android:id="@+id/imageView" android:id="@+id/imageView"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -17,6 +17,8 @@ limitations under the License.
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<include layout="@layout/toolbar" />
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/maker_detail_activity_view_pager" android:id="@+id/maker_detail_activity_view_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
+2
View File
@@ -18,6 +18,8 @@ limitations under the License.
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/toolbar" />
<ScrollView style="@style/ScrollView.WithBottomButtons"> <ScrollView style="@style/ScrollView.WithBottomButtons">
<LinearLayout <LinearLayout
+6 -3
View File
@@ -13,9 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<merge xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<ListView <ListView
android:id="@+id/marker_list" android:id="@+id/marker_list"
@@ -26,4 +29,4 @@ limitations under the License.
android:id="@+id/marker_list_empty" android:id="@+id/marker_list_empty"
style="@style/ListEmptyTextView" style="@style/ListEmptyTextView"
android:text="@string/marker_list_empty_message" /> android:text="@string/marker_list_empty_message" />
</merge> </LinearLayout>
+6 -3
View File
@@ -13,9 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<merge xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<ListView <ListView
android:id="@+id/search_list" android:id="@+id/search_list"
@@ -26,4 +29,4 @@ limitations under the License.
android:id="@+id/search_list_empty" android:id="@+id/search_list_empty"
style="@style/ListEmptyTextView" style="@style/ListEmptyTextView"
android:text="@string/search_list_empty_message" /> android:text="@string/search_list_empty_message" />
</merge> </LinearLayout>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<FrameLayout
android:id="@+id/settings_fragment"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_logo_color_24dp" />
</merge>
+2
View File
@@ -18,6 +18,8 @@ limitations under the License.
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/toolbar" />
<TabHost <TabHost
android:id="@+id/tackdetail_tabhost" android:id="@+id/tackdetail_tabhost"
android:layout_width="match_parent" android:layout_width="match_parent"
+2
View File
@@ -18,6 +18,8 @@ limitations under the License.
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/toolbar" />
<ScrollView style="@style/ScrollView.WithBottomButtons"> <ScrollView style="@style/ScrollView.WithBottomButtons">
<LinearLayout <LinearLayout
+2
View File
@@ -18,6 +18,8 @@ limitations under the License.
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/toolbar" />
<ListView <ListView
android:id="@+id/track_list" android:id="@+id/track_list"
android:layout_width="match_parent" android:layout_width="match_parent"
+13 -12
View File
@@ -13,30 +13,31 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/list_context_menu_share" android:id="@+id/list_context_menu_share"
android:actionProviderClass="android.widget.ShareActionProvider" android:title="@string/menu_share"
android:showAsAction="ifRoom" app:actionProviderClass="androidx.appcompat.widget.ShareActionProvider"
android:title="@string/menu_share" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/list_context_menu_show_on_map" android:id="@+id/list_context_menu_show_on_map"
android:icon="@drawable/ic_map_24dp" android:icon="@drawable/ic_map_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_show_on_map"
android:title="@string/menu_show_on_map" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/list_context_menu_edit" android:id="@+id/list_context_menu_edit"
android:icon="@drawable/ic_edit_24dp" android:icon="@drawable/ic_edit_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_edit"
android:title="@string/menu_edit" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/list_context_menu_delete" android:id="@+id/list_context_menu_delete"
android:icon="@drawable/ic_delete_forever_24dp" android:icon="@drawable/ic_delete_forever_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_delete"
android:title="@string/menu_delete" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/list_context_menu_select_all" android:id="@+id/list_context_menu_select_all"
android:icon="@drawable/ic_select_all_24dp" android:icon="@drawable/ic_select_all_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_select_all"
android:title="@string/menu_select_all" /> app:showAsAction="ifRoom" />
</menu> </menu>
+8 -7
View File
@@ -13,20 +13,21 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/marker_detail_show_on_map" android:id="@+id/marker_detail_show_on_map"
android:icon="@drawable/ic_map_24dp" android:icon="@drawable/ic_map_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_show_on_map"
android:title="@string/menu_show_on_map" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/marker_detail_edit" android:id="@+id/marker_detail_edit"
android:icon="@drawable/ic_edit_24dp" android:icon="@drawable/ic_edit_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_edit"
android:title="@string/menu_edit" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/marker_detail_delete" android:id="@+id/marker_detail_delete"
android:icon="@drawable/ic_delete_forever_24dp" android:icon="@drawable/ic_delete_forever_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_delete"
android:title="@string/menu_delete" /> app:showAsAction="ifRoom" />
</menu> </menu>
+7 -6
View File
@@ -13,15 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/marker_list_insert_marker" android:id="@+id/marker_list_insert_marker"
android:icon="@drawable/ic_marker_show_24dp" android:icon="@drawable/ic_marker_show_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_insert_marker"
android:title="@string/menu_insert_marker" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/marker_list_search" android:id="@+id/marker_list_search"
android:actionViewClass="android.widget.SearchView" android:title="@string/menu_search"
android:showAsAction="ifRoom" app:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/menu_search" /> app:showAsAction="ifRoom" />
</menu> </menu>
+7 -6
View File
@@ -13,29 +13,30 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/track_detail_insert_marker" android:id="@+id/track_detail_insert_marker"
android:icon="@drawable/ic_marker_add_24dp" android:icon="@drawable/ic_marker_add_24dp"
android:orderInCategory="1" android:orderInCategory="1"
android:showAsAction="ifRoom" app:showAsAction="ifRoom"
android:title="@string/menu_insert_marker" /> android:title="@string/menu_insert_marker" />
<item <item
android:id="@+id/track_detail_insert_photo" android:id="@+id/track_detail_insert_photo"
android:icon="@drawable/ic_camera_24dp" android:icon="@drawable/ic_camera_24dp"
android:orderInCategory="2" android:orderInCategory="2"
android:showAsAction="ifRoom" app:showAsAction="ifRoom"
android:title="@string/menu_insert_photo" /> android:title="@string/menu_insert_photo" />
<item <item
android:id="@+id/track_detail_menu_show_on_map" android:id="@+id/track_detail_menu_show_on_map"
android:icon="@drawable/ic_map_24dp" android:icon="@drawable/ic_map_24dp"
android:showAsAction="ifRoom" app:showAsAction="ifRoom"
android:title="@string/menu_show_on_map" /> android:title="@string/menu_show_on_map" />
<item <item
android:id="@+id/track_detail_share" android:id="@+id/track_detail_share"
android:actionProviderClass="android.widget.ShareActionProvider" app:actionProviderClass="androidx.appcompat.widget.ShareActionProvider"
android:orderInCategory="4" android:orderInCategory="4"
android:showAsAction="ifRoom" app:showAsAction="ifRoom"
android:title="@string/menu_share" /> android:title="@string/menu_share" />
<item <item
android:id="@+id/track_detail_markers" android:id="@+id/track_detail_markers"
+9 -12
View File
@@ -13,27 +13,24 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/track_list_search" android:id="@+id/track_list_search"
android:actionViewClass="android.widget.SearchView" android:title="@string/menu_search"
android:showAsAction="ifRoom" app:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/menu_search" /> app:showAsAction="ifRoom" />
<!-- Icon and title is overwritten by TrackListActivity --> <!-- Icon and title is overwritten by TrackListActivity -->
<item <item
android:id="@+id/track_list_start_gps" android:id="@+id/track_list_start_gps"
android:icon="@drawable/ic_gps_off_24dp" android:icon="@drawable/ic_gps_off_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_start_gps"
android:title="@string/menu_start_gps" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/track_list_markers" android:id="@+id/track_list_markers"
android:icon="@drawable/ic_marker_show_24dp" android:icon="@drawable/ic_marker_show_24dp"
android:showAsAction="ifRoom" android:title="@string/menu_markers"
android:title="@string/menu_markers" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/track_list_aggregated_statistics"
android:icon="@drawable/ic_statistics_24dp"
android:title="@string/menu_aggregated_statistics" />
<item <item
android:id="@+id/track_list_delete_all" android:id="@+id/track_list_delete_all"
android:icon="@drawable/ic_delete_forever_24dp" android:icon="@drawable/ic_delete_forever_24dp"
-5
View File
@@ -15,11 +15,6 @@ limitations under the License.
--> -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="ActionBarStyleCustom" parent="@android:style/Widget.Holo.ActionBar">
<!-- Same image as the source except using a grey line instead of a blue line -->
<item name="android:background">@drawable/ab_transparent_dark_holo</item>
</style>
<style name="BottomButton" parent="@android:style/Widget.Holo.Button.Borderless"> <style name="BottomButton" parent="@android:style/Widget.Holo.Button.Borderless">
<!-- Same as BottomButton in values/styles.xml except inheriting from Widget.Holo.Button.Borderles. --> <!-- Same as BottomButton in values/styles.xml except inheriting from Widget.Holo.Button.Borderles. -->
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
+8 -24
View File
@@ -15,7 +15,8 @@ limitations under the License.
--> -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="ThemeCustom" parent="android:Theme.Holo"> <style name="ThemeCustom" parent="Theme.AppCompat.NoActionBar">
<!-- TODO Cleanup necessary due to change to appcompat -->
<!-- Auto generated --> <!-- Auto generated -->
<item name="android:activatedBackgroundIndicator">@drawable/activated_background_holo_dark <item name="android:activatedBackgroundIndicator">@drawable/activated_background_holo_dark
</item> </item>
@@ -26,10 +27,12 @@ limitations under the License.
<item name="android:listChoiceIndicatorMultiple">@drawable/btn_check_holo_dark</item> <item name="android:listChoiceIndicatorMultiple">@drawable/btn_check_holo_dark</item>
<item name="android:listChoiceIndicatorSingle">@drawable/btn_radio_holo_dark</item> <item name="android:listChoiceIndicatorSingle">@drawable/btn_radio_holo_dark</item>
<item name="android:progressBarStyleHorizontal">@style/ProgressBarCustom</item> <item name="android:progressBarStyleHorizontal">@style/ProgressBarCustom</item>
<!-- Manually added -->
<item name="android:actionBarStyle">@style/ActionBarStyleCustom</item> <!-- <item name="colorPrimary">TODO</item>-->
<item name="android:actionModeBackground">@drawable/custom_action_mode_background</item> <!-- <item name="colorPrimaryDark">TODO</item>-->
<item name="android:alertDialogTheme">@style/CustomAlertDialogTheme</item>
<item name="android:colorAccent">@color/holo_orange_dark</item>
<item name="windowActionModeOverlay">true</item>
</style> </style>
<style name="ThemeCustomTransparent" parent="ThemeCustom"> <style name="ThemeCustomTransparent" parent="ThemeCustom">
@@ -41,23 +44,4 @@ limitations under the License.
<item name="android:windowIsFloating">true</item> <item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item> <item name="android:backgroundDimEnabled">false</item>
</style> </style>
<style name="CustomAlertDialogTheme" parent="@android:style/Theme.Holo.Dialog">
<!-- From auto generated -->
<item name="android:activatedBackgroundIndicator">@drawable/activated_background_holo_dark
</item>
<item name="android:autoCompleteTextViewStyle">@style/AutoCompleteTextViewCustom</item>
<item name="android:dropDownSpinnerStyle">@style/SpinnerCustom</item>
<item name="android:editTextBackground">@drawable/edit_text_holo_dark</item>
<item name="android:listChoiceBackgroundIndicator">@drawable/list_selector_holo_dark</item>
<item name="android:listChoiceIndicatorMultiple">@drawable/btn_check_holo_dark</item>
<item name="android:listChoiceIndicatorSingle">@drawable/btn_radio_holo_dark</item>
<item name="android:progressBarStyleHorizontal">@style/ProgressBarCustom</item>
<!-- From Theme.Holo.Dialog.Alert -->
<item name="android:textColor">@color/holo_orange_dark</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
</style>
</resources> </resources>
+102 -32
View File
@@ -15,7 +15,36 @@ limitations under the License.
--> -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/menu_settings"> android:title="@string/menu_settings">
<PreferenceScreen android:title="@string/track_detail_chart_tab">
<PreferenceCategory android:title="@string/track_detail_stats_tab">
<ListPreference
android:defaultValue="@string/stats_units_metric"
android:entries="@array/stats_units_options"
android:entryValues="@array/stats_units_values"
android:key="@string/stats_units_key"
android:summary="%s"
android:title="@string/settings_stats_units_title" />
<ListPreference
android:defaultValue="@string/stats_rate_speed"
android:entries="@array/stats_rate_metric_options"
android:entryValues="@array/stats_rate_values"
android:key="@string/stats_rate_key"
android:summary="%s"
android:title="@string/settings_stats_rate_title" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/settings_show">
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/stats_show_grade_elevation_key"
android:title="@string/settings_stats_grade_elevation" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/stats_show_coordinate_key"
android:title="@string/settings_stats_coordinate" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/track_detail_chart_tab">
<ListPreference <ListPreference
android:defaultValue="@string/chart_x_axis_distance" android:defaultValue="@string/chart_x_axis_distance"
android:entries="@array/chart_x_axis_options" android:entries="@array/chart_x_axis_options"
@@ -23,11 +52,11 @@ limitations under the License.
android:key="@string/chart_x_axis_key" android:key="@string/chart_x_axis_key"
android:summary="%s" android:summary="%s"
android:title="@string/settings_chart_x_axis" /> android:title="@string/settings_chart_x_axis" />
<PreferenceCategory android:title="@string/settings_show">
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="true" android:defaultValue="true"
android:key="@string/chart_show_elevation_key" android:key="@string/chart_show_elevation_key"
android:title="@string/stats_elevation" /> android:title="@string/stats_elevation" />
<!-- title set in code -->
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="true" android:defaultValue="true"
android:key="@string/chart_show_speed_key" /> android:key="@string/chart_show_speed_key" />
@@ -35,45 +64,85 @@ limitations under the License.
android:defaultValue="true" android:defaultValue="true"
android:key="@string/chart_show_heart_rate_key" android:key="@string/chart_show_heart_rate_key"
android:title="@string/sensor_state_heart_rate" /> android:title="@string/sensor_state_heart_rate" />
<!--
TODO: Sensors are not implemented.
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/chart_show_cadence_key"
android:title="@string/sensor_state_cadence" />
<CheckBoxPreference
android:defaultValue="true"
android:key="@string/chart_show_power_key"
android:title="@string/sensor_state_power" />
-->
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen>
<Preference <PreferenceCategory android:title="@string/settings_recording">
android:key="@string/settings_stats_key" <CheckBoxPreference
android:persistent="false" android:defaultValue="false"
android:title="@string/track_detail_stats_tab" /> android:key="@string/trackdetail_show_on_lockscreen_while_recording_key"
<Preference android:summary="@string/settings_recording_show_on_lockscreen_while_recording_summary"
android:key="@string/settings_recording_key" android:title="@string/settings_recording_show_on_lockscreen_while_recording_title" />
android:persistent="false" <ListPreference
android:title="@string/settings_recording" /> android:defaultValue="0"
android:entryValues="@array/frequency_values"
android:key="@string/voice_frequency_key"
android:summary="%s"
android:title="@string/menu_voice_frequency" />
<ListPreference
android:defaultValue="0"
android:entryValues="@array/frequency_values"
android:key="@string/split_frequency_key"
android:summary="%s"
android:title="@string/menu_split_frequency" />
<ListPreference
android:defaultValue="@string/settings_recording_track_name_date_iso_8601_value"
android:entries="@array/track_name_options"
android:entryValues="@array/track_name_values"
android:key="@string/track_name_key"
android:summary="%s"
android:title="@string/settings_recording_track_name_title" />
<ListPreference
android:defaultValue="0"
android:entryValues="@array/min_recording_interval_values"
android:key="@string/min_recording_interval_key"
android:summary="%s"
android:title="@string/settings_recording_min_recording_interval_title" />
<ListPreference
android:defaultValue="10"
android:entryValues="@array/recording_distance_interval_values"
android:key="@string/recording_distance_interval_key"
android:summary="%s"
android:title="@string/settings_recording_min_recording_distance_title" />
<ListPreference
android:defaultValue="200"
android:entryValues="@array/max_recording_distance_values"
android:key="@string/max_recording_distance_key"
android:summary="%s"
android:title="@string/settings_recording_max_recording_distance_title" />
<ListPreference
android:defaultValue="50"
android:entryValues="@array/recording_gps_accuracy_values"
android:key="@string/recording_gps_accuracy_key"
android:summary="%s"
android:title="@string/settings_recording_min_required_accuracy_title" />
<ListPreference
android:defaultValue="10"
android:entries="@array/auto_resume_track_timeout_values"
android:entryValues="@array/auto_resume_track_timeout_values"
android:key="@string/auto_resume_track_timeout_key"
android:summary="%s"
android:title="@string/settings_recording_auto_resume_track_timeout_title" />
<de.dennisguse.opentracks.settings.ActivityTypePreference
android:defaultValue="@string/activity_type_running"
android:key="@string/default_activity_key"
android:title="@string/settings_recording_default_activity_title" />
</PreferenceCategory>
<PreferenceScreen
android:title="@string/settings_sensor">
<PreferenceCategory <PreferenceCategory
android:title="@string/settings_sensor_bluetooth"> android:title="@string/settings_sensor">
<Preference
android:key="@string/settings_sensor_bluetooth_pairing_key"
android:persistent="false"
android:summary="@string/settings_sensor_bluetooth_pairing_summary"
android:title="@string/settings_sensor_bluetooth_pairing" />
<ListPreference <ListPreference
android:defaultValue="@string/sensor_type_value_none" android:defaultValue="@string/sensor_type_value_none"
android:key="@string/bluetooth_sensor_key" android:key="@string/bluetooth_sensor_key"
android:summary="%s" android:summary="%s"
android:title="@string/settings_sensor_bluetooth_sensor" /> android:title="@string/settings_sensor_bluetooth_sensor" />
<Preference
android:key="@string/settings_sensor_bluetooth_pairing_key"
android:persistent="false"
android:summary="@string/settings_sensor_bluetooth_pairing_summary"
android:title="@string/settings_sensor_bluetooth_pairing">
<intent android:action="android.settings.BLUETOOTH_SETTINGS" />
</Preference>
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen>
<PreferenceCategory android:title="@string/generic_tracks"> <PreferenceCategory android:title="@string/generic_tracks">
<Preference android:title="@string/menu_import_all"> <Preference android:title="@string/menu_import_all">
@@ -90,14 +159,15 @@ limitations under the License.
<PreferenceCategory android:title="@string/settings_reset"> <PreferenceCategory android:title="@string/settings_reset">
<de.dennisguse.opentracks.settings.ResetDialogPreference <de.dennisguse.opentracks.settings.ResetDialogPreference
android:dialogMessage="@string/settings_reset_confirm_message"
android:dialogTitle="@string/settings_reset_confirm_title" android:dialogTitle="@string/settings_reset_confirm_title"
android:dialogMessage="@string/settings_reset_confirm_message"
android:key="@string/settings_reset_key" android:key="@string/settings_reset_key"
android:negativeButtonText="@string/generic_cancel" android:negativeButtonText="@string/generic_cancel"
android:persistent="false" android:persistent="false"
android:positiveButtonText="@string/generic_yes" android:positiveButtonText="@string/generic_yes"
android:title="@string/settings_reset" /> android:title="@string/settings_reset" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/about_preference_title"> <PreferenceCategory android:title="@string/about_preference_title">
<Preference android:title="@string/about_preference_title"> <Preference android:title="@string/about_preference_title">
<intent <intent
-78
View File
@@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/settings_recording">
<ListPreference
android:defaultValue="0"
android:entryValues="@array/frequency_values"
android:key="@string/voice_frequency_key"
android:summary="%s"
android:title="@string/menu_voice_frequency" />
<ListPreference
android:defaultValue="0"
android:entryValues="@array/frequency_values"
android:key="@string/split_frequency_key"
android:summary="%s"
android:title="@string/menu_split_frequency" />
<ListPreference
android:defaultValue="@string/settings_recording_track_name_date_iso_8601_value"
android:entries="@array/track_name_options"
android:entryValues="@array/track_name_values"
android:key="@string/track_name_key"
android:summary="%s"
android:title="@string/settings_recording_track_name_title" />
<ListPreference
android:defaultValue="0"
android:entryValues="@array/min_recording_interval_values"
android:key="@string/min_recording_interval_key"
android:summary="%s"
android:title="@string/settings_recording_min_recording_interval_title" />
<ListPreference
android:defaultValue="10"
android:entryValues="@array/recording_distance_interval_values"
android:key="@string/recording_distance_interval_key"
android:summary="%s"
android:title="@string/settings_recording_min_recording_distance_title" />
<ListPreference
android:defaultValue="200"
android:entryValues="@array/max_recording_distance_values"
android:key="@string/max_recording_distance_key"
android:summary="%s"
android:title="@string/settings_recording_max_recording_distance_title" />
<ListPreference
android:defaultValue="50"
android:entryValues="@array/recording_gps_accuracy_values"
android:key="@string/recording_gps_accuracy_key"
android:summary="%s"
android:title="@string/settings_recording_min_required_accuracy_title" />
<ListPreference
android:defaultValue="10"
android:entryValues="@array/auto_resume_track_timeout_values"
android:key="@string/auto_resume_track_timeout_key"
android:summary="%s"
android:title="@string/settings_recording_auto_resume_track_timeout_title" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/trackdetail_show_on_lockscreen_while_recording_key"
android:summary="@string/settings_recording_show_on_lockscreen_while_recording_summary"
android:title="@string/settings_recording_show_on_lockscreen_while_recording_title" />
<de.dennisguse.opentracks.settings.ActivityTypePreference
android:defaultValue="@string/activity_type_running"
android:key="@string/default_activity_key"
android:summary="%s"
android:title="@string/settings_recording_default_activity_title" />
</PreferenceScreen>
-42
View File
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/track_detail_stats_tab">
<ListPreference
android:defaultValue="@string/stats_units_metric"
android:entries="@array/stats_units_options"
android:entryValues="@array/stats_units_values"
android:key="@string/stats_units_key"
android:summary="%s"
android:title="@string/settings_stats_units_title" />
<ListPreference
android:defaultValue="@string/stats_rate_speed"
android:entries="@array/stats_rate_metric_options"
android:entryValues="@array/stats_rate_values"
android:key="@string/stats_rate_key"
android:summary="%s"
android:title="@string/settings_stats_rate_title" />
<PreferenceCategory android:title="@string/settings_show">
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/stats_show_grade_elevation_key"
android:title="@string/settings_stats_grade_elevation" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/stats_show_coordinate_key"
android:title="@string/settings_stats_coordinate" />
</PreferenceCategory>
</PreferenceScreen>