Autoformatted all classes (non-testing).

This commit is contained in:
Dennis Guse
2019-07-23 22:22:17 +02:00
parent 5cfae845b7
commit 363cb14bff
141 changed files with 21719 additions and 21793 deletions
@@ -39,7 +39,7 @@ public class ChartValueSeriesTest {
InstrumentationRegistry.getInstrumentation().getContext(), InstrumentationRegistry.getInstrumentation().getContext(),
Integer.MIN_VALUE, Integer.MIN_VALUE,
Integer.MAX_VALUE, Integer.MAX_VALUE,
new int[] {100, 1000 }, new int[]{100, 1000},
R.string.description_elevation_metric, R.string.description_elevation_metric,
R.string.description_elevation_imperial, R.string.description_elevation_imperial,
R.color.chart_elevation_fill, R.color.chart_elevation_fill,
@@ -20,7 +20,6 @@ import android.location.Location;
import com.google.android.apps.mytracks.content.SensorDataSetLocation; import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.sensor.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.sensor.SensorDataSet; import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
/** /**
@@ -31,13 +30,13 @@ import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
*/ */
public class TrackStubUtils { public class TrackStubUtils {
public static final double INITIAL_ALTITUDE = 22;
public static final long INITIAL_TIME = 1000L;
static final String LOCATION_PROVIDER = "gps"; static final String LOCATION_PROVIDER = "gps";
static final double INITIAL_LATITUDE = 22; static final double INITIAL_LATITUDE = 22;
static final double INITIAL_LONGITUDE = 22; static final double INITIAL_LONGITUDE = 22;
public static final double INITIAL_ALTITUDE = 22;
static final float INITIAL_ACCURACY = 5; static final float INITIAL_ACCURACY = 5;
static final float INITIAL_SPEED = 10; static final float INITIAL_SPEED = 10;
public static final long INITIAL_TIME = 1000L;
static final float INITIAL_BEARING = 3.0f; static final float INITIAL_BEARING = 3.0f;
// Used to change the value of latitude, longitude, and altitude. // Used to change the value of latitude, longitude, and altitude.
static final double DIFFERENCE = 0.01; static final double DIFFERENCE = 0.01;
@@ -50,13 +50,12 @@ import java.util.concurrent.atomic.AtomicInteger;
*/ */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class CustomContentProviderUtilsImplTest { public class CustomContentProviderUtilsImplTest {
private Context context;
private ContentProviderUtils providerUtils;
private static final String NAME_PREFIX = "test name"; private static final String NAME_PREFIX = "test name";
private static final String MOCK_DESC = "Mock Next Waypoint Desc!"; private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
private static final String TEST_DESC = "Test Desc!"; private static final String TEST_DESC = "Test Desc!";
private static final String TEST_DESC_NEW = "Test Desc new!"; private static final String TEST_DESC_NEW = "Test Desc new!";
private Context context;
private ContentProviderUtils providerUtils;
private double INITIAL_LATITUDE = 37.0; private double INITIAL_LATITUDE = 37.0;
private double INITIAL_LONGITUDE = -57.0; private double INITIAL_LONGITUDE = -57.0;
private double ALTITUDE_INTERVAL = 2.5; private double ALTITUDE_INTERVAL = 2.5;
@@ -859,12 +858,12 @@ public class CustomContentProviderUtilsImplTest {
* @param numPoints the location number in the track * @param numPoints the location number in the track
* @return the simulated track * @return the simulated track
*/ */
private Track getTrack(long id,int numPoints) { private Track getTrack(long id, int numPoints) {
Track track = new Track(); Track track = new Track();
track.setId(id); track.setId(id);
track.setName("Test: " + id); track.setName("Test: " + id);
track.setNumberOfPoints(numPoints); track.setNumberOfPoints(numPoints);
for(int i = 0; i < numPoints; i++) { for (int i = 0; i < numPoints; i++) {
track.addLocation(createLocation(i)); track.addLocation(createLocation(i));
} }
return track; return track;
@@ -872,6 +871,7 @@ public class CustomContentProviderUtilsImplTest {
/** /**
* Creates a location. * Creates a location.
*
* @param i the index to set the value of location. * @param i the index to set the value of location.
* @return created location * @return created location
*/ */
@@ -75,7 +75,7 @@ public class TrackDataHubTest {
SharedPreferences.OnSharedPreferenceChangeListener>(); SharedPreferences.OnSharedPreferenceChangeListener>();
@Before @Before
@UsesMocks({ ContentProviderUtils.class, DataSource.class, TrackDataListener.class }) @UsesMocks({ContentProviderUtils.class, DataSource.class, TrackDataListener.class})
@Override @Override
protected void setUp() { protected void setUp() {
context = new MockContext(new MockContentResolver(), new RenamingDelegatingContext( context = new MockContext(new MockContentResolver(), new RenamingDelegatingContext(
@@ -605,6 +605,94 @@ public class TrackDataHubTest {
AndroidMock.reset(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2); AndroidMock.reset(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
} }
/**
* Tests the method {@link TrackDataHub#start()}.
*/
public void testRegisterTracksTableListener() {
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerContentObserver(eq(TracksColumns.CONTENT_URI), capture(observerCapture));
Track track = TrackStubUtils.createTrack(1);
expect(contentProviderUtils.getTrack(capture(new Capture<Long>()))).andReturn(track);
// Make the track id is unique.
PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
trackDataListener1.onTrackUpdated(track);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#start()}.
*/
public void testRegisterWaypointsTableListener() {
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_URI), capture(observerCapture));
expect(contentProviderUtils.getWaypointCursor(
capture(new Capture<Long>()), capture(new Capture<Long>()),
capture(new Capture<Integer>()))).andReturn(null);
trackDataListener1.clearWaypoints();
trackDataListener1.onNewWaypointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.min_required_accuracy_key.
*/
public void testNotifyPreferenceChanged_minRequiredAccuracy() {
int value = 1;
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.recording_gps_accuracy_key));
assertEquals(value, trackDataHub.getRecordingGpsAccuracy());
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value + 1);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.recording_gps_accuracy_key));
assertEquals(value + 1, trackDataHub.getRecordingGpsAccuracy());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
public void testNotifyPreferenceChanged_metricUnitsNoNotify() {
String imperialUnits = context.getString(R.string.stats_units_imperial);
PreferencesUtils.setString(context, R.string.stats_units_key, imperialUnits);
trackDataHub.notifyPreferenceChanged(
PreferencesUtils.getKey(context, R.string.stats_units_key));
assertEquals(false, trackDataHub.isMetricUnits());
PreferencesUtils.setString(
context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
trackDataHub.notifyPreferenceChanged(
PreferencesUtils.getKey(context, R.string.stats_units_key));
assertEquals(true, trackDataHub.isMetricUnits());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
public void testNotifyPreferenceChanged_reportSpeedNoNotify() {
String value = context.getString(R.string.stats_rate_pace);
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.stats_rate_key));
assertEquals(false, trackDataHub.isReportSpeed());
value = context.getString(R.string.stats_rate_speed);
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.stats_rate_key));
assertEquals(true, trackDataHub.isReportSpeed());
}
/** /**
* Fixed size cursor answer. * Fixed size cursor answer.
* *
@@ -619,9 +707,9 @@ public class TrackDataHubTest {
@Override @Override
public Cursor answer() throws Throwable { public Cursor answer() throws Throwable {
MatrixCursor cursor = new MatrixCursor(new String[] { BaseColumns._ID }); MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID});
for (long i = 0; i < size; i++) { for (long i = 0; i < size; i++) {
cursor.addRow(new Object[] { i }); cursor.addRow(new Object[]{i});
} }
return cursor; return cursor;
} }
@@ -717,93 +805,4 @@ public class TrackDataHubTest {
// Do nothing // Do nothing
} }
} }
/**
* Tests the method {@link TrackDataHub#start()}.
*/
public void testRegisterTracksTableListener() {
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerContentObserver(eq(TracksColumns.CONTENT_URI), capture(observerCapture));
Track track = TrackStubUtils.createTrack(1);
expect(contentProviderUtils.getTrack(capture(new Capture<Long>()))).andReturn(track);
// Make the track id is unique.
PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
trackDataListener1.onTrackUpdated(track);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#start()}.
*/
public void testRegisterWaypointsTableListener() {
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_URI), capture(observerCapture));
expect(contentProviderUtils.getWaypointCursor(
capture(new Capture<Long>()), capture(new Capture<Long>()),
capture(new Capture<Integer>()))).andReturn(null);
trackDataListener1.clearWaypoints();
trackDataListener1.onNewWaypointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.min_required_accuracy_key.
*/
public void testNotifyPreferenceChanged_minRequiredAccuracy() {
int value = 1;
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.recording_gps_accuracy_key));
assertEquals(value, trackDataHub.getRecordingGpsAccuracy());
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value + 1);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.recording_gps_accuracy_key));
assertEquals(value + 1, trackDataHub.getRecordingGpsAccuracy());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
public void testNotifyPreferenceChanged_metricUnitsNoNotify() {
String imperialUnits = context.getString(R.string.stats_units_imperial);
PreferencesUtils.setString(context, R.string.stats_units_key, imperialUnits);
trackDataHub.notifyPreferenceChanged(
PreferencesUtils.getKey(context, R.string.stats_units_key));
assertEquals(false, trackDataHub.isMetricUnits());
PreferencesUtils.setString(
context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
trackDataHub.notifyPreferenceChanged(
PreferencesUtils.getKey(context, R.string.stats_units_key));
assertEquals(true, trackDataHub.isMetricUnits());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
public void testNotifyPreferenceChanged_reportSpeedNoNotify() {
String value = context.getString(R.string.stats_rate_pace);
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.stats_rate_key));
assertEquals(false, trackDataHub.isReportSpeed());
value = context.getString(R.string.stats_rate_speed);
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.stats_rate_key));
assertEquals(true, trackDataHub.isReportSpeed());
}
} }
@@ -16,8 +16,11 @@
package com.google.android.apps.mytracks.io.file.importer; package com.google.android.apps.mytracks.io.file.importer;
import static com.google.android.testing.mocking.AndroidMock.eq; import android.content.ContentUris;
import static com.google.android.testing.mocking.AndroidMock.expect; import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.test.AndroidTestCase;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.ContentProviderUtils.Factory; import com.google.android.apps.mytracks.content.ContentProviderUtils.Factory;
@@ -29,17 +32,14 @@ import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
import com.google.android.testing.mocking.AndroidMock; import com.google.android.testing.mocking.AndroidMock;
import com.google.android.testing.mocking.UsesMocks; import com.google.android.testing.mocking.UsesMocks;
import android.content.ContentUris; import org.easymock.Capture;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.test.AndroidTestCase;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Locale; import java.util.Locale;
import java.util.SimpleTimeZone; import java.util.SimpleTimeZone;
import org.easymock.Capture; import static com.google.android.testing.mocking.AndroidMock.eq;
import static com.google.android.testing.mocking.AndroidMock.expect;
/** /**
* Abstract class for testing file track importers. * Abstract class for testing file track importers.
@@ -67,6 +67,18 @@ public class AbstractTestFileTrackImporter extends AndroidTestCase {
"yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US); "yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);
protected static final SimpleDateFormat DATE_FORMAT_1 = new SimpleDateFormat( protected static final SimpleDateFormat DATE_FORMAT_1 = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US); "yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
protected static final long TRACK_ID_0 = 1;
protected static final long TRACK_ID_1 = 2;
protected static final long TRACK_POINT_ID_0 = 1;
protected static final long TRACK_POINT_ID_1 = 2;
protected static final long TRACK_POINT_ID_3 = 4;
protected static final long WAYPOINT_ID_0 = 1;
protected static final Uri TRACK_ID_0_URI = ContentUris.appendId(
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_0).build();
protected static final Uri TRACK_ID_1_URI = ContentUris.appendId(
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_1).build();
protected static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(
WaypointsColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
static { static {
/* /*
@@ -78,20 +90,6 @@ public class AbstractTestFileTrackImporter extends AndroidTestCase {
DATE_FORMAT_1.setTimeZone(utc); DATE_FORMAT_1.setTimeZone(utc);
} }
protected static final long TRACK_ID_0 = 1;
protected static final long TRACK_ID_1 = 2;
protected static final long TRACK_POINT_ID_0 = 1;
protected static final long TRACK_POINT_ID_1 = 2;
protected static final long TRACK_POINT_ID_3 = 4;
protected static final long WAYPOINT_ID_0 = 1;
protected static final Uri TRACK_ID_0_URI = ContentUris.appendId(
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_0).build();
protected static final Uri TRACK_ID_1_URI = ContentUris.appendId(
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_1).build();
protected static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(
WaypointsColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
protected ContentProviderUtils contentProviderUtils; protected ContentProviderUtils contentProviderUtils;
private Factory oldMyTracksProviderUtilsFactory; private Factory oldMyTracksProviderUtilsFactory;
@@ -16,20 +16,20 @@
package com.google.android.apps.mytracks.io.file.importer; package com.google.android.apps.mytracks.io.file.importer;
import static com.google.android.testing.mocking.AndroidMock.eq; import android.location.Location;
import static com.google.android.testing.mocking.AndroidMock.expect;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import com.google.android.testing.mocking.AndroidMock; import com.google.android.testing.mocking.AndroidMock;
import android.location.Location; import org.easymock.Capture;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import org.easymock.Capture; import static com.google.android.testing.mocking.AndroidMock.eq;
import static com.google.android.testing.mocking.AndroidMock.expect;
/** /**
* Tests for {@link GpxFileTrackImporter}. * Tests for {@link GpxFileTrackImporter}.
@@ -38,6 +38,30 @@ import org.easymock.Capture;
*/ */
public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter { public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg><trkseg>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, null) + getTrackPoint(1, null) + "</trkseg><trkseg>"
+ getTrackPoint(2, null) + getTrackPoint(3, null) + "</trkseg></trk></gpx>";
private static final String INVALID_XML_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.substring(
0, VALID_ONE_TRACK_ONE_SEGMENT_GPX.length() - 50);
private static final String INVALID_LOCATION_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_LATITUDE), "1000.0");
private static final String INVALID_TIME_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
TRACK_TIME_0, "invalid");
private static final String INVALID_ALTITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_ELEVATION), "invalid");
private static final String INVALID_LATITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_LATITUDE), "invalid");
private static final String INVALID_LONGITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_LONGITUDE), "invalid");
private static String getNameAndDescription(String name, String description) { private static String getNameAndDescription(String name, String description) {
return "<name><![CDATA[" + name + "]]></name>" + "<desc><![CDATA[" + description + "]]></desc>"; return "<name><![CDATA[" + name + "]]></name>" + "<desc><![CDATA[" + description + "]]></desc>";
} }
@@ -56,31 +80,6 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
return buffer.toString(); return buffer.toString();
} }
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg><trkseg>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, null) + getTrackPoint(1, null) + "</trkseg><trkseg>"
+ getTrackPoint(2, null) + getTrackPoint(3, null) + "</trkseg></trk></gpx>";
private static final String INVALID_XML_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.substring(
0, VALID_ONE_TRACK_ONE_SEGMENT_GPX.length() - 50);
private static final String INVALID_LOCATION_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_LATITUDE), "1000.0");
private static final String INVALID_TIME_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
TRACK_TIME_0, "invalid");
private static final String INVALID_ALTITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_ELEVATION), "invalid");
private static final String INVALID_LATITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_LATITUDE), "invalid");
private static final String INVALID_LONGITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
Double.toString(TRACK_LONGITUDE), "invalid");
/** /**
* Tests one track with one segment. * Tests one track with one segment.
*/ */
@@ -16,20 +16,20 @@
package com.google.android.apps.mytracks.io.file.importer; package com.google.android.apps.mytracks.io.file.importer;
import static com.google.android.testing.mocking.AndroidMock.eq; import android.location.Location;
import static com.google.android.testing.mocking.AndroidMock.expect;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import com.google.android.testing.mocking.AndroidMock; import com.google.android.testing.mocking.AndroidMock;
import android.location.Location; import org.easymock.Capture;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import org.easymock.Capture; import static com.google.android.testing.mocking.AndroidMock.eq;
import static com.google.android.testing.mocking.AndroidMock.expect;
/** /**
* Tests for {@link KmlFileTrackImporter}. * Tests for {@link KmlFileTrackImporter}.
@@ -38,6 +38,18 @@ import org.easymock.Capture;
*/ */
public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter { public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX =
"<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX =
"<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</gx:Track><gx:Track>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
private static String getNameAndDescription(String name, String description) { private static String getNameAndDescription(String name, String description) {
return "<name><![CDATA[" + name + "]]></name><description><![CDATA[" + description return "<name><![CDATA[" + name + "]]></name><description><![CDATA[" + description
+ "]]></description>"; + "]]></description>";
@@ -53,18 +65,6 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
return buffer.toString(); return buffer.toString();
} }
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX =
"<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX =
"<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</gx:Track><gx:Track>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
/** /**
* Tests one track with one segment. * Tests one track with one segment.
*/ */
@@ -16,14 +16,14 @@
package com.google.android.apps.mytracks.io.file.importer; package com.google.android.apps.mytracks.io.file.importer;
import com.google.android.testing.mocking.AndroidMock;
import android.location.Location; import android.location.Location;
import java.util.Arrays; import com.google.android.testing.mocking.AndroidMock;
import org.easymock.IArgumentMatcher; import org.easymock.IArgumentMatcher;
import java.util.Arrays;
/** /**
* Locations matcher. Workaround because of capture bug 2617107 in easymock: * Locations matcher. Workaround because of capture bug 2617107 in easymock:
* http://sourceforge.net/tracker/?func=detail&aid=2617107&group_id=82958&atid=567837 * http://sourceforge.net/tracker/?func=detail&aid=2617107&group_id=82958&atid=567837
@@ -32,8 +32,14 @@ import org.easymock.IArgumentMatcher;
*/ */
public class LocationsMatcher implements IArgumentMatcher { public class LocationsMatcher implements IArgumentMatcher {
private final Location[] expectedLocations;
private LocationsMatcher(Location[] expected) {
this.expectedLocations = expected;
}
public static Location[] eqLoc(Location expected) { public static Location[] eqLoc(Location expected) {
return eqLoc(new Location[] { expected }); return eqLoc(new Location[]{expected});
} }
public static Location[] eqLoc(Location[] expected) { public static Location[] eqLoc(Location[] expected) {
@@ -42,12 +48,6 @@ public class LocationsMatcher implements IArgumentMatcher {
return null; return null;
} }
private final Location[] expectedLocations;
private LocationsMatcher(Location[] expected) {
this.expectedLocations = expected;
}
@Override @Override
public void appendTo(StringBuffer buf) { public void appendTo(StringBuffer buf) {
buf.append("eqLoc(").append(Arrays.toString(expectedLocations)).append(")"); buf.append("eqLoc(").append(Arrays.toString(expectedLocations)).append(")");
@@ -24,7 +24,6 @@ import junit.framework.TestCase;
*/ */
public class AdaptiveLocationListenerPolicyTest extends TestCase { public class AdaptiveLocationListenerPolicyTest extends TestCase {
private AdaptiveLocationListenerPolicy adocationListenerPolicy;
private static final long MIN = 1000; private static final long MIN = 1000;
private static final long MAX = 3000; private static final long MAX = 3000;
private static final int MIN_DISTANCE = 10; private static final int MIN_DISTANCE = 10;
@@ -32,6 +31,7 @@ public class AdaptiveLocationListenerPolicyTest extends TestCase {
private static final long NEW_IDLE_TIME_NORMAL = 5000; private static final long NEW_IDLE_TIME_NORMAL = 5000;
private static final long NEW_IDLE_TIME_SMALL = 2000; private static final long NEW_IDLE_TIME_SMALL = 2000;
private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500; private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500;
private AdaptiveLocationListenerPolicy adocationListenerPolicy;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
@@ -15,14 +15,14 @@
*/ */
package com.google.android.apps.mytracks.services; package com.google.android.apps.mytracks.services;
import com.google.android.maps.mytracks.R;
import com.google.android.testing.mocking.UsesMocks;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.RemoteException; import android.os.RemoteException;
import android.test.ServiceTestCase; import android.test.ServiceTestCase;
import com.google.android.maps.mytracks.R;
import com.google.android.testing.mocking.UsesMocks;
import org.easymock.EasyMock; import org.easymock.EasyMock;
/** /**
@@ -38,8 +38,8 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.CustomContentProvider;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.CustomContentProvider;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
@@ -79,24 +79,6 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
super(TestRecordingService.class); super(TestRecordingService.class);
} }
/**
* A context wrapper with the user provided {@link ContentResolver}. TODO:
* Move to test utils package.
*/
public static class MockContext extends ContextWrapper {
private final ContentResolver contentResolver;
public MockContext(ContentResolver contentResolver, Context base) {
super(base);
this.contentResolver = contentResolver;
}
@Override
public ContentResolver getContentResolver() {
return contentResolver;
}
}
@Override @Override
protected IBinder bindService(Intent intent) { protected IBinder bindService(Intent intent) {
if (getService() != null) { if (getService() != null) {
@@ -328,47 +310,6 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
assertEquals(-1L, service.getRecordingTrackId()); assertEquals(-1L, service.getRecordingTrackId());
} }
/**
* Synchronous/waitable broadcast receiver to be used in testing.
*/
private class BlockingBroadcastReceiver extends BroadcastReceiver {
private static final long MAX_WAIT_TIME_MS = 10000;
private List<Intent> receivedIntents = new ArrayList<Intent>();
public List<Intent> getReceivedIntents() {
return receivedIntents;
}
@Override
public void onReceive(Context ctx, Intent intent) {
Log.d("Test", "Got broadcast: " + intent);
synchronized (receivedIntents) {
receivedIntents.add(intent);
receivedIntents.notifyAll();
}
}
public boolean waitUntilReceived(int receiveCount) {
long deadline = System.currentTimeMillis() + MAX_WAIT_TIME_MS;
synchronized (receivedIntents) {
while (receivedIntents.size() < receiveCount) {
try {
// Wait releases synchronized lock until it returns
receivedIntents.wait(500);
} catch (InterruptedException e) {
// Do nothing
}
if (System.currentTimeMillis() > deadline) {
return false;
}
}
}
return true;
}
}
@MediumTest @MediumTest
public void testStartNewTrack_noRecording() throws Exception { public void testStartNewTrack_noRecording() throws Exception {
// NOTICE: due to the way Android permissions work, if this fails, // NOTICE: due to the way Android permissions work, if this fails,
@@ -757,4 +698,63 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
trackRecordingService.insertTrackPoint(location); trackRecordingService.insertTrackPoint(location);
Thread.sleep(200); Thread.sleep(200);
} }
/**
* A context wrapper with the user provided {@link ContentResolver}. TODO:
* Move to test utils package.
*/
public static class MockContext extends ContextWrapper {
private final ContentResolver contentResolver;
public MockContext(ContentResolver contentResolver, Context base) {
super(base);
this.contentResolver = contentResolver;
}
@Override
public ContentResolver getContentResolver() {
return contentResolver;
}
}
/**
* Synchronous/waitable broadcast receiver to be used in testing.
*/
private class BlockingBroadcastReceiver extends BroadcastReceiver {
private static final long MAX_WAIT_TIME_MS = 10000;
private List<Intent> receivedIntents = new ArrayList<Intent>();
public List<Intent> getReceivedIntents() {
return receivedIntents;
}
@Override
public void onReceive(Context ctx, Intent intent) {
Log.d("Test", "Got broadcast: " + intent);
synchronized (receivedIntents) {
receivedIntents.add(intent);
receivedIntents.notifyAll();
}
}
public boolean waitUntilReceived(int receiveCount) {
long deadline = System.currentTimeMillis() + MAX_WAIT_TIME_MS;
synchronized (receivedIntents) {
while (receivedIntents.size() < receiveCount) {
try {
// Wait releases synchronized lock until it returns
receivedIntents.wait(500);
} catch (InterruptedException e) {
// Do nothing
}
if (System.currentTimeMillis() > deadline) {
return false;
}
}
}
return true;
}
}
} }
@@ -15,16 +15,6 @@
*/ */
package com.google.android.apps.mytracks.services.tasks; package com.google.android.apps.mytracks.services.tasks;
import static com.google.android.testing.mocking.AndroidMock.capture;
import static com.google.android.testing.mocking.AndroidMock.eq;
import static com.google.android.testing.mocking.AndroidMock.expect;
import static com.google.android.testing.mocking.AndroidMock.same;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.testing.mocking.AndroidMock;
import com.google.android.testing.mocking.UsesMocks;
import android.content.Context; import android.content.Context;
import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener; import android.speech.tts.TextToSpeech.OnInitListener;
@@ -33,12 +23,22 @@ import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.testing.mocking.AndroidMock;
import com.google.android.testing.mocking.UsesMocks;
import org.easymock.Capture;
import org.easymock.EasyMock;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.easymock.Capture; import static com.google.android.testing.mocking.AndroidMock.capture;
import org.easymock.EasyMock; import static com.google.android.testing.mocking.AndroidMock.eq;
import static com.google.android.testing.mocking.AndroidMock.expect;
import static com.google.android.testing.mocking.AndroidMock.same;
/** /**
* Tests for {@link AnnouncementPeriodicTask}. * Tests for {@link AnnouncementPeriodicTask}.
@@ -61,197 +61,6 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
private TextToSpeechDelegate ttsDelegate; private TextToSpeechDelegate ttsDelegate;
private TextToSpeechInterface tts; private TextToSpeechInterface tts;
/**
* Mockable interface that we delegate TTS calls to.
*/
interface TextToSpeechInterface {
int addEarcon(String earcon, String packagename, int resourceId);
int addEarcon(String earcon, String filename);
int addSpeech(String text, String packagename, int resourceId);
int addSpeech(String text, String filename);
boolean areDefaultsEnforced();
String getDefaultEngine();
Locale getLanguage();
int isLanguageAvailable(Locale loc);
boolean isSpeaking();
int playEarcon(String earcon, int queueMode,
HashMap<String, String> params);
int playSilence(long durationInMs, int queueMode,
HashMap<String, String> params);
int setEngineByPackageName(String enginePackageName);
int setLanguage(Locale loc);
int setOnUtteranceCompletedListener(OnUtteranceCompletedListener listener);
int setPitch(float pitch);
int setSpeechRate(float speechRate);
void shutdown();
int speak(String text, int queueMode, HashMap<String, String> params);
int stop();
int synthesizeToFile(String text, HashMap<String, String> params,
String filename);
}
/**
* Subclass of {@link TextToSpeech} which delegates calls to the interface
* above.
* The logic here is stupid and the author is ashamed of having to write it
* like this, but basically the issue is that TextToSpeech cannot be mocked
* without running its constructor, its constructor runs async operations
* which call other methods (and then if the methods are part of a mock we'd
* have to set a behavior, but we can't 'cause the object hasn't been fully
* built yet).
* The logic is that calls made during the constructor (when tts is not yet
* set) will go up to the original class, but after tts is set we'll forward
* them all to the mock.
*/
private class TextToSpeechDelegate
extends TextToSpeech implements TextToSpeechInterface {
public TextToSpeechDelegate(Context context, OnInitListener listener) {
super(context, listener);
}
@Override
public int addEarcon(String earcon, String packagename, int resourceId) {
if (tts == null) {
return super.addEarcon(earcon, packagename, resourceId);
}
return tts.addEarcon(earcon, packagename, resourceId);
}
@Override
public int addEarcon(String earcon, String filename) {
if (tts == null) {
return super.addEarcon(earcon, filename);
}
return tts.addEarcon(earcon, filename);
}
@Override
public int addSpeech(String text, String packagename, int resourceId) {
if (tts == null) {
return super.addSpeech(text, packagename, resourceId);
}
return tts.addSpeech(text, packagename, resourceId);
}
@Override
public int addSpeech(String text, String filename) {
if (tts == null) {
return super.addSpeech(text, filename);
}
return tts.addSpeech(text, filename);
}
@Override
public Locale getLanguage() {
if (tts == null) {
return super.getLanguage();
}
return tts.getLanguage();
}
@Override
public int isLanguageAvailable(Locale loc) {
if (tts == null) {
return super.isLanguageAvailable(loc);
}
return tts.isLanguageAvailable(loc);
}
@Override
public boolean isSpeaking() {
if (tts == null) {
return super.isSpeaking();
}
return tts.isSpeaking();
}
@Override
public int playEarcon(String earcon, int queueMode,
HashMap<String, String> params) {
if (tts == null) {
return super.playEarcon(earcon, queueMode, params);
}
return tts.playEarcon(earcon, queueMode, params);
}
@Override
public int playSilence(long durationInMs, int queueMode,
HashMap<String, String> params) {
if (tts == null) {
return super.playSilence(durationInMs, queueMode, params);
}
return tts.playSilence(durationInMs, queueMode, params);
}
@Override
public int setLanguage(Locale loc) {
if (tts == null) {
return super.setLanguage(loc);
}
return tts.setLanguage(loc);
}
@Override
public int setOnUtteranceCompletedListener(
OnUtteranceCompletedListener listener) {
if (tts == null) {
return super.setOnUtteranceCompletedListener(listener);
}
return tts.setOnUtteranceCompletedListener(listener);
}
@Override
public int setPitch(float pitch) {
if (tts == null) {
return super.setPitch(pitch);
}
return tts.setPitch(pitch);
}
@Override
public int setSpeechRate(float speechRate) {
if (tts == null) {
return super.setSpeechRate(speechRate);
}
return tts.setSpeechRate(speechRate);
}
@Override
public void shutdown() {
if (tts == null) {
super.shutdown();
return;
}
tts.shutdown();
}
@Override
public int speak(
String text, int queueMode, HashMap<String, String> params) {
if (tts == null) {
return super.speak(text, queueMode, params);
}
return tts.speak(text, queueMode, params);
}
@Override
public int stop() {
if (tts == null) {
return super.stop();
}
return tts.stop();
}
@Override
public int synthesizeToFile(String text, HashMap<String, String> params,
String filename) {
if (tts == null) {
return super.synthesizeToFile(text, params, filename);
}
return tts.synthesizeToFile(text, params, filename);
}
}
@UsesMocks({ @UsesMocks({
AnnouncementPeriodicTask.class, AnnouncementPeriodicTask.class,
StringUtils.class, StringUtils.class,
@@ -530,4 +339,214 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
AndroidMock.replay(mockTask); AndroidMock.replay(mockTask);
task.start(); task.start();
} }
/**
* Mockable interface that we delegate TTS calls to.
*/
interface TextToSpeechInterface {
int addEarcon(String earcon, String packagename, int resourceId);
int addEarcon(String earcon, String filename);
int addSpeech(String text, String packagename, int resourceId);
int addSpeech(String text, String filename);
boolean areDefaultsEnforced();
String getDefaultEngine();
Locale getLanguage();
int isLanguageAvailable(Locale loc);
boolean isSpeaking();
int playEarcon(String earcon, int queueMode,
HashMap<String, String> params);
int playSilence(long durationInMs, int queueMode,
HashMap<String, String> params);
int setEngineByPackageName(String enginePackageName);
int setLanguage(Locale loc);
int setOnUtteranceCompletedListener(OnUtteranceCompletedListener listener);
int setPitch(float pitch);
int setSpeechRate(float speechRate);
void shutdown();
int speak(String text, int queueMode, HashMap<String, String> params);
int stop();
int synthesizeToFile(String text, HashMap<String, String> params,
String filename);
}
/**
* Subclass of {@link TextToSpeech} which delegates calls to the interface
* above.
* The logic here is stupid and the author is ashamed of having to write it
* like this, but basically the issue is that TextToSpeech cannot be mocked
* without running its constructor, its constructor runs async operations
* which call other methods (and then if the methods are part of a mock we'd
* have to set a behavior, but we can't 'cause the object hasn't been fully
* built yet).
* The logic is that calls made during the constructor (when tts is not yet
* set) will go up to the original class, but after tts is set we'll forward
* them all to the mock.
*/
private class TextToSpeechDelegate
extends TextToSpeech implements TextToSpeechInterface {
public TextToSpeechDelegate(Context context, OnInitListener listener) {
super(context, listener);
}
@Override
public int addEarcon(String earcon, String packagename, int resourceId) {
if (tts == null) {
return super.addEarcon(earcon, packagename, resourceId);
}
return tts.addEarcon(earcon, packagename, resourceId);
}
@Override
public int addEarcon(String earcon, String filename) {
if (tts == null) {
return super.addEarcon(earcon, filename);
}
return tts.addEarcon(earcon, filename);
}
@Override
public int addSpeech(String text, String packagename, int resourceId) {
if (tts == null) {
return super.addSpeech(text, packagename, resourceId);
}
return tts.addSpeech(text, packagename, resourceId);
}
@Override
public int addSpeech(String text, String filename) {
if (tts == null) {
return super.addSpeech(text, filename);
}
return tts.addSpeech(text, filename);
}
@Override
public Locale getLanguage() {
if (tts == null) {
return super.getLanguage();
}
return tts.getLanguage();
}
@Override
public int isLanguageAvailable(Locale loc) {
if (tts == null) {
return super.isLanguageAvailable(loc);
}
return tts.isLanguageAvailable(loc);
}
@Override
public boolean isSpeaking() {
if (tts == null) {
return super.isSpeaking();
}
return tts.isSpeaking();
}
@Override
public int playEarcon(String earcon, int queueMode,
HashMap<String, String> params) {
if (tts == null) {
return super.playEarcon(earcon, queueMode, params);
}
return tts.playEarcon(earcon, queueMode, params);
}
@Override
public int playSilence(long durationInMs, int queueMode,
HashMap<String, String> params) {
if (tts == null) {
return super.playSilence(durationInMs, queueMode, params);
}
return tts.playSilence(durationInMs, queueMode, params);
}
@Override
public int setLanguage(Locale loc) {
if (tts == null) {
return super.setLanguage(loc);
}
return tts.setLanguage(loc);
}
@Override
public int setOnUtteranceCompletedListener(
OnUtteranceCompletedListener listener) {
if (tts == null) {
return super.setOnUtteranceCompletedListener(listener);
}
return tts.setOnUtteranceCompletedListener(listener);
}
@Override
public int setPitch(float pitch) {
if (tts == null) {
return super.setPitch(pitch);
}
return tts.setPitch(pitch);
}
@Override
public int setSpeechRate(float speechRate) {
if (tts == null) {
return super.setSpeechRate(speechRate);
}
return tts.setSpeechRate(speechRate);
}
@Override
public void shutdown() {
if (tts == null) {
super.shutdown();
return;
}
tts.shutdown();
}
@Override
public int speak(
String text, int queueMode, HashMap<String, String> params) {
if (tts == null) {
return super.speak(text, queueMode, params);
}
return tts.speak(text, queueMode, params);
}
@Override
public int stop() {
if (tts == null) {
return super.stop();
}
return tts.stop();
}
@Override
public int synthesizeToFile(String text, HashMap<String, String> params,
String filename) {
if (tts == null) {
return super.synthesizeToFile(text, params, filename);
}
return tts.synthesizeToFile(text, params, filename);
}
}
} }
@@ -80,7 +80,7 @@ public class TripStatisticsTest extends TestCase {
assertEquals(1300L, statistics.getMovingTime()); assertEquals(1300L, statistics.getMovingTime());
assertEquals(1100.0, statistics.getTotalDistance()); assertEquals(1100.0, statistics.getTotalDistance());
assertEquals(900.0, statistics.getTotalElevationGain()); assertEquals(900.0, statistics.getTotalElevationGain());
assertEquals(statistics.getTotalDistance()/ (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed()); assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed());
assertEquals(-10000, statistics.getLeft()); assertEquals(-10000, statistics.getLeft());
assertEquals(30000, statistics.getRight()); assertEquals(30000, statistics.getRight());
assertEquals(35000, statistics.getTop()); assertEquals(35000, statistics.getTop());
@@ -17,10 +17,10 @@ package com.google.android.apps.mytracks.util;
import android.os.Environment; import android.os.Environment;
import java.io.File;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.File;
/** /**
* Tests for {@link FileUtils}. * Tests for {@link FileUtils}.
* *
@@ -27,7 +27,6 @@ import androidx.fragment.app.FragmentActivity;
import com.google.android.apps.mytracks.util.TrackIconUtils; import com.google.android.apps.mytracks.util.TrackIconUtils;
/** /**
*
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public abstract class AbstractActivity extends FragmentActivity { public abstract class AbstractActivity extends FragmentActivity {
@@ -51,17 +51,8 @@ import java.util.ArrayList;
*/ */
public abstract class AbstractTrackActivity extends AbstractActivity implements ConfirmDeleteCaller { public abstract class AbstractTrackActivity extends AbstractActivity implements ConfirmDeleteCaller {
private static final int DELETE_REQUEST_CODE = 3;
protected static final int GPS_REQUEST_CODE = 6; protected static final int GPS_REQUEST_CODE = 6;
private static final int DELETE_REQUEST_CODE = 3;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DELETE_REQUEST_CODE) {
onDeleted();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) { public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
@@ -166,6 +157,15 @@ public abstract class AbstractTrackActivity extends AbstractActivity implements
}); });
} }
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DELETE_REQUEST_CODE) {
onDeleted();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
/** /**
* Delete tracks. * Delete tracks.
* *
@@ -16,14 +16,14 @@
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import android.os.Bundle;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StatsUtils; import com.google.android.apps.mytracks.util.StatsUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.os.Bundle;
import java.util.List; import java.util.List;
/** /**
@@ -15,24 +15,24 @@
*/ */
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
import com.google.android.apps.mytracks.services.TrackRecordingService;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.services.TrackRecordingService;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
/** /**
* This class react to the BOOT_COMPLETED broadcast. * This class react to the BOOT_COMPLETED broadcast.
* * <p>
* One example of a broadcast message that this class is interested in, * One example of a broadcast message that this class is interested in,
* is notification about the phone boot. We may want to resume a previously * is notification about the phone boot. We may want to resume a previously
* started tracking session if the phone crashed (hopefully not), or the user * started tracking session if the phone crashed (hopefully not), or the user
* decided to swap the battery or some external event occurred which forced * decided to swap the battery or some external event occurred which forced
* a phone reboot. * a phone reboot.
* * <p>
* This class simply delegates to {@link TrackRecordingService} to make a * This class simply delegates to {@link TrackRecordingService} to make a
* decision whether to continue with the previous track (if any), or just * decision whether to continue with the previous track (if any), or just
* abandon it. * abandon it.
@@ -22,6 +22,7 @@ import android.graphics.Paint;
import android.graphics.Paint.Align; import android.graphics.Paint.Align;
import android.graphics.Paint.Style; import android.graphics.Paint.Style;
import android.graphics.Path; import android.graphics.Path;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.stats.ExtremityMonitor; import com.google.android.apps.mytracks.stats.ExtremityMonitor;
@@ -104,6 +105,13 @@ public class ChartValueSeries {
path = new Path(); path = new Path();
} }
/**
* Returns true if the series is enabled.
*/
public boolean isEnabled() {
return enabled;
}
/** /**
* Sets the series enabled value. * Sets the series enabled value.
* *
@@ -113,13 +121,6 @@ public class ChartValueSeries {
this.enabled = enabled; this.enabled = enabled;
} }
/**
* Returns true if the series is enabled.
*/
public boolean isEnabled() {
return enabled;
}
/** /**
* Returns true if the series has data. * Returns true if the series has data.
*/ */
@@ -71,30 +71,27 @@ public class ChartView extends View {
private static final NumberFormat X_NUMBER_FORMAT = NumberFormat.getIntegerInstance(); private static final NumberFormat X_NUMBER_FORMAT = NumberFormat.getIntegerInstance();
private static final NumberFormat X_FRACTION_FORMAT = NumberFormat.getNumberInstance(); private static final NumberFormat X_FRACTION_FORMAT = NumberFormat.getNumberInstance();
private static final int BORDER = 8;
private static final int SPACER = 4;
private static final int Y_AXIS_OFFSET = 16;
static { static {
X_FRACTION_FORMAT.setMaximumFractionDigits(1); X_FRACTION_FORMAT.setMaximumFractionDigits(1);
X_FRACTION_FORMAT.setMinimumFractionDigits(1); X_FRACTION_FORMAT.setMinimumFractionDigits(1);
} }
private static final int BORDER = 8;
private static final int SPACER = 4;
private static final int Y_AXIS_OFFSET = 16;
private final ChartValueSeries[] series = new ChartValueSeries[NUM_SERIES]; private final ChartValueSeries[] series = new ChartValueSeries[NUM_SERIES];
private final ArrayList<double[]> chartData = new ArrayList<>(); private final ArrayList<double[]> chartData = new ArrayList<>();
private final ArrayList<Waypoint> waypoints = new ArrayList<>(); private final ArrayList<Waypoint> waypoints = new ArrayList<>();
private final ExtremityMonitor xExtremityMonitor = new ExtremityMonitor(); private final ExtremityMonitor xExtremityMonitor = new ExtremityMonitor();
private double maxX = 1.0;
private final Paint axisPaint; private final Paint axisPaint;
private final Paint xAxisMarkerPaint; private final Paint xAxisMarkerPaint;
private final Paint gridPaint; private final Paint gridPaint;
private final Drawable pointer; private final Drawable pointer;
private final int markerWidth; private final int markerWidth;
private final int markerHeight; private final int markerHeight;
private final Scroller scroller; private final Scroller scroller;
private double maxX = 1.0;
private VelocityTracker velocityTracker = null; private VelocityTracker velocityTracker = null;
private float lastMotionEventX = -1; private float lastMotionEventX = -1;
private int zoomLevel = 1; private int zoomLevel = 1;
@@ -127,7 +124,7 @@ public class ChartView extends View {
series[ELEVATION_SERIES] = new ChartValueSeries(context, series[ELEVATION_SERIES] = new ChartValueSeries(context,
Integer.MIN_VALUE, Integer.MIN_VALUE,
Integer.MAX_VALUE, Integer.MAX_VALUE,
new int[] { 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000 }, new int[]{5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
R.string.description_elevation_metric, R.string.description_elevation_metric,
R.string.description_elevation_imperial, R.string.description_elevation_imperial,
R.color.chart_elevation_fill, R.color.chart_elevation_fill,
@@ -135,7 +132,7 @@ public class ChartView extends View {
series[SPEED_SERIES] = new ChartValueSeries(context, series[SPEED_SERIES] = new ChartValueSeries(context,
0, 0,
Integer.MAX_VALUE, Integer.MAX_VALUE,
new int[] {1, 5, 10, 20, 50, 100 }, new int[]{1, 5, 10, 20, 50, 100},
R.string.description_speed_metric, R.string.description_speed_metric,
R.string.description_speed_imperial, R.string.description_speed_imperial,
R.color.chart_speed_fill, R.color.chart_speed_fill,
@@ -143,7 +140,7 @@ public class ChartView extends View {
series[PACE_SERIES] = new ChartValueSeries(context, series[PACE_SERIES] = new ChartValueSeries(context,
0, 0,
Integer.MAX_VALUE, Integer.MAX_VALUE,
new int[] {1, 2, 5, 10, 15, 20, 30, 60, 120 }, new int[]{1, 2, 5, 10, 15, 20, 30, 60, 120},
R.string.description_pace_metric, R.string.description_pace_metric,
R.string.description_pace_imperial, R.string.description_pace_imperial,
R.color.chart_pace_fill, R.color.chart_pace_fill,
@@ -151,7 +148,7 @@ public class ChartView extends View {
series[HEART_RATE_SERIES] = new ChartValueSeries(context, series[HEART_RATE_SERIES] = new ChartValueSeries(context,
0, 0,
Integer.MAX_VALUE, Integer.MAX_VALUE,
new int[] {25, 50 }, new int[]{25, 50},
R.string.description_sensor_heart_rate, R.string.description_sensor_heart_rate,
R.string.description_sensor_heart_rate, R.string.description_sensor_heart_rate,
R.color.chart_heart_rate_fill, R.color.chart_heart_rate_fill,
@@ -159,7 +156,7 @@ public class ChartView extends View {
series[CADENCE_SERIES] = new ChartValueSeries(context, series[CADENCE_SERIES] = new ChartValueSeries(context,
0, 0,
Integer.MAX_VALUE, Integer.MAX_VALUE,
new int[] {5, 10, 25, 50 }, new int[]{5, 10, 25, 50},
R.string.description_sensor_cadence, R.string.description_sensor_cadence,
R.string.description_sensor_cadence, R.string.description_sensor_cadence,
R.color.chart_cadence_fill, R.color.chart_cadence_fill,
@@ -167,7 +164,7 @@ public class ChartView extends View {
series[POWER_SERIES] = new ChartValueSeries(context, series[POWER_SERIES] = new ChartValueSeries(context,
0, 0,
1000, 1000,
new int[] { 5, 50, 100, 200 }, new int[]{5, 50, 100, 200},
R.string.description_sensor_power, R.string.description_sensor_power,
R.string.description_sensor_power, R.string.description_sensor_power,
R.color.chart_power_fill, R.color.chart_power_fill,
@@ -188,7 +185,7 @@ public class ChartView extends View {
gridPaint.setStyle(Style.STROKE); gridPaint.setStyle(Style.STROKE);
gridPaint.setColor(context.getResources().getColor(android.R.color.darker_gray)); gridPaint.setColor(context.getResources().getColor(android.R.color.darker_gray));
gridPaint.setAntiAlias(false); gridPaint.setAntiAlias(false);
gridPaint.setPathEffect(new DashPathEffect(new float[] { 3, 2 }, 0)); gridPaint.setPathEffect(new DashPathEffect(new float[]{3, 2}, 0));
Paint markerPaint = new Paint(); Paint markerPaint = new Paint();
markerPaint.setStyle(Style.STROKE); markerPaint.setStyle(Style.STROKE);
@@ -606,7 +603,7 @@ public class ChartView extends View {
} }
} }
} }
return new int[] { lines, lineHeight }; return new int[]{lines, lineHeight};
} }
/** /**
@@ -862,7 +859,7 @@ public class ChartView extends View {
yAxisOffset = (int) (density * Y_AXIS_OFFSET); yAxisOffset = (int) (density * Y_AXIS_OFFSET);
int markerLength = 0; int markerLength = 0;
for (int i = 0; i < series.length; i ++) { for (int i = 0; i < series.length; i++) {
ChartValueSeries chartValueSeries = series[i]; ChartValueSeries chartValueSeries = series[i];
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(i)) { if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(i)) {
Rect rect = getRect(chartValueSeries.getMarkerPaint(), chartValueSeries.getLargestMarker()); Rect rect = getRect(chartValueSeries.getMarkerPaint(), chartValueSeries.getLargestMarker());
@@ -23,15 +23,15 @@ package com.google.android.apps.mytracks;
*/ */
public class Constants { public class Constants {
private Constants() {}
/** /**
* Maximum number of waypoints that will be loaded at one time. * Maximum number of waypoints that will be loaded at one time.
*/ */
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000; public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
/** /**
* The settings file name. * The settings file name.
*/ */
public static final String SETTINGS_NAME = "SettingsActivity"; public static final String SETTINGS_NAME = "SettingsActivity";
private Constants() {
}
} }
@@ -16,15 +16,15 @@
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.maps.mytracks.R;
/** /**
* An activity for delete tracks. * An activity for delete tracks.
* *
@@ -28,10 +28,9 @@ import com.google.android.apps.mytracks.content.ContentProviderUtils;
*/ */
public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> { public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private DeleteActivity deleteActivity;
private final long[] trackIds; private final long[] trackIds;
private final Context context; private final Context context;
private DeleteActivity deleteActivity;
// true if the AsyncTask has completed // true if the AsyncTask has completed
private boolean completed; private boolean completed;
@@ -16,14 +16,6 @@
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.maps.mytracks.R;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
@@ -33,6 +25,14 @@ import android.widget.AutoCompleteTextView;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.maps.mytracks.R;
/** /**
* An activity to add/edit a marker. * An activity to add/edit a marker.
* *
@@ -40,11 +40,9 @@ import android.widget.EditText;
*/ */
public class MarkerEditActivity extends AbstractActivity { public class MarkerEditActivity extends AbstractActivity {
private static final String TAG = MarkerEditActivity.class.getSimpleName();
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";
private static final String TAG = MarkerEditActivity.class.getSimpleName();
private long trackId; private long trackId;
private long markerId; private long markerId;
private TrackRecordingServiceConnection trackRecordingServiceConnection; private TrackRecordingServiceConnection trackRecordingServiceConnection;
@@ -62,34 +62,14 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
private static final String TAG = MarkerListActivity.class.getSimpleName(); private static final String TAG = MarkerListActivity.class.getSimpleName();
private static final String[] PROJECTION = new String[] { WaypointsColumns._ID, private static final String[] PROJECTION = new String[]{WaypointsColumns._ID,
WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY, WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY,
WaypointsColumns.TYPE, WaypointsColumns.TIME, WaypointsColumns.PHOTOURL, WaypointsColumns.TYPE, WaypointsColumns.TIME, WaypointsColumns.PHOTOURL,
WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE}; WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE};
private ContentProviderUtils contentProviderUtils;
// Callback when an item is selected in the contextual action mode private SharedPreferences sharedPreferences;
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() { private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
@Override private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
boolean isSingleSelection = ids.length == 1;
menu.findItem(R.id.list_context_menu_share).setVisible(false);
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection);
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
menu.findItem(R.id.list_context_menu_delete).setVisible(true);
/*
* Set select all to the same visibility as delete since delete is the
* only action that can be applied to multiple markers.
*/
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
}
@Override
public boolean onClick(int itemId, int[] positions, long[] ids) {
return handleContextItem(itemId, ids);
}
};
/* /*
* Note that sharedPreferenceChangeListener cannot be an anonymous inner * Note that sharedPreferenceChangeListener cannot be an anonymous inner
* class. Anonymous inner class will get garbage collected. * class. Anonymous inner class will get garbage collected.
@@ -115,18 +95,32 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
} }
} }
}; };
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
private Track track; private Track track;
private ResourceCursorAdapter resourceCursorAdapter; private ResourceCursorAdapter resourceCursorAdapter;
// UI elements // UI elements
private ListView listView; private ListView listView;
// Callback when an item is selected in the contextual action mode
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@Override
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
boolean isSingleSelection = ids.length == 1;
menu.findItem(R.id.list_context_menu_share).setVisible(false);
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection);
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
menu.findItem(R.id.list_context_menu_delete).setVisible(true);
/*
* Set select all to the same visibility as delete since delete is the
* only action that can be applied to multiple markers.
*/
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
}
@Override
public boolean onClick(int itemId, int[] positions, long[] ids) {
return handleContextItem(itemId, ids);
}
};
private MenuItem insertMarkerMenuItem; private MenuItem insertMarkerMenuItem;
private MenuItem searchMenuItem; private MenuItem searchMenuItem;
@@ -188,10 +182,10 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
if (track != null) { if (track != null) {
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION, return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + "!=?", WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + "!=?",
new String[] { String.valueOf(track.getId()), String.valueOf(trackFirstWaypointId) }, null); new String[]{String.valueOf(track.getId()), String.valueOf(trackFirstWaypointId)}, null);
} else { } else {
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION, return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
WaypointsColumns.STARTTIME + " IS NULL",null, null); WaypointsColumns.STARTTIME + " IS NULL", null, null);
} }
} }
@@ -266,13 +260,13 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
getMenuInflater().inflate(R.menu.list_context_menu, menu); getMenuInflater().inflate(R.menu.list_context_menu, menu);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
contextualActionModeCallback.onPrepare(menu, new int[] { info.position }, new long[] { info.id }, false); contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
} }
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
if (handleContextItem(item.getItemId(), new long[] {info.id})) { if (handleContextItem(item.getItemId(), new long[]{info.id})) {
return true; return true;
} }
return super.onContextItemSelected(item); return super.onContextItemSelected(item);
@@ -305,7 +299,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
return true; return true;
case R.id.list_context_menu_delete: case R.id.list_context_menu_delete:
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.newInstance(markerIds)
.show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG); .show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
@@ -70,7 +70,7 @@ import java.util.SortedSet;
* An activity to display a list of search results. * An activity to display a list of search results.
* *
* @author Rodrigo Damazio * @author Rodrigo Damazio
* * <p>
* TODO: allow to refine search (present search in context menu) * TODO: allow to refine search (present search in context menu)
*/ */
public class SearchListActivity extends AbstractTrackActivity implements DeleteMarkerCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller { public class SearchListActivity extends AbstractTrackActivity implements DeleteMarkerCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
@@ -94,37 +94,21 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
private static final String TRACK_ID_FIELD = "trackId"; private static final String TRACK_ID_FIELD = "trackId";
private static final String MARKER_ID_FIELD = "markerId"; private static final String MARKER_ID_FIELD = "markerId";
private ContentProviderUtils contentProviderUtils;
private final OnSharedPreferenceChangeListener private SharedPreferences sharedPreferences;
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() { private TrackRecordingServiceConnection trackRecordingServiceConnection;
@Override private SearchEngine searchEngine;
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { private SearchRecentSuggestions searchRecentSuggestions;
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.stats_units_key))) { private ArrayAdapter<Map<String, Object>> arrayAdapter;
metricUnits = PreferencesUtils.isMetricUnits(SearchListActivity.this); private boolean metricUnits = true;
} private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_id_key))) {
recordingTrackId = PreferencesUtils.getLong(SearchListActivity.this, R.string.recording_track_id_key);
}
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_paused_key))) {
recordingTrackPaused = PreferencesUtils.getBoolean(SearchListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
arrayAdapter.notifyDataSetChanged();
}
});
}
}
};
// Callback when an item is selected in the contextual action mode // Callback when an item is selected in the contextual action mode
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() { private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
@Override @Override
public boolean onClick(int itemId, int[] positions, long[] ids) { public boolean onClick(int itemId, int[] positions, long[] ids) {
return handleContextItem(itemId, positions); return handleContextItem(itemId, positions);
} }
@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 = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
@@ -153,17 +137,30 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
menu.findItem(R.id.list_context_menu_select_all).setVisible(false); menu.findItem(R.id.list_context_menu_select_all).setVisible(false);
} }
}; };
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private SearchEngine searchEngine;
private SearchRecentSuggestions searchRecentSuggestions;
private ArrayAdapter<Map<String, Object>> arrayAdapter;
private boolean metricUnits = true;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT; private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
private final OnSharedPreferenceChangeListener
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.stats_units_key))) {
metricUnits = PreferencesUtils.isMetricUnits(SearchListActivity.this);
}
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_id_key))) {
recordingTrackId = PreferencesUtils.getLong(SearchListActivity.this, R.string.recording_track_id_key);
}
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_paused_key))) {
recordingTrackPaused = PreferencesUtils.getBoolean(SearchListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
arrayAdapter.notifyDataSetChanged();
}
});
}
}
};
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -284,13 +281,13 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
getMenuInflater().inflate(R.menu.list_context_menu, menu); getMenuInflater().inflate(R.menu.list_context_menu, menu);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
contextualActionModeCallback.onPrepare(menu, new int[] { info.position }, new long[] { info.id }, false); contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
} }
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
if (handleContextItem(item.getItemId(), new int[] {info.position})) { if (handleContextItem(item.getItemId(), new int[]{info.position})) {
return true; return true;
} }
return super.onContextItemSelected(item); return super.onContextItemSelected(item);
@@ -325,7 +322,7 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
Log.e(TAG, "Not implemented"); Log.e(TAG, "Not implemented");
return true; return true;
case R.id.list_context_menu_show_on_map: case R.id.list_context_menu_show_on_map:
intent = IntentUtils.newShowOnMapIntent((double)item.get(MARKER_LATITUDE_FIELD), (double)item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + ""); intent = IntentUtils.newShowOnMapIntent((double) item.get(MARKER_LATITUDE_FIELD), (double) item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + "");
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.list_context_menu_edit: case R.id.list_context_menu_edit:
@@ -343,9 +340,9 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
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.newInstance(new long[]{markerId}).show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
} else { } else {
deleteTracks(new long[] { trackId }); deleteTracks(new long[]{trackId});
} }
return true; return true;
default: default:
@@ -18,16 +18,17 @@ package com.google.android.apps.mytracks;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TabHost; import android.widget.TabHost;
import android.widget.TabHost.TabContentFactory; import android.widget.TabHost.TabContentFactory;
import android.widget.TabWidget; import android.widget.TabWidget;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@@ -52,45 +53,6 @@ public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabCh
private final ViewPager viewPager; private final ViewPager viewPager;
private final ArrayList<TabInfo> tabInfos = new ArrayList<>(); private final ArrayList<TabInfo> tabInfos = new ArrayList<>();
/**
* An object to hold a tab's info.
*
* @author Jimmy Shih
*/
private static final class TabInfo {
private final Class<?> clss;
private final Bundle bundle;
public TabInfo(Class<?> clss, Bundle bunlde) {
this.clss = clss;
this.bundle = bunlde;
}
}
/**
* A dummy {@link TabContentFactory} that creates an empty view to satisfy the
* {@link TabHost} API.
*
* @author Jimmy Shih
*/
private static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context context;
public DummyTabFactory(Context context) {
this.context = context;
}
@Override
public View createTabContent(String tag) {
View view = new View(context);
view.setMinimumWidth(0);
view.setMinimumHeight(0);
return view;
}
}
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) { public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) {
super(activity.getSupportFragmentManager()); super(activity.getSupportFragmentManager());
this.context = activity; this.context = activity;
@@ -129,7 +91,8 @@ public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabCh
} }
@Override @Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override @Override
public void onPageSelected(int position) { public void onPageSelected(int position) {
@@ -146,5 +109,45 @@ public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabCh
} }
@Override @Override
public void onPageScrollStateChanged(int state) {} public void onPageScrollStateChanged(int state) {
}
/**
* An object to hold a tab's info.
*
* @author Jimmy Shih
*/
private static final class TabInfo {
private final Class<?> clss;
private final Bundle bundle;
public TabInfo(Class<?> clss, Bundle bunlde) {
this.clss = clss;
this.bundle = bunlde;
}
}
/**
* A dummy {@link TabContentFactory} that creates an empty view to satisfy the
* {@link TabHost} API.
*
* @author Jimmy Shih
*/
private static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context context;
public DummyTabFactory(Context context) {
this.context = context;
}
@Override
public View createTabContent(String tag) {
View view = new View(context);
view.setMinimumWidth(0);
view.setMinimumHeight(0);
return view;
}
}
} }
@@ -76,11 +76,9 @@ import java.util.Date;
*/ */
public class TrackDetailActivity extends AbstractTrackActivity implements ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller { public class TrackDetailActivity extends AbstractTrackActivity implements ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
private static final String TAG = TrackDetailActivity.class.getSimpleName();
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";
private static final String TAG = TrackDetailActivity.class.getSimpleName();
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key"; private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
private static final String PHOTO_URI_KEY = "photo_uri_key"; private static final String PHOTO_URI_KEY = "photo_uri_key";
private static final String HAS_PHOTO_KEY = "has_photo_key"; private static final String HAS_PHOTO_KEY = "has_photo_key";
@@ -106,14 +104,6 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
// Preferences // Preferences
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT; private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
private MenuItem insertMarkerMenuItem;
private MenuItem insertPhotoMenuItem;
private MenuItem markerListMenuItem;
private MenuItem shareMenuItem;
private MenuItem voiceFrequencyMenuItem;
private MenuItem splitFrequencyMenuItem;
private final Runnable bindChangedCallback = new Runnable() { private final Runnable bindChangedCallback = new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -135,7 +125,6 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
}); });
} }
}; };
/* /*
* Note that sharedPreferenceChangeListener cannot be an anonymous inner * Note that sharedPreferenceChangeListener cannot be an anonymous inner
* class. Anonymous inner class will get garbage collected. * class. Anonymous inner class will get garbage collected.
@@ -164,7 +153,12 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
}); });
} }
}; };
private MenuItem insertMarkerMenuItem;
private MenuItem insertPhotoMenuItem;
private MenuItem markerListMenuItem;
private MenuItem shareMenuItem;
private MenuItem voiceFrequencyMenuItem;
private MenuItem splitFrequencyMenuItem;
private final OnClickListener recordListener = new OnClickListener() { private final OnClickListener recordListener = new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -214,7 +208,7 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
TabSpec statsTabSpec = tabHost.newTabSpec(StatsFragment.STATS_FRAGMENT_TAG).setIndicator(getString(R.string.track_detail_stats_tab), getResources().getDrawable(R.drawable.ic_tab_stats)); TabSpec statsTabSpec = tabHost.newTabSpec(StatsFragment.STATS_FRAGMENT_TAG).setIndicator(getString(R.string.track_detail_stats_tab), getResources().getDrawable(R.drawable.ic_tab_stats));
tabsAdapter.addTab(statsTabSpec, StatsFragment.class, null); tabsAdapter.addTab(statsTabSpec, StatsFragment.class, null);
TabSpec chartTabSpec = tabHost.newTabSpec(ChartFragment.CHART_FRAGMENT_TAG).setIndicator(getString(R.string.track_detail_chart_tab),getResources().getDrawable(R.drawable.ic_tab_chart)); TabSpec chartTabSpec = tabHost.newTabSpec(ChartFragment.CHART_FRAGMENT_TAG).setIndicator(getString(R.string.track_detail_chart_tab), getResources().getDrawable(R.drawable.ic_tab_chart));
tabsAdapter.addTab(chartTabSpec, ChartFragment.class, null); tabsAdapter.addTab(chartTabSpec, ChartFragment.class, null);
if (savedInstanceState != null) { if (savedInstanceState != null) {
@@ -381,7 +375,7 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.track_detail_delete: case R.id.track_detail_delete:
deleteTracks(new long[] { trackId }); deleteTracks(new long[]{trackId});
return true; return true;
case R.id.track_detail_settings: case R.id.track_detail_settings:
intent = IntentUtils.newIntent(this, SettingsActivity.class); intent = IntentUtils.newIntent(this, SettingsActivity.class);
@@ -84,10 +84,113 @@ import java.util.Locale;
public class TrackListActivity extends AbstractTrackActivity implements FileTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller { public class TrackListActivity extends AbstractTrackActivity implements FileTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
private static final String TAG = TrackListActivity.class.getSimpleName(); private static final String TAG = TrackListActivity.class.getSimpleName();
private static final String[] PROJECTION = new String[] { TracksColumns._ID, TracksColumns.NAME, private static final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME, TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME,
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON }; TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON};
// The following are set in onCreate
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private TrackController trackController;
private ListView listView;
private ResourceCursorAdapter resourceCursorAdapter;
private final LoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null,
null, TrackUtils.TRACK_SORT_ORDER);
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
resourceCursorAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
resourceCursorAdapter.swapCursor(null);
}
};
// Preferences
private boolean metricUnits = true;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
// Callback when an item is selected in the contextual action mode
private final ContextualActionModeCallback
contextualActionModeCallback = new ContextualActionModeCallback() {
@Override
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
boolean isSingleSelection = ids.length == 1;
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection);
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(false);
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
}
@Override
public boolean onClick(int itemId, int[] positions, long[] ids) {
return handleContextItem(itemId, ids);
}
};
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
/*
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
* Anonymous inner class will get garbage collected.
*/
private final OnSharedPreferenceChangeListener
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key == null || key.equals(
PreferencesUtils.getKey(TrackListActivity.this, R.string.stats_units_key))) {
metricUnits = PreferencesUtils.isMetricUnits(TrackListActivity.this);
}
if (key == null || key.equals(
PreferencesUtils.getKey(TrackListActivity.this, R.string.recording_track_id_key))) {
recordingTrackId = PreferencesUtils.getLong(
TrackListActivity.this, R.string.recording_track_id_key);
if (key != null && recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
trackRecordingServiceConnection.startAndBind();
}
}
if (key == null || key.equals(PreferencesUtils.getKey(
TrackListActivity.this, R.string.recording_track_paused_key))) {
recordingTrackPaused = PreferencesUtils.getBoolean(TrackListActivity.this,
R.string.recording_track_paused_key,
PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
TrackListActivity.this.invalidateOptionsMenu();
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
trackController.update(isRecording, recordingTrackPaused);
}
});
}
}
};
// Menu items
private MenuItem searchMenuItem;
private MenuItem startGpsMenuItem;
private MenuItem aggregatedStatisticsMenuItem;
private MenuItem exportAllMenuItem;
private MenuItem importAllMenuItem;
private MenuItem deleteAllMenuItem;
private final OnClickListener stopListener = new OnClickListener() {
@Override
public void onClick(View v) {
updateMenuItems(false, false);
TrackRecordingServiceConnectionUtils.stopRecording(
TrackListActivity.this, trackRecordingServiceConnection, true);
}
};
private boolean startGps = false; // true to start gps
private boolean startNewRecording = false; // true to start a new recording
// Callback when the trackRecordingServiceConnection binding changes. // Callback when the trackRecordingServiceConnection binding changes.
private final Runnable bindChangedCallback = new Runnable() { private final Runnable bindChangedCallback = new Runnable() {
@Override @Override
@@ -130,68 +233,6 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
} }
} }
}; };
/*
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
* Anonymous inner class will get garbage collected.
*/
private final OnSharedPreferenceChangeListener
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key == null || key.equals(
PreferencesUtils.getKey(TrackListActivity.this, R.string.stats_units_key))) {
metricUnits = PreferencesUtils.isMetricUnits(TrackListActivity.this);
}
if (key == null || key.equals(
PreferencesUtils.getKey(TrackListActivity.this, R.string.recording_track_id_key))) {
recordingTrackId = PreferencesUtils.getLong(
TrackListActivity.this, R.string.recording_track_id_key);
if (key != null && recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
trackRecordingServiceConnection.startAndBind();
}
}
if (key == null || key.equals(PreferencesUtils.getKey(
TrackListActivity.this, R.string.recording_track_paused_key))) {
recordingTrackPaused = PreferencesUtils.getBoolean(TrackListActivity.this,
R.string.recording_track_paused_key,
PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
TrackListActivity.this.invalidateOptionsMenu();
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
trackController.update(isRecording, recordingTrackPaused);
}
});
}
}
};
// Callback when an item is selected in the contextual action mode
private final ContextualActionModeCallback
contextualActionModeCallback = new ContextualActionModeCallback() {
@Override
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
boolean isSingleSelection = ids.length == 1;
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection);
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(false);
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
}
@Override
public boolean onClick(int itemId, int[] positions, long[] ids) {
return handleContextItem(itemId, ids);
}
};
private final OnClickListener recordListener = new OnClickListener() { private final OnClickListener recordListener = new OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
if (recordingTrackId == PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) { if (recordingTrackId == PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
@@ -214,57 +255,6 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
} }
}; };
private final OnClickListener stopListener = new OnClickListener() {
@Override
public void onClick(View v) {
updateMenuItems(false, false);
TrackRecordingServiceConnectionUtils.stopRecording(
TrackListActivity.this, trackRecordingServiceConnection, true);
}
};
private final LoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null,
null, TrackUtils.TRACK_SORT_ORDER);
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
resourceCursorAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
resourceCursorAdapter.swapCursor(null);
}
};
// The following are set in onCreate
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private TrackController trackController;
private ListView listView;
private ResourceCursorAdapter resourceCursorAdapter;
// Preferences
private boolean metricUnits = true;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
// Menu items
private MenuItem searchMenuItem;
private MenuItem startGpsMenuItem;
private MenuItem aggregatedStatisticsMenuItem;
private MenuItem exportAllMenuItem;
private MenuItem importAllMenuItem;
private MenuItem deleteAllMenuItem;
private boolean startGps = false; // true to start gps
private boolean startNewRecording = false; // true to start a new recording
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -481,7 +471,7 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG); .show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
return true; 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;
case R.id.track_list_settings: case R.id.track_list_settings:
intent = IntentUtils.newIntent(this, SettingsActivity.class); intent = IntentUtils.newIntent(this, SettingsActivity.class);
@@ -498,13 +488,13 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
contextualActionModeCallback.onPrepare( contextualActionModeCallback.onPrepare(
menu, new int[] { info.position }, new long[] { info.id }, false); menu, new int[]{info.position}, new long[]{info.id}, false);
} }
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
if (handleContextItem(item.getItemId(), new long[] { info.id })) { if (handleContextItem(item.getItemId(), new long[]{info.id})) {
return true; return true;
} }
return super.onContextItemSelected(item); return super.onContextItemSelected(item);
@@ -541,7 +531,7 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
switch (menuId) { switch (menuId) {
case R.id.track_list_export_all: case R.id.track_list_export_all:
intent = IntentUtils.newIntent(this, SaveActivity.class) intent = IntentUtils.newIntent(this, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_TRACK_IDS, new long[] {-1L}) .putExtra(SaveActivity.EXTRA_TRACK_IDS, new long[]{-1L})
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat); .putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat);
startActivity(intent); startActivity(intent);
break; break;
@@ -640,7 +630,7 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
return true; return true;
case R.id.list_context_menu_delete: case R.id.list_context_menu_delete:
if (trackIds.length > 1 && trackIds.length == listView.getCount()) { if (trackIds.length > 1 && trackIds.length == listView.getCount()) {
trackIds = new long[] {-1L}; trackIds = new long[]{-1L};
} }
deleteTracks(trackIds); deleteTracks(trackIds);
return true; return true;
@@ -16,14 +16,14 @@
package com.google.android.apps.mytracks.content; package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.location.Location; import android.location.Location;
import android.location.LocationManager; import android.location.LocationManager;
import android.net.Uri; import android.net.Uri;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -39,6 +39,15 @@ public interface ContentProviderUtils {
* provider. * provider.
*/ */
String AUTHORITY = "com.google.android.maps.mytracks"; String AUTHORITY = "com.google.android.maps.mytracks";
/**
* The default {@link LocationFactory} which creates a location each time.
*/
LocationFactory DEFAULT_LOCATION_FACTORY = new LocationFactory() {
@Override
public Location createLocation() {
return new SensorDataSetLocation(LocationManager.GPS_PROVIDER);
}
};
/** /**
* Clears a track. Removes waypoints and trackpoints. Only keeps the track id. * Clears a track. Removes waypoints and trackpoints. Only keeps the track id.
@@ -267,7 +276,6 @@ public interface ContentProviderUtils {
*/ */
Location getLastValidTrackPoint(long trackId); Location getLastValidTrackPoint(long trackId);
/** /**
* Gets the last valid location. * Gets the last valid location.
*/ */
@@ -344,16 +352,6 @@ public interface ContentProviderUtils {
Location createLocation(); Location createLocation();
} }
/**
* The default {@link LocationFactory} which creates a location each time.
*/
LocationFactory DEFAULT_LOCATION_FACTORY = new LocationFactory() {
@Override
public Location createLocation() {
return new SensorDataSetLocation(LocationManager.GPS_PROVIDER);
}
};
/** /**
* A factory which can produce instances of {@link ContentProviderUtils}, and * A factory which can produce instances of {@link ContentProviderUtils}, and
* can be overridden for testing. * can be overridden for testing.
@@ -172,7 +172,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
// Delete track last since it triggers a database vaccum call // Delete track last since it triggers a database vaccum call
contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?", contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?",
new String[] { Long.toString(trackId) }); new String[]{Long.toString(trackId)});
} }
/** /**
@@ -186,12 +186,12 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
Track track = getTrack(trackId); Track track = getTrack(trackId);
if (track != null) { if (track != null) {
String where = TrackPointsColumns._ID + ">=? AND " + TrackPointsColumns._ID + "<=?"; String where = TrackPointsColumns._ID + ">=? AND " + TrackPointsColumns._ID + "<=?";
String[] selectionArgs = new String[] { String[] selectionArgs = new String[]{
Long.toString(track.getStartId()), Long.toString(track.getStopId()) }; Long.toString(track.getStartId()), Long.toString(track.getStopId())};
contentResolver.delete(TrackPointsColumns.CONTENT_URI, where, selectionArgs); contentResolver.delete(TrackPointsColumns.CONTENT_URI, where, selectionArgs);
} }
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?", contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?",
new String[] { Long.toString(trackId) }); new String[]{Long.toString(trackId)});
deleteDirectoryRecurse(context, FileUtils.getPhotoDir(trackId)); deleteDirectoryRecurse(context, FileUtils.getPhotoDir(trackId));
} }
@@ -228,7 +228,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
@Override @Override
public Track getLastTrack() { public Track getLastTrack() {
try (Cursor cursor = getTrackCursor(null, null, null,TracksColumns.STARTTIME + " DESC")) { try (Cursor cursor = getTrackCursor(null, null, null, TracksColumns.STARTTIME + " DESC")) {
// Using the same order as shown in the track list // Using the same order as shown in the track list
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor); return createTrack(cursor);
@@ -264,7 +264,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
@Override @Override
public void updateTrack(Track track) { public void updateTrack(Track track) {
contentResolver.update(TracksColumns.CONTENT_URI, createContentValues(track), contentResolver.update(TracksColumns.CONTENT_URI, createContentValues(track),
TracksColumns._ID + "=?", new String[] { Long.toString(track.getId()) }); TracksColumns._ID + "=?", new String[]{Long.toString(track.getId())});
} }
private ContentValues createContentValues(Track track) { private ContentValues createContentValues(Track track) {
@@ -486,7 +486,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
} }
} }
} }
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns._ID + "=?", new String[] { Long.toString(waypointId) }); contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns._ID + "=?", new String[]{Long.toString(waypointId)});
} }
@Override @Override
@@ -512,8 +512,8 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
Cursor cursor = null; Cursor cursor = null;
try { try {
String selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns.TYPE + "=?"; String selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns.TYPE + "=?";
String[] selectionArgs = new String[] { String[] selectionArgs = new String[]{
Long.toString(trackId), Integer.toString(waypointType.ordinal()) }; Long.toString(trackId), Integer.toString(waypointType.ordinal())};
cursor = getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID + " DESC", 1); cursor = getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID + " DESC", 1);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return createWaypoint(cursor); return createWaypoint(cursor);
@@ -533,10 +533,10 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
} }
Cursor cursor = null; Cursor cursor = null;
try { try {
String[] projection = { WaypointsColumns._ID }; String[] projection = {WaypointsColumns._ID};
String selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns.TYPE + "=?"; String selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns.TYPE + "=?";
String[] selectionArgs = new String[] { String[] selectionArgs = new String[]{
Long.toString(trackId), Integer.toString(waypointType.ordinal()) }; Long.toString(trackId), Integer.toString(waypointType.ordinal())};
cursor = getWaypointCursor(projection, selection, selectionArgs, WaypointsColumns._ID, -1); cursor = getWaypointCursor(projection, selection, selectionArgs, WaypointsColumns._ID, -1);
if (cursor != null) { if (cursor != null) {
int count = cursor.getCount(); int count = cursor.getCount();
@@ -584,10 +584,10 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
String[] selectionArgs; String[] selectionArgs;
if (minWaypointId >= 0) { if (minWaypointId >= 0) {
selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + ">=?"; selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + ">=?";
selectionArgs = new String[] { Long.toString(trackId), Long.toString(minWaypointId) }; selectionArgs = new String[]{Long.toString(trackId), Long.toString(minWaypointId)};
} else { } else {
selection = WaypointsColumns.TRACKID + "=?"; selection = WaypointsColumns.TRACKID + "=?";
selectionArgs = new String[] { Long.toString(trackId) }; selectionArgs = new String[]{Long.toString(trackId)};
} }
return getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, maxWaypoints); return getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, maxWaypoints);
} }
@@ -598,9 +598,9 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
return 0; return 0;
} }
String[] projection = new String[] { "count(*) AS count" }; String[] projection = new String[]{"count(*) AS count"};
String selection = WaypointsColumns.TRACKID + "=?"; String selection = WaypointsColumns.TRACKID + "=?";
String[] selectionArgs = new String[] { Long.toString(trackId) }; String[] selectionArgs = new String[]{Long.toString(trackId)};
Cursor cursor = contentResolver.query(WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, WaypointsColumns._ID); Cursor cursor = contentResolver.query(WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, WaypointsColumns._ID);
cursor.moveToFirst(); cursor.moveToFirst();
@@ -619,7 +619,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
@Override @Override
public boolean updateWaypoint(Waypoint waypoint) { public boolean updateWaypoint(Waypoint waypoint) {
int rows = contentResolver.update(WaypointsColumns.CONTENT_URI, createContentValues(waypoint), int rows = contentResolver.update(WaypointsColumns.CONTENT_URI, createContentValues(waypoint),
WaypointsColumns._ID + "=?", new String[] { Long.toString(waypoint.getId()) }); WaypointsColumns._ID + "=?", new String[]{Long.toString(waypoint.getId())});
return rows == 1; return rows == 1;
} }
@@ -685,8 +685,8 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
try { try {
String selection = WaypointsColumns._ID + ">? AND " + WaypointsColumns.TRACKID + "=? AND " String selection = WaypointsColumns._ID + ">? AND " + WaypointsColumns.TRACKID + "=? AND "
+ WaypointsColumns.TYPE + "=" + WaypointType.STATISTICS.ordinal(); + WaypointsColumns.TYPE + "=" + WaypointType.STATISTICS.ordinal();
String[] selectionArgs = new String[] { String[] selectionArgs = new String[]{
Long.toString(waypoint.getId()), Long.toString(waypoint.getTrackId()) }; Long.toString(waypoint.getId()), Long.toString(waypoint.getTrackId())};
cursor = getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, 1); cursor = getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, 1);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return createWaypoint(cursor); return createWaypoint(cursor);
@@ -749,8 +749,8 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
String selection = TrackPointsColumns._ID + "=(select min(" + TrackPointsColumns._ID String selection = TrackPointsColumns._ID + "=(select min(" + TrackPointsColumns._ID
+ ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID
+ "=?)"; + "=?)";
String[] selectionArgs = new String[] { Long.toString(trackId) }; String[] selectionArgs = new String[]{Long.toString(trackId)};
cursor = getTrackPointCursor(new String[] { TrackPointsColumns._ID }, selection, cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection,
selectionArgs, TrackPointsColumns._ID); selectionArgs, TrackPointsColumns._ID);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID)); return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
@@ -773,8 +773,8 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID
+ ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID
+ "=?)"; + "=?)";
String[] selectionArgs = new String[] { Long.toString(trackId) }; String[] selectionArgs = new String[]{Long.toString(trackId)};
cursor = getTrackPointCursor(new String[] { TrackPointsColumns._ID }, selection, cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection,
selectionArgs, TrackPointsColumns._ID); selectionArgs, TrackPointsColumns._ID);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID)); return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
@@ -797,9 +797,9 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID
+ ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID
+ "=? AND " + TrackPointsColumns.TIME + "=?)"; + "=? AND " + TrackPointsColumns.TIME + "=?)";
String[] selectionArgs = new String[] { String[] selectionArgs = new String[]{
Long.toString(trackId), Long.toString(location.getTime()) }; Long.toString(trackId), Long.toString(location.getTime())};
cursor = getTrackPointCursor(new String[] { TrackPointsColumns._ID }, selection, cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection,
selectionArgs, TrackPointsColumns._ID); selectionArgs, TrackPointsColumns._ID);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID)); return cursor.getLong(cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
@@ -820,7 +820,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
String selection = TrackPointsColumns._ID + "=(select min(" + TrackPointsColumns._ID + ") from " String selection = TrackPointsColumns._ID + "=(select min(" + TrackPointsColumns._ID + ") from "
+ TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND "
+ TrackPointsColumns.LATITUDE + "<=" + MAX_LATITUDE + ")"; + TrackPointsColumns.LATITUDE + "<=" + MAX_LATITUDE + ")";
String[] selectionArgs = new String[] { Long.toString(trackId) }; String[] selectionArgs = new String[]{Long.toString(trackId)};
return findTrackPointBy(selection, selectionArgs); return findTrackPointBy(selection, selectionArgs);
} }
@@ -832,7 +832,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID + ") from " String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID + ") from "
+ TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND "
+ TrackPointsColumns.LATITUDE + "<=" + MAX_LATITUDE + ")"; + TrackPointsColumns.LATITUDE + "<=" + MAX_LATITUDE + ")";
String[] selectionArgs = new String[] { Long.toString(trackId) }; String[] selectionArgs = new String[]{Long.toString(trackId)};
return findTrackPointBy(selection, selectionArgs); return findTrackPointBy(selection, selectionArgs);
} }
@@ -857,10 +857,10 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
String comparison = descending ? "<=" : ">="; String comparison = descending ? "<=" : ">=";
selection = TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns._ID + comparison selection = TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns._ID + comparison
+ "?"; + "?";
selectionArgs = new String[] { Long.toString(trackId), Long.toString(startTrackPointId) }; selectionArgs = new String[]{Long.toString(trackId), Long.toString(startTrackPointId)};
} else { } else {
selection = TrackPointsColumns.TRACKID + "=?"; selection = TrackPointsColumns.TRACKID + "=?";
selectionArgs = new String[] { Long.toString(trackId) }; selectionArgs = new String[]{Long.toString(trackId)};
} }
String sortOrder = TrackPointsColumns._ID; String sortOrder = TrackPointsColumns._ID;
@@ -1080,6 +1080,15 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
TrackPointsColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder); TrackPointsColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
} }
/**
* Sets the default cursor batch size. For testing purpose.
*
* @param defaultCursorBatchSize the default cursor batch size
*/
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
this.defaultCursorBatchSize = defaultCursorBatchSize;
}
/** /**
* A cache of track points indexes. * A cache of track points indexes.
*/ */
@@ -1110,13 +1119,4 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER); sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
} }
} }
/**
* Sets the default cursor batch size. For testing purpose.
*
* @param defaultCursorBatchSize the default cursor batch size
*/
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
this.defaultCursorBatchSize = defaultCursorBatchSize;
}
} }
@@ -23,8 +23,6 @@ package com.google.android.apps.mytracks.content;
*/ */
public class ContentTypeIds { public class ContentTypeIds {
private ContentTypeIds() {}
public static final byte BOOLEAN_TYPE_ID = 0; public static final byte BOOLEAN_TYPE_ID = 0;
public static final byte LONG_TYPE_ID = 1; public static final byte LONG_TYPE_ID = 1;
public static final byte INT_TYPE_ID = 2; public static final byte INT_TYPE_ID = 2;
@@ -32,4 +30,6 @@ public class ContentTypeIds {
public static final byte DOUBLE_TYPE_ID = 4; public static final byte DOUBLE_TYPE_ID = 4;
public static final byte STRING_TYPE_ID = 5; public static final byte STRING_TYPE_ID = 5;
public static final byte BLOB_TYPE_ID = 6; public static final byte BLOB_TYPE_ID = 6;
private ContentTypeIds() {
}
} }
@@ -49,51 +49,11 @@ import java.io.FileNotFoundException;
*/ */
public class CustomContentProvider extends ContentProvider { public class CustomContentProvider extends ContentProvider {
private static final String TAG = CustomContentProvider.class.getSimpleName();
@VisibleForTesting @VisibleForTesting
static final int DATABASE_VERSION = 23; static final int DATABASE_VERSION = 23;
@VisibleForTesting @VisibleForTesting
static final String DATABASE_NAME = "database.db"; static final String DATABASE_NAME = "database.db";
private static final String TAG = CustomContentProvider.class.getSimpleName();
/**
* Database helper for creating and upgrading the database.
*/
@VisibleForTesting
static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
this(context, DATABASE_NAME);
}
@VisibleForTesting
public DatabaseHelper(Context context, String databaseName) {
super(context, databaseName, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TrackPointsColumns.CREATE_TABLE);
db.execSQL(TracksColumns.CREATE_TABLE);
db.execSQL(WaypointsColumns.CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
/**
* Types of url.
*
* @author Jimmy Shih
*/
@VisibleForTesting
enum UrlType {
TRACKPOINTS, TRACKPOINTS_ID, TRACKS, TRACKS_ID, WAYPOINTS, WAYPOINTS_ID
}
private final UriMatcher uriMatcher; private final UriMatcher uriMatcher;
private SQLiteDatabase db; private SQLiteDatabase db;
@@ -415,4 +375,41 @@ public class CustomContentProvider extends ContentProvider {
} }
throw new SQLException("Failed to insert a waypoint " + url); throw new SQLException("Failed to insert a waypoint " + url);
} }
/**
* Types of url.
*
* @author Jimmy Shih
*/
@VisibleForTesting
enum UrlType {
TRACKPOINTS, TRACKPOINTS_ID, TRACKS, TRACKS_ID, WAYPOINTS, WAYPOINTS_ID
}
/**
* Database helper for creating and upgrading the database.
*/
@VisibleForTesting
static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
this(context, DATABASE_NAME);
}
@VisibleForTesting
public DatabaseHelper(Context context, String databaseName) {
super(context, databaseName, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TrackPointsColumns.CREATE_TABLE);
db.execSQL(TracksColumns.CREATE_TABLE);
db.execSQL(WaypointsColumns.CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
} }
@@ -16,8 +16,6 @@
package com.google.android.apps.mytracks.content; package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.Constants;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@@ -25,6 +23,8 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.net.Uri; import android.net.Uri;
import com.google.android.apps.mytracks.Constants;
/** /**
* Data source on the phone. * Data source on the phone.
* *
@@ -35,83 +35,15 @@ import java.util.Set;
public class DataSourceManager { public class DataSourceManager {
private static final String TAG = DataSourceManager.class.getSimpleName(); private static final String TAG = DataSourceManager.class.getSimpleName();
/**
* Observer when the tracks table is updated.
*
* @author Jimmy Shih
*/
private class TracksTableObserver extends ContentObserver {
public TracksTableObserver() {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
dataSourceListener.notifyTracksTableUpdated();
}
}
/**
* Observer when the waypoints table is updated.
*
* @author Jimmy Shih
*/
private class WaypointsTableObserver extends ContentObserver {
public WaypointsTableObserver() {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
dataSourceListener.notifyWaypointsTableUpdated();
}
}
/**
* Observer when the track points table is updated.
*
* @author Jimmy Shih
*/
private class TrackPointsTableObserver extends ContentObserver {
public TrackPointsTableObserver() {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
dataSourceListener.notifyTrackPointsTableUpdated();
}
}
/**
* Listener for preference changes.
*
* @author Jimmy Shih
*/
private class PreferenceListener implements OnSharedPreferenceChangeListener {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
dataSourceListener.notifyPreferenceChanged(key);
}
}
private final DataSource dataSource; private final DataSource dataSource;
private final DataSourceListener dataSourceListener; private final DataSourceListener dataSourceListener;
// Registered listeners // Registered listeners
private final Set<TrackDataType> registeredListeners = EnumSet.noneOf(TrackDataType.class); private final Set<TrackDataType> registeredListeners = EnumSet.noneOf(TrackDataType.class);
private final Handler handler; private final Handler handler;
private final TracksTableObserver tracksTableObserver; private final TracksTableObserver tracksTableObserver;
private final WaypointsTableObserver waypointsTableObserver; private final WaypointsTableObserver waypointsTableObserver;
private final TrackPointsTableObserver trackPointsTableObserver; private final TrackPointsTableObserver trackPointsTableObserver;
private final PreferenceListener preferenceListener; private final PreferenceListener preferenceListener;
public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) { public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
this.dataSource = dataSource; this.dataSource = dataSource;
this.dataSourceListener = dataSourceListener; this.dataSourceListener = dataSourceListener;
@@ -228,4 +160,68 @@ public class DataSourceManager {
unregisterListener(trackDataType); unregisterListener(trackDataType);
} }
} }
/**
* Observer when the tracks table is updated.
*
* @author Jimmy Shih
*/
private class TracksTableObserver extends ContentObserver {
public TracksTableObserver() {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
dataSourceListener.notifyTracksTableUpdated();
}
}
/**
* Observer when the waypoints table is updated.
*
* @author Jimmy Shih
*/
private class WaypointsTableObserver extends ContentObserver {
public WaypointsTableObserver() {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
dataSourceListener.notifyWaypointsTableUpdated();
}
}
/**
* Observer when the track points table is updated.
*
* @author Jimmy Shih
*/
private class TrackPointsTableObserver extends ContentObserver {
public TrackPointsTableObserver() {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
dataSourceListener.notifyTrackPointsTableUpdated();
}
}
/**
* Listener for preference changes.
*
* @author Jimmy Shih
*/
private class PreferenceListener implements OnSharedPreferenceChangeListener {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
dataSourceListener.notifyPreferenceChanged(key);
}
}
} }
@@ -17,6 +17,7 @@
package com.google.android.apps.mytracks.content; package com.google.android.apps.mytracks.content;
import android.content.Context; import android.content.Context;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
@@ -38,94 +38,69 @@ import java.util.TreeSet;
*/ */
public class SearchEngine { public class SearchEngine {
/** WHERE query to get tracks by name. */ /**
* WHERE query to get tracks by name.
*/
private static final String TRACK_SELECTION_QUERY = private static final String TRACK_SELECTION_QUERY =
TracksColumns.NAME + " LIKE ? OR " + TracksColumns.NAME + " LIKE ? OR " +
TracksColumns.DESCRIPTION + " LIKE ? OR " + TracksColumns.DESCRIPTION + " LIKE ? OR " +
TracksColumns.CATEGORY + " LIKE ?"; TracksColumns.CATEGORY + " LIKE ?";
/** WHERE query to get waypoints by name. */ /**
* WHERE query to get waypoints by name.
*/
private static final String WAYPOINT_SELECTION_QUERY = private static final String WAYPOINT_SELECTION_QUERY =
WaypointsColumns.NAME + " LIKE ? OR " + WaypointsColumns.NAME + " LIKE ? OR " +
WaypointsColumns.DESCRIPTION + " LIKE ? OR " + WaypointsColumns.DESCRIPTION + " LIKE ? OR " +
WaypointsColumns.CATEGORY + " LIKE ?"; WaypointsColumns.CATEGORY + " LIKE ?";
/** Order of track results. */ /**
* Order of track results.
*/
private static final String TRACK_SELECTION_ORDER = TracksColumns._ID + " DESC LIMIT 1000"; private static final String TRACK_SELECTION_ORDER = TracksColumns._ID + " DESC LIMIT 1000";
/** Order of waypoint results. */ /**
* Order of waypoint results.
*/
private static final String WAYPOINT_SELECTION_ORDER = WaypointsColumns._ID + " DESC"; private static final String WAYPOINT_SELECTION_ORDER = WaypointsColumns._ID + " DESC";
/** How much we promote a match in the track category. */ /**
* How much we promote a match in the track category.
*/
private static final double TRACK_CATEGORY_PROMOTION = 2.0; private static final double TRACK_CATEGORY_PROMOTION = 2.0;
/** How much we promote a match in the track description. */ /**
* How much we promote a match in the track description.
*/
private static final double TRACK_DESCRIPTION_PROMOTION = 8.0; private static final double TRACK_DESCRIPTION_PROMOTION = 8.0;
/** How much we promote a match in the track name. */ /**
* How much we promote a match in the track name.
*/
private static final double TRACK_NAME_PROMOTION = 16.0; private static final double TRACK_NAME_PROMOTION = 16.0;
/** How much we promote a waypoint result if it's in the currently-selected track. */ /**
* How much we promote a waypoint result if it's in the currently-selected track.
*/
private static final double CURRENT_TRACK_WAYPOINT_PROMOTION = 2.0; private static final double CURRENT_TRACK_WAYPOINT_PROMOTION = 2.0;
/** How much we promote a track result if it's the currently-selected track. */ /**
* How much we promote a track result if it's the currently-selected track.
*/
private static final double CURRENT_TRACK_DEMOTION = 0.5; private static final double CURRENT_TRACK_DEMOTION = 0.5;
/** Maximum number of waypoints which will be retrieved and scored. */ /**
* Maximum number of waypoints which will be retrieved and scored.
*/
private static final int MAX_SCORED_WAYPOINTS = 100; private static final int MAX_SCORED_WAYPOINTS = 100;
/** Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000) */ /**
* Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000)
*/
private static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L; private static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
/** /**
* Description of a search query, along with all contextual data needed to execute it. * Comparador for scored results.
*/ */
public static class SearchQuery {
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId,
long currentTimestamp) {
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
this.currentLocation = currentLocation;
this.currentTrackId = currentTrackId;
this.currentTimestamp = currentTimestamp;
}
public final String textQuery;
public final Location currentLocation;
public final long currentTrackId;
public final long currentTimestamp;
}
/**
* Description of a search result which has been retrieved and scored.
*/
public static class ScoredResult {
ScoredResult(Track track, double score) {
this.track = track;
this.waypoint = null;
this.score = score;
}
ScoredResult(Waypoint waypoint, double score) {
this.track = null;
this.waypoint = waypoint;
this.score = score;
}
public final Track track;
public final Waypoint waypoint;
public final double score;
@NonNull
@Override
public String toString() {
return "ScoredResult ["
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
+ "score=" + score + "]";
}
}
/** Comparador for scored results. */
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR = private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR =
new Comparator<ScoredResult>() { new Comparator<ScoredResult>() {
@Override @Override
@@ -150,13 +125,19 @@ public class SearchEngine {
return Long.signum(idDiff); return Long.signum(idDiff);
} }
}; };
private final ContentProviderUtils providerUtils; private final ContentProviderUtils providerUtils;
public SearchEngine(ContentProviderUtils providerUtils) { public SearchEngine(ContentProviderUtils providerUtils) {
this.providerUtils = providerUtils; this.providerUtils = providerUtils;
} }
/**
* Squashes a number by calculating 1 / log (1 + x).
*/
private static double squash(double x) {
return 1.0 / Math.log1p(x);
}
/** /**
* Executes a search query and returns a set of sorted results. * Executes a search query and returns a set of sorted results.
* *
@@ -185,10 +166,10 @@ public class SearchEngine {
*/ */
private void retrieveTracks(SearchQuery query, ArrayList<Track> tracks) { private void retrieveTracks(SearchQuery query, ArrayList<Track> tracks) {
String queryLikeSelection = "%" + query.textQuery + "%"; String queryLikeSelection = "%" + query.textQuery + "%";
String[] trackSelectionArgs = new String[] { String[] trackSelectionArgs = new String[]{
queryLikeSelection, queryLikeSelection,
queryLikeSelection, queryLikeSelection,
queryLikeSelection }; queryLikeSelection};
try (Cursor cursor = providerUtils.getTrackCursor( try (Cursor cursor = providerUtils.getTrackCursor(
TRACK_SELECTION_QUERY, trackSelectionArgs, TRACK_SELECTION_ORDER)) { TRACK_SELECTION_QUERY, trackSelectionArgs, TRACK_SELECTION_ORDER)) {
@@ -209,10 +190,10 @@ public class SearchEngine {
*/ */
private void retrieveWaypoints(SearchQuery query, ArrayList<Waypoint> waypoints) { private void retrieveWaypoints(SearchQuery query, ArrayList<Waypoint> waypoints) {
String queryLikeSelection2 = "%" + query.textQuery + "%"; String queryLikeSelection2 = "%" + query.textQuery + "%";
String[] waypointSelectionArgs = new String[] { String[] waypointSelectionArgs = new String[]{
queryLikeSelection2, queryLikeSelection2,
queryLikeSelection2, queryLikeSelection2,
queryLikeSelection2 }; queryLikeSelection2};
try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs, try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs,
WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) { WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) {
if (cursor != null) { if (cursor != null) {
@@ -393,9 +374,47 @@ public class SearchEngine {
} }
/** /**
* Squashes a number by calculating 1 / log (1 + x). * Description of a search query, along with all contextual data needed to execute it.
*/ */
private static double squash(double x) { public static class SearchQuery {
return 1.0 / Math.log1p(x); public final String textQuery;
public final Location currentLocation;
public final long currentTrackId;
public final long currentTimestamp;
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId,
long currentTimestamp) {
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
this.currentLocation = currentLocation;
this.currentTrackId = currentTrackId;
this.currentTimestamp = currentTimestamp;
}
}
/**
* Description of a search result which has been retrieved and scored.
*/
public static class ScoredResult {
public final Track track;
public final Waypoint waypoint;
public final double score;
ScoredResult(Track track, double score) {
this.track = track;
this.waypoint = null;
this.score = score;
}
ScoredResult(Waypoint waypoint, double score) {
this.track = null;
this.waypoint = waypoint;
this.score = score;
}
@NonNull
@Override
public String toString() {
return "ScoredResult ["
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
+ "score=" + score + "]";
}
} }
} }
@@ -16,12 +16,12 @@
package com.google.android.apps.mytracks.content; package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.stats.TripStatistics;
import android.location.Location; import android.location.Location;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.google.android.apps.mytracks.stats.TripStatistics;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@@ -32,13 +32,23 @@ import java.util.ArrayList;
*/ */
public class Track implements Parcelable { public class Track implements Parcelable {
public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {
@Override
public Track createFromParcel(Parcel in) {
return new Track(in);
}
@Override
public Track[] newArray(int size) {
return new Track[size];
}
};
private long id = -1L; private long id = -1L;
private String name = ""; private String name = "";
private String description = ""; private String description = "";
private String category = ""; private String category = "";
private long startId = -1L; private long startId = -1L;
private long stopId = -1L; private long stopId = -1L;
/* /*
* The number of location points (present even if the points themselves are * The number of location points (present even if the points themselves are
* not loaded) * not loaded)
@@ -46,13 +56,12 @@ public class Track implements Parcelable {
private int numberOfPoints = 0; private int numberOfPoints = 0;
private String icon = ""; private String icon = "";
private long modifiedTime = -1L; private long modifiedTime = -1L;
private TripStatistics tripStatistics = new TripStatistics(); private TripStatistics tripStatistics = new TripStatistics();
// Location points (which may not have been loaded) // Location points (which may not have been loaded)
private ArrayList<Location> locations = new ArrayList<>(); private ArrayList<Location> locations = new ArrayList<>();
public Track() {} public Track() {
}
private Track(Parcel in) { private Track(Parcel in) {
id = in.readLong(); id = in.readLong();
@@ -97,18 +106,6 @@ public class Track implements Parcelable {
} }
} }
public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {
@Override
public Track createFromParcel(Parcel in) {
return new Track(in);
}
@Override
public Track[] newArray(int size) {
return new Track[size];
}
};
public long getId() { public long getId() {
return id; return id;
} }
@@ -42,20 +42,17 @@ import java.util.Set;
*/ */
public class TrackDataHub implements DataSourceListener { public class TrackDataHub implements DataSourceListener {
private static final String TAG = TrackDataHub.class.getSimpleName();
/**
* Maximum number of waypoints to displayed.
*/
@VisibleForTesting
static final int MAX_DISPLAYED_WAYPOINTS = 128;
/** /**
* Target number of track points displayed by the map overlay. We may display * Target number of track points displayed by the map overlay. We may display
* more than this number of points. * more than this number of points.
*/ */
public static final int TARGET_DISPLAYED_TRACK_POINTS = 5000; public static final int TARGET_DISPLAYED_TRACK_POINTS = 5000;
/**
* Maximum number of waypoints to displayed.
*/
@VisibleForTesting
static final int MAX_DISPLAYED_WAYPOINTS = 128;
private static final String TAG = TrackDataHub.class.getSimpleName();
private final Context context; private final Context context;
private final TrackDataManager trackDataManager; private final TrackDataManager trackDataManager;
private final ContentProviderUtils contentProviderUtils; private final ContentProviderUtils contentProviderUtils;
@@ -81,14 +78,6 @@ public class TrackDataHub implements DataSourceListener {
private long firstSeenLocationId; private long firstSeenLocationId;
private long lastSeenLocationId; private long lastSeenLocationId;
/**
* Creates a new instance.
*/
public synchronized static TrackDataHub newInstance(Context context) {
return new TrackDataHub(context, new TrackDataManager(), ContentProviderUtils.Factory.get(
context), TARGET_DISPLAYED_TRACK_POINTS);
}
/** /**
* Constructor. * Constructor.
* *
@@ -107,6 +96,14 @@ public class TrackDataHub implements DataSourceListener {
resetSamplingState(); resetSamplingState();
} }
/**
* Creates a new instance.
*/
public synchronized static TrackDataHub newInstance(Context context) {
return new TrackDataHub(context, new TrackDataManager(), ContentProviderUtils.Factory.get(
context), TARGET_DISPLAYED_TRACK_POINTS);
}
/** /**
* Starts. * Starts.
*/ */
@@ -390,7 +387,7 @@ public class TrackDataHub implements DataSourceListener {
resetSamplingState(); resetSamplingState();
} }
Set<TrackDataListener> sampledOutListeners = hasSampledOut ? trackDataListeners Set<TrackDataListener> sampledOutListeners = hasSampledOut ? trackDataListeners
: Collections.<TrackDataListener> emptySet(); : Collections.<TrackDataListener>emptySet();
notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners); notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners);
} }
@@ -31,10 +31,17 @@ import com.google.android.apps.mytracks.stats.TripStatistics;
*/ */
public final class Waypoint implements Parcelable { public final class Waypoint implements Parcelable {
public enum WaypointType { public static final Parcelable.Creator<Waypoint> CREATOR = new Parcelable.Creator<Waypoint>() {
WAYPOINT, STATISTICS @Override
public Waypoint createFromParcel(Parcel in) {
return new Waypoint(in);
} }
@Override
public Waypoint[] newArray(int size) {
return new Waypoint[size];
}
};
private long id = -1L; private long id = -1L;
private String name = ""; private String name = "";
private String description = ""; private String description = "";
@@ -50,7 +57,8 @@ public final class Waypoint implements Parcelable {
private TripStatistics tripStatistics = null; private TripStatistics tripStatistics = null;
private String photoUrl = ""; private String photoUrl = "";
public Waypoint() {} public Waypoint() {
}
public Waypoint(String name, String description, String category, String icon, long trackId, public Waypoint(String name, String description, String category, String icon, long trackId,
WaypointType type, double length, long duration, long startId, long stopId, Location location, WaypointType type, double length, long duration, long startId, long stopId, Location location,
@@ -124,18 +132,6 @@ public final class Waypoint implements Parcelable {
dest.writeString(photoUrl); dest.writeString(photoUrl);
} }
public static final Parcelable.Creator<Waypoint> CREATOR = new Parcelable.Creator<Waypoint>() {
@Override
public Waypoint createFromParcel(Parcel in) {
return new Waypoint(in);
}
@Override
public Waypoint[] newArray(int size) {
return new Waypoint[size];
}
};
public long getId() { public long getId() {
return id; return id;
} }
@@ -244,6 +240,10 @@ public final class Waypoint implements Parcelable {
return photoUrl; return photoUrl;
} }
public void setPhotoUrl(String photoUrl) {
this.photoUrl = photoUrl;
}
public Uri getPhotoURI() { public Uri getPhotoURI() {
return Uri.parse(photoUrl); return Uri.parse(photoUrl);
} }
@@ -252,7 +252,7 @@ public final class Waypoint implements Parcelable {
return photoUrl != null && !"".equals(photoUrl); return photoUrl != null && !"".equals(photoUrl);
} }
public void setPhotoUrl(String photoUrl) { public enum WaypointType {
this.photoUrl = photoUrl; WAYPOINT, STATISTICS
} }
} }
@@ -15,11 +15,11 @@
*/ */
package com.google.android.apps.mytracks.content; package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
/** /**
* A request for the service to create a waypoint at the current location. * A request for the service to create a waypoint at the current location.
* *
@@ -27,6 +27,13 @@ import android.os.Parcelable;
*/ */
public class WaypointCreationRequest implements Parcelable { public class WaypointCreationRequest implements Parcelable {
public final static WaypointCreationRequest DEFAULT_WAYPOINT = new WaypointCreationRequest(
WaypointType.WAYPOINT, false);
public final static WaypointCreationRequest DEFAULT_STATISTICS = new WaypointCreationRequest(
WaypointType.STATISTICS, false);
public final static WaypointCreationRequest DEFAULT_START_TRACK = new WaypointCreationRequest(
WaypointType.STATISTICS, true);
public static final Creator CREATOR = new Creator();
private WaypointType type; private WaypointType type;
// true if this marker contains the track statistics // true if this marker contains the track statistics
private boolean isTrackStatistics; private boolean isTrackStatistics;
@@ -36,13 +43,6 @@ public class WaypointCreationRequest implements Parcelable {
private String iconUrl; private String iconUrl;
private String photoUrl; private String photoUrl;
public final static WaypointCreationRequest DEFAULT_WAYPOINT = new WaypointCreationRequest(
WaypointType.WAYPOINT, false);
public final static WaypointCreationRequest DEFAULT_STATISTICS = new WaypointCreationRequest(
WaypointType.STATISTICS, false);
public final static WaypointCreationRequest DEFAULT_START_TRACK = new WaypointCreationRequest(
WaypointType.STATISTICS, true);
private WaypointCreationRequest(WaypointType type, boolean isTrackStatistics) { private WaypointCreationRequest(WaypointType type, boolean isTrackStatistics) {
this(type, isTrackStatistics, null, null, null, null, null); this(type, isTrackStatistics, null, null, null, null, null);
} }
@@ -58,27 +58,6 @@ public class WaypointCreationRequest implements Parcelable {
this.photoUrl = photoUrl; this.photoUrl = photoUrl;
} }
public static class Creator implements Parcelable.Creator<WaypointCreationRequest> {
@Override
public WaypointCreationRequest createFromParcel(Parcel source) {
return new WaypointCreationRequest(
WaypointType.values()[source.readInt()],
source.readByte() == 1,
source.readString(),
source.readString(),
source.readString(),
source.readString(),
source.readString());
}
public WaypointCreationRequest[] newArray(int size) {
return new WaypointCreationRequest[size];
}
}
public static final Creator CREATOR = new Creator();
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;
@@ -122,4 +101,23 @@ public class WaypointCreationRequest implements Parcelable {
public String getPhotoUrl() { public String getPhotoUrl() {
return photoUrl; return photoUrl;
} }
public static class Creator implements Parcelable.Creator<WaypointCreationRequest> {
@Override
public WaypointCreationRequest createFromParcel(Parcel source) {
return new WaypointCreationRequest(
WaypointType.values()[source.readInt()],
source.readByte() == 1,
source.readString(),
source.readString(),
source.readString(),
source.readString(),
source.readString());
}
public WaypointCreationRequest[] newArray(int size) {
return new WaypointCreationRequest[size];
}
}
} }
@@ -5,16 +5,14 @@ public final class SensorDataSet {
public static final float DATA_UNAVAILABLE = Integer.MAX_VALUE; public static final float DATA_UNAVAILABLE = Integer.MAX_VALUE;
//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 sensorAddress;
private float heartRate; private float heartRate;
private float cadence; private float cadence;
private float power; private float power;
private float batteryLevel; private float batteryLevel;
private long creationTimestamp; private long creationTimestamp;
public String sensorName;
public String sensorAddress;
public SensorDataSet(float heartRate, float cadence, float power, float batteryLevel, long creationTimestamp) { public SensorDataSet(float heartRate, float cadence, float power, float batteryLevel, long creationTimestamp) {
this.heartRate = heartRate; this.heartRate = heartRate;
this.cadence = cadence; this.cadence = cadence;
@@ -71,6 +69,7 @@ public final class SensorDataSet {
/** /**
* Is the data recent considering the current time. * Is the data recent considering the current time.
*
* @param maxAge the maximal age in milliseconds. * @param maxAge the maximal age in milliseconds.
*/ */
public boolean isRecent(long maxAge) { public boolean isRecent(long maxAge) {
@@ -16,16 +16,16 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.util.DialogUtils;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
/** import com.google.android.apps.mytracks.util.DialogUtils;
/**
* @author Jimmy Shih * @author Jimmy Shih
*/ */
public abstract class AbstractDialogFragment extends DialogFragment { public abstract class AbstractDialogFragment extends DialogFragment {
@@ -72,7 +72,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
// Modes of operation // Modes of operation
private boolean chartByDistance = true; private boolean chartByDistance = true;
private boolean[] chartShow = new boolean[] { true, true, true, true, true, true }; private boolean[] chartShow = new boolean[]{true, true, true, true, true, true};
// UI elements // UI elements
private ChartView chartView; private ChartView chartView;
@@ -46,23 +46,8 @@ import java.util.List;
*/ */
public class ChooseActivityTypeDialogFragment extends DialogFragment { public class ChooseActivityTypeDialogFragment extends DialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author apoorvn
*/
public interface ChooseActivityTypeCaller {
/**
* Called when choose activity type is done.
*/
void onChooseActivityTypeDone(String iconValue);
}
public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType"; public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
private static final String KEY_CATEGORY = "category"; private static final String KEY_CATEGORY = "category";
private ChooseActivityTypeCaller caller; private ChooseActivityTypeCaller caller;
public static ChooseActivityTypeDialogFragment newInstance(String category) { public static ChooseActivityTypeDialogFragment newInstance(String category) {
@@ -74,22 +59,6 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment {
return fragment; return fragment;
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
caller = (ChooseActivityTypeCaller) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + ChooseActivityTypeCaller.class.getSimpleName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return getDialog(getActivity(), getArguments().getString(KEY_CATEGORY), caller);
}
public static Dialog getDialog(final Activity activity, final String category, final ChooseActivityTypeCaller caller) { public static Dialog getDialog(final Activity activity, final String category, final ChooseActivityTypeCaller caller) {
View view = activity.getLayoutInflater().inflate(R.layout.choose_activity_type, null); View view = activity.getLayoutInflater().inflate(R.layout.choose_activity_type, null);
GridView gridView = view.findViewById(R.id.choose_activity_type_grid_view); GridView gridView = view.findViewById(R.id.choose_activity_type_grid_view);
@@ -154,4 +123,33 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment {
boolean showWeight = position == 0 || position == 1 || position == 2; boolean showWeight = position == 0 || position == 1 || position == 2;
weightContainer.setVisibility(showWeight ? View.VISIBLE : View.GONE); weightContainer.setVisibility(showWeight ? View.VISIBLE : View.GONE);
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
caller = (ChooseActivityTypeCaller) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + ChooseActivityTypeCaller.class.getSimpleName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return getDialog(getActivity(), getArguments().getString(KEY_CATEGORY), caller);
}
/**
* Interface for caller of this dialog fragment.
*
* @author apoorvn
*/
public interface ChooseActivityTypeCaller {
/**
* Called when choose activity type is done.
*/
void onChooseActivityTypeDone(String iconValue);
}
} }
@@ -16,8 +16,6 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.view.View; import android.view.View;
@@ -25,6 +23,8 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import com.google.android.maps.mytracks.R;
import java.util.List; import java.util.List;
/** /**
@@ -31,24 +31,9 @@ import com.google.android.maps.mytracks.R;
*/ */
public class ConfirmDeleteDialogFragment extends AbstractDialogFragment { public class ConfirmDeleteDialogFragment extends AbstractDialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface ConfirmDeleteCaller {
/**
* Called when confirm delete is done.
*
* @param trackIds list of track ids to delete. To delete all, set to size 1
* with trackIds[0] == -1L
*/
void onConfirmDeleteDone(long[] trackIds);
}
public static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog"; public static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
private static final String KEY_TRACK_IDS = "trackIds"; private static final String KEY_TRACK_IDS = "trackIds";
private ConfirmDeleteCaller caller;
/** /**
* Create a new instance. * Create a new instance.
@@ -65,8 +50,6 @@ public class ConfirmDeleteDialogFragment extends AbstractDialogFragment {
return deleteTrackDialogFragment; return deleteTrackDialogFragment;
} }
private ConfirmDeleteCaller caller;
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
@@ -94,4 +77,20 @@ public class ConfirmDeleteDialogFragment extends AbstractDialogFragment {
} }
}); });
} }
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface ConfirmDeleteCaller {
/**
* Called when confirm delete is done.
*
* @param trackIds list of track ids to delete. To delete all, set to size 1
* with trackIds[0] == -1L
*/
void onConfirmDeleteDone(long[] trackIds);
}
} }
@@ -23,8 +23,8 @@ import android.os.Bundle;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.util.DialogUtils; import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
@@ -35,21 +35,9 @@ import com.google.android.maps.mytracks.R;
*/ */
public class DeleteMarkerDialogFragment extends AbstractDialogFragment { public class DeleteMarkerDialogFragment extends AbstractDialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface DeleteMarkerCaller {
/**
* Called when delete marker is done.
*/
void onDeleteMarkerDone();
}
public static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog"; public static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
private static final String KEY_MARKER_IDS = "markerIds"; private static final String KEY_MARKER_IDS = "markerIds";
private DeleteMarkerCaller caller;
public static DeleteMarkerDialogFragment newInstance(long[] markerIds) { public static DeleteMarkerDialogFragment newInstance(long[] markerIds) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@@ -60,8 +48,6 @@ public class DeleteMarkerDialogFragment extends AbstractDialogFragment {
return deleteMarkerDialogFragment; return deleteMarkerDialogFragment;
} }
private DeleteMarkerCaller caller;
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
@@ -106,4 +92,17 @@ public class DeleteMarkerDialogFragment extends AbstractDialogFragment {
} }
}); });
} }
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface DeleteMarkerCaller {
/**
* Called when delete marker is done.
*/
void onDeleteMarkerDone();
}
} }
@@ -34,24 +34,11 @@ import com.google.android.maps.mytracks.R;
*/ */
public class FileTypeDialogFragment extends AbstractDialogFragment { public class FileTypeDialogFragment extends AbstractDialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface FileTypeCaller {
/**
* Called when file type selection is done.
*/
void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat);
}
public static final String FILE_TYPE_DIALOG_TAG = "fileType"; public static final String FILE_TYPE_DIALOG_TAG = "fileType";
private static final String KEY_MENU_ID = "menuId"; private static final String KEY_MENU_ID = "menuId";
private static final String KEY_TITLE_ID = "titleId"; private static final String KEY_TITLE_ID = "titleId";
private static final String KEY_OPTION_ID = "optionId"; private static final String KEY_OPTION_ID = "optionId";
private FileTypeCaller caller;
public static FileTypeDialogFragment newInstance(int menuId, int titleId, int optionId) { public static FileTypeDialogFragment newInstance(int menuId, int titleId, int optionId) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@@ -64,8 +51,6 @@ public class FileTypeDialogFragment extends AbstractDialogFragment {
return fileTypeDialogFragment; return fileTypeDialogFragment;
} }
private FileTypeCaller caller;
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
@@ -97,4 +82,17 @@ public class FileTypeDialogFragment extends AbstractDialogFragment {
} }
}).setSingleChoiceItems(choices, 0, null).setTitle(titleId).create(); }).setSingleChoiceItems(choices, 0, null).setTitle(titleId).create();
} }
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface FileTypeCaller {
/**
* Called when file type selection is done.
*/
void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat);
}
} }
@@ -16,16 +16,17 @@
package com.google.android.apps.mytracks.fragments; package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
/** /**
* A DialogFragment to configure frequency. * A DialogFragment to configure frequency.
* *
@@ -57,28 +57,16 @@ import com.google.android.maps.mytracks.R;
*/ */
public class MarkerDetailFragment extends Fragment { public class MarkerDetailFragment extends Fragment {
public static MarkerDetailFragment newInstance(long markerId, String title) {
Bundle bundle = new Bundle();
bundle.putLong(KEY_MARKER_ID, markerId);
bundle.putString(KEY_TITLE, title);
MarkerDetailFragment fragment = new MarkerDetailFragment();
fragment.setArguments(bundle);
return fragment;
}
private static final String TAG = MarkerDetailFragment.class.getSimpleName(); private static final String TAG = MarkerDetailFragment.class.getSimpleName();
private static final String KEY_MARKER_ID = "markerId"; private static final String KEY_MARKER_ID = "markerId";
private static final String KEY_TITLE = "title"; private static final String KEY_TITLE = "title";
private static final long HIDE_TEXT_DELAY = 4000L; // 4 seconds private static final long HIDE_TEXT_DELAY = 4000L; // 4 seconds
private ContentProviderUtils contentProviderUtils; private ContentProviderUtils contentProviderUtils;
private Handler handler; private Handler handler;
private ImageView photo; private ImageView photo;
private ImageView textGradient; private ImageView textGradient;
private LinearLayout waypointInfo; private LinearLayout waypointInfo;
private Waypoint waypoint; private Waypoint waypoint;
private Runnable hideText = new Runnable() { private Runnable hideText = new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -86,10 +74,12 @@ public class MarkerDetailFragment extends Fragment {
animation.setAnimationListener(new AnimationListener() { animation.setAnimationListener(new AnimationListener() {
@Override @Override
public void onAnimationStart(Animation anim) {} public void onAnimationStart(Animation anim) {
}
@Override @Override
public void onAnimationRepeat(Animation anim) {} public void onAnimationRepeat(Animation anim) {
}
@Override @Override
public void onAnimationEnd(Animation anim) { public void onAnimationEnd(Animation anim) {
@@ -102,6 +92,16 @@ public class MarkerDetailFragment extends Fragment {
} }
}; };
public static MarkerDetailFragment newInstance(long markerId, String title) {
Bundle bundle = new Bundle();
bundle.putLong(KEY_MARKER_ID, markerId);
bundle.putString(KEY_TITLE, title);
MarkerDetailFragment fragment = new MarkerDetailFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -199,7 +199,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.newInstance(new long[]{markerId})
.show(getChildFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG); .show(getChildFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
return true; return true;
default: default:
@@ -69,9 +69,6 @@ public class StatsFragment extends Fragment implements TrackDataListener {
private Location lastLocation = null; private Location lastLocation = null;
private TripStatistics lastTripStatistics = null; private TripStatistics lastTripStatistics = null;
private SensorDataSet sensorDataSet = null; private SensorDataSet sensorDataSet = null;
private String category = "";
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
private final Runnable updateUIeachSecond = new Runnable() { private final Runnable updateUIeachSecond = new Runnable() {
public void run() { public void run() {
if (isResumed() && isSelectedTrackRecording()) { if (isResumed() && isSelectedTrackRecording()) {
@@ -84,7 +81,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
} }
} }
}; };
private String category = "";
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -1,14 +1,14 @@
package com.google.android.apps.mytracks.io.file; package com.google.android.apps.mytracks.io.file;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.android.apps.mytracks.io.file.exporter.GpxTrackWriter; import com.google.android.apps.mytracks.io.file.exporter.GpxTrackWriter;
import com.google.android.apps.mytracks.io.file.exporter.KmlTrackWriter; import com.google.android.apps.mytracks.io.file.exporter.KmlTrackWriter;
import com.google.android.apps.mytracks.io.file.exporter.TrackWriter; import com.google.android.apps.mytracks.io.file.exporter.TrackWriter;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Locale; import java.util.Locale;
/** /**
@@ -28,16 +28,6 @@ public enum TrackFileFormat implements Parcelable {
} }
}; };
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeInt(ordinal());
}
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() { public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
@Override @Override
public TrackFileFormat createFromParcel(final Parcel source) { public TrackFileFormat createFromParcel(final Parcel source) {
@@ -50,6 +40,16 @@ public enum TrackFileFormat implements Parcelable {
} }
}; };
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeInt(ordinal());
}
/** /**
* Creates a new track writer for the format. * Creates a new track writer for the format.
* *
@@ -16,18 +16,18 @@
package com.google.android.apps.mytracks.io.file.exporter; package com.google.android.apps.mytracks.io.file.exporter;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.ContentProviderUtils.LocationIterator;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.util.LocationUtils;
import android.database.Cursor; import android.database.Cursor;
import android.location.Location; import android.location.Location;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.ContentProviderUtils.LocationIterator;
import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.util.LocationUtils;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
@@ -38,6 +38,7 @@ public class GpxTrackWriter implements TrackWriter {
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US); private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US); private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
static { static {
/* /*
* GPX readers expect to see fractional numbers with US-style punctuation. * GPX readers expect to see fractional numbers with US-style punctuation.
@@ -22,10 +22,10 @@ import android.net.Uri;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.DescriptionGenerator; import com.google.android.apps.mytracks.content.DescriptionGenerator;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl; import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.SensorDataSetLocation; import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
@@ -16,12 +16,6 @@
package com.google.android.apps.mytracks.io.file.exporter; package com.google.android.apps.mytracks.io.file.exporter;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -29,6 +23,12 @@ import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@@ -16,12 +16,6 @@
package com.google.android.apps.mytracks.io.file.exporter; package com.google.android.apps.mytracks.io.file.exporter;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.maps.mytracks.R;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
@@ -31,6 +25,12 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.maps.mytracks.R;
import java.io.File; import java.io.File;
/** /**
@@ -44,8 +44,6 @@ import java.io.IOException;
public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> { public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = SaveAsyncTask.class.getSimpleName(); private static final String TAG = SaveAsyncTask.class.getSimpleName();
private SaveActivity saveActivity;
private final long[] trackIds; private final long[] trackIds;
private final TrackFileFormat trackFileFormat; private final TrackFileFormat trackFileFormat;
@Deprecated //TODO Seems to be a left over from Google Earth integration and can be removed. @Deprecated //TODO Seems to be a left over from Google Earth integration and can be removed.
@@ -53,7 +51,7 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private final File directory; private final File directory;
private final Context context; private final Context context;
private final ContentProviderUtils contentProviderUtils; private final ContentProviderUtils contentProviderUtils;
private SaveActivity saveActivity;
private WakeLock wakeLock; private WakeLock wakeLock;
// true if the AsyncTask has completed // true if the AsyncTask has completed
@@ -15,11 +15,11 @@
*/ */
package com.google.android.apps.mytracks.io.file.exporter; package com.google.android.apps.mytracks.io.file.exporter;
import android.location.Location;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import android.location.Location;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
@@ -22,9 +22,9 @@ import android.location.LocationManager;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.ContentProviderUtils.LocationIterator; import com.google.android.apps.mytracks.content.ContentProviderUtils.LocationIterator;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
@@ -62,62 +62,17 @@ import javax.xml.parsers.SAXParserFactory;
*/ */
abstract class AbstractFileTrackImporter extends DefaultHandler implements TrackImporter { abstract class AbstractFileTrackImporter extends DefaultHandler implements TrackImporter {
/**
* Data for the current track.
*
* @author Jimmy Shih
*/
private class TrackData {
// The current track
Track track = new Track();
// The number of segments processed for the current track
int numberOfSegments = 0;
/*
* The last location in the current segment. Null if the current segment
* doesn't have a last location.
*/
Location lastLocationInCurrentSegment;
// The number of locations processed for the current track
int numberOfLocations = 0;
// The trip statistics updater for the current track
TripStatisticsUpdater tripStatisticsUpdater;
// The import time of the track.
long importTime = System.currentTimeMillis();
// The buffered locations
Location[] bufferedLocations = new Location[MAX_BUFFERED_LOCATIONS];
// The number of buffered locations
int numBufferedLocations = 0;
}
private static final String TAG = AbstractFileTrackImporter.class.getSimpleName(); private static final String TAG = AbstractFileTrackImporter.class.getSimpleName();
// The maximum number of buffered locations for bulk-insertion // The maximum number of buffered locations for bulk-insertion
private static final int MAX_BUFFERED_LOCATIONS = 512; private static final int MAX_BUFFERED_LOCATIONS = 512;
private final Context context; private final Context context;
private final long importTrackId; private final long importTrackId;
private final ContentProviderUtils contentProviderUtils; private final ContentProviderUtils contentProviderUtils;
private final int recordingDistanceInterval; private final int recordingDistanceInterval;
private final List<Long> trackIds; private final List<Long> trackIds;
private final List<Waypoint> waypoints; private final List<Waypoint> waypoints;
// The current track data
private TrackData trackData;
// The SAX locator to get the current line information
private Locator locator;
// The current element content // The current element content
protected String content; protected String content;
protected String name; protected String name;
protected String description; protected String description;
protected String category; protected String category;
@@ -127,7 +82,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
protected String time; protected String time;
protected String waypointType; protected String waypointType;
protected String photoUrl; protected String photoUrl;
// The current track data
private TrackData trackData;
// The SAX locator to get the current line information
private Locator locator;
/** /**
* Constructor. * Constructor.
* *
@@ -616,4 +574,39 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
contentProviderUtils.deleteTrack(context, trackId); contentProviderUtils.deleteTrack(context, trackId);
} }
} }
/**
* Data for the current track.
*
* @author Jimmy Shih
*/
private class TrackData {
// The current track
Track track = new Track();
// The number of segments processed for the current track
int numberOfSegments = 0;
/*
* The last location in the current segment. Null if the current segment
* doesn't have a last location.
*/
Location lastLocationInCurrentSegment;
// The number of locations processed for the current track
int numberOfLocations = 0;
// The trip statistics updater for the current track
TripStatisticsUpdater tripStatisticsUpdater;
// The import time of the track.
long importTime = System.currentTimeMillis();
// The buffered locations
Location[] bufferedLocations = new Location[MAX_BUFFERED_LOCATIONS];
// The number of buffered locations
int numBufferedLocations = 0;
}
} }
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.io.file.importer;
import android.content.Context; import android.content.Context;
import android.location.Location; import android.location.Location;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
@@ -16,6 +16,12 @@
package com.google.android.apps.mytracks.io.file.importer; package com.google.android.apps.mytracks.io.file.importer;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.io.file.TrackFileFormat; import com.google.android.apps.mytracks.io.file.TrackFileFormat;
@@ -25,12 +31,6 @@ import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.SystemUtils; import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -46,12 +46,11 @@ import java.util.List;
public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> { public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = ImportAsyncTask.class.getSimpleName(); private static final String TAG = ImportAsyncTask.class.getSimpleName();
private ImportActivity importActivity;
private final boolean importAll; private final boolean importAll;
private final TrackFileFormat trackFileFormat; private final TrackFileFormat trackFileFormat;
private final String path; private final String path;
private final Context context; private final Context context;
private ImportActivity importActivity;
private WakeLock wakeLock; private WakeLock wakeLock;
// true if the AsyncTask has completed // true if the AsyncTask has completed
@@ -22,8 +22,8 @@ import android.net.Uri;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.android.apps.mytracks.content.sensor.SensorDataSet; import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
@@ -16,14 +16,14 @@
package com.google.android.apps.mytracks.io.file.importer; package com.google.android.apps.mytracks.io.file.importer;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.io.file.exporter.KmzTrackExporter;
import com.google.android.apps.mytracks.util.FileUtils;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.io.file.exporter.KmzTrackExporter;
import com.google.android.apps.mytracks.util.FileUtils;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@@ -59,7 +59,7 @@ public class ControlRecordingService extends IntentService implements ServiceCon
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
trackRecordingService = (ITrackRecordingService)service; trackRecordingService = (ITrackRecordingService) service;
notifyConnected(); notifyConnected();
} }
@@ -89,7 +89,7 @@ public interface ITrackRecordingService {
/** /**
* Inserts a track point in the current recording track. * Inserts a track point in the current recording track.
* * <p>
* When recording a track, GPS locations are automatically inserted. This is used for * When recording a track, GPS locations are automatically inserted. This is used for
* inserting special track points or for testing. * inserting special track points or for testing.
* *
@@ -99,14 +99,14 @@ public interface ITrackRecordingService {
/** /**
* Gets the current sensor data. Returns null if there is no data. * Gets the current sensor data. Returns null if there is no data.
*
* @return SensorDataSet object. * @return SensorDataSet object.
*/ */
SensorDataSet getSensorData(); SensorDataSet getSensorData();
/** /**
* Gets the current sensor manager state. * Gets the current sensor manager state.
* * <p>
* return a Sensor.SensorState enum value. * return a Sensor.SensorState enum value.
*/ */
SensorState getSensorState(); SensorState getSensorState();
@@ -44,11 +44,11 @@ import androidx.core.app.TaskStackBuilder;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.TrackDetailActivity; import com.google.android.apps.mytracks.TrackDetailActivity;
import com.google.android.apps.mytracks.TrackListActivity; import com.google.android.apps.mytracks.TrackListActivity;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.CustomContentProvider;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.ContentProviderUtils.LocationIterator; import com.google.android.apps.mytracks.content.ContentProviderUtils.LocationIterator;
import com.google.android.apps.mytracks.content.CustomContentProvider;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.SensorDataSetLocation;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
@@ -95,15 +95,11 @@ public class TrackRecordingService extends Service {
* Anything faster than that (in meters per second) will be considered moving. * Anything faster than that (in meters per second) will be considered moving.
*/ */
public static final double MAX_NO_MOVEMENT_SPEED = 0.224; public static final double MAX_NO_MOVEMENT_SPEED = 0.224;
private static final String TAG = TrackRecordingService.class.getSimpleName();
// 1 minute in milliseconds
private static final long ONE_MINUTE = (long) (UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
@VisibleForTesting @VisibleForTesting
static final int MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS = 3; static final int MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS = 3;
private static final String TAG = TrackRecordingService.class.getSimpleName();
// 1 minute in milliseconds
private static final long ONE_MINUTE = (long) (UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
// The following variables are set in onCreate: // The following variables are set in onCreate:
private ExecutorService executorService; private ExecutorService executorService;
private Context context; private Context context;
@@ -124,15 +120,6 @@ public class TrackRecordingService extends Service {
// The following variables are set when recording: // The following variables are set when recording:
private TripStatisticsUpdater trackTripStatisticsUpdater; private TripStatisticsUpdater trackTripStatisticsUpdater;
private TripStatisticsUpdater markerTripStatisticsUpdater;
private WakeLock wakeLock;
private SensorManager sensorManager;
private Location lastLocation;
private boolean currentSegmentHasLocation;
private boolean isIdle; // true if idle
private ServiceBinder binder = new ServiceBinder(this);
/* /*
* Note that sharedPreferenceChangeListener cannot be an anonymous inner * Note that sharedPreferenceChangeListener cannot be an anonymous inner
* class. Anonymous inner class will get garbage collected. * class. Anonymous inner class will get garbage collected.
@@ -193,7 +180,13 @@ public class TrackRecordingService extends Service {
} }
} }
}; };
private TripStatisticsUpdater markerTripStatisticsUpdater;
private WakeLock wakeLock;
private SensorManager sensorManager;
private Location lastLocation;
private boolean currentSegmentHasLocation;
private boolean isIdle; // true if idle
private ServiceBinder binder = new ServiceBinder(this);
private LocationListener locationListener = new LocationListener() { private LocationListener locationListener = new LocationListener() {
@Override @Override
@@ -16,9 +16,6 @@
package com.google.android.apps.mytracks.services; package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.maps.mytracks.BuildConfig;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -28,6 +25,9 @@ import android.os.IBinder.DeathRecipient;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.maps.mytracks.BuildConfig;
/** /**
* Wrapper for the track recording service. This handles service * Wrapper for the track recording service. This handles service
* start/bind/unbind/stop. The service must be started before it can be bound. * start/bind/unbind/stop. The service must be started before it can be bound.
@@ -38,7 +38,9 @@ import android.util.Log;
public class TrackRecordingServiceConnection { public class TrackRecordingServiceConnection {
private static final String TAG = TrackRecordingServiceConnection.class.getSimpleName(); private static final String TAG = TrackRecordingServiceConnection.class.getSimpleName();
private final Context context;
private final Runnable callback;
private ITrackRecordingService trackRecordingService;
private final DeathRecipient deathRecipient = new DeathRecipient() { private final DeathRecipient deathRecipient = new DeathRecipient() {
@Override @Override
public void binderDied() { public void binderDied() {
@@ -46,7 +48,6 @@ public class TrackRecordingServiceConnection {
setTrackRecordingService(null); setTrackRecordingService(null);
} }
}; };
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
@@ -56,7 +57,7 @@ public class TrackRecordingServiceConnection {
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Failed to bind a death recipient.", e); Log.e(TAG, "Failed to bind a death recipient.", e);
} }
setTrackRecordingService((ITrackRecordingService)service); setTrackRecordingService((ITrackRecordingService) service);
} }
@Override @Override
@@ -66,10 +67,6 @@ public class TrackRecordingServiceConnection {
} }
}; };
private final Context context;
private final Runnable callback;
private ITrackRecordingService trackRecordingService;
/** /**
* Constructor. * Constructor.
* *
@@ -41,15 +41,13 @@ import java.util.UUID;
*/ */
public class BluetoothConnectionManager { public class BluetoothConnectionManager {
private static final UUID HEART_RATE_SERVICE_UUID = new UUID(0x180D00001000L, 0x800000805f9b34fbL);
private static final UUID HEART_RATE_MEASUREMENT_CHAR_UUID = new UUID(0x2A3700001000L, 0x800000805f9b34fbL);
private static final UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = new UUID(0x290200001000L, 0x800000805f9b34fbL);
// Message types sent to handler // Message types sent to handler
public static final int MESSAGE_DEVICE_NAME = 1; public static final int MESSAGE_DEVICE_NAME = 1;
public static final int MESSAGE_READ = 2; public static final int MESSAGE_READ = 2;
public static final String KEY_DEVICE_NAME = "device_name"; public static final String KEY_DEVICE_NAME = "device_name";
private static final UUID HEART_RATE_SERVICE_UUID = new UUID(0x180D00001000L, 0x800000805f9b34fbL);
private static final UUID HEART_RATE_MEASUREMENT_CHAR_UUID = new UUID(0x2A3700001000L, 0x800000805f9b34fbL);
private static final UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = new UUID(0x290200001000L, 0x800000805f9b34fbL);
private static final String TAG = BluetoothConnectionManager.class.getSimpleName(); private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
private final Context context; private final Context context;
@@ -4,7 +4,8 @@ import android.bluetooth.BluetoothGattCharacteristic;
public final class BluetoothLEUtils { public final class BluetoothLEUtils {
private BluetoothLEUtils() {} private BluetoothLEUtils() {
}
public static int parseHeartRate(BluetoothGattCharacteristic characteristic) { public static int parseHeartRate(BluetoothGattCharacteristic characteristic) {
//DOCUMENTATION https://www.bluetooth.com/specifications/gatt/characteristics/ //DOCUMENTATION https://www.bluetooth.com/specifications/gatt/characteristics/
@@ -23,7 +23,6 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.util.Log; import android.util.Log;
import android.widget.Toast;
import com.google.android.apps.mytracks.content.sensor.SensorDataSet; import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.sensor.SensorState; import com.google.android.apps.mytracks.content.sensor.SensorState;
@@ -35,15 +34,47 @@ import java.util.ArrayList;
/** /**
* Bluetooth sensor manager. * Bluetooth sensor manager.
* * <p>
* TODO: Handle a BluetoothGatt.STATE_DISCONNECTED * TODO: Handle a BluetoothGatt.STATE_DISCONNECTED
* *
* @author Sandor Dornbush * @author Sandor Dornbush
*/ */
public class BluetoothSensorManager extends SensorManager { public class BluetoothSensorManager extends SensorManager {
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
private static final String TAG = BluetoothConnectionManager.class.getSimpleName(); private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
private final Context context;
private final BluetoothConnectionManager bluetoothConnectionManager;
private SensorDataSet sensorDataSet = null;
// Handler that gets information back from the bluetoothConnectionManager
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
String deviceName = message.getData().getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
break;
case BluetoothConnectionManager.MESSAGE_READ:
if (!(message.obj instanceof SensorDataSet)) {
Log.e(TAG, "Received message did not contain a SensorDataSet.");
sensorDataSet = null;
} else {
sensorDataSet = (SensorDataSet) message.obj;
}
break;
default:
break;
}
}
};
/**
* @param context the context
*/
public BluetoothSensorManager(Context context) {
this.context = context;
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
}
private static BluetoothAdapter getDefaultBluetoothAdapter() { private static BluetoothAdapter getDefaultBluetoothAdapter() {
// If from the main application thread, return directly // If from the main application thread, return directly
@@ -83,40 +114,6 @@ public class BluetoothSensorManager extends SensorManager {
return adapters.get(0); return adapters.get(0);
} }
private final Context context;
private final BluetoothConnectionManager bluetoothConnectionManager;
private SensorDataSet sensorDataSet = null;
// Handler that gets information back from the bluetoothConnectionManager
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
String deviceName = message.getData().getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
break;
case BluetoothConnectionManager.MESSAGE_READ:
if (!(message.obj instanceof SensorDataSet)) {
Log.e(TAG ,"Received message did not contain a SensorDataSet.");
sensorDataSet = null;
} else {
sensorDataSet = (SensorDataSet) message.obj;
}
break;
default:
break;
}
}
};
/**
* @param context the context
*/
public BluetoothSensorManager(Context context) {
this.context = context;
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
}
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled(); return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
@@ -108,6 +108,13 @@ public abstract class SensorManager {
tearDownChannel(); tearDownChannel();
} }
/**
* Gets the sensor state.
*/
public SensorState getSensorState() {
return sensorState;
}
/** /**
* Sets the sensor state. * Sets the sensor state.
* *
@@ -118,13 +125,6 @@ public abstract class SensorManager {
this.sensorState = sensorState; this.sensorState = sensorState;
} }
/**
* Gets the sensor state.
*/
public SensorState getSensorState() {
return sensorState;
}
/** /**
* Returns true if the sensor data set is valid. * Returns true if the sensor data set is valid.
*/ */
@@ -28,7 +28,8 @@ public class SensorManagerFactory {
private static SensorManager systemSensorManager = null; private static SensorManager systemSensorManager = null;
private static SensorManager sensorManagerTemporary = null; private static SensorManager sensorManagerTemporary = null;
private SensorManagerFactory() {} private SensorManagerFactory() {
}
/** /**
* Gets the system sensor manager. * Gets the system sensor manager.
@@ -50,7 +50,8 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
static final float TTS_SPEECH_RATE = 0.9f; static final float TTS_SPEECH_RATE = 0.9f;
private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName(); private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName();
private final Context context;
private final AudioManager audioManager;
private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() { private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() {
@Override @Override
public void onStart(String utteranceId) { public void onStart(String utteranceId) {
@@ -68,21 +69,13 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
public void onError(String utteranceId) { public void onError(String utteranceId) {
} }
}; };
private final Context context;
private TextToSpeech tts; private TextToSpeech tts;
// Response from TTS after its initialization // Response from TTS after its initialization
private int initStatus = TextToSpeech.ERROR; private int initStatus = TextToSpeech.ERROR;
// True if TTS engine is ready // True if TTS engine is ready
private boolean ready = false; private boolean ready = false;
// True if speech is allowed // True if speech is allowed
private boolean speechAllowed; private boolean speechAllowed;
private final AudioManager audioManager;
/** /**
* Listener which updates {@link #speechAllowed} when the phone state changes. * Listener which updates {@link #speechAllowed} when the phone state changes.
*/ */
@@ -15,13 +15,13 @@
*/ */
package com.google.android.apps.mytracks.services.tasks; package com.google.android.apps.mytracks.services.tasks;
import android.util.Log;
import com.google.android.apps.mytracks.services.TrackRecordingService; import com.google.android.apps.mytracks.services.TrackRecordingService;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.UnitConversions; import com.google.android.apps.mytracks.util.UnitConversions;
import android.util.Log;
/** /**
* Execute a periodic task on a time or distance schedule. * Execute a periodic task on a time or distance schedule.
* *
@@ -27,7 +27,8 @@ import com.google.android.apps.mytracks.services.TrackRecordingService;
public class SplitPeriodicTask implements PeriodicTask { public class SplitPeriodicTask implements PeriodicTask {
@Override @Override
public void start() {} public void start() {
}
@Override @Override
public void run(TrackRecordingService trackRecordingService) { public void run(TrackRecordingService trackRecordingService) {
@@ -35,5 +36,6 @@ public class SplitPeriodicTask implements PeriodicTask {
} }
@Override @Override
public void shutdown() {} public void shutdown() {
}
} }
@@ -16,11 +16,6 @@
package com.google.android.apps.mytracks.settings; package com.google.android.apps.mytracks.settings;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.maps.mytracks.R;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
@@ -34,6 +29,11 @@ import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.Spinner; import android.widget.Spinner;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.maps.mytracks.R;
/** /**
* For entering the default activity type. * For entering the default activity type.
* *
@@ -16,11 +16,6 @@
package com.google.android.apps.mytracks.settings; package com.google.android.apps.mytracks.settings;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@@ -28,14 +23,17 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener; import android.preference.Preference.OnPreferenceClickListener;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
/** /**
* An activity for advanced settings. * An activity for advanced settings.
* *
@@ -16,13 +16,13 @@
package com.google.android.apps.mytracks.settings; package com.google.android.apps.mytracks.settings;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.ListPreference; import android.preference.ListPreference;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
/** /**
* An activity for accessing chart settings. * An activity for accessing chart settings.
* *
@@ -16,14 +16,14 @@
package com.google.android.apps.mytracks.settings; package com.google.android.apps.mytracks.settings;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.os.Bundle; import android.os.Bundle;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceChangeListener;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
/** /**
* An activity for accessing stats settings. * An activity for accessing stats settings.
* *
@@ -27,12 +27,10 @@ import androidx.annotation.NonNull;
*/ */
public class DoubleBuffer { public class DoubleBuffer {
// The location that the next write will occur at.
private int index;
// The sliding buffer of doubles. // The sliding buffer of doubles.
private final double[] buffer; private final double[] buffer;
// The location that the next write will occur at.
private int index;
// True if the buffer is full // True if the buffer is full
private boolean isFull; private boolean isFull;
@@ -88,7 +86,7 @@ public class DoubleBuffer {
public double[] getAverageAndVariance() { public double[] getAverageAndVariance() {
int numberOfEntries = isFull ? buffer.length : index; int numberOfEntries = isFull ? buffer.length : index;
if (numberOfEntries == 0) { if (numberOfEntries == 0) {
return new double[] { 0, 0 }; return new double[]{0, 0};
} }
double sum = 0; double sum = 0;
@@ -100,7 +98,7 @@ public class DoubleBuffer {
double average = sum / numberOfEntries; double average = sum / numberOfEntries;
double variance = sumSquares / numberOfEntries - Math.pow(average, 2); double variance = sumSquares / numberOfEntries - Math.pow(average, 2);
return new double[] { average, variance }; return new double[]{average, variance};
} }
/** /**
@@ -50,6 +50,15 @@ public class ExtremityMonitor {
return min; return min;
} }
/**
* Sets the minimum value.
*
* @param min the minimum value
*/
public void setMin(double min) {
this.min = min;
}
/** /**
* Gets the maximum value seen. * Gets the maximum value seen.
*/ */
@@ -57,6 +66,15 @@ public class ExtremityMonitor {
return max; return max;
} }
/**
* Sets the maximum value.
*
* @param max the maximum value
*/
public void setMax(double max) {
this.max = max;
}
/** /**
* Updates the min and the max with a new value. * Updates the min and the max with a new value.
* *
@@ -87,24 +105,6 @@ public class ExtremityMonitor {
this.max = max; this.max = max;
} }
/**
* Sets the minimum value.
*
* @param min the minimum value
*/
public void setMin(double min) {
this.min = min;
}
/**
* Sets the maximum value.
*
* @param max the maximum value
*/
public void setMax(double max) {
this.max = max;
}
/** /**
* Returns true if has data. * Returns true if has data.
*/ */
@@ -31,43 +31,38 @@ import androidx.annotation.NonNull;
*/ */
public class TripStatistics implements Parcelable { public class TripStatistics implements Parcelable {
// The trip start time. This is the system time, might not match the GPs time. /**
private long startTime = -1L; * Creator of {@link TripStatistics} from parcels.
*/
// The trip stop time. This is the system time, might not match the GPS time. public static final Creator CREATOR = new Creator();
private long stopTime = -1L;
// The total trip distance (meters).
private double totalDistance;
// The total time (ms). Updated when new points are received, may be stale.
private long totalTime;
// The total moving time (ms). Based on when we believe the user is traveling.
private long movingTime;
// The min and max latitude seen in this trip. // The min and max latitude seen in this trip.
private final ExtremityMonitor latitudeExtremities = new ExtremityMonitor(); private final ExtremityMonitor latitudeExtremities = new ExtremityMonitor();
// The min and max longitude seen in this trip. // The min and max longitude seen in this trip.
private final ExtremityMonitor longitudeExtremities = new ExtremityMonitor(); private final ExtremityMonitor longitudeExtremities = new ExtremityMonitor();
// The maximum speed (meters/second) that we believe is valid.
private double maxSpeed;
// The min and max elevation (meters) seen on this trip. // The min and max elevation (meters) seen on this trip.
private final ExtremityMonitor elevationExtremities = new ExtremityMonitor(); private final ExtremityMonitor elevationExtremities = new ExtremityMonitor();
// The total elevation gained (meters).
private double totalElevationGain;
// The min and max grade seen on this trip. // The min and max grade seen on this trip.
private final ExtremityMonitor gradeExtremities = new ExtremityMonitor(); private final ExtremityMonitor gradeExtremities = new ExtremityMonitor();
// The trip start time. This is the system time, might not match the GPs time.
private long startTime = -1L;
// The trip stop time. This is the system time, might not match the GPS time.
private long stopTime = -1L;
// The total trip distance (meters).
private double totalDistance;
// The total time (ms). Updated when new points are received, may be stale.
private long totalTime;
// The total moving time (ms). Based on when we believe the user is traveling.
private long movingTime;
// The maximum speed (meters/second) that we believe is valid.
private double maxSpeed;
// The total elevation gained (meters).
private double totalElevationGain;
/** /**
* Default constructor. * Default constructor.
*/ */
public TripStatistics() {} public TripStatistics() {
}
/** /**
* Copy constructor. * Copy constructor.
@@ -446,12 +441,12 @@ public class TripStatistics implements Parcelable {
} }
/** /**
* Sets the maximum grade. * Sets the minimum grade.
* *
* @param grade the grade as a fraction (1.0 would mean vertical upwards) * @param grade the grade as a fraction (-1.0 would mean vertical downwards)
*/ */
public void setMaxGrade(double grade) { public void setMinGrade(double grade) {
gradeExtremities.setMax(grade); gradeExtremities.setMin(grade);
} }
/** /**
@@ -462,12 +457,12 @@ public class TripStatistics implements Parcelable {
} }
/** /**
* Sets the minimum grade. * Sets the maximum grade.
* *
* @param grade the grade as a fraction (-1.0 would mean vertical downwards) * @param grade the grade as a fraction (1.0 would mean vertical upwards)
*/ */
public void setMinGrade(double grade) { public void setMaxGrade(double grade) {
gradeExtremities.setMin(grade); gradeExtremities.setMax(grade);
} }
/** /**
@@ -492,6 +487,30 @@ public class TripStatistics implements Parcelable {
+ "; Max Grade: " + getMaxGrade() + "}"; + "; Max Grade: " + getMaxGrade() + "}";
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(startTime);
dest.writeLong(stopTime);
dest.writeDouble(totalDistance);
dest.writeLong(totalTime);
dest.writeLong(movingTime);
dest.writeDouble(latitudeExtremities.getMin());
dest.writeDouble(latitudeExtremities.getMax());
dest.writeDouble(longitudeExtremities.getMin());
dest.writeDouble(longitudeExtremities.getMax());
dest.writeDouble(maxSpeed);
dest.writeDouble(elevationExtremities.getMin());
dest.writeDouble(elevationExtremities.getMax());
dest.writeDouble(totalElevationGain);
dest.writeDouble(gradeExtremities.getMin());
dest.writeDouble(gradeExtremities.getMax());
}
/** /**
* Creator of statistics data from parcels. * Creator of statistics data from parcels.
*/ */
@@ -534,33 +553,4 @@ public class TripStatistics implements Parcelable {
return new TripStatistics[size]; return new TripStatistics[size];
} }
} }
/**
* Creator of {@link TripStatistics} from parcels.
*/
public static final Creator CREATOR = new Creator();
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(startTime);
dest.writeLong(stopTime);
dest.writeDouble(totalDistance);
dest.writeLong(totalTime);
dest.writeLong(movingTime);
dest.writeDouble(latitudeExtremities.getMin());
dest.writeDouble(latitudeExtremities.getMax());
dest.writeDouble(longitudeExtremities.getMin());
dest.writeDouble(longitudeExtremities.getMax());
dest.writeDouble(maxSpeed);
dest.writeDouble(elevationExtremities.getMin());
dest.writeDouble(elevationExtremities.getMax());
dest.writeDouble(totalElevationGain);
dest.writeDouble(gradeExtremities.getMin());
dest.writeDouble(gradeExtremities.getMax());
}
} }
@@ -36,8 +36,10 @@ import static com.google.android.apps.mytracks.services.TrackRecordingService.PA
*/ */
public class TripStatisticsUpdater { public class TripStatisticsUpdater {
private static final String TAG = TripStatisticsUpdater.class.getSimpleName(); /**
* The number of grade readings to smooth to get a somewhat accurate signal.
*/
public static final int GRADE_SMOOTHING_FACTOR = 5;
/** /**
* The number of elevation readings to smooth to get a somewhat accurate * The number of elevation readings to smooth to get a somewhat accurate
* signal. * signal.
@@ -50,18 +52,12 @@ public class TripStatisticsUpdater {
*/ */
@VisibleForTesting @VisibleForTesting
static final int RUN_SMOOTHING_FACTOR = 25; static final int RUN_SMOOTHING_FACTOR = 25;
/**
* The number of grade readings to smooth to get a somewhat accurate signal.
*/
public static final int GRADE_SMOOTHING_FACTOR = 5;
/** /**
* The number of speed reading to smooth to get a somewhat accurate signal. * The number of speed reading to smooth to get a somewhat accurate signal.
*/ */
@VisibleForTesting @VisibleForTesting
static final int SPEED_SMOOTHING_FACTOR = 25; static final int SPEED_SMOOTHING_FACTOR = 25;
private static final String TAG = TripStatisticsUpdater.class.getSimpleName();
/** /**
* Ignore any acceleration faster than this. Will ignore any speeds that imply * Ignore any acceleration faster than this. Will ignore any speeds that imply
* acceleration greater than 2g's 2g = 19.6 m/s^2 = 0.0002 m/ms^2 = 0.02 * acceleration greater than 2g's 2g = 19.6 m/s^2 = 0.0002 m/ms^2 = 0.02
@@ -71,27 +67,20 @@ public class TripStatisticsUpdater {
// The track's trip statistics // The track's trip statistics
private final TripStatistics tripStatistics; private final TripStatistics tripStatistics;
// The current segment's trip statistics
private TripStatistics currentSegment;
// Current segment's last location.
private Location lastLocation;
// Current segment's last moving location
private Location lastMovingLocation;
// A buffer of the recent elevation readings (m) // A buffer of the recent elevation readings (m)
private final DoubleBuffer elevationBuffer = new DoubleBuffer(ELEVATION_SMOOTHING_FACTOR); private final DoubleBuffer elevationBuffer = new DoubleBuffer(ELEVATION_SMOOTHING_FACTOR);
// A buffer of the recent run readings (m) for calculating grade // A buffer of the recent run readings (m) for calculating grade
private final DoubleBuffer runBuffer = new DoubleBuffer(RUN_SMOOTHING_FACTOR); private final DoubleBuffer runBuffer = new DoubleBuffer(RUN_SMOOTHING_FACTOR);
// A buffer of the recent grade calculations (%) // A buffer of the recent grade calculations (%)
private final DoubleBuffer gradeBuffer = new DoubleBuffer(GRADE_SMOOTHING_FACTOR); private final DoubleBuffer gradeBuffer = new DoubleBuffer(GRADE_SMOOTHING_FACTOR);
// A buffer of the recent speed readings (m/s) for calculating max speed // A buffer of the recent speed readings (m/s) for calculating max speed
private final DoubleBuffer speedBuffer = new DoubleBuffer(SPEED_SMOOTHING_FACTOR); private final DoubleBuffer speedBuffer = new DoubleBuffer(SPEED_SMOOTHING_FACTOR);
// The current segment's trip statistics
private TripStatistics currentSegment;
// Current segment's last location.
private Location lastLocation;
// Current segment's last moving location
private Location lastMovingLocation;
/** /**
* Creates a new trip statistics updater. * Creates a new trip statistics updater.
@@ -29,7 +29,8 @@ import java.util.Set;
*/ */
public class BluetoothDeviceUtils { public class BluetoothDeviceUtils {
private BluetoothDeviceUtils() {} private BluetoothDeviceUtils() {
}
/** /**
* Populates the device names and the device addresses with all the suitable * Populates the device names and the device addresses with all the suitable
@@ -16,8 +16,6 @@
package com.google.android.apps.mytracks.util; package com.google.android.apps.mytracks.util;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
@@ -28,6 +26,8 @@ import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import com.google.android.maps.mytracks.R;
/** /**
* Utilities for creating dialogs. * Utilities for creating dialogs.
* *
@@ -35,7 +35,8 @@ import android.widget.LinearLayout;
*/ */
public class DialogUtils { public class DialogUtils {
private DialogUtils() {} private DialogUtils() {
}
/** /**
* Creates a confirmation dialog. * Creates a confirmation dialog.
@@ -29,36 +29,32 @@ import java.io.File;
*/ */
public class FileUtils { public class FileUtils {
private FileUtils() {}
/**
* Our external storage directory.
*/
static final String SDCARD_TOP_DIR = "MyTracks";
static final String TRACK_PREFIX = "MyTracks";
/** /**
* Used to transfer picture from the camera. * Used to transfer picture from the camera.
*/ */
public static final String FILEPROVIDER = "com.google.android.apps.mytracks.fileprovider"; public static final String FILEPROVIDER = "com.google.android.apps.mytracks.fileprovider";
/**
* The play tracks directory in the app's cache directory.
*/
public static final String PLAY_TRACKS_DIR = "temp";
/**
* Our external storage directory.
*/
static final String SDCARD_TOP_DIR = "MyTracks";
static final String TRACK_PREFIX = "MyTracks";
/** /**
* The maximum FAT32 path length. See the FAT32 spec at * The maximum FAT32 path length. See the FAT32 spec at
* http://msdn.microsoft.com/en-us/windows/hardware/gg463080 * http://msdn.microsoft.com/en-us/windows/hardware/gg463080
*/ */
static final int MAX_FAT32_PATH_LENGTH = 260; static final int MAX_FAT32_PATH_LENGTH = 260;
/**
* The play tracks directory in the app's cache directory.
*/
public static final String PLAY_TRACKS_DIR = "temp";
/** /**
* The pictures directory under the app's external storage directory. * The pictures directory under the app's external storage directory.
*/ */
private static final String PICTURES_DIR = "pictures"; private static final String PICTURES_DIR = "pictures";
private FileUtils() {
}
/** /**
* Returns true if the external storage is available. * Returns true if the external storage is available.
*/ */
@@ -20,8 +20,8 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.file.TrackFileFormat; import com.google.android.apps.mytracks.io.file.TrackFileFormat;
@@ -38,7 +38,8 @@ public class IntentUtils {
public static final String TEXT_PLAIN_TYPE = "text/plain"; public static final String TEXT_PLAIN_TYPE = "text/plain";
private IntentUtils() {} private IntentUtils() {
}
/** /**
* Creates an intent with {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} and * Creates an intent with {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} and
@@ -39,7 +39,8 @@ public class ListItemUtils {
private static final int LIST_PREFERRED_ITEM_HEIGHT_DEFAULT = 128; private static final int LIST_PREFERRED_ITEM_HEIGHT_DEFAULT = 128;
private ListItemUtils() {} private ListItemUtils() {
}
/** /**
* Sets a list item. * Sets a list item.
@@ -193,24 +194,24 @@ public class ListItemUtils {
*/ */
private static String[] getDateTime(boolean isRecording, Context context, long time, boolean useRelativeTime) { private static String[] getDateTime(boolean isRecording, Context context, long time, boolean useRelativeTime) {
if (isRecording || time == 0L) { if (isRecording || time == 0L) {
return new String[] { null, null }; return new String[]{null, null};
} }
boolean isToday = DateUtils.isToday(time); boolean isToday = DateUtils.isToday(time);
int timeFlags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL; int timeFlags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL;
if (isToday) { if (isToday) {
if (useRelativeTime) { if (useRelativeTime) {
return new String[] { DateUtils.getRelativeTimeSpanString( return new String[]{DateUtils.getRelativeTimeSpanString(
time, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, time, System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE).toString(), null }; DateUtils.FORMAT_ABBREV_RELATIVE).toString(), null};
} else { } else {
return new String[] { DateUtils.formatDateTime(context, time, timeFlags), null }; return new String[]{DateUtils.formatDateTime(context, time, timeFlags), null};
} }
} }
return new String[] { DateUtils.formatDateTime( return new String[]{DateUtils.formatDateTime(
context, time, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL), context, time, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL),
DateUtils.formatDateTime(context, time, timeFlags) }; DateUtils.formatDateTime(context, time, timeFlags)};
} }
/** /**
@@ -241,7 +242,7 @@ public class ListItemUtils {
* @param context the context * @param context the context
*/ */
private static int getPhotoHeight(Context context) { private static int getPhotoHeight(Context context) {
int[] attrs = new int[] { android.R.attr.listPreferredItemHeight }; int[] attrs = new int[]{android.R.attr.listPreferredItemHeight};
TypedArray typeArray = context.obtainStyledAttributes(attrs); TypedArray typeArray = context.obtainStyledAttributes(attrs);
int height = typeArray.getDimensionPixelSize(0, LIST_PREFERRED_ITEM_HEIGHT_DEFAULT); int height = typeArray.getDimensionPixelSize(0, LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
typeArray.recycle(); typeArray.recycle();
@@ -15,11 +15,11 @@
*/ */
package com.google.android.apps.mytracks.util; package com.google.android.apps.mytracks.util;
import com.google.android.apps.mytracks.content.Track;
import android.location.Location; import android.location.Location;
import android.util.Log; import android.util.Log;
import com.google.android.apps.mytracks.content.Track;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Stack; import java.util.Stack;
@@ -36,7 +36,8 @@ public class LocationUtils {
private static final long MAX_LOCATION_AGE_MS = (long) (UnitConversions.MIN_TO_S private static final long MAX_LOCATION_AGE_MS = (long) (UnitConversions.MIN_TO_S
* UnitConversions.S_TO_MS); * UnitConversions.S_TO_MS);
private LocationUtils() {} private LocationUtils() {
}
/** /**
* Computes the distance on the two sphere between the point c0 and the line * Computes the distance on the two sphere between the point c0 and the line
@@ -103,7 +104,7 @@ public class LocationUtils {
int[] current; int[] current;
if (n > 2) { if (n > 2) {
int[] stackVal = new int[] { 0, (n - 1) }; int[] stackVal = new int[]{0, (n - 1)};
stack.push(stackVal); stack.push(stackVal);
while (stack.size() > 0) { while (stack.size() > 0) {
current = stack.pop(); current = stack.pop();
@@ -118,9 +119,9 @@ public class LocationUtils {
} }
if (maxDist > tolerance) { if (maxDist > tolerance) {
dists[maxIdx] = maxDist; dists[maxIdx] = maxDist;
int[] stackValCurMax = { current[0], maxIdx }; int[] stackValCurMax = {current[0], maxIdx};
stack.push(stackValCurMax); stack.push(stackValCurMax);
int[] stackValMaxCur = { maxIdx, current[1] }; int[] stackValMaxCur = {maxIdx, current[1]};
stack.push(stackValMaxCur); stack.push(stackValMaxCur);
} }
} }
@@ -19,7 +19,6 @@ package com.google.android.apps.mytracks.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.text.TextUtils;
import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.Constants;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
@@ -92,7 +91,8 @@ public class PreferencesUtils {
public static final int TRACK_WIDGET_ITEM4_DEFAULT = 2; // average speed public static final int TRACK_WIDGET_ITEM4_DEFAULT = 2; // average speed
public static final int VOICE_FREQUENCY_DEFAULT = 0; public static final int VOICE_FREQUENCY_DEFAULT = 0;
private PreferencesUtils() {} private PreferencesUtils() {
}
/** /**
* Gets a preference key * Gets a preference key
@@ -145,7 +145,7 @@ public class PreferencesUtils {
int intValue = defaultValue; int intValue = defaultValue;
try { try {
intValue = Integer.parseInt(stringValue); intValue = Integer.parseInt(stringValue);
} catch (NumberFormatException e){ } catch (NumberFormatException e) {
} }
return intValue; return intValue;
} }
@@ -40,7 +40,8 @@ public class StatsUtils {
private static final String GRADE_PERCENTAGE = "%"; private static final String GRADE_PERCENTAGE = "%";
private static final String GRADE_FORMAT = "%1$d"; private static final String GRADE_FORMAT = "%1$d";
private StatsUtils() {} private StatsUtils() {
}
/** /**
* Sets the location values. * Sets the location values.
@@ -167,7 +168,7 @@ public class StatsUtils {
setSpeed(context, getView(activity, view, R.id.stats_average_moving_speed), R.string.stats_average_moving_speed, R.string.stats_average_moving_pace, averageMovingSpeed, metricUnits, reportSpeed); setSpeed(context, getView(activity, view, R.id.stats_average_moving_speed), R.string.stats_average_moving_speed, R.string.stats_average_moving_pace, averageMovingSpeed, metricUnits, reportSpeed);
// Set grade/elevation // Set grade/elevation
boolean showGradeElevation = PreferencesUtils.getBoolean(context,R.string.stats_show_grade_elevation_key, PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT); boolean showGradeElevation = PreferencesUtils.getBoolean(context, R.string.stats_show_grade_elevation_key, PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT);
View gradeElevationSeparator = getView(activity, view, R.id.stats_grade_elevation_separator); View gradeElevationSeparator = getView(activity, view, R.id.stats_grade_elevation_separator);
gradeElevationSeparator.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE); gradeElevationSeparator.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE);
@@ -47,12 +47,14 @@ public class StringUtils {
"yyyy-MM-dd'T'HH:mm:ss", Locale.US); "yyyy-MM-dd'T'HH:mm:ss", Locale.US);
private static final Pattern ISO_8601_EXTRAS = Pattern.compile( private static final Pattern ISO_8601_EXTRAS = Pattern.compile(
"^(\\.\\d+)?(?:Z|([+-])(\\d{2}):(\\d{2}))?$"); "^(\\.\\d+)?(?:Z|([+-])(\\d{2}):(\\d{2}))?$");
static { static {
ISO_8601_DATE_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); ISO_8601_DATE_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
ISO_8601_BASE.setTimeZone(TimeZone.getTimeZone("UTC")); ISO_8601_BASE.setTimeZone(TimeZone.getTimeZone("UTC"));
} }
private StringUtils() {} private StringUtils() {
}
/** /**
* Formats the date and time based on user's phone date/time preferences. * Formats the date and time based on user's phone date/time preferences.
@@ -156,7 +158,7 @@ public class StringUtils {
} }
public static String formatDecimal(double value, int decimalPlaces) { public static String formatDecimal(double value, int decimalPlaces) {
String result = String.format(Locale.getDefault(), "%1$,." + decimalPlaces +"f", value); String result = String.format(Locale.getDefault(), "%1$,." + decimalPlaces + "f", value);
return result.replaceAll("[0]*$", "").replaceAll("\\.$", ""); return result.replaceAll("[0]*$", "").replaceAll("\\.$", "");
} }
@@ -34,7 +34,8 @@ public class SystemUtils {
private static final String TAG = SystemUtils.class.getSimpleName(); private static final String TAG = SystemUtils.class.getSimpleName();
private SystemUtils() {} private SystemUtils() {
}
/** /**
* Get the app version from the manifest. * Get the app version from the manifest.
@@ -55,28 +55,28 @@ public class TrackIconUtils {
private static final String SNOW_BOARDING = "SNOW_BOARDING"; private static final String SNOW_BOARDING = "SNOW_BOARDING";
private static final String WALK = "WALK"; private static final String WALK = "WALK";
private static final int[] AIRPLANE_LIST = new int[] { R.string.activity_type_airplane, private static final int[] AIRPLANE_LIST = new int[]{R.string.activity_type_airplane,
R.string.activity_type_commercial_airplane, R.string.activity_type_rc_airplane }; R.string.activity_type_commercial_airplane, R.string.activity_type_rc_airplane};
private static final int[] BIKE_LIST = new int[] { R.string.activity_type_biking, private static final int[] BIKE_LIST = new int[]{R.string.activity_type_biking,
R.string.activity_type_cycling, R.string.activity_type_dirt_bike, R.string.activity_type_cycling, R.string.activity_type_dirt_bike,
R.string.activity_type_motor_bike, R.string.activity_type_mountain_biking, R.string.activity_type_motor_bike, R.string.activity_type_mountain_biking,
R.string.activity_type_road_biking, R.string.activity_type_track_cycling }; R.string.activity_type_road_biking, R.string.activity_type_track_cycling};
private static final int[] BOAT_LIST = new int[] { R.string.activity_type_boat, private static final int[] BOAT_LIST = new int[]{R.string.activity_type_boat,
R.string.activity_type_ferry, R.string.activity_type_motor_boating, R.string.activity_type_ferry, R.string.activity_type_motor_boating,
R.string.activity_type_rc_boat }; R.string.activity_type_rc_boat};
private static final int[] DRIVE_LIST = new int[] { R.string.activity_type_atv, private static final int[] DRIVE_LIST = new int[]{R.string.activity_type_atv,
R.string.activity_type_driving, R.string.activity_type_driving_bus, R.string.activity_type_driving, R.string.activity_type_driving_bus,
R.string.activity_type_driving_car }; R.string.activity_type_driving_car};
private static final int[] RUN_LIST = new int[] { R.string.activity_type_running, private static final int[] RUN_LIST = new int[]{R.string.activity_type_running,
R.string.activity_type_street_running, R.string.activity_type_track_running, R.string.activity_type_street_running, R.string.activity_type_track_running,
R.string.activity_type_trail_running }; R.string.activity_type_trail_running};
private static final int[] SKI_LIST = new int[] { private static final int[] SKI_LIST = new int[]{
R.string.activity_type_cross_country_skiing, R.string.activity_type_skiing }; R.string.activity_type_cross_country_skiing, R.string.activity_type_skiing};
private static final int[] SNOW_BOARDING_LIST = new int[] { private static final int[] SNOW_BOARDING_LIST = new int[]{
R.string.activity_type_snow_boarding }; R.string.activity_type_snow_boarding};
private static final int[] WALK_LIST = new int[] { R.string.activity_type_hiking, private static final int[] WALK_LIST = new int[]{R.string.activity_type_hiking,
R.string.activity_type_off_trail_hiking, R.string.activity_type_speed_walking, R.string.activity_type_off_trail_hiking, R.string.activity_type_speed_walking,
R.string.activity_type_trail_hiking, R.string.activity_type_walking }; R.string.activity_type_trail_hiking, R.string.activity_type_walking};
private static final LinkedHashMap<String, Pair<Integer, Integer>> private static final LinkedHashMap<String, Pair<Integer, Integer>>
MAP = new LinkedHashMap<>(); MAP = new LinkedHashMap<>();
@@ -88,11 +88,12 @@ public class TrackIconUtils {
MAP.put(DRIVE, new Pair<>(R.string.activity_type_driving, R.drawable.ic_track_drive)); MAP.put(DRIVE, new Pair<>(R.string.activity_type_driving, R.drawable.ic_track_drive));
MAP.put(SKI, new Pair<>(R.string.activity_type_skiing, R.drawable.ic_track_ski)); MAP.put(SKI, new Pair<>(R.string.activity_type_skiing, R.drawable.ic_track_ski));
MAP.put(SNOW_BOARDING, new Pair<>(R.string.activity_type_snow_boarding, R.drawable.ic_track_snow_boarding)); MAP.put(SNOW_BOARDING, new Pair<>(R.string.activity_type_snow_boarding, R.drawable.ic_track_snow_boarding));
MAP.put(AIRPLANE,new Pair<>(R.string.activity_type_airplane, R.drawable.ic_track_airplane)); MAP.put(AIRPLANE, new Pair<>(R.string.activity_type_airplane, R.drawable.ic_track_airplane));
MAP.put(BOAT, new Pair<>(R.string.activity_type_boat, R.drawable.ic_track_boat)); MAP.put(BOAT, new Pair<>(R.string.activity_type_boat, R.drawable.ic_track_boat));
} }
private TrackIconUtils() {} private TrackIconUtils() {
}
/** /**
* Gets the icon drawable. * Gets the icon drawable.
@@ -175,7 +176,7 @@ public class TrackIconUtils {
public static ArrayAdapter<StringBuilder> getIconSpinnerAdapter( public static ArrayAdapter<StringBuilder> getIconSpinnerAdapter(
final Context context, String iconValue) { final Context context, String iconValue) {
return new ArrayAdapter<StringBuilder>(context, android.R.layout.simple_spinner_item, return new ArrayAdapter<StringBuilder>(context, android.R.layout.simple_spinner_item,
new StringBuilder[] { new StringBuilder(iconValue) }) { new StringBuilder[]{new StringBuilder(iconValue)}) {
@NonNull @NonNull
@Override @Override
public View getView(int position, View convertView, @NonNull android.view.ViewGroup parent) { public View getView(int position, View convertView, @NonNull android.view.ViewGroup parent) {
@@ -20,6 +20,7 @@ import android.content.Context;
import android.location.Address; import android.location.Address;
import android.location.Geocoder; import android.location.Geocoder;
import android.location.Location; import android.location.Location;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
@@ -42,7 +42,8 @@ public class TrackRecordingServiceConnectionUtils {
private static final String TAG = TrackRecordingServiceConnectionUtils.class.getSimpleName(); private static final String TAG = TrackRecordingServiceConnectionUtils.class.getSimpleName();
private TrackRecordingServiceConnectionUtils() {} private TrackRecordingServiceConnectionUtils() {
}
/** /**
* Returns true if the recording service is running. * Returns true if the recording service is running.
@@ -16,12 +16,12 @@
package com.google.android.apps.mytracks.util; package com.google.android.apps.mytracks.util;
import android.content.Context;
import com.google.android.apps.mytracks.content.ContentProviderUtils; import com.google.android.apps.mytracks.content.ContentProviderUtils;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns; import com.google.android.apps.mytracks.content.TracksColumns;
import android.content.Context;
/** /**
* Utilities for updating track. * Utilities for updating track.
* *
@@ -29,10 +29,11 @@ import android.content.Context;
*/ */
public class TrackUtils { public class TrackUtils {
private TrackUtils() {}
public static String TRACK_SORT_ORDER = TracksColumns.STARTTIME + " DESC"; public static String TRACK_SORT_ORDER = TracksColumns.STARTTIME + " DESC";
private TrackUtils() {
}
public static void updateTrack(Context context, Track track, String name, String category, String description, ContentProviderUtils contentProviderUtils) { public static void updateTrack(Context context, Track track, String name, String category, String description, ContentProviderUtils contentProviderUtils) {
if (name != null) { if (name != null) {
track.setName(name); track.setName(name);
@@ -22,58 +22,44 @@ package com.google.android.apps.mytracks.util;
*/ */
public class UnitConversions { public class UnitConversions {
private UnitConversions() {} // multiplication factor to convert seconds to milliseconds
public static final double S_TO_MS = 1000.0;
// Time // Time
// 1 second in milliseconds // 1 second in milliseconds
public static final long ONE_SECOND = (long) UnitConversions.S_TO_MS; public static final long ONE_SECOND = (long) UnitConversions.S_TO_MS;
// multiplication factor to convert seconds to milliseconds
public static final double S_TO_MS = 1000.0;
// multiplication factor to convert milliseconds to seconds // multiplication factor to convert milliseconds to seconds
public static final double MS_TO_S = 1 / S_TO_MS; public static final double MS_TO_S = 1 / S_TO_MS;
// multiplication factor to convert minutes to seconds // multiplication factor to convert minutes to seconds
public static final double MIN_TO_S = 60.0; public static final double MIN_TO_S = 60.0;
// multiplication factor to convert seconds to minutes // multiplication factor to convert seconds to minutes
public static final double S_TO_MIN = 1 / MIN_TO_S; public static final double S_TO_MIN = 1 / MIN_TO_S;
// multiplication factor to convert hours to minutes // multiplication factor to convert hours to minutes
public static final double HR_TO_MIN = 60.0; public static final double HR_TO_MIN = 60.0;
// multiplication factor to convert minutes to hours // multiplication factor to convert minutes to hours
public static final double MIN_TO_HR = 1 / HR_TO_MIN; public static final double MIN_TO_HR = 1 / HR_TO_MIN;
// Distance
// multiplication factor to convert kilometers to miles // multiplication factor to convert kilometers to miles
public static final double KM_TO_MI = 0.621371192; public static final double KM_TO_MI = 0.621371192;
// Distance
// multiplication factor to convert miles to feet // multiplication factor to convert miles to feet
public static final double MI_TO_FT = 5280.0; public static final double MI_TO_FT = 5280.0;
// multiplication factor to convert feet to miles // multiplication factor to convert feet to miles
public static final double FT_TO_MI = 1 / MI_TO_FT; public static final double FT_TO_MI = 1 / MI_TO_FT;
// multiplication factor to covert kilometers to meters // multiplication factor to covert kilometers to meters
public static final double KM_TO_M = 1000.0; public static final double KM_TO_M = 1000.0;
// multiplication factor to convert meters to kilometers // multiplication factor to convert meters to kilometers
public static final double M_TO_KM = 1 / KM_TO_M; public static final double M_TO_KM = 1 / KM_TO_M;
// multiplication factor to convert meters to miles // multiplication factor to convert meters to miles
public static final double M_TO_MI = M_TO_KM * KM_TO_MI; public static final double M_TO_MI = M_TO_KM * KM_TO_MI;
// multiplication factor to convert meters to feet // multiplication factor to convert meters to feet
public static final double M_TO_FT = M_TO_MI * MI_TO_FT; public static final double M_TO_FT = M_TO_MI * MI_TO_FT;
// Others
// multiplication factor to convert meters per second to kilometers per hour // multiplication factor to convert meters per second to kilometers per hour
public static final double MS_TO_KMH = M_TO_KM / (S_TO_MIN * MIN_TO_HR); public static final double MS_TO_KMH = M_TO_KM / (S_TO_MIN * MIN_TO_HR);
// Others
// multiplication factor to convert degrees to radians // multiplication factor to convert degrees to radians
public static final double DEG_TO_RAD = Math.PI / 180.0; public static final double DEG_TO_RAD = Math.PI / 180.0;
private UnitConversions() {
}
} }

Some files were not shown because too many files have changed in this diff Show More