mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-11-04 23:34:44 +01:00
Autoformatted all classes (non-testing).
This commit is contained in:
@@ -38,63 +38,63 @@ import java.util.List;
|
|||||||
* @author Youtao Liu
|
* @author Youtao Liu
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class BootReceiverTest {
|
public class BootReceiverTest {
|
||||||
private static final String SERVICE_NAME = "com.google.android.apps.mytracks.services.TrackRecordingService";
|
private static final String SERVICE_NAME = "com.google.android.apps.mytracks.services.TrackRecordingService";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the behavior when receive notification which is the phone boot.
|
* Tests the behavior when receive notification which is the phone boot.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnReceive_startService() {
|
public void testOnReceive_startService() {
|
||||||
// Make sure no TrackRecordingService
|
// Make sure no TrackRecordingService
|
||||||
Intent stopIntent = new Intent(InstrumentationRegistry.getInstrumentation().getContext(), TrackRecordingService.class);
|
Intent stopIntent = new Intent(InstrumentationRegistry.getInstrumentation().getContext(), TrackRecordingService.class);
|
||||||
InstrumentationRegistry.getInstrumentation().getContext().stopService(stopIntent);
|
InstrumentationRegistry.getInstrumentation().getContext().stopService(stopIntent);
|
||||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||||
|
|
||||||
BootReceiver bootReceiver = new BootReceiver();
|
BootReceiver bootReceiver = new BootReceiver();
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_BOOT_COMPLETED);
|
intent.setAction(Intent.ACTION_BOOT_COMPLETED);
|
||||||
bootReceiver.onReceive(InstrumentationRegistry.getInstrumentation().getContext(), intent);
|
bootReceiver.onReceive(InstrumentationRegistry.getInstrumentation().getContext(), intent);
|
||||||
// Check if the service is started
|
// Check if the service is started
|
||||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the behavior when receive notification which is not the phone boot.
|
* Tests the behavior when receive notification which is not the phone boot.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOnReceive_noStartService() {
|
public void testOnReceive_noStartService() {
|
||||||
// Make sure no TrackRecordingService
|
// Make sure no TrackRecordingService
|
||||||
Intent stopIntent = new Intent(InstrumentationRegistry.getInstrumentation().getContext(), TrackRecordingService.class);
|
Intent stopIntent = new Intent(InstrumentationRegistry.getInstrumentation().getContext(), TrackRecordingService.class);
|
||||||
InstrumentationRegistry.getInstrumentation().getContext().stopService(stopIntent);
|
InstrumentationRegistry.getInstrumentation().getContext().stopService(stopIntent);
|
||||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||||
|
|
||||||
BootReceiver bootReceiver = new BootReceiver();
|
BootReceiver bootReceiver = new BootReceiver();
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_BUG_REPORT);
|
intent.setAction(Intent.ACTION_BUG_REPORT);
|
||||||
bootReceiver.onReceive(InstrumentationRegistry.getInstrumentation().getContext(), intent);
|
bootReceiver.onReceive(InstrumentationRegistry.getInstrumentation().getContext(), intent);
|
||||||
|
|
||||||
// Check if the service is not started
|
// Check if the service is not started
|
||||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a service is started in a context.
|
* Checks if a service is started in a context.
|
||||||
*
|
*
|
||||||
* @param context the context for checking a service
|
* @param context the context for checking a service
|
||||||
* @param serviceName the service name to find if existed
|
* @param serviceName the service name to find if existed
|
||||||
*/
|
*/
|
||||||
private boolean isServiceExisted(Context context, String serviceName) {
|
private boolean isServiceExisted(Context context, String serviceName) {
|
||||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(Integer.MAX_VALUE);
|
List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(Integer.MAX_VALUE);
|
||||||
|
|
||||||
for (int i = 0; i < serviceList.size(); i++) {
|
for (int i = 0; i < serviceList.size(); i++) {
|
||||||
RunningServiceInfo serviceInfo = serviceList.get(i);
|
RunningServiceInfo serviceInfo = serviceList.get(i);
|
||||||
ComponentName componentName = serviceInfo.service;
|
ComponentName componentName = serviceInfo.service;
|
||||||
if (componentName.getClassName().equals(serviceName)) {
|
if (componentName.getClassName().equals(serviceName)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -31,57 +31,57 @@ import org.junit.runner.RunWith;
|
|||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class ChartValueSeriesTest {
|
public class ChartValueSeriesTest {
|
||||||
private ChartValueSeries series;
|
private ChartValueSeries series;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
protected void setUp() {
|
protected void setUp() {
|
||||||
series = new ChartValueSeries(
|
series = new ChartValueSeries(
|
||||||
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,
|
||||||
R.color.chart_elevation_border);
|
R.color.chart_elevation_border);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInitialConditions() {
|
public void testInitialConditions() {
|
||||||
Assert.assertEquals(1, series.getInterval());
|
Assert.assertEquals(1, series.getInterval());
|
||||||
Assert.assertEquals(0, series.getMinMarkerValue());
|
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||||
Assert.assertEquals(5, series.getMaxMarkerValue());
|
Assert.assertEquals(5, series.getMaxMarkerValue());
|
||||||
Assert.assertTrue(series.isEnabled());
|
Assert.assertTrue(series.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEnabled() {
|
public void testEnabled() {
|
||||||
series.setEnabled(false);
|
series.setEnabled(false);
|
||||||
Assert.assertFalse(series.isEnabled());
|
Assert.assertFalse(series.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSmallUpdates() {
|
public void testSmallUpdates() {
|
||||||
series.update(0);
|
series.update(0);
|
||||||
series.update(10);
|
series.update(10);
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
Assert.assertEquals(100, series.getInterval());
|
Assert.assertEquals(100, series.getInterval());
|
||||||
Assert.assertEquals(0, series.getMinMarkerValue());
|
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||||
Assert.assertEquals(500, series.getMaxMarkerValue());
|
Assert.assertEquals(500, series.getMaxMarkerValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBigUpdates() {
|
public void testBigUpdates() {
|
||||||
series.update(0);
|
series.update(0);
|
||||||
series.update(901);
|
series.update(901);
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
Assert.assertEquals(1000, series.getInterval());
|
Assert.assertEquals(1000, series.getInterval());
|
||||||
Assert.assertEquals(0, series.getMinMarkerValue());
|
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||||
Assert.assertEquals(5000, series.getMaxMarkerValue());
|
Assert.assertEquals(5000, series.getMaxMarkerValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNotZeroBasedUpdates() {
|
public void testNotZeroBasedUpdates() {
|
||||||
series.update(220);
|
series.update(220);
|
||||||
series.update(250);
|
series.update(250);
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
Assert.assertEquals(100, series.getInterval());
|
Assert.assertEquals(100, series.getInterval());
|
||||||
Assert.assertEquals(200, series.getMinMarkerValue());
|
Assert.assertEquals(200, series.getMinMarkerValue());
|
||||||
Assert.assertEquals(700, series.getMaxMarkerValue());
|
Assert.assertEquals(700, series.getMaxMarkerValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,61 +30,61 @@ import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
|
|||||||
*/
|
*/
|
||||||
public class TrackStubUtils {
|
public class TrackStubUtils {
|
||||||
|
|
||||||
static final String LOCATION_PROVIDER = "gps";
|
public static final double INITIAL_ALTITUDE = 22;
|
||||||
static final double INITIAL_LATITUDE = 22;
|
public static final long INITIAL_TIME = 1000L;
|
||||||
static final double INITIAL_LONGITUDE = 22;
|
static final String LOCATION_PROVIDER = "gps";
|
||||||
public static final double INITIAL_ALTITUDE = 22;
|
static final double INITIAL_LATITUDE = 22;
|
||||||
static final float INITIAL_ACCURACY = 5;
|
static final double INITIAL_LONGITUDE = 22;
|
||||||
static final float INITIAL_SPEED = 10;
|
static final float INITIAL_ACCURACY = 5;
|
||||||
public static final long INITIAL_TIME = 1000L;
|
static final float INITIAL_SPEED = 10;
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a a {@link Track} stub with specified number of locations.
|
* Gets a a {@link Track} stub with specified number of locations.
|
||||||
*
|
*
|
||||||
* @param numberOfLocations the number of locations for the track
|
* @param numberOfLocations the number of locations for the track
|
||||||
* @return a track stub.
|
* @return a track stub.
|
||||||
*/
|
*/
|
||||||
public static Track createTrack(int numberOfLocations) {
|
public static Track createTrack(int numberOfLocations) {
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
for (int i = 0; i < numberOfLocations; i++) {
|
for (int i = 0; i < numberOfLocations; i++) {
|
||||||
track.addLocation(createSensorDataSetLocation(INITIAL_LATITUDE + i * DIFFERENCE, INITIAL_LONGITUDE
|
track.addLocation(createSensorDataSetLocation(INITIAL_LATITUDE + i * DIFFERENCE, INITIAL_LONGITUDE
|
||||||
+ i * DIFFERENCE, INITIAL_ALTITUDE + i * DIFFERENCE));
|
+ i * DIFFERENCE, INITIAL_ALTITUDE + i * DIFFERENCE));
|
||||||
|
}
|
||||||
|
|
||||||
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
return track;
|
/**
|
||||||
}
|
* Create a MyTracks location with default values.
|
||||||
|
*
|
||||||
|
* @return a track stub.
|
||||||
|
*/
|
||||||
|
public static SensorDataSetLocation createSensorDataSetLocation() {
|
||||||
|
return createSensorDataSetLocation(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a MyTracks location with default values.
|
* Creates a {@link SensorDataSetLocation} stub with specified values.
|
||||||
*
|
*
|
||||||
* @return a track stub.
|
* @return a SensorDataSetLocation stub.
|
||||||
*/
|
*/
|
||||||
public static SensorDataSetLocation createSensorDataSetLocation() {
|
public static SensorDataSetLocation createSensorDataSetLocation(double latitude, double longitude,
|
||||||
return createSensorDataSetLocation(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
|
double altitude) {
|
||||||
}
|
// Initial Location
|
||||||
|
Location loc = new Location(LOCATION_PROVIDER);
|
||||||
|
loc.setLatitude(latitude);
|
||||||
|
loc.setLongitude(longitude);
|
||||||
|
loc.setAltitude(altitude);
|
||||||
|
loc.setAccuracy(INITIAL_ACCURACY);
|
||||||
|
loc.setSpeed(INITIAL_SPEED);
|
||||||
|
loc.setTime(INITIAL_TIME);
|
||||||
|
loc.setBearing(INITIAL_BEARING);
|
||||||
|
SensorDataSet sd = new SensorDataSet(100, 100);
|
||||||
|
|
||||||
/**
|
return new SensorDataSetLocation(loc, sd);
|
||||||
* Creates a {@link SensorDataSetLocation} stub with specified values.
|
}
|
||||||
*
|
|
||||||
* @return a SensorDataSetLocation stub.
|
|
||||||
*/
|
|
||||||
public static SensorDataSetLocation createSensorDataSetLocation(double latitude, double longitude,
|
|
||||||
double altitude) {
|
|
||||||
// Initial Location
|
|
||||||
Location loc = new Location(LOCATION_PROVIDER);
|
|
||||||
loc.setLatitude(latitude);
|
|
||||||
loc.setLongitude(longitude);
|
|
||||||
loc.setAltitude(altitude);
|
|
||||||
loc.setAccuracy(INITIAL_ACCURACY);
|
|
||||||
loc.setSpeed(INITIAL_SPEED);
|
|
||||||
loc.setTime(INITIAL_TIME);
|
|
||||||
loc.setBearing(INITIAL_BEARING);
|
|
||||||
SensorDataSet sd = new SensorDataSet(100, 100);
|
|
||||||
|
|
||||||
return new SensorDataSetLocation(loc, sd);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+97
-97
@@ -36,108 +36,108 @@ import org.junit.runner.RunWith;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class CustomContentProviderTest {
|
public class CustomContentProviderTest {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "test.db";
|
private static final String DATABASE_NAME = "test.db";
|
||||||
|
|
||||||
private SQLiteDatabase db;
|
private SQLiteDatabase db;
|
||||||
private CustomContentProvider customContentProvider;
|
private CustomContentProvider customContentProvider;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
protected void setUp() {
|
protected void setUp() {
|
||||||
InstrumentationRegistry.getInstrumentation().getContext().deleteDatabase(DATABASE_NAME);
|
InstrumentationRegistry.getInstrumentation().getContext().deleteDatabase(DATABASE_NAME);
|
||||||
db = (new DatabaseHelper(InstrumentationRegistry.getInstrumentation().getContext(), DATABASE_NAME)).getWritableDatabase();
|
db = (new DatabaseHelper(InstrumentationRegistry.getInstrumentation().getContext(), DATABASE_NAME)).getWritableDatabase();
|
||||||
customContentProvider = new CustomContentProvider();
|
customContentProvider = new CustomContentProvider();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link CustomContentProvider.DatabaseHelper#onCreate(SQLiteDatabase)}.
|
|
||||||
*/
|
|
||||||
public void testDatabaseHelper_OnCreate() {
|
|
||||||
Assert.assertTrue(hasTable(TracksColumns.TABLE_NAME));
|
|
||||||
Assert.assertTrue(hasTable(TrackPointsColumns.TABLE_NAME));
|
|
||||||
Assert.assertTrue(hasTable(WaypointsColumns.TABLE_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link CustomContentProvider#onCreate(android.content.Context)}.
|
|
||||||
*/
|
|
||||||
public void testOnCreate() {
|
|
||||||
Assert.assertTrue(customContentProvider.onCreate(InstrumentationRegistry.getInstrumentation().getContext()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link CustomContentProvider#getType(Uri)}.
|
|
||||||
*/
|
|
||||||
public void testGetType() {
|
|
||||||
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
|
||||||
Assert.assertEquals(
|
|
||||||
TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI));
|
|
||||||
Assert.assertEquals(
|
|
||||||
WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a table, containing one column.
|
|
||||||
*
|
|
||||||
* @param table the table name
|
|
||||||
*/
|
|
||||||
private void createTable(String table) {
|
|
||||||
db.execSQL("CREATE TABLE " + table + " (test INTEGER)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Drops a table in database.
|
|
||||||
*
|
|
||||||
* @param table the table name
|
|
||||||
*/
|
|
||||||
private void dropTable(String table) {
|
|
||||||
db.execSQL("Drop TABLE " + table);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the table exists.
|
|
||||||
*
|
|
||||||
* @param table the table name
|
|
||||||
*/
|
|
||||||
private boolean hasTable(String table) {
|
|
||||||
try {
|
|
||||||
db.rawQuery("select count(*) from " + table, null);
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the column in the table exists.
|
* Tests {@link CustomContentProvider.DatabaseHelper#onCreate(SQLiteDatabase)}.
|
||||||
*
|
*/
|
||||||
* @param table the table name
|
public void testDatabaseHelper_OnCreate() {
|
||||||
* @param column the column name
|
Assert.assertTrue(hasTable(TracksColumns.TABLE_NAME));
|
||||||
*/
|
Assert.assertTrue(hasTable(TrackPointsColumns.TABLE_NAME));
|
||||||
private boolean hasColumn(String table, String column) {
|
Assert.assertTrue(hasTable(WaypointsColumns.TABLE_NAME));
|
||||||
try {
|
|
||||||
db.execSQL("SElECT count(*) from " + table + " order by " + column);
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (e.getMessage().indexOf("no such column") > -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up upgrade.
|
* Tests {@link CustomContentProvider#onCreate(android.content.Context)}.
|
||||||
*
|
*/
|
||||||
* @param oldVersion thd old database version
|
public void testOnCreate() {
|
||||||
*/
|
Assert.assertTrue(customContentProvider.onCreate(InstrumentationRegistry.getInstrumentation().getContext()));
|
||||||
private void setupUpgrade(int oldVersion) {
|
}
|
||||||
dropTable(TracksColumns.TABLE_NAME);
|
|
||||||
dropTable(TrackPointsColumns.TABLE_NAME);
|
|
||||||
dropTable(WaypointsColumns.TABLE_NAME);
|
|
||||||
createTable(TracksColumns.TABLE_NAME);
|
|
||||||
createTable(TrackPointsColumns.TABLE_NAME);
|
|
||||||
createTable(WaypointsColumns.TABLE_NAME);
|
|
||||||
|
|
||||||
DatabaseHelper databaseHelper = new DatabaseHelper(InstrumentationRegistry.getInstrumentation().getContext());
|
/**
|
||||||
databaseHelper.onUpgrade(db, oldVersion, CustomContentProvider.DATABASE_VERSION);
|
* Tests {@link CustomContentProvider#getType(Uri)}.
|
||||||
}
|
*/
|
||||||
|
public void testGetType() {
|
||||||
|
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
||||||
|
Assert.assertEquals(
|
||||||
|
TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI));
|
||||||
|
Assert.assertEquals(
|
||||||
|
WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a table, containing one column.
|
||||||
|
*
|
||||||
|
* @param table the table name
|
||||||
|
*/
|
||||||
|
private void createTable(String table) {
|
||||||
|
db.execSQL("CREATE TABLE " + table + " (test INTEGER)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drops a table in database.
|
||||||
|
*
|
||||||
|
* @param table the table name
|
||||||
|
*/
|
||||||
|
private void dropTable(String table) {
|
||||||
|
db.execSQL("Drop TABLE " + table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the table exists.
|
||||||
|
*
|
||||||
|
* @param table the table name
|
||||||
|
*/
|
||||||
|
private boolean hasTable(String table) {
|
||||||
|
try {
|
||||||
|
db.rawQuery("select count(*) from " + table, null);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the column in the table exists.
|
||||||
|
*
|
||||||
|
* @param table the table name
|
||||||
|
* @param column the column name
|
||||||
|
*/
|
||||||
|
private boolean hasColumn(String table, String column) {
|
||||||
|
try {
|
||||||
|
db.execSQL("SElECT count(*) from " + table + " order by " + column);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e.getMessage().indexOf("no such column") > -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up upgrade.
|
||||||
|
*
|
||||||
|
* @param oldVersion thd old database version
|
||||||
|
*/
|
||||||
|
private void setupUpgrade(int oldVersion) {
|
||||||
|
dropTable(TracksColumns.TABLE_NAME);
|
||||||
|
dropTable(TrackPointsColumns.TABLE_NAME);
|
||||||
|
dropTable(WaypointsColumns.TABLE_NAME);
|
||||||
|
createTable(TracksColumns.TABLE_NAME);
|
||||||
|
createTable(TrackPointsColumns.TABLE_NAME);
|
||||||
|
createTable(WaypointsColumns.TABLE_NAME);
|
||||||
|
|
||||||
|
DatabaseHelper databaseHelper = new DatabaseHelper(InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
|
databaseHelper.onUpgrade(db, oldVersion, CustomContentProvider.DATABASE_VERSION);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+846
-846
File diff suppressed because it is too large
Load Diff
+155
-155
@@ -35,170 +35,170 @@ import org.junit.runner.RunWith;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class DescriptionGeneratorImplTest {
|
public class DescriptionGeneratorImplTest {
|
||||||
|
|
||||||
private static final long START_TIME = 1288721514000L;
|
private static final long START_TIME = 1288721514000L;
|
||||||
private DescriptionGeneratorImpl descriptionGenerator;
|
private DescriptionGeneratorImpl descriptionGenerator;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
descriptionGenerator = new DescriptionGeneratorImpl(InstrumentationRegistry.getInstrumentation().getContext());
|
descriptionGenerator = new DescriptionGeneratorImpl(InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#generateTrackDescription(Track,
|
* Tests {@link DescriptionGeneratorImpl#generateTrackDescription(Track,
|
||||||
* java.util.Vector, java.util.Vector, boolean)}.
|
* java.util.Vector, java.util.Vector, boolean)}.
|
||||||
*/
|
*/
|
||||||
public void testGenerateTrackDescription() {
|
public void testGenerateTrackDescription() {
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
TripStatistics stats = new TripStatistics();
|
TripStatistics stats = new TripStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(20000);
|
||||||
stats.setTotalTime(600000);
|
stats.setTotalTime(600000);
|
||||||
stats.setMovingTime(300000);
|
stats.setMovingTime(300000);
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(100);
|
||||||
stats.setMaxElevation(550);
|
stats.setMaxElevation(550);
|
||||||
stats.setMinElevation(-500);
|
stats.setMinElevation(-500);
|
||||||
stats.setTotalElevationGain(6000);
|
stats.setTotalElevationGain(6000);
|
||||||
stats.setMaxGrade(0.42);
|
stats.setMaxGrade(0.42);
|
||||||
stats.setMinGrade(0.11);
|
stats.setMinGrade(0.11);
|
||||||
stats.setStartTime(START_TIME);
|
stats.setStartTime(START_TIME);
|
||||||
track.setTripStatistics(stats);
|
track.setTripStatistics(stats);
|
||||||
track.setCategory("hiking");
|
track.setCategory("hiking");
|
||||||
String expected = "Created by"
|
String expected = "Created by"
|
||||||
+ " <a href='TODO'>TODO</a> on Android<p>"
|
+ " <a href='TODO'>TODO</a> on Android<p>"
|
||||||
+ "Name: -<br>"
|
+ "Name: -<br>"
|
||||||
+ "Activity type: hiking<br>"
|
+ "Activity type: hiking<br>"
|
||||||
+ "Description: -<br>"
|
+ "Description: -<br>"
|
||||||
+ "Total distance: 20.00 km (12.4 mi)<br>"
|
+ "Total distance: 20.00 km (12.4 mi)<br>"
|
||||||
+ "Total time: 10:00<br>"
|
+ "Total time: 10:00<br>"
|
||||||
+ "Moving time: 05:00<br>"
|
+ "Moving time: 05:00<br>"
|
||||||
+ "Average speed: 120.00 km/h (74.6 mi/h)<br>"
|
+ "Average speed: 120.00 km/h (74.6 mi/h)<br>"
|
||||||
+ "Average moving speed: 240.00 km/h (149.1 mi/h)<br>"
|
+ "Average moving speed: 240.00 km/h (149.1 mi/h)<br>"
|
||||||
+ "Max speed: 360.00 km/h (223.7 mi/h)<br>"
|
+ "Max speed: 360.00 km/h (223.7 mi/h)<br>"
|
||||||
+ "Average pace: 0:30 min/km (0:48 min/mi)<br>"
|
+ "Average pace: 0:30 min/km (0:48 min/mi)<br>"
|
||||||
+ "Average moving pace: 0:15 min/km (0:24 min/mi)<br>"
|
+ "Average moving pace: 0:15 min/km (0:24 min/mi)<br>"
|
||||||
+ "Fastest pace: 0:10 min/km (0:16 min/mi)<br>"
|
+ "Fastest pace: 0:10 min/km (0:16 min/mi)<br>"
|
||||||
+ "Max elevation: 550 m (1804 ft)<br>"
|
+ "Max elevation: 550 m (1804 ft)<br>"
|
||||||
+ "Min elevation: -500 m (-1640 ft)<br>"
|
+ "Min elevation: -500 m (-1640 ft)<br>"
|
||||||
+ "Elevation gain: 6000 m (19685 ft)<br>"
|
+ "Elevation gain: 6000 m (19685 ft)<br>"
|
||||||
+ "Max grade: 42 %<br>"
|
+ "Max grade: 42 %<br>"
|
||||||
+ "Min grade: 11 %<br>"
|
+ "Min grade: 11 %<br>"
|
||||||
+ "Recorded: " + StringUtils.formatDateTime(InstrumentationRegistry.getInstrumentation().getContext(), START_TIME) + "<br>";
|
+ "Recorded: " + StringUtils.formatDateTime(InstrumentationRegistry.getInstrumentation().getContext(), START_TIME) + "<br>";
|
||||||
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, null, null, true));
|
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, null, null, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#generateWaypointDescription(TripStatistics)}.
|
* Tests {@link DescriptionGeneratorImpl#generateWaypointDescription(TripStatistics)}.
|
||||||
*/
|
*/
|
||||||
public void testGenerateWaypointDescription() {
|
public void testGenerateWaypointDescription() {
|
||||||
Waypoint waypoint = new Waypoint();
|
Waypoint waypoint = new Waypoint();
|
||||||
TripStatistics stats = new TripStatistics();
|
TripStatistics stats = new TripStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(20000);
|
||||||
stats.setTotalTime(600000);
|
stats.setTotalTime(600000);
|
||||||
stats.setMovingTime(300000);
|
stats.setMovingTime(300000);
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(100);
|
||||||
stats.setMaxElevation(550);
|
stats.setMaxElevation(550);
|
||||||
stats.setMinElevation(-500);
|
stats.setMinElevation(-500);
|
||||||
stats.setTotalElevationGain(6000);
|
stats.setTotalElevationGain(6000);
|
||||||
stats.setMaxGrade(0.42);
|
stats.setMaxGrade(0.42);
|
||||||
stats.setMinGrade(0.11);
|
stats.setMinGrade(0.11);
|
||||||
stats.setStartTime(START_TIME);
|
stats.setStartTime(START_TIME);
|
||||||
waypoint.setTripStatistics(stats);
|
waypoint.setTripStatistics(stats);
|
||||||
String expected = "Total distance: 20.00 km (12.4 mi)\n"
|
String expected = "Total distance: 20.00 km (12.4 mi)\n"
|
||||||
+ "Total time: 10:00\n"
|
+ "Total time: 10:00\n"
|
||||||
+ "Moving time: 05:00\n"
|
+ "Moving time: 05:00\n"
|
||||||
+ "Average speed: 120.00 km/h (74.6 mi/h)\n"
|
+ "Average speed: 120.00 km/h (74.6 mi/h)\n"
|
||||||
+ "Average moving speed: 240.00 km/h (149.1 mi/h)\n"
|
+ "Average moving speed: 240.00 km/h (149.1 mi/h)\n"
|
||||||
+ "Max speed: 360.00 km/h (223.7 mi/h)\n"
|
+ "Max speed: 360.00 km/h (223.7 mi/h)\n"
|
||||||
+ "Average pace: 0:30 min/km (0:48 min/mi)\n"
|
+ "Average pace: 0:30 min/km (0:48 min/mi)\n"
|
||||||
+ "Average moving pace: 0:15 min/km (0:24 min/mi)\n"
|
+ "Average moving pace: 0:15 min/km (0:24 min/mi)\n"
|
||||||
+ "Fastest pace: 0:10 min/km (0:16 min/mi)\n"
|
+ "Fastest pace: 0:10 min/km (0:16 min/mi)\n"
|
||||||
+ "Max elevation: 550 m (1804 ft)\n"
|
+ "Max elevation: 550 m (1804 ft)\n"
|
||||||
+ "Min elevation: -500 m (-1640 ft)\n"
|
+ "Min elevation: -500 m (-1640 ft)\n"
|
||||||
+ "Elevation gain: 6000 m (19685 ft)\n"
|
+ "Elevation gain: 6000 m (19685 ft)\n"
|
||||||
+ "Max grade: 42 %\n"
|
+ "Max grade: 42 %\n"
|
||||||
+ "Min grade: 11 %\n"
|
+ "Min grade: 11 %\n"
|
||||||
+ "Recorded: " + StringUtils.formatDateTime(InstrumentationRegistry.getInstrumentation().getContext(), START_TIME) + "\n";
|
+ "Recorded: " + StringUtils.formatDateTime(InstrumentationRegistry.getInstrumentation().getContext(), START_TIME) + "\n";
|
||||||
Assert.assertEquals(
|
Assert.assertEquals(
|
||||||
expected, descriptionGenerator.generateWaypointDescription(waypoint.getTripStatistics()));
|
expected, descriptionGenerator.generateWaypointDescription(waypoint.getTripStatistics()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writeDistance(double, StringBuilder,
|
* Tests {@link DescriptionGeneratorImpl#writeDistance(double, StringBuilder,
|
||||||
* int, String)}.
|
* int, String)}.
|
||||||
*/
|
*/
|
||||||
public void testWriteDistance() {
|
public void testWriteDistance() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>");
|
descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>");
|
||||||
Assert.assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
|
Assert.assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writeTime(long, StringBuilder, int,
|
* Tests {@link DescriptionGeneratorImpl#writeTime(long, StringBuilder, int,
|
||||||
* String)}.
|
* String)}.
|
||||||
*/
|
*/
|
||||||
public void testWriteTime() {
|
public void testWriteTime() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
|
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
|
||||||
Assert.assertEquals("Total time: 00:01<br>", builder.toString());
|
Assert.assertEquals("Total time: 00:01<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writeSpeed(double, StringBuilder,
|
* Tests {@link DescriptionGeneratorImpl#writeSpeed(double, StringBuilder,
|
||||||
* int, String)}.
|
* int, String)}.
|
||||||
*/
|
*/
|
||||||
public void testWriteSpeed() {
|
public void testWriteSpeed() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n");
|
descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n");
|
||||||
Assert.assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
|
Assert.assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writeElevation(double, StringBuilder,
|
* Tests {@link DescriptionGeneratorImpl#writeElevation(double, StringBuilder,
|
||||||
* int, String)}.
|
* int, String)}.
|
||||||
*/
|
*/
|
||||||
public void testWriteElevation() {
|
public void testWriteElevation() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>");
|
descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>");
|
||||||
Assert.assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
|
Assert.assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writePace(double, StringBuilder, int,
|
* Tests {@link DescriptionGeneratorImpl#writePace(double, StringBuilder, int,
|
||||||
* String)}.
|
* String)}.
|
||||||
*/
|
*/
|
||||||
public void testWritePace() {
|
public void testWritePace() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace_in_minute, "\n");
|
descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace_in_minute, "\n");
|
||||||
Assert.assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
|
Assert.assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
||||||
* int, String)}.
|
* int, String)}.
|
||||||
*/
|
*/
|
||||||
public void testWriteGrade() {
|
public void testWriteGrade() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeGrade(.042, builder, R.string.description_max_grade, "<br>");
|
descriptionGenerator.writeGrade(.042, builder, R.string.description_max_grade, "<br>");
|
||||||
Assert.assertEquals("Max grade: 4 %<br>", builder.toString());
|
Assert.assertEquals("Max grade: 4 %<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
||||||
* int, String)} with a NaN.
|
* int, String)} with a NaN.
|
||||||
*/
|
*/
|
||||||
public void testWriteGrade_nan() {
|
public void testWriteGrade_nan() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeGrade(Double.NaN, builder, R.string.description_max_grade, "<br>");
|
descriptionGenerator.writeGrade(Double.NaN, builder, R.string.description_max_grade, "<br>");
|
||||||
Assert.assertEquals("Max grade: 0 %<br>", builder.toString());
|
Assert.assertEquals("Max grade: 0 %<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
||||||
* int, String)} with an infinite number.
|
* int, String)} with an infinite number.
|
||||||
*/
|
*/
|
||||||
public void testWriteGrade_infinite() {
|
public void testWriteGrade_infinite() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeGrade(
|
descriptionGenerator.writeGrade(
|
||||||
Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>");
|
Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>");
|
||||||
Assert.assertEquals("Max grade: 0 %<br>", builder.toString());
|
Assert.assertEquals("Max grade: 0 %<br>", builder.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+265
-265
@@ -48,281 +48,281 @@ import java.util.List;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class SearchEngineTest {
|
public class SearchEngineTest {
|
||||||
|
|
||||||
private static final Location HERE = new Location("gps");
|
private static final Location HERE = new Location("gps");
|
||||||
private static final long NOW = 1234567890000L; // After OLDEST_ALLOWED_TIMESTAMP
|
private static final long NOW = 1234567890000L; // After OLDEST_ALLOWED_TIMESTAMP
|
||||||
private ContentProviderUtils providerUtils;
|
private ContentProviderUtils providerUtils;
|
||||||
private SearchEngine engine;
|
private SearchEngine engine;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
protected void setUp() {
|
protected void setUp() {
|
||||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||||
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
||||||
getContext(), getContext(), "test.");
|
getContext(), getContext(), "test.");
|
||||||
MockContext context = new MockContext(mockContentResolver, targetContext);
|
MockContext context = new MockContext(mockContentResolver, targetContext);
|
||||||
CustomContentProvider provider = new CustomContentProvider();
|
CustomContentProvider provider = new CustomContentProvider();
|
||||||
provider.attachInfo(context, null);
|
provider.attachInfo(context, null);
|
||||||
mockContentResolver.addProvider(ContentProviderUtils.AUTHORITY, provider);
|
mockContentResolver.addProvider(ContentProviderUtils.AUTHORITY, provider);
|
||||||
setContext(context);
|
setContext(context);
|
||||||
|
|
||||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||||
engine = new SearchEngine(providerUtils);
|
engine = new SearchEngine(providerUtils);
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
protected void tearDown() throws Exception {
|
|
||||||
providerUtils.deleteAllTracks(getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
private long insertTrack(String title, String description, String category, double distance, long hoursAgo) {
|
|
||||||
Track track = new Track();
|
|
||||||
track.setName(title);
|
|
||||||
track.setDescription(description);
|
|
||||||
track.setCategory(category);
|
|
||||||
|
|
||||||
TripStatistics stats = track.getTripStatistics();
|
|
||||||
if (hoursAgo > 0) {
|
|
||||||
// Started twice hoursAgo, so the average time is hoursAgo.
|
|
||||||
stats.setStartTime(NOW - hoursAgo * 1000L * 60L * 60L * 2);
|
|
||||||
stats.setStopTime(NOW);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int latitude = (int) ((HERE.getLatitude() + distance) * 1E6);
|
@After
|
||||||
int longitude = (int) ((HERE.getLongitude() + distance) * 1E6);
|
protected void tearDown() throws Exception {
|
||||||
stats.setBounds(latitude, longitude, latitude, longitude);
|
providerUtils.deleteAllTracks(getContext());
|
||||||
|
|
||||||
Uri uri = providerUtils.insertTrack(track);
|
|
||||||
return ContentUris.parseId(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long insertTrack(String title, String description, String category) {
|
|
||||||
return insertTrack(title, description, category, 0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long insertTrack(String title, double distance) {
|
|
||||||
return insertTrack(title, "", "", distance, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long insertTrack(String title, long hoursAgo) {
|
|
||||||
return insertTrack(title, "", "", 0.0, hoursAgo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long insertWaypoint(String title, String description, String category, double distance, long hoursAgo, long trackId) {
|
|
||||||
Waypoint waypoint = new Waypoint();
|
|
||||||
waypoint.setName(title);
|
|
||||||
waypoint.setDescription(description);
|
|
||||||
waypoint.setCategory(category);
|
|
||||||
waypoint.setTrackId(trackId);
|
|
||||||
|
|
||||||
Location location = new Location(HERE);
|
|
||||||
location.setLatitude(location.getLatitude() + distance);
|
|
||||||
location.setLongitude(location.getLongitude() + distance);
|
|
||||||
if (hoursAgo >= 0) {
|
|
||||||
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
|
|
||||||
}
|
}
|
||||||
waypoint.setLocation(location);
|
|
||||||
|
|
||||||
Uri uri = providerUtils.insertWaypoint(waypoint);
|
private long insertTrack(String title, String description, String category, double distance, long hoursAgo) {
|
||||||
return ContentUris.parseId(uri);
|
Track track = new Track();
|
||||||
}
|
track.setName(title);
|
||||||
|
track.setDescription(description);
|
||||||
|
track.setCategory(category);
|
||||||
|
|
||||||
private long insertWaypoint(String title, String description, String category) {
|
TripStatistics stats = track.getTripStatistics();
|
||||||
return insertWaypoint(title, description, category, 0.0, -1, -1);
|
if (hoursAgo > 0) {
|
||||||
}
|
// Started twice hoursAgo, so the average time is hoursAgo.
|
||||||
|
stats.setStartTime(NOW - hoursAgo * 1000L * 60L * 60L * 2);
|
||||||
|
stats.setStopTime(NOW);
|
||||||
|
}
|
||||||
|
|
||||||
private long insertWaypoint(String title, double distance) {
|
int latitude = (int) ((HERE.getLatitude() + distance) * 1E6);
|
||||||
return insertWaypoint(title, "", "", distance, -1, -1);
|
int longitude = (int) ((HERE.getLongitude() + distance) * 1E6);
|
||||||
}
|
stats.setBounds(latitude, longitude, latitude, longitude);
|
||||||
|
|
||||||
private long insertWaypoint(String title, long hoursAgo) {
|
Uri uri = providerUtils.insertTrack(track);
|
||||||
return insertWaypoint(title, "", "", 0.0, hoursAgo, -1);
|
return ContentUris.parseId(uri);
|
||||||
}
|
|
||||||
|
|
||||||
private long insertWaypoint(String title, long hoursAgo, long trackId) {
|
|
||||||
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchText() {
|
|
||||||
// Insert 7 tracks (purposefully out of result order):
|
|
||||||
// - one which won't match
|
|
||||||
// - one which will match the description
|
|
||||||
// - one which will match the category
|
|
||||||
// - one which will match the title
|
|
||||||
// - one which will match in title and category
|
|
||||||
// - one which will match in title and description
|
|
||||||
// - one which will match in all fields
|
|
||||||
insertTrack("bb", "cc", "dd");
|
|
||||||
long descriptionMatchId = insertTrack("bb", "aa", "cc");
|
|
||||||
long categoryMatchId = insertTrack("bb", "cc", "aa");
|
|
||||||
long titleMatchId = insertTrack("aa", "bb", "cc");
|
|
||||||
long titleCategoryMatchId = insertTrack("aa", "bb", "ca");
|
|
||||||
long titleDescriptionMatchId = insertTrack("aa", "ba", "cc");
|
|
||||||
long allMatchId = insertTrack("aa", "ba", "ca");
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Title > Description > Category.
|
|
||||||
assertTrackResults(results,
|
|
||||||
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
|
|
||||||
categoryMatchId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchWaypointText() {
|
|
||||||
// Insert 7 waypoints (purposefully out of result order):
|
|
||||||
// - one which won't match
|
|
||||||
// - one which will match the description
|
|
||||||
// - one which will match the category
|
|
||||||
// - one which will match the title
|
|
||||||
// - one which will match in title and category
|
|
||||||
// - one which will match in title and description
|
|
||||||
// - one which will match in all fields
|
|
||||||
insertWaypoint("bb", "cc", "dd");
|
|
||||||
long descriptionMatchId = insertWaypoint("bb", "aa", "cc");
|
|
||||||
long categoryMatchId = insertWaypoint("bb", "cc", "aa");
|
|
||||||
long titleMatchId = insertWaypoint("aa", "bb", "cc");
|
|
||||||
long titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
|
|
||||||
long titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
|
|
||||||
long allMatchId = insertWaypoint("aa", "ba", "ca");
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Title > Description > Category.
|
|
||||||
assertWaypointResults(results,
|
|
||||||
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
|
|
||||||
categoryMatchId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchMixedText() {
|
|
||||||
// Insert 5 entries (purposefully out of result order):
|
|
||||||
// - one waypoint which will match by description
|
|
||||||
// - one waypoint which won't match
|
|
||||||
// - one waypoint which will match by title
|
|
||||||
// - one track which won't match
|
|
||||||
// - one track which will match by title
|
|
||||||
long descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
|
|
||||||
insertWaypoint("bb", "cc", "dd");
|
|
||||||
long titleWaypointId = insertWaypoint("aa", "bb", "cc");
|
|
||||||
insertTrack("bb", "cc", "dd");
|
|
||||||
long trackId = insertTrack("aa", "bb", "cc");
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Title > Description > Category.
|
|
||||||
assertEquals(results.toString(), 3, results.size());
|
|
||||||
assertTrackResult(trackId, results.get(0));
|
|
||||||
assertWaypointResult(titleWaypointId, results.get(1));
|
|
||||||
assertWaypointResult(descriptionWaypointId, results.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchTrackDistance() {
|
|
||||||
// All results match text, but they're at difference distances from the user.
|
|
||||||
long farFarAwayId = insertTrack("aa", 0.3);
|
|
||||||
long nearId = insertTrack("ab", 0.1);
|
|
||||||
long farId = insertTrack("ac", 0.2);
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Distance order.
|
|
||||||
assertTrackResults(results, nearId, farId, farFarAwayId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchWaypointDistance() {
|
|
||||||
// All results match text, but they're at difference distances from the user.
|
|
||||||
long farFarAwayId = insertWaypoint("aa", 0.3);
|
|
||||||
long nearId = insertWaypoint("ab", 0.1);
|
|
||||||
long farId = insertWaypoint("ac", 0.2);
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Distance order.
|
|
||||||
assertWaypointResults(results, nearId, farId, farFarAwayId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchTrackRecent() {
|
|
||||||
// All results match text, but they're were recorded at different times.
|
|
||||||
long oldestId = insertTrack("aa", 3);
|
|
||||||
long recentId = insertTrack("ab", 1);
|
|
||||||
long oldId = insertTrack("ac", 2);
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Reverse time order.
|
|
||||||
assertTrackResults(results, recentId, oldId, oldestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchWaypointRecent() {
|
|
||||||
// All results match text, but they're were recorded at different times.
|
|
||||||
long oldestId = insertWaypoint("aa", 2);
|
|
||||||
long recentId = insertWaypoint("ab", 0);
|
|
||||||
long oldId = insertWaypoint("ac", 1);
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Reverse time order.
|
|
||||||
assertWaypointResults(results, recentId, oldId, oldestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchCurrentTrack() {
|
|
||||||
// All results match text, but one of them is the current track.
|
|
||||||
long currentId = insertTrack("ab", 1);
|
|
||||||
long otherId = insertTrack("aa", 1);
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", null, currentId, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Current track should be demoted.
|
|
||||||
assertTrackResults(results, otherId, currentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSearchCurrentTrackWaypoint() {
|
|
||||||
// All results match text, but one of them is in the current track.
|
|
||||||
long otherId = insertWaypoint("aa", 1, 456);
|
|
||||||
long currentId = insertWaypoint("ab", 1, 123);
|
|
||||||
|
|
||||||
SearchQuery query = new SearchQuery("a", null, 123, NOW);
|
|
||||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
|
||||||
|
|
||||||
// Waypoint in current track should be promoted.
|
|
||||||
assertWaypointResults(results, currentId, otherId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertTrackResult(long trackId, ScoredResult result) {
|
|
||||||
assertNotNull("Not a track", result.track);
|
|
||||||
assertNull("Ambiguous result", result.waypoint);
|
|
||||||
assertEquals(trackId, result.track.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertTrackResults(List<ScoredResult> results, long... trackIds) {
|
|
||||||
String errMsg = "Expected IDs=" + Arrays.toString(trackIds) + "; results=" + results;
|
|
||||||
assertEquals(results.size(), trackIds.length);
|
|
||||||
for (int i = 0; i < results.size(); i++) {
|
|
||||||
ScoredResult result = results.get(i);
|
|
||||||
assertNotNull(errMsg, result.track);
|
|
||||||
assertNull(errMsg, result.waypoint);
|
|
||||||
assertEquals(errMsg, trackIds[i], result.track.getId());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void assertWaypointResult(long waypointId, ScoredResult result) {
|
private long insertTrack(String title, String description, String category) {
|
||||||
assertNotNull("Not a waypoint", result.waypoint);
|
return insertTrack(title, description, category, 0, -1);
|
||||||
assertNull("Ambiguous result", result.track);
|
}
|
||||||
assertEquals(waypointId, result.waypoint.getId());
|
|
||||||
}
|
private long insertTrack(String title, double distance) {
|
||||||
|
return insertTrack(title, "", "", distance, -1);
|
||||||
private void assertWaypointResults(List<ScoredResult> results, long... waypointIds) {
|
}
|
||||||
String errMsg = "Expected IDs=" + Arrays.toString(waypointIds) + "; results=" + results;
|
|
||||||
assertEquals(results.size(), waypointIds.length);
|
private long insertTrack(String title, long hoursAgo) {
|
||||||
for (int i = 0; i < results.size(); i++) {
|
return insertTrack(title, "", "", 0.0, hoursAgo);
|
||||||
ScoredResult result = results.get(i);
|
}
|
||||||
assertNotNull(errMsg, result.waypoint);
|
|
||||||
assertNull(errMsg, result.track);
|
private long insertWaypoint(String title, String description, String category, double distance, long hoursAgo, long trackId) {
|
||||||
assertEquals(errMsg, waypointIds[i], result.waypoint.getId());
|
Waypoint waypoint = new Waypoint();
|
||||||
|
waypoint.setName(title);
|
||||||
|
waypoint.setDescription(description);
|
||||||
|
waypoint.setCategory(category);
|
||||||
|
waypoint.setTrackId(trackId);
|
||||||
|
|
||||||
|
Location location = new Location(HERE);
|
||||||
|
location.setLatitude(location.getLatitude() + distance);
|
||||||
|
location.setLongitude(location.getLongitude() + distance);
|
||||||
|
if (hoursAgo >= 0) {
|
||||||
|
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
|
||||||
|
}
|
||||||
|
waypoint.setLocation(location);
|
||||||
|
|
||||||
|
Uri uri = providerUtils.insertWaypoint(waypoint);
|
||||||
|
return ContentUris.parseId(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long insertWaypoint(String title, String description, String category) {
|
||||||
|
return insertWaypoint(title, description, category, 0.0, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long insertWaypoint(String title, double distance) {
|
||||||
|
return insertWaypoint(title, "", "", distance, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long insertWaypoint(String title, long hoursAgo) {
|
||||||
|
return insertWaypoint(title, "", "", 0.0, hoursAgo, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long insertWaypoint(String title, long hoursAgo, long trackId) {
|
||||||
|
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchText() {
|
||||||
|
// Insert 7 tracks (purposefully out of result order):
|
||||||
|
// - one which won't match
|
||||||
|
// - one which will match the description
|
||||||
|
// - one which will match the category
|
||||||
|
// - one which will match the title
|
||||||
|
// - one which will match in title and category
|
||||||
|
// - one which will match in title and description
|
||||||
|
// - one which will match in all fields
|
||||||
|
insertTrack("bb", "cc", "dd");
|
||||||
|
long descriptionMatchId = insertTrack("bb", "aa", "cc");
|
||||||
|
long categoryMatchId = insertTrack("bb", "cc", "aa");
|
||||||
|
long titleMatchId = insertTrack("aa", "bb", "cc");
|
||||||
|
long titleCategoryMatchId = insertTrack("aa", "bb", "ca");
|
||||||
|
long titleDescriptionMatchId = insertTrack("aa", "ba", "cc");
|
||||||
|
long allMatchId = insertTrack("aa", "ba", "ca");
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Title > Description > Category.
|
||||||
|
assertTrackResults(results,
|
||||||
|
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
|
||||||
|
categoryMatchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchWaypointText() {
|
||||||
|
// Insert 7 waypoints (purposefully out of result order):
|
||||||
|
// - one which won't match
|
||||||
|
// - one which will match the description
|
||||||
|
// - one which will match the category
|
||||||
|
// - one which will match the title
|
||||||
|
// - one which will match in title and category
|
||||||
|
// - one which will match in title and description
|
||||||
|
// - one which will match in all fields
|
||||||
|
insertWaypoint("bb", "cc", "dd");
|
||||||
|
long descriptionMatchId = insertWaypoint("bb", "aa", "cc");
|
||||||
|
long categoryMatchId = insertWaypoint("bb", "cc", "aa");
|
||||||
|
long titleMatchId = insertWaypoint("aa", "bb", "cc");
|
||||||
|
long titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
|
||||||
|
long titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
|
||||||
|
long allMatchId = insertWaypoint("aa", "ba", "ca");
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Title > Description > Category.
|
||||||
|
assertWaypointResults(results,
|
||||||
|
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
|
||||||
|
categoryMatchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchMixedText() {
|
||||||
|
// Insert 5 entries (purposefully out of result order):
|
||||||
|
// - one waypoint which will match by description
|
||||||
|
// - one waypoint which won't match
|
||||||
|
// - one waypoint which will match by title
|
||||||
|
// - one track which won't match
|
||||||
|
// - one track which will match by title
|
||||||
|
long descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
|
||||||
|
insertWaypoint("bb", "cc", "dd");
|
||||||
|
long titleWaypointId = insertWaypoint("aa", "bb", "cc");
|
||||||
|
insertTrack("bb", "cc", "dd");
|
||||||
|
long trackId = insertTrack("aa", "bb", "cc");
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Title > Description > Category.
|
||||||
|
assertEquals(results.toString(), 3, results.size());
|
||||||
|
assertTrackResult(trackId, results.get(0));
|
||||||
|
assertWaypointResult(titleWaypointId, results.get(1));
|
||||||
|
assertWaypointResult(descriptionWaypointId, results.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchTrackDistance() {
|
||||||
|
// All results match text, but they're at difference distances from the user.
|
||||||
|
long farFarAwayId = insertTrack("aa", 0.3);
|
||||||
|
long nearId = insertTrack("ab", 0.1);
|
||||||
|
long farId = insertTrack("ac", 0.2);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Distance order.
|
||||||
|
assertTrackResults(results, nearId, farId, farFarAwayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchWaypointDistance() {
|
||||||
|
// All results match text, but they're at difference distances from the user.
|
||||||
|
long farFarAwayId = insertWaypoint("aa", 0.3);
|
||||||
|
long nearId = insertWaypoint("ab", 0.1);
|
||||||
|
long farId = insertWaypoint("ac", 0.2);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Distance order.
|
||||||
|
assertWaypointResults(results, nearId, farId, farFarAwayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchTrackRecent() {
|
||||||
|
// All results match text, but they're were recorded at different times.
|
||||||
|
long oldestId = insertTrack("aa", 3);
|
||||||
|
long recentId = insertTrack("ab", 1);
|
||||||
|
long oldId = insertTrack("ac", 2);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Reverse time order.
|
||||||
|
assertTrackResults(results, recentId, oldId, oldestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchWaypointRecent() {
|
||||||
|
// All results match text, but they're were recorded at different times.
|
||||||
|
long oldestId = insertWaypoint("aa", 2);
|
||||||
|
long recentId = insertWaypoint("ab", 0);
|
||||||
|
long oldId = insertWaypoint("ac", 1);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, -1, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Reverse time order.
|
||||||
|
assertWaypointResults(results, recentId, oldId, oldestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchCurrentTrack() {
|
||||||
|
// All results match text, but one of them is the current track.
|
||||||
|
long currentId = insertTrack("ab", 1);
|
||||||
|
long otherId = insertTrack("aa", 1);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, currentId, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Current track should be demoted.
|
||||||
|
assertTrackResults(results, otherId, currentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchCurrentTrackWaypoint() {
|
||||||
|
// All results match text, but one of them is in the current track.
|
||||||
|
long otherId = insertWaypoint("aa", 1, 456);
|
||||||
|
long currentId = insertWaypoint("ab", 1, 123);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, 123, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||||
|
|
||||||
|
// Waypoint in current track should be promoted.
|
||||||
|
assertWaypointResults(results, currentId, otherId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTrackResult(long trackId, ScoredResult result) {
|
||||||
|
assertNotNull("Not a track", result.track);
|
||||||
|
assertNull("Ambiguous result", result.waypoint);
|
||||||
|
assertEquals(trackId, result.track.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTrackResults(List<ScoredResult> results, long... trackIds) {
|
||||||
|
String errMsg = "Expected IDs=" + Arrays.toString(trackIds) + "; results=" + results;
|
||||||
|
assertEquals(results.size(), trackIds.length);
|
||||||
|
for (int i = 0; i < results.size(); i++) {
|
||||||
|
ScoredResult result = results.get(i);
|
||||||
|
assertNotNull(errMsg, result.track);
|
||||||
|
assertNull(errMsg, result.waypoint);
|
||||||
|
assertEquals(errMsg, trackIds[i], result.track.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertWaypointResult(long waypointId, ScoredResult result) {
|
||||||
|
assertNotNull("Not a waypoint", result.waypoint);
|
||||||
|
assertNull("Ambiguous result", result.track);
|
||||||
|
assertEquals(waypointId, result.waypoint.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertWaypointResults(List<ScoredResult> results, long... waypointIds) {
|
||||||
|
String errMsg = "Expected IDs=" + Arrays.toString(waypointIds) + "; results=" + results;
|
||||||
|
assertEquals(results.size(), waypointIds.length);
|
||||||
|
for (int i = 0; i < results.size(); i++) {
|
||||||
|
ScoredResult result = results.get(i);
|
||||||
|
assertNotNull(errMsg, result.waypoint);
|
||||||
|
assertNull(errMsg, result.track);
|
||||||
|
assertEquals(errMsg, waypointIds[i], result.waypoint.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+727
-728
File diff suppressed because it is too large
Load Diff
+26
-26
@@ -33,31 +33,31 @@ import org.junit.runner.RunWith;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class WaypointCreationRequestTest {
|
public class WaypointCreationRequestTest {
|
||||||
|
|
||||||
public void testTypeParceling() {
|
public void testTypeParceling() {
|
||||||
WaypointCreationRequest original = WaypointCreationRequest.DEFAULT_WAYPOINT;
|
WaypointCreationRequest original = WaypointCreationRequest.DEFAULT_WAYPOINT;
|
||||||
Parcel p = Parcel.obtain();
|
Parcel p = Parcel.obtain();
|
||||||
original.writeToParcel(p, 0);
|
original.writeToParcel(p, 0);
|
||||||
p.setDataPosition(0);
|
p.setDataPosition(0);
|
||||||
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
|
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
|
||||||
Assert.assertEquals(original.getType(), copy.getType());
|
Assert.assertEquals(original.getType(), copy.getType());
|
||||||
Assert.assertFalse(copy.isTrackStatistics());
|
Assert.assertFalse(copy.isTrackStatistics());
|
||||||
Assert.assertNull(copy.getName());
|
Assert.assertNull(copy.getName());
|
||||||
Assert.assertNull(copy.getDescription());
|
Assert.assertNull(copy.getDescription());
|
||||||
Assert.assertNull(copy.getIconUrl());
|
Assert.assertNull(copy.getIconUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAllAttributesParceling() {
|
public void testAllAttributesParceling() {
|
||||||
WaypointCreationRequest original = new WaypointCreationRequest(
|
WaypointCreationRequest original = new WaypointCreationRequest(
|
||||||
WaypointType.WAYPOINT, false, "name", "category", "description", "img.png", null);
|
WaypointType.WAYPOINT, false, "name", "category", "description", "img.png", null);
|
||||||
Parcel p = Parcel.obtain();
|
Parcel p = Parcel.obtain();
|
||||||
original.writeToParcel(p, 0);
|
original.writeToParcel(p, 0);
|
||||||
p.setDataPosition(0);
|
p.setDataPosition(0);
|
||||||
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
|
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
|
||||||
Assert.assertEquals(original.getType(), copy.getType());
|
Assert.assertEquals(original.getType(), copy.getType());
|
||||||
Assert.assertFalse(copy.isTrackStatistics());
|
Assert.assertFalse(copy.isTrackStatistics());
|
||||||
Assert.assertEquals("name", copy.getName());
|
Assert.assertEquals("name", copy.getName());
|
||||||
Assert.assertEquals("category", copy.getCategory());
|
Assert.assertEquals("category", copy.getCategory());
|
||||||
Assert.assertEquals("description", copy.getDescription());
|
Assert.assertEquals("description", copy.getDescription());
|
||||||
Assert.assertEquals("img.png", copy.getIconUrl());
|
Assert.assertEquals("img.png", copy.getIconUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+283
-283
@@ -40,318 +40,318 @@ import org.junit.runner.RunWith;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class ChartFragmentTest {
|
public class ChartFragmentTest {
|
||||||
|
|
||||||
private static final double HOURS_PER_UNIT = 60.0;
|
private static final double HOURS_PER_UNIT = 60.0;
|
||||||
|
|
||||||
private ChartFragment chartFragment;
|
private ChartFragment chartFragment;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
protected void setUp() {
|
protected void setUp() {
|
||||||
chartFragment = new ChartFragment();
|
chartFragment = new ChartFragment();
|
||||||
chartFragment.setChartView(new ChartView(InstrumentationRegistry.getInstrumentation().getContext()));
|
chartFragment.setChartView(new ChartView(InstrumentationRegistry.getInstrumentation().getContext()));
|
||||||
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#fillDataPoint(Location,
|
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#fillDataPoint(Location,
|
||||||
* double[])}
|
* double[])}
|
||||||
*/
|
|
||||||
public void testFillDataPoint_sensorIncorrect() {
|
|
||||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
|
|
||||||
// No input.
|
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
|
||||||
|
|
||||||
// Input incorrect state.
|
|
||||||
// Creates SensorData.
|
|
||||||
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(100).setState(Sensor.SensorState.NONE);
|
|
||||||
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(101).setState(Sensor.SensorState.NONE);
|
|
||||||
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(102).setState(Sensor.SensorState.NONE);
|
|
||||||
|
|
||||||
// Creates SensorDataSet.
|
|
||||||
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
|
||||||
sensorDataSet = sensorDataSet.toBuilder()
|
|
||||||
.setHeartRate(heartRateData).setCadence(cadenceData).setPower(powerData).build();
|
|
||||||
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
|
|
||||||
// Test.
|
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the correct values of sensor in {@link ChartFragment#fillDataPoint(Location,
|
|
||||||
* double[])}.
|
|
||||||
*/
|
|
||||||
public void testFillDataPoint_sensorCorrect() {
|
|
||||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
// No input.
|
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
|
||||||
|
|
||||||
// Creates SensorData.
|
|
||||||
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(100).setState(Sensor.SensorState.SENDING);
|
|
||||||
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(101).setState(Sensor.SensorState.SENDING);
|
|
||||||
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
|
||||||
.setValue(102).setState(Sensor.SensorState.SENDING);
|
|
||||||
|
|
||||||
// Creates SensorDataSet.
|
|
||||||
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
|
||||||
sensorDataSet = sensorDataSet.toBuilder()
|
|
||||||
.setHeartRate(heartRateData).setCadence(cadenceData).setPower(powerData).build();
|
|
||||||
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
|
|
||||||
// Test.
|
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation);
|
|
||||||
Assert.assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1]);
|
|
||||||
Assert.assertEquals(102.0, point[ChartView.POWER_SERIES + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the value of metric Distance in {@link ChartFragment#fillDataPoint(Location,
|
|
||||||
* double[])}.
|
|
||||||
*/
|
|
||||||
public void testFillDataPoint_distanceMetric() {
|
|
||||||
// By distance.
|
|
||||||
chartFragment.setChartByDistance(true);
|
|
||||||
// Resets last location and writes first location.
|
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
|
||||||
Assert.assertEquals(0.0, point[0]);
|
|
||||||
|
|
||||||
// The second is a same location, just different time.
|
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
|
||||||
Assert.assertEquals(0.0, point[0]);
|
|
||||||
|
|
||||||
// The third location is a new location, and use metric.
|
|
||||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
sensorDataSetLocation3.setLatitude(23);
|
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
|
||||||
|
|
||||||
// Computes the distance between Latitude 22 and 23.
|
|
||||||
float[] results = new float[4];
|
|
||||||
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
|
|
||||||
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
|
|
||||||
double distance1 = results[0] * UnitConversions.M_TO_KM;
|
|
||||||
Assert.assertEquals(distance1, point[0]);
|
|
||||||
|
|
||||||
// The fourth location is a new location, and use metric.
|
|
||||||
SensorDataSetLocation sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
sensorDataSetLocation4.setLatitude(24);
|
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation4);
|
|
||||||
|
|
||||||
// Computes the distance between Latitude 23 and 24.
|
|
||||||
Location.distanceBetween(sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(),
|
|
||||||
sensorDataSetLocation4.getLatitude(), sensorDataSetLocation4.getLongitude(), results);
|
|
||||||
double distance2 = results[0] * UnitConversions.M_TO_KM;
|
|
||||||
Assert.assertEquals((distance1 + distance2), point[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#fillDataPoint(Location,
|
|
||||||
* double[])}.
|
|
||||||
*/
|
|
||||||
public void testFillDataPoint_distanceImperial() {
|
|
||||||
// By distance.
|
|
||||||
chartFragment.setChartByDistance(true);
|
|
||||||
// Setups to use imperial.
|
|
||||||
chartFragment.setMetricUnits(false);
|
|
||||||
|
|
||||||
// The first is a same location, just different time.
|
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
|
||||||
Assert.assertEquals(0.0, point[0]);
|
|
||||||
|
|
||||||
// The second location is a new location, and use imperial.
|
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
sensorDataSetLocation2.setLatitude(23);
|
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Computes the distance between Latitude 22 and 23. And for we set using
|
|
||||||
* imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
|
|
||||||
*/
|
*/
|
||||||
float[] results = new float[4];
|
public void testFillDataPoint_sensorIncorrect() {
|
||||||
Location.distanceBetween(sensorDataSetLocation1.getLatitude(), sensorDataSetLocation1.getLongitude(),
|
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(), results);
|
|
||||||
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
|
||||||
Assert.assertEquals(distance1, point[0]);
|
|
||||||
|
|
||||||
// The third location is a new location, and use imperial.
|
// No input.
|
||||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
sensorDataSetLocation3.setLatitude(24);
|
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
|
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||||
|
|
||||||
/*
|
// Input incorrect state.
|
||||||
* Computes the distance between Latitude 23 and 24. And for we set using
|
// Creates SensorData.
|
||||||
* imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
|
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(100).setState(Sensor.SensorState.NONE);
|
||||||
|
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(101).setState(Sensor.SensorState.NONE);
|
||||||
|
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(102).setState(Sensor.SensorState.NONE);
|
||||||
|
|
||||||
|
// Creates SensorDataSet.
|
||||||
|
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
||||||
|
sensorDataSet = sensorDataSet.toBuilder()
|
||||||
|
.setHeartRate(heartRateData).setCadence(cadenceData).setPower(powerData).build();
|
||||||
|
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
|
||||||
|
// Test.
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
|
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
|
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
|
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the logic to get the correct values of sensor in {@link ChartFragment#fillDataPoint(Location,
|
||||||
|
* double[])}.
|
||||||
*/
|
*/
|
||||||
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
|
public void testFillDataPoint_sensorCorrect() {
|
||||||
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
|
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||||
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
// No input.
|
||||||
Assert.assertEquals(distance1 + distance2, point[0]);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
}
|
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
|
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
|
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||||
|
|
||||||
/**
|
// Creates SensorData.
|
||||||
* Tests the logic to get the values of time in {@link ChartFragment#fillDataPoint(Location,
|
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
||||||
* double[])}.
|
.setValue(100).setState(Sensor.SensorState.SENDING);
|
||||||
*/
|
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
||||||
public void testFillDataPoint_time() {
|
.setValue(101).setState(Sensor.SensorState.SENDING);
|
||||||
// By time
|
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
||||||
chartFragment.setChartByDistance(false);
|
.setValue(102).setState(Sensor.SensorState.SENDING);
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
|
||||||
Assert.assertEquals(0.0, point[0]);
|
|
||||||
long timeSpan = 222;
|
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + timeSpan);
|
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
|
||||||
Assert.assertEquals((double) timeSpan, point[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Creates SensorDataSet.
|
||||||
* Tests the logic to get the value of elevation in {@link ChartFragment#fillDataPoint(Location,
|
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
||||||
* double[])} by one and two points.
|
sensorDataSet = sensorDataSet.toBuilder()
|
||||||
*/
|
.setHeartRate(heartRateData).setCadence(cadenceData).setPower(powerData).build();
|
||||||
public void testFillDataPoint_elevation() {
|
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
// Test.
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
|
Assert.assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||||
|
Assert.assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1]);
|
||||||
|
Assert.assertEquals(102.0, point[ChartView.POWER_SERIES + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* At first, clear old points of elevation, so give true to the second
|
* Tests the logic to get the value of metric Distance in {@link ChartFragment#fillDataPoint(Location,
|
||||||
* parameter. Then only one value INITIALLONGTITUDE in buffer.
|
* double[])}.
|
||||||
*/
|
*/
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
public void testFillDataPoint_distanceMetric() {
|
||||||
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1]);
|
// By distance.
|
||||||
|
chartFragment.setChartByDistance(true);
|
||||||
|
// Resets last location and writes first location.
|
||||||
|
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
|
Assert.assertEquals(0.0, point[0]);
|
||||||
|
|
||||||
/*
|
// The second is a same location, just different time.
|
||||||
* Send another value to buffer, now there are two values, INITIALALTITUDE
|
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
* and INITIALALTITUDE * 2.
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
|
Assert.assertEquals(0.0, point[0]);
|
||||||
|
|
||||||
|
// The third location is a new location, and use metric.
|
||||||
|
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
sensorDataSetLocation3.setLatitude(23);
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
||||||
|
|
||||||
|
// Computes the distance between Latitude 22 and 23.
|
||||||
|
float[] results = new float[4];
|
||||||
|
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
|
||||||
|
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
|
||||||
|
double distance1 = results[0] * UnitConversions.M_TO_KM;
|
||||||
|
Assert.assertEquals(distance1, point[0]);
|
||||||
|
|
||||||
|
// The fourth location is a new location, and use metric.
|
||||||
|
SensorDataSetLocation sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
sensorDataSetLocation4.setLatitude(24);
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation4);
|
||||||
|
|
||||||
|
// Computes the distance between Latitude 23 and 24.
|
||||||
|
Location.distanceBetween(sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(),
|
||||||
|
sensorDataSetLocation4.getLatitude(), sensorDataSetLocation4.getLongitude(), results);
|
||||||
|
double distance2 = results[0] * UnitConversions.M_TO_KM;
|
||||||
|
Assert.assertEquals((distance1 + distance2), point[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#fillDataPoint(Location,
|
||||||
|
* double[])}.
|
||||||
*/
|
*/
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
public void testFillDataPoint_distanceImperial() {
|
||||||
sensorDataSetLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
// By distance.
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
chartFragment.setChartByDistance(true);
|
||||||
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
|
// Setups to use imperial.
|
||||||
point[ChartView.ELEVATION_SERIES + 1]);
|
chartFragment.setMetricUnits(false);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// The first is a same location, just different time.
|
||||||
* Tests the logic to get the value of speed in {@link ChartFragment#fillDataPoint(Location,
|
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
* double[])}. In this test, firstly remove all
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
* points in memory, and then fill in two points one by one. The speed values
|
Assert.assertEquals(0.0, point[0]);
|
||||||
* of these points are 129, 130.
|
|
||||||
*/
|
// The second location is a new location, and use imperial.
|
||||||
public void testFillDataPoint_speed() {
|
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
/*
|
sensorDataSetLocation2.setLatitude(23);
|
||||||
* At first, clear old points of speed, so give true to the second
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
* parameter. It will not be filled in to the speed buffer.
|
|
||||||
|
/*
|
||||||
|
* Computes the distance between Latitude 22 and 23. And for we set using
|
||||||
|
* imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
|
||||||
|
*/
|
||||||
|
float[] results = new float[4];
|
||||||
|
Location.distanceBetween(sensorDataSetLocation1.getLatitude(), sensorDataSetLocation1.getLongitude(),
|
||||||
|
sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(), results);
|
||||||
|
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
||||||
|
Assert.assertEquals(distance1, point[0]);
|
||||||
|
|
||||||
|
// The third location is a new location, and use imperial.
|
||||||
|
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
sensorDataSetLocation3.setLatitude(24);
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Computes the distance between Latitude 23 and 24. And for we set using
|
||||||
|
* imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
|
||||||
|
*/
|
||||||
|
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
|
||||||
|
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
|
||||||
|
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
||||||
|
Assert.assertEquals(distance1 + distance2, point[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the logic to get the values of time in {@link ChartFragment#fillDataPoint(Location,
|
||||||
|
* double[])}.
|
||||||
*/
|
*/
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
public void testFillDataPoint_time() {
|
||||||
sensorDataSetLocation1.setSpeed(128.5f);
|
// By time
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
chartFragment.setChartByDistance(false);
|
||||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
|
Assert.assertEquals(0.0, point[0]);
|
||||||
|
long timeSpan = 222;
|
||||||
|
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + timeSpan);
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
|
Assert.assertEquals((double) timeSpan, point[0]);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Tests the logic when both metricUnits and reportSpeed are true.This
|
* Tests the logic to get the value of elevation in {@link ChartFragment#fillDataPoint(Location,
|
||||||
* location will be filled into speed buffer.
|
* double[])} by one and two points.
|
||||||
*/
|
*/
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
public void testFillDataPoint_elevation() {
|
||||||
|
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a time span here to make sure the second point is valid, the value
|
* At first, clear old points of elevation, so give true to the second
|
||||||
* 222 here is doesn't matter.
|
* parameter. Then only one value INITIALLONGTITUDE in buffer.
|
||||||
|
*/
|
||||||
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
|
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send another value to buffer, now there are two values, INITIALALTITUDE
|
||||||
|
* and INITIALALTITUDE * 2.
|
||||||
|
*/
|
||||||
|
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
sensorDataSetLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
|
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
|
||||||
|
point[ChartView.ELEVATION_SERIES + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the logic to get the value of speed in {@link ChartFragment#fillDataPoint(Location,
|
||||||
|
* double[])}. In this test, firstly remove all
|
||||||
|
* points in memory, and then fill in two points one by one. The speed values
|
||||||
|
* of these points are 129, 130.
|
||||||
*/
|
*/
|
||||||
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + 222);
|
public void testFillDataPoint_speed() {
|
||||||
sensorDataSetLocation2.setSpeed(130);
|
/*
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
* At first, clear old points of speed, so give true to the second
|
||||||
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1]);
|
* parameter. It will not be filled in to the speed buffer.
|
||||||
}
|
*/
|
||||||
|
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
sensorDataSetLocation1.setSpeed(128.5f);
|
||||||
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
|
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Tests the logic to compute speed when use Imperial.
|
* Tests the logic when both metricUnits and reportSpeed are true.This
|
||||||
*/
|
* location will be filled into speed buffer.
|
||||||
public void testFillDataPoint_speedImperial() {
|
*/
|
||||||
// Setups to use imperial.
|
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
chartFragment.setMetricUnits(false);
|
|
||||||
|
|
||||||
// First data point is not added to the speed buffer
|
/*
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
* Add a time span here to make sure the second point is valid, the value
|
||||||
sensorDataSetLocation1.setSpeed(100.0f);
|
* 222 here is doesn't matter.
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
*/
|
||||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + 222);
|
||||||
|
sensorDataSetLocation2.setSpeed(130);
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
|
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
/**
|
||||||
|
* Tests the logic to compute speed when use Imperial.
|
||||||
/*
|
|
||||||
* Add a time span here to make sure the second point is valid and the speed
|
|
||||||
* is valid. Speed is valid if: speedDifference > Constants.MAX_ACCELERATION
|
|
||||||
* * timeDifference speedDifference = 102 -100 timeDifference = 222
|
|
||||||
*/
|
*/
|
||||||
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
|
public void testFillDataPoint_speedImperial() {
|
||||||
sensorDataSetLocation2.setSpeed(102);
|
// Setups to use imperial.
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
chartFragment.setMetricUnits(false);
|
||||||
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[ChartView.SPEED_SERIES + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// First data point is not added to the speed buffer
|
||||||
* Tests the logic to get pace value when reportSpeed is false.
|
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
*/
|
sensorDataSetLocation1.setSpeed(100.0f);
|
||||||
public void testFillDataPoint_pace_nonZeroSpeed() {
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
// Setups reportSpeed to false.
|
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||||
chartFragment.setReportSpeed(false);
|
|
||||||
|
|
||||||
// First data point is not added to the speed buffer
|
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
sensorDataSetLocation1.setSpeed(100.0f);
|
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
|
||||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
|
||||||
|
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
/*
|
||||||
|
* Add a time span here to make sure the second point is valid and the speed
|
||||||
|
* is valid. Speed is valid if: speedDifference > Constants.MAX_ACCELERATION
|
||||||
|
* * timeDifference speedDifference = 102 -100 timeDifference = 222
|
||||||
|
*/
|
||||||
|
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
|
||||||
|
sensorDataSetLocation2.setSpeed(102);
|
||||||
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
|
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[ChartView.SPEED_SERIES + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Add a time span here to make sure the second point is valid and the speed
|
* Tests the logic to get pace value when reportSpeed is false.
|
||||||
* is valid. Speed is valid if: speedDifference > Constants.MAX_ACCELERATION
|
|
||||||
* * timeDifference speedDifference = 102 -100 timeDifference = 222
|
|
||||||
*/
|
*/
|
||||||
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
|
public void testFillDataPoint_pace_nonZeroSpeed() {
|
||||||
sensorDataSetLocation2.setSpeed(102);
|
// Setups reportSpeed to false.
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
chartFragment.setReportSpeed(false);
|
||||||
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// First data point is not added to the speed buffer
|
||||||
* Tests the logic to get pace value when reportSpeed is false and average
|
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
* speed is zero.
|
sensorDataSetLocation1.setSpeed(100.0f);
|
||||||
*/
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
public void testFillDataPoint_pace_zeroSpeed() {
|
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||||
// Setups reportSpeed to false.
|
|
||||||
chartFragment.setReportSpeed(false);
|
|
||||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
|
||||||
sensorDataSetLocation.setSpeed(0);
|
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
|
||||||
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
* Helper method to test fillDataPoint.
|
|
||||||
*
|
/*
|
||||||
* @param location location to fill
|
* Add a time span here to make sure the second point is valid and the speed
|
||||||
* @return data of this location
|
* is valid. Speed is valid if: speedDifference > Constants.MAX_ACCELERATION
|
||||||
*/
|
* * timeDifference speedDifference = 102 -100 timeDifference = 222
|
||||||
private double[] fillDataPointTestHelper(Location location) {
|
*/
|
||||||
double[] point = new double[ChartView.NUM_SERIES + 1];
|
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
|
||||||
chartFragment.fillDataPoint(location, point);
|
sensorDataSetLocation2.setSpeed(102);
|
||||||
return point;
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
}
|
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the logic to get pace value when reportSpeed is false and average
|
||||||
|
* speed is zero.
|
||||||
|
*/
|
||||||
|
public void testFillDataPoint_pace_zeroSpeed() {
|
||||||
|
// Setups reportSpeed to false.
|
||||||
|
chartFragment.setReportSpeed(false);
|
||||||
|
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
sensorDataSetLocation.setSpeed(0);
|
||||||
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
|
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to test fillDataPoint.
|
||||||
|
*
|
||||||
|
* @param location location to fill
|
||||||
|
* @return data of this location
|
||||||
|
*/
|
||||||
|
private double[] fillDataPointTestHelper(Location location) {
|
||||||
|
double[] point = new double[ChartView.NUM_SERIES + 1];
|
||||||
|
chartFragment.fillDataPoint(location, point);
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-112
@@ -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.
|
||||||
@@ -48,117 +48,115 @@ import org.easymock.Capture;
|
|||||||
*/
|
*/
|
||||||
public class AbstractTestFileTrackImporter extends AndroidTestCase {
|
public class AbstractTestFileTrackImporter extends AndroidTestCase {
|
||||||
|
|
||||||
protected static final String TRACK_NAME_0 = "blablub";
|
protected static final String TRACK_NAME_0 = "blablub";
|
||||||
protected static final String TRACK_DESCRIPTION_0 = "s'Laebe isch koi Schlotzer";
|
protected static final String TRACK_DESCRIPTION_0 = "s'Laebe isch koi Schlotzer";
|
||||||
|
|
||||||
protected static final String TRACK_NAME_1 = "another track";
|
protected static final String TRACK_NAME_1 = "another track";
|
||||||
protected static final String TRACK_DESCRIPTION_1 = "another description";
|
protected static final String TRACK_DESCRIPTION_1 = "another description";
|
||||||
|
|
||||||
protected static final double TRACK_LATITUDE = 48.768364;
|
protected static final double TRACK_LATITUDE = 48.768364;
|
||||||
protected static final double TRACK_LONGITUDE = 9.177886;
|
protected static final double TRACK_LONGITUDE = 9.177886;
|
||||||
protected static final double TRACK_ELEVATION = 324.0;
|
protected static final double TRACK_ELEVATION = 324.0;
|
||||||
|
|
||||||
protected static final String TRACK_TIME_0 = "2010-04-22T18:21:00Z";
|
protected static final String TRACK_TIME_0 = "2010-04-22T18:21:00Z";
|
||||||
protected static final String TRACK_TIME_1 = "2010-04-22T18:21:50.123";
|
protected static final String TRACK_TIME_1 = "2010-04-22T18:21:50.123";
|
||||||
protected static final String TRACK_TIME_2 = "2010-04-22T18:23:00.123";
|
protected static final String TRACK_TIME_2 = "2010-04-22T18:23:00.123";
|
||||||
protected static final String TRACK_TIME_3 = "2010-04-22T18:24:50.123";
|
protected static final String TRACK_TIME_3 = "2010-04-22T18:24:50.123";
|
||||||
|
|
||||||
protected static final SimpleDateFormat DATE_FORMAT_0 = new SimpleDateFormat(
|
protected static final SimpleDateFormat DATE_FORMAT_0 = new SimpleDateFormat(
|
||||||
"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 {
|
||||||
/*
|
/*
|
||||||
* We can't omit the timezones in the test, otherwise it'll use the local
|
* We can't omit the timezones in the test, otherwise it'll use the local
|
||||||
* timezone and fail depending on where the test runner is.
|
* timezone and fail depending on where the test runner is.
|
||||||
|
*/
|
||||||
|
SimpleTimeZone utc = new SimpleTimeZone(0, "UTC");
|
||||||
|
DATE_FORMAT_0.setTimeZone(utc);
|
||||||
|
DATE_FORMAT_1.setTimeZone(utc);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
|
private Factory oldMyTracksProviderUtilsFactory;
|
||||||
|
|
||||||
|
@UsesMocks(ContentProviderUtils.class)
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
contentProviderUtils = AndroidMock.createMock(ContentProviderUtils.class);
|
||||||
|
oldMyTracksProviderUtilsFactory = TestingProviderUtilsFactory.installWithInstance(
|
||||||
|
contentProviderUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
TestingProviderUtilsFactory.restoreOldFactory(oldMyTracksProviderUtilsFactory);
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Location createLocation(int index, long time) {
|
||||||
|
Location location = new Location(LocationManager.GPS_PROVIDER);
|
||||||
|
location.setLatitude(TRACK_LATITUDE + index);
|
||||||
|
location.setLongitude(TRACK_LONGITUDE + index);
|
||||||
|
location.setAltitude(TRACK_ELEVATION + index);
|
||||||
|
location.setTime(time);
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expects the first track point to be added.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param trackPointId the track point id
|
||||||
*/
|
*/
|
||||||
SimpleTimeZone utc = new SimpleTimeZone(0, "UTC");
|
protected void expectFirstTrackPoint(Location location, long trackId, long trackPointId) {
|
||||||
DATE_FORMAT_0.setTimeZone(utc);
|
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||||
DATE_FORMAT_1.setTimeZone(utc);
|
location != null ? LocationsMatcher.eqLoc(location) : (Location[]) AndroidMock.anyObject(),
|
||||||
}
|
eq(1), eq(trackId))).andReturn(1);
|
||||||
|
expect(contentProviderUtils.getFirstTrackPointId(trackId)).andReturn(trackPointId);
|
||||||
protected static final long TRACK_ID_0 = 1;
|
expect(contentProviderUtils.getLastTrackPointId(trackId)).andReturn(trackPointId);
|
||||||
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;
|
|
||||||
|
|
||||||
private Factory oldMyTracksProviderUtilsFactory;
|
|
||||||
|
|
||||||
@UsesMocks(ContentProviderUtils.class)
|
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
contentProviderUtils = AndroidMock.createMock(ContentProviderUtils.class);
|
|
||||||
oldMyTracksProviderUtilsFactory = TestingProviderUtilsFactory.installWithInstance(
|
|
||||||
contentProviderUtils);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tearDown() throws Exception {
|
|
||||||
TestingProviderUtilsFactory.restoreOldFactory(oldMyTracksProviderUtilsFactory);
|
|
||||||
super.tearDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Location createLocation(int index, long time) {
|
|
||||||
Location location = new Location(LocationManager.GPS_PROVIDER);
|
|
||||||
location.setLatitude(TRACK_LATITUDE + index);
|
|
||||||
location.setLongitude(TRACK_LONGITUDE + index);
|
|
||||||
location.setAltitude(TRACK_ELEVATION + index);
|
|
||||||
location.setTime(time);
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expects the first track point to be added.
|
|
||||||
*
|
|
||||||
* @param location the location
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param trackPointId the track point id
|
|
||||||
*/
|
|
||||||
protected void expectFirstTrackPoint(Location location, long trackId, long trackPointId) {
|
|
||||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
|
||||||
location != null ? LocationsMatcher.eqLoc(location) : (Location[]) AndroidMock.anyObject(),
|
|
||||||
eq(1), eq(trackId))).andReturn(1);
|
|
||||||
expect(contentProviderUtils.getFirstTrackPointId(trackId)).andReturn(trackPointId);
|
|
||||||
expect(contentProviderUtils.getLastTrackPointId(trackId)).andReturn(trackPointId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expects the track to be updated.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
* @param lastTrack true if it is the last track in the gpx
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
protected void expectUpdateTrack(Capture<Track> track, boolean lastTrack, long trackId) {
|
|
||||||
contentProviderUtils.updateTrack(AndroidMock.capture(track));
|
|
||||||
expect(contentProviderUtils.insertWaypoint((Waypoint) AndroidMock.anyObject()))
|
|
||||||
.andReturn(WAYPOINT_ID_O_URI);
|
|
||||||
if (lastTrack) {
|
|
||||||
// Return null to not add waypoints
|
|
||||||
expect(contentProviderUtils.getTrack(trackId)).andReturn(null);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected void verifyTrack(Track track, String name, String description, long time) {
|
/**
|
||||||
assertEquals(name, track.getName());
|
* Expects the track to be updated.
|
||||||
assertEquals(description, track.getDescription());
|
*
|
||||||
if (time != -1L) {
|
* @param track the track
|
||||||
assertEquals(time, track.getTripStatistics().getStartTime());
|
* @param lastTrack true if it is the last track in the gpx
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
protected void expectUpdateTrack(Capture<Track> track, boolean lastTrack, long trackId) {
|
||||||
|
contentProviderUtils.updateTrack(AndroidMock.capture(track));
|
||||||
|
expect(contentProviderUtils.insertWaypoint((Waypoint) AndroidMock.anyObject()))
|
||||||
|
.andReturn(WAYPOINT_ID_O_URI);
|
||||||
|
if (lastTrack) {
|
||||||
|
// Return null to not add waypoints
|
||||||
|
expect(contentProviderUtils.getTrack(trackId)).andReturn(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void verifyTrack(Track track, String name, String description, long time) {
|
||||||
|
assertEquals(name, track.getName());
|
||||||
|
assertEquals(description, track.getDescription());
|
||||||
|
if (time != -1L) {
|
||||||
|
assertEquals(time, track.getTripStatistics().getStartTime());
|
||||||
|
}
|
||||||
|
assertNotSame(-1, track.getStartId());
|
||||||
|
assertNotSame(-1, track.getStopId());
|
||||||
}
|
}
|
||||||
assertNotSame(-1, track.getStartId());
|
|
||||||
assertNotSame(-1, track.getStopId());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+190
-191
@@ -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,220 +38,219 @@ import org.easymock.Capture;
|
|||||||
*/
|
*/
|
||||||
public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||||
|
|
||||||
private static String getNameAndDescription(String name, String description) {
|
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "<gpx><trk>"
|
||||||
return "<name><![CDATA[" + name + "]]></name>" + "<desc><![CDATA[" + description + "]]></desc>";
|
+ 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 getTrackPoint(int index, String time) {
|
private static String getNameAndDescription(String name, String description) {
|
||||||
String latitude = Double.toString(TRACK_LATITUDE + index);
|
return "<name><![CDATA[" + name + "]]></name>" + "<desc><![CDATA[" + description + "]]></desc>";
|
||||||
String longitude = Double.toString(TRACK_LONGITUDE + index);
|
|
||||||
String elevation = Double.toString(TRACK_ELEVATION + index);
|
|
||||||
StringBuffer buffer = new StringBuffer();
|
|
||||||
buffer.append(
|
|
||||||
"<trkpt lat=\"" + latitude + "\" lon=\"" + longitude + "\"><ele>" + elevation + "</ele>");
|
|
||||||
if (time != null) {
|
|
||||||
buffer.append("<time>" + time + "</time>");
|
|
||||||
}
|
}
|
||||||
buffer.append("</trkpt>");
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "<gpx><trk>"
|
private static String getTrackPoint(int index, String time) {
|
||||||
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
|
String latitude = Double.toString(TRACK_LATITUDE + index);
|
||||||
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg></trk></gpx>";
|
String longitude = Double.toString(TRACK_LONGITUDE + index);
|
||||||
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "<gpx><trk>"
|
String elevation = Double.toString(TRACK_ELEVATION + index);
|
||||||
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
|
StringBuffer buffer = new StringBuffer();
|
||||||
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg><trkseg>"
|
buffer.append(
|
||||||
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3) + "</trkseg></trk></gpx>";
|
"<trkpt lat=\"" + latitude + "\" lon=\"" + longitude + "\"><ele>" + elevation + "</ele>");
|
||||||
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX = "<gpx><trk>"
|
if (time != null) {
|
||||||
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
|
buffer.append("<time>" + time + "</time>");
|
||||||
+ getTrackPoint(0, null) + getTrackPoint(1, null) + "</trkseg><trkseg>"
|
}
|
||||||
+ getTrackPoint(2, null) + getTrackPoint(3, null) + "</trkseg></trk></gpx>";
|
buffer.append("</trkpt>");
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private static final String INVALID_XML_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.substring(
|
/**
|
||||||
0, VALID_ONE_TRACK_ONE_SEGMENT_GPX.length() - 50);
|
* Tests one track with one segment.
|
||||||
private static final String INVALID_LOCATION_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
|
*/
|
||||||
Double.toString(TRACK_LATITUDE), "1000.0");
|
public void testOneTrackOneSegment() throws Exception {
|
||||||
private static final String INVALID_TIME_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
|
Capture<Track> track = new Capture<Track>();
|
||||||
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");
|
|
||||||
|
|
||||||
/**
|
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||||
* Tests one track with one segment.
|
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
||||||
*/
|
|
||||||
public void testOneTrackOneSegment() throws Exception {
|
|
||||||
Capture<Track> track = new Capture<Track>();
|
|
||||||
|
|
||||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||||
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
.andReturn(TRACK_ID_0_URI);
|
||||||
|
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||||
|
|
||||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
// A flush happens at the end
|
||||||
.andReturn(TRACK_ID_0_URI);
|
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1);
|
||||||
|
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1);
|
||||||
|
expect(
|
||||||
|
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||||
|
R.string.recording_track_id_key))).andStubReturn(null);
|
||||||
|
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||||
|
AndroidMock.replay(contentProviderUtils);
|
||||||
|
|
||||||
// A flush happens at the end
|
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
||||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||||
LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1);
|
getContext(), contentProviderUtils);
|
||||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1);
|
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||||
expect(
|
assertEquals(TRACK_ID_0, trackId);
|
||||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
|
||||||
R.string.recording_track_id_key))).andStubReturn(null);
|
|
||||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
|
||||||
AndroidMock.replay(contentProviderUtils);
|
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||||
getContext(), contentProviderUtils);
|
assertEquals(time1 - time0, track.getValue().getTripStatistics().getTotalTime());
|
||||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
AndroidMock.verify(contentProviderUtils);
|
||||||
assertEquals(TRACK_ID_0, trackId);
|
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||||
|
}
|
||||||
|
|
||||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
/**
|
||||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
* Tests one track with two segments.
|
||||||
assertEquals(time1 - time0, track.getValue().getTripStatistics().getTotalTime());
|
*/
|
||||||
AndroidMock.verify(contentProviderUtils);
|
public void testOneTrackTwoSegments() throws Exception {
|
||||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
Capture<Track> track = new Capture<Track>();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||||
* Tests one track with two segments.
|
|
||||||
*/
|
|
||||||
public void testOneTrackTwoSegments() throws Exception {
|
|
||||||
Capture<Track> track = new Capture<Track>();
|
|
||||||
|
|
||||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||||
|
.andReturn(TRACK_ID_0_URI);
|
||||||
|
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||||
|
// A flush happens at the end
|
||||||
|
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||||
|
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
||||||
|
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
||||||
|
expect(
|
||||||
|
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||||
|
R.string.recording_track_id_key))).andStubReturn(null);
|
||||||
|
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||||
|
AndroidMock.replay(contentProviderUtils);
|
||||||
|
|
||||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
||||||
.andReturn(TRACK_ID_0_URI);
|
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
getContext(), contentProviderUtils);
|
||||||
// A flush happens at the end
|
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
assertEquals(TRACK_ID_0, trackId);
|
||||||
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
|
||||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
|
||||||
expect(
|
|
||||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
|
||||||
R.string.recording_track_id_key))).andStubReturn(null);
|
|
||||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
|
||||||
AndroidMock.replay(contentProviderUtils);
|
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||||
getContext(), contentProviderUtils);
|
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
|
||||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
||||||
assertEquals(TRACK_ID_0, trackId);
|
assertEquals(
|
||||||
|
time1 - time0 + time3 - time2, track.getValue().getTripStatistics().getTotalTime());
|
||||||
|
|
||||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
AndroidMock.verify(contentProviderUtils);
|
||||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0,
|
||||||
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
|
DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||||
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
}
|
||||||
assertEquals(
|
|
||||||
time1 - time0 + time3 - time2, track.getValue().getTripStatistics().getTotalTime());
|
|
||||||
|
|
||||||
AndroidMock.verify(contentProviderUtils);
|
/**
|
||||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0,
|
* Tests one track with two segments, but no time in the track points.
|
||||||
DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
*/
|
||||||
}
|
public void testOneTrackTwoSegmentsNoTime() throws Exception {
|
||||||
|
Capture<Track> track = new Capture<Track>();
|
||||||
|
|
||||||
/**
|
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||||
* Tests one track with two segments, but no time in the track points.
|
.andReturn(TRACK_ID_0_URI);
|
||||||
*/
|
expectFirstTrackPoint(null, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||||
public void testOneTrackTwoSegmentsNoTime() throws Exception {
|
|
||||||
Capture<Track> track = new Capture<Track>();
|
|
||||||
|
|
||||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
// A flush happens at the end
|
||||||
.andReturn(TRACK_ID_0_URI);
|
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||||
expectFirstTrackPoint(null, TRACK_ID_0, TRACK_POINT_ID_0);
|
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
||||||
|
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
||||||
|
expect(
|
||||||
|
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||||
|
R.string.recording_track_id_key))).andStubReturn(null);
|
||||||
|
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||||
|
AndroidMock.replay(contentProviderUtils);
|
||||||
|
|
||||||
// A flush happens at the end
|
InputStream inputStream = new ByteArrayInputStream(
|
||||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX.getBytes());
|
||||||
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
getContext(), contentProviderUtils);
|
||||||
expect(
|
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
assertEquals(TRACK_ID_0, trackId);
|
||||||
R.string.recording_track_id_key))).andStubReturn(null);
|
assertEquals(0, track.getValue().getTripStatistics().getTotalTime());
|
||||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
|
||||||
AndroidMock.replay(contentProviderUtils);
|
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(
|
AndroidMock.verify(contentProviderUtils);
|
||||||
VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX.getBytes());
|
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, -1L);
|
||||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
}
|
||||||
getContext(), contentProviderUtils);
|
|
||||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
|
||||||
assertEquals(TRACK_ID_0, trackId);
|
|
||||||
assertEquals(0, track.getValue().getTripStatistics().getTotalTime());
|
|
||||||
|
|
||||||
AndroidMock.verify(contentProviderUtils);
|
/**
|
||||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, -1L);
|
* Test an invalid xml input.
|
||||||
}
|
*/
|
||||||
|
public void testInvalidXml() throws Exception {
|
||||||
|
testInvalidGpx(INVALID_XML_GPX);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an invalid xml input.
|
* Test an invalid location.
|
||||||
*/
|
*/
|
||||||
public void testInvalidXml() throws Exception {
|
public void testInvalidLocation() throws Exception {
|
||||||
testInvalidGpx(INVALID_XML_GPX);
|
testInvalidGpx(INVALID_LOCATION_GPX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an invalid location.
|
* Test an invalid time.
|
||||||
*/
|
*/
|
||||||
public void testInvalidLocation() throws Exception {
|
public void testInvalidTime() throws Exception {
|
||||||
testInvalidGpx(INVALID_LOCATION_GPX);
|
testInvalidGpx(INVALID_TIME_GPX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an invalid time.
|
* Test an invalid altitude.
|
||||||
*/
|
*/
|
||||||
public void testInvalidTime() throws Exception {
|
public void testInvalidAltitude() throws Exception {
|
||||||
testInvalidGpx(INVALID_TIME_GPX);
|
testInvalidGpx(INVALID_ALTITUDE_GPX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an invalid altitude.
|
* Test an invalid latitude.
|
||||||
*/
|
*/
|
||||||
public void testInvalidAltitude() throws Exception {
|
public void testInvalidLatitude() throws Exception {
|
||||||
testInvalidGpx(INVALID_ALTITUDE_GPX);
|
testInvalidGpx(INVALID_LATITUDE_GPX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an invalid latitude.
|
* Test an invalid longitude.
|
||||||
*/
|
*/
|
||||||
public void testInvalidLatitude() throws Exception {
|
public void testInvalidLongitude() throws Exception {
|
||||||
testInvalidGpx(INVALID_LATITUDE_GPX);
|
testInvalidGpx(INVALID_LONGITUDE_GPX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void testInvalidGpx(String xml) throws Exception {
|
||||||
* Test an invalid longitude.
|
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||||
*/
|
.andReturn(TRACK_ID_0_URI);
|
||||||
public void testInvalidLongitude() throws Exception {
|
|
||||||
testInvalidGpx(INVALID_LONGITUDE_GPX);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testInvalidGpx(String xml) throws Exception {
|
// For the following, use StubReturn since we don't care whether they are
|
||||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
// invoked or not.
|
||||||
.andReturn(TRACK_ID_0_URI);
|
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||||
|
(Location[]) AndroidMock.anyObject(), AndroidMock.anyInt(), AndroidMock.anyLong()))
|
||||||
|
.andStubReturn(1);
|
||||||
|
expect(contentProviderUtils.getFirstTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0);
|
||||||
|
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0);
|
||||||
|
expect(
|
||||||
|
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||||
|
R.string.recording_track_id_key))).andStubReturn(null);
|
||||||
|
contentProviderUtils.deleteTrack(getContext(), TRACK_ID_0);
|
||||||
|
AndroidMock.replay(contentProviderUtils);
|
||||||
|
|
||||||
// For the following, use StubReturn since we don't care whether they are
|
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
|
||||||
// invoked or not.
|
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
getContext(), contentProviderUtils);
|
||||||
(Location[]) AndroidMock.anyObject(), AndroidMock.anyInt(), AndroidMock.anyLong()))
|
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||||
.andStubReturn(1);
|
assertEquals(-1L, trackId);
|
||||||
expect(contentProviderUtils.getFirstTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0);
|
AndroidMock.verify(contentProviderUtils);
|
||||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0);
|
}
|
||||||
expect(
|
|
||||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
|
||||||
R.string.recording_track_id_key))).andStubReturn(null);
|
|
||||||
contentProviderUtils.deleteTrack(getContext(), TRACK_ID_0);
|
|
||||||
AndroidMock.replay(contentProviderUtils);
|
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
|
|
||||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
|
||||||
getContext(), contentProviderUtils);
|
|
||||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
|
||||||
assertEquals(-1L, trackId);
|
|
||||||
AndroidMock.verify(contentProviderUtils);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-89
@@ -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,104 +38,104 @@ import org.easymock.Capture;
|
|||||||
*/
|
*/
|
||||||
public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||||
|
|
||||||
private static String getNameAndDescription(String name, String description) {
|
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX =
|
||||||
return "<name><![CDATA[" + name + "]]></name><description><![CDATA[" + description
|
"<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
|
||||||
+ "]]></description>";
|
+ 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 getTrackPoint(int index, String time) {
|
private static String getNameAndDescription(String name, String description) {
|
||||||
String latitude = Double.toString(TRACK_LATITUDE + index);
|
return "<name><![CDATA[" + name + "]]></name><description><![CDATA[" + description
|
||||||
String longitude = Double.toString(TRACK_LONGITUDE + index);
|
+ "]]></description>";
|
||||||
String altitude = Double.toString(TRACK_ELEVATION + index);
|
}
|
||||||
StringBuffer buffer = new StringBuffer();
|
|
||||||
buffer.append("<when>" + time + "</when>" + "<gx:coord>" + longitude + " " + latitude + " "
|
|
||||||
+ altitude + "</gx:coord>");
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX =
|
private static String getTrackPoint(int index, String time) {
|
||||||
"<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
|
String latitude = Double.toString(TRACK_LATITUDE + index);
|
||||||
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
|
String longitude = Double.toString(TRACK_LONGITUDE + index);
|
||||||
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1)
|
String altitude = Double.toString(TRACK_ELEVATION + index);
|
||||||
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
|
StringBuffer buffer = new StringBuffer();
|
||||||
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX =
|
buffer.append("<when>" + time + "</when>" + "<gx:coord>" + longitude + " " + latitude + " "
|
||||||
"<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
|
+ altitude + "</gx:coord>");
|
||||||
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
|
return buffer.toString();
|
||||||
+ 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.
|
||||||
*/
|
*/
|
||||||
public void testOneTrackOneSegment() throws Exception {
|
public void testOneTrackOneSegment() throws Exception {
|
||||||
Capture<Track> track = new Capture<Track>();
|
Capture<Track> track = new Capture<Track>();
|
||||||
|
|
||||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||||
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
||||||
|
|
||||||
contentProviderUtils.clearTrack(getContext(), TRACK_ID_0);
|
contentProviderUtils.clearTrack(getContext(), TRACK_ID_0);
|
||||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||||
|
|
||||||
// A flush happens at the end
|
// A flush happens at the end
|
||||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||||
LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1);
|
LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1);
|
||||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1);
|
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1);
|
||||||
expect(
|
expect(
|
||||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||||
R.string.recording_track_id_key))).andStubReturn(null);
|
R.string.recording_track_id_key))).andStubReturn(null);
|
||||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||||
AndroidMock.replay(contentProviderUtils);
|
AndroidMock.replay(contentProviderUtils);
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
||||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(
|
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(
|
||||||
getContext(), TRACK_ID_0, contentProviderUtils);
|
getContext(), TRACK_ID_0, contentProviderUtils);
|
||||||
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
||||||
assertEquals(TRACK_ID_0, trackId);
|
assertEquals(TRACK_ID_0, trackId);
|
||||||
|
|
||||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||||
assertEquals(time1 - time0, track.getValue().getTripStatistics().getTotalTime());
|
assertEquals(time1 - time0, track.getValue().getTripStatistics().getTotalTime());
|
||||||
AndroidMock.verify(contentProviderUtils);
|
AndroidMock.verify(contentProviderUtils);
|
||||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests one track with two segments.
|
* Tests one track with two segments.
|
||||||
*/
|
*/
|
||||||
public void testOneTrackTwoSegments() throws Exception {
|
public void testOneTrackTwoSegments() throws Exception {
|
||||||
Capture<Track> track = new Capture<Track>();
|
Capture<Track> track = new Capture<Track>();
|
||||||
|
|
||||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||||
|
|
||||||
contentProviderUtils.clearTrack(getContext(), TRACK_ID_0);
|
contentProviderUtils.clearTrack(getContext(), TRACK_ID_0);
|
||||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||||
|
|
||||||
// A flush happens at the end
|
// A flush happens at the end
|
||||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||||
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
||||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
||||||
expect(
|
expect(
|
||||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||||
R.string.recording_track_id_key))).andStubReturn(null);
|
R.string.recording_track_id_key))).andStubReturn(null);
|
||||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||||
AndroidMock.replay(contentProviderUtils);
|
AndroidMock.replay(contentProviderUtils);
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
||||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(
|
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(
|
||||||
getContext(), TRACK_ID_0, contentProviderUtils);
|
getContext(), TRACK_ID_0, contentProviderUtils);
|
||||||
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
||||||
assertEquals(TRACK_ID_0, trackId);
|
assertEquals(TRACK_ID_0, trackId);
|
||||||
|
|
||||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||||
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
|
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
|
||||||
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
||||||
assertEquals(
|
assertEquals(
|
||||||
time1 - time0 + time3 - time2, track.getValue().getTripStatistics().getTotalTime());
|
time1 - time0 + time3 - time2, track.getValue().getTripStatistics().getTotalTime());
|
||||||
|
|
||||||
AndroidMock.verify(contentProviderUtils);
|
AndroidMock.verify(contentProviderUtils);
|
||||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0,
|
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0,
|
||||||
DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-44
@@ -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,50 +32,50 @@ import org.easymock.IArgumentMatcher;
|
|||||||
*/
|
*/
|
||||||
public class LocationsMatcher implements IArgumentMatcher {
|
public class LocationsMatcher implements IArgumentMatcher {
|
||||||
|
|
||||||
public static Location[] eqLoc(Location expected) {
|
private final Location[] expectedLocations;
|
||||||
return eqLoc(new Location[] { expected });
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Location[] eqLoc(Location[] expected) {
|
private LocationsMatcher(Location[] expected) {
|
||||||
IArgumentMatcher matcher = new LocationsMatcher(expected);
|
this.expectedLocations = expected;
|
||||||
AndroidMock.reportMatcher(matcher);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Location[] expectedLocations;
|
|
||||||
|
|
||||||
private LocationsMatcher(Location[] expected) {
|
|
||||||
this.expectedLocations = expected;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void appendTo(StringBuffer buf) {
|
|
||||||
buf.append("eqLoc(").append(Arrays.toString(expectedLocations)).append(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(Object obj) {
|
|
||||||
if (!(obj instanceof Location[])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Location[] locations = (Location[]) obj;
|
|
||||||
if (locations.length < expectedLocations.length) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only check the first elements (those that will be taken into account)
|
public static Location[] eqLoc(Location expected) {
|
||||||
for (int i = 0; i < expectedLocations.length; i++) {
|
return eqLoc(new Location[]{expected});
|
||||||
if (!matchLocation(locations[i], expectedLocations[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchLocation(Location location1, Location location2) {
|
public static Location[] eqLoc(Location[] expected) {
|
||||||
return (location1.getTime() == location2.getTime())
|
IArgumentMatcher matcher = new LocationsMatcher(expected);
|
||||||
&& (location1.getLatitude() == location2.getLatitude())
|
AndroidMock.reportMatcher(matcher);
|
||||||
&& (location1.getLongitude() == location2.getLongitude())
|
return null;
|
||||||
&& (location1.getAltitude() == location2.getAltitude());
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public void appendTo(StringBuffer buf) {
|
||||||
|
buf.append("eqLoc(").append(Arrays.toString(expectedLocations)).append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object obj) {
|
||||||
|
if (!(obj instanceof Location[])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Location[] locations = (Location[]) obj;
|
||||||
|
if (locations.length < expectedLocations.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only check the first elements (those that will be taken into account)
|
||||||
|
for (int i = 0; i < expectedLocations.length; i++) {
|
||||||
|
if (!matchLocation(locations[i], expectedLocations[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchLocation(Location location1, Location location2) {
|
||||||
|
return (location1.getTime() == location2.getTime())
|
||||||
|
&& (location1.getLatitude() == location2.getLatitude())
|
||||||
|
&& (location1.getLongitude() == location2.getLongitude())
|
||||||
|
&& (location1.getAltitude() == location2.getAltitude());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-43
@@ -24,53 +24,53 @@ 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;
|
private static final long NEW_IDLE_TIME_BIG = 10000;
|
||||||
private static final long NEW_IDLE_TIME_BIG = 10000;
|
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 {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
adocationListenerPolicy = new AdaptiveLocationListenerPolicy(MIN, MAX, MIN_DISTANCE);
|
adocationListenerPolicy = new AdaptiveLocationListenerPolicy(MIN, MAX, MIN_DISTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the
|
* Tests the
|
||||||
* {@link AdaptiveLocationListenerPolicy#getDesiredPollingInterval()} in four
|
* {@link AdaptiveLocationListenerPolicy#getDesiredPollingInterval()} in four
|
||||||
* situations.
|
* situations.
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>The newIdleTime is bigger than max interval.</li>
|
* <li>The newIdleTime is bigger than max interval.</li>
|
||||||
* <li>The newIdleTime is between min and max interval.</li>
|
* <li>The newIdleTime is between min and max interval.</li>
|
||||||
* <li>The newIdleTime is smaller than max interval.</li>
|
* <li>The newIdleTime is smaller than max interval.</li>
|
||||||
* <li>The newIdleTime is smaller than the smallest interval unit.</li>
|
* <li>The newIdleTime is smaller than the smallest interval unit.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public void testGetDesiredPollingInterval() {
|
public void testGetDesiredPollingInterval() {
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
||||||
assertEquals(MAX, adocationListenerPolicy.getDesiredPollingInterval());
|
assertEquals(MAX, adocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
||||||
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the
|
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the
|
||||||
// nearest second.
|
// nearest second.
|
||||||
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000,
|
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000,
|
||||||
adocationListenerPolicy.getDesiredPollingInterval());
|
adocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
||||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link AdaptiveLocationListenerPolicy#getMinDistance()}.
|
* Tests the method {@link AdaptiveLocationListenerPolicy#getMinDistance()}.
|
||||||
*/
|
*/
|
||||||
public void testGetMinDistance() {
|
public void testGetMinDistance() {
|
||||||
assertEquals(MIN_DISTANCE, adocationListenerPolicy.getMinDistance());
|
assertEquals(MIN_DISTANCE, adocationListenerPolicy.getMinDistance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-67
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,74 +32,74 @@ import org.easymock.EasyMock;
|
|||||||
*/
|
*/
|
||||||
public class ControlRecordingServiceTest extends ServiceTestCase<ControlRecordingService> {
|
public class ControlRecordingServiceTest extends ServiceTestCase<ControlRecordingService> {
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private ControlRecordingService controlRecordingService;
|
private ControlRecordingService controlRecordingService;
|
||||||
|
|
||||||
public ControlRecordingServiceTest() {
|
public ControlRecordingServiceTest() {
|
||||||
super(ControlRecordingService.class);
|
super(ControlRecordingService.class);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
context = getContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the start of {@link ControlRecordingService} and tests the method
|
|
||||||
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
|
||||||
* to start a track recording.
|
|
||||||
*/
|
|
||||||
@UsesMocks(ITrackRecordingService.class)
|
|
||||||
public void testStartRecording() {
|
|
||||||
assertNull(controlRecordingService);
|
|
||||||
Intent intent = startControlRecordingService(context.getString(R.string.track_action_start));
|
|
||||||
assertNotNull(controlRecordingService);
|
|
||||||
|
|
||||||
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
|
||||||
.createStrictMock(ITrackRecordingService.class);
|
|
||||||
try {
|
|
||||||
EasyMock.expect(iTrackRecordingServiceMock.startNewTrack()).andReturn(1L);
|
|
||||||
EasyMock.replay(iTrackRecordingServiceMock);
|
|
||||||
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
|
||||||
EasyMock.verify(iTrackRecordingServiceMock);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
fail();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Tests the method
|
protected void setUp() throws Exception {
|
||||||
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
super.setUp();
|
||||||
* to stop a track recording.
|
context = getContext();
|
||||||
*/
|
|
||||||
@UsesMocks(ITrackRecordingService.class)
|
|
||||||
public void testStopRecording() {
|
|
||||||
Intent intent = startControlRecordingService(context.getString(R.string.track_action_end));
|
|
||||||
|
|
||||||
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
|
||||||
.createStrictMock(ITrackRecordingService.class);
|
|
||||||
try {
|
|
||||||
iTrackRecordingServiceMock.endCurrentTrack();
|
|
||||||
EasyMock.replay(iTrackRecordingServiceMock);
|
|
||||||
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
|
||||||
EasyMock.verify(iTrackRecordingServiceMock);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
fail();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a ControlRecordingService with a specified action.
|
* Tests the start of {@link ControlRecordingService} and tests the method
|
||||||
*
|
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
||||||
* @param action the action string in the start intent
|
* to start a track recording.
|
||||||
*/
|
*/
|
||||||
private Intent startControlRecordingService(String action) {
|
@UsesMocks(ITrackRecordingService.class)
|
||||||
Intent intent = new Intent(context, ControlRecordingService.class);
|
public void testStartRecording() {
|
||||||
intent.setAction(action);
|
assertNull(controlRecordingService);
|
||||||
startService(intent);
|
Intent intent = startControlRecordingService(context.getString(R.string.track_action_start));
|
||||||
controlRecordingService = getService();
|
assertNotNull(controlRecordingService);
|
||||||
return intent;
|
|
||||||
}
|
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
||||||
|
.createStrictMock(ITrackRecordingService.class);
|
||||||
|
try {
|
||||||
|
EasyMock.expect(iTrackRecordingServiceMock.startNewTrack()).andReturn(1L);
|
||||||
|
EasyMock.replay(iTrackRecordingServiceMock);
|
||||||
|
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||||
|
EasyMock.verify(iTrackRecordingServiceMock);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the method
|
||||||
|
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
||||||
|
* to stop a track recording.
|
||||||
|
*/
|
||||||
|
@UsesMocks(ITrackRecordingService.class)
|
||||||
|
public void testStopRecording() {
|
||||||
|
Intent intent = startControlRecordingService(context.getString(R.string.track_action_end));
|
||||||
|
|
||||||
|
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
||||||
|
.createStrictMock(ITrackRecordingService.class);
|
||||||
|
try {
|
||||||
|
iTrackRecordingServiceMock.endCurrentTrack();
|
||||||
|
EasyMock.replay(iTrackRecordingServiceMock);
|
||||||
|
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||||
|
EasyMock.verify(iTrackRecordingServiceMock);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a ControlRecordingService with a specified action.
|
||||||
|
*
|
||||||
|
* @param action the action string in the start intent
|
||||||
|
*/
|
||||||
|
private Intent startControlRecordingService(String action) {
|
||||||
|
Intent intent = new Intent(context, ControlRecordingService.class);
|
||||||
|
intent.setAction(action);
|
||||||
|
startService(intent);
|
||||||
|
controlRecordingService = getService();
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+660
-660
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -30,9 +30,9 @@ import org.junit.runner.RunWith;
|
|||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class AnnouncementPeriodicTaskFactoryTest {
|
public class AnnouncementPeriodicTaskFactoryTest {
|
||||||
public void testCreate() {
|
public void testCreate() {
|
||||||
PeriodicTaskFactory factory = new AnnouncementPeriodicTaskFactory();
|
PeriodicTaskFactory factory = new AnnouncementPeriodicTaskFactory();
|
||||||
PeriodicTask task = factory.create(InstrumentationRegistry.getInstrumentation().getContext());
|
PeriodicTask task = factory.create(InstrumentationRegistry.getInstrumentation().getContext());
|
||||||
Assert.assertTrue(task instanceof AnnouncementPeriodicTask);
|
Assert.assertTrue(task instanceof AnnouncementPeriodicTask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+454
-435
@@ -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,501 +23,530 @@ import android.telephony.PhoneStateListener;
|
|||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
import java.util.Locale;
|
import com.google.android.apps.mytracks.util.StringUtils;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import com.google.android.testing.mocking.AndroidMock;
|
||||||
|
import com.google.android.testing.mocking.UsesMocks;
|
||||||
|
|
||||||
import org.easymock.Capture;
|
import org.easymock.Capture;
|
||||||
import org.easymock.EasyMock;
|
import org.easymock.EasyMock;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link AnnouncementPeriodicTask}.
|
* Tests for {@link AnnouncementPeriodicTask}.
|
||||||
* WARNING: I'm not responsible if your eyes start bleeding while reading this
|
* WARNING: I'm not responsible if your eyes start bleeding while reading this
|
||||||
* code. You have been warned. It's still better than no test, though.
|
* code. You have been warned. It's still better than no test, though.
|
||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||||
|
|
||||||
// Use something other than our hardcoded value
|
// Use something other than our hardcoded value
|
||||||
private static final Locale DEFAULT_LOCALE = Locale.KOREAN;
|
private static final Locale DEFAULT_LOCALE = Locale.KOREAN;
|
||||||
private static final String ANNOUNCEMENT = "I can haz cheeseburger?";
|
private static final String ANNOUNCEMENT = "I can haz cheeseburger?";
|
||||||
private Locale oldDefaultLocale;
|
private Locale oldDefaultLocale;
|
||||||
|
|
||||||
private AnnouncementPeriodicTask task;
|
private AnnouncementPeriodicTask task;
|
||||||
private AnnouncementPeriodicTask mockTask;
|
private AnnouncementPeriodicTask mockTask;
|
||||||
private Capture<OnInitListener> initListenerCapture;
|
private Capture<OnInitListener> initListenerCapture;
|
||||||
private Capture<PhoneStateListener> phoneListenerCapture;
|
private Capture<PhoneStateListener> phoneListenerCapture;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@UsesMocks({
|
||||||
|
AnnouncementPeriodicTask.class,
|
||||||
|
StringUtils.class,
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
public int addEarcon(String earcon, String packagename, int resourceId) {
|
protected void setUp() throws Exception {
|
||||||
if (tts == null) {
|
super.setUp();
|
||||||
return super.addEarcon(earcon, packagename, resourceId);
|
|
||||||
}
|
|
||||||
return tts.addEarcon(earcon, packagename, resourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
oldDefaultLocale = Locale.getDefault();
|
||||||
public int addEarcon(String earcon, String filename) {
|
Locale.setDefault(DEFAULT_LOCALE);
|
||||||
if (tts == null) {
|
|
||||||
return super.addEarcon(earcon, filename);
|
|
||||||
}
|
|
||||||
return tts.addEarcon(earcon, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Eww, the effort required just to mock TextToSpeech is insane
|
||||||
public int addSpeech(String text, String packagename, int resourceId) {
|
final AtomicBoolean listenerCalled = new AtomicBoolean();
|
||||||
if (tts == null) {
|
OnInitListener blockingListener = new OnInitListener() {
|
||||||
return super.addSpeech(text, packagename, resourceId);
|
@Override
|
||||||
}
|
public void onInit(int status) {
|
||||||
return tts.addSpeech(text, packagename, resourceId);
|
synchronized (this) {
|
||||||
}
|
listenerCalled.set(true);
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
ttsDelegate = new TextToSpeechDelegate(getContext(), blockingListener);
|
||||||
public int addSpeech(String text, String filename) {
|
|
||||||
if (tts == null) {
|
|
||||||
return super.addSpeech(text, filename);
|
|
||||||
}
|
|
||||||
return tts.addSpeech(text, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Wait for all async operations done in the constructor to finish.
|
||||||
public Locale getLanguage() {
|
synchronized (blockingListener) {
|
||||||
if (tts == null) {
|
while (!listenerCalled.get()) {
|
||||||
return super.getLanguage();
|
// Releases the synchronized lock until we're woken up.
|
||||||
}
|
blockingListener.wait();
|
||||||
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({
|
|
||||||
AnnouncementPeriodicTask.class,
|
|
||||||
StringUtils.class,
|
|
||||||
})
|
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
oldDefaultLocale = Locale.getDefault();
|
|
||||||
Locale.setDefault(DEFAULT_LOCALE);
|
|
||||||
|
|
||||||
// Eww, the effort required just to mock TextToSpeech is insane
|
|
||||||
final AtomicBoolean listenerCalled = new AtomicBoolean();
|
|
||||||
OnInitListener blockingListener = new OnInitListener() {
|
|
||||||
@Override
|
|
||||||
public void onInit(int status) {
|
|
||||||
synchronized (this) {
|
|
||||||
listenerCalled.set(true);
|
|
||||||
notify();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ttsDelegate = new TextToSpeechDelegate(getContext(), blockingListener);
|
// Phew, done, now we can start forwarding calls
|
||||||
|
tts = AndroidMock.createMock(TextToSpeechInterface.class);
|
||||||
|
|
||||||
// Wait for all async operations done in the constructor to finish.
|
initListenerCapture = new Capture<OnInitListener>();
|
||||||
synchronized (blockingListener) {
|
phoneListenerCapture = new Capture<PhoneStateListener>();
|
||||||
while (!listenerCalled.get()) {
|
|
||||||
// Releases the synchronized lock until we're woken up.
|
// Create a partial forwarding mock
|
||||||
blockingListener.wait();
|
mockTask = AndroidMock.createMock(AnnouncementPeriodicTask.class, getContext());
|
||||||
}
|
task = new AnnouncementPeriodicTask(getContext()) {
|
||||||
|
@Override
|
||||||
|
protected TextToSpeech newTextToSpeech(Context ctx,
|
||||||
|
OnInitListener onInitListener) {
|
||||||
|
return mockTask.newTextToSpeech(ctx, onInitListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getAnnouncement(TripStatistics stats) {
|
||||||
|
return mockTask.getAnnouncement(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void listenToPhoneState(
|
||||||
|
PhoneStateListener listener, int events) {
|
||||||
|
mockTask.listenToPhoneState(listener, events);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phew, done, now we can start forwarding calls
|
@Override
|
||||||
tts = AndroidMock.createMock(TextToSpeechInterface.class);
|
protected void tearDown() {
|
||||||
|
Locale.setDefault(oldDefaultLocale);
|
||||||
|
}
|
||||||
|
|
||||||
initListenerCapture = new Capture<OnInitListener>();
|
public void testStart() {
|
||||||
phoneListenerCapture = new Capture<PhoneStateListener>();
|
doStart();
|
||||||
|
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||||
|
assertNotNull(ttsInitListener);
|
||||||
|
|
||||||
// Create a partial forwarding mock
|
AndroidMock.replay(tts);
|
||||||
mockTask = AndroidMock.createMock(AnnouncementPeriodicTask.class, getContext());
|
|
||||||
task = new AnnouncementPeriodicTask(getContext()) {
|
|
||||||
@Override
|
|
||||||
protected TextToSpeech newTextToSpeech(Context ctx,
|
|
||||||
OnInitListener onInitListener) {
|
|
||||||
return mockTask.newTextToSpeech(ctx, onInitListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
ttsInitListener.onInit(TextToSpeech.SUCCESS);
|
||||||
protected String getAnnouncement(TripStatistics stats) {
|
|
||||||
return mockTask.getAnnouncement(stats);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
AndroidMock.verify(mockTask, tts);
|
||||||
protected void listenToPhoneState(
|
}
|
||||||
PhoneStateListener listener, int events) {
|
|
||||||
mockTask.listenToPhoneState(listener, events);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public void testStart_notReady() {
|
||||||
protected void tearDown() {
|
doStart();
|
||||||
Locale.setDefault(oldDefaultLocale);
|
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||||
}
|
assertNotNull(ttsInitListener);
|
||||||
|
|
||||||
public void testStart() {
|
AndroidMock.replay(tts);
|
||||||
doStart();
|
|
||||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
|
||||||
assertNotNull(ttsInitListener);
|
|
||||||
|
|
||||||
AndroidMock.replay(tts);
|
ttsInitListener.onInit(TextToSpeech.ERROR);
|
||||||
|
|
||||||
ttsInitListener.onInit(TextToSpeech.SUCCESS);
|
AndroidMock.verify(mockTask, tts);
|
||||||
|
}
|
||||||
|
|
||||||
AndroidMock.verify(mockTask, tts);
|
public void testShutdown() {
|
||||||
}
|
// First, start
|
||||||
|
doStart();
|
||||||
|
AndroidMock.verify(mockTask);
|
||||||
|
AndroidMock.reset(mockTask);
|
||||||
|
|
||||||
public void testStart_notReady() {
|
// Then, shut down
|
||||||
doStart();
|
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
mockTask.listenToPhoneState(
|
||||||
assertNotNull(ttsInitListener);
|
same(phoneListener), eq(PhoneStateListener.LISTEN_NONE));
|
||||||
|
tts.shutdown();
|
||||||
|
AndroidMock.replay(mockTask, tts);
|
||||||
|
task.shutdown();
|
||||||
|
AndroidMock.verify(mockTask, tts);
|
||||||
|
}
|
||||||
|
|
||||||
AndroidMock.replay(tts);
|
public void testRun() throws Exception {
|
||||||
|
// Expect service data calls
|
||||||
|
TripStatistics stats = new TripStatistics();
|
||||||
|
|
||||||
ttsInitListener.onInit(TextToSpeech.ERROR);
|
// Expect announcement building call
|
||||||
|
expect(mockTask.getAnnouncement(same(stats))).andStubReturn(ANNOUNCEMENT);
|
||||||
|
|
||||||
AndroidMock.verify(mockTask, tts);
|
// Put task in "ready" state
|
||||||
}
|
startTask(TextToSpeech.SUCCESS);
|
||||||
|
|
||||||
public void testShutdown() {
|
expect(tts.isLanguageAvailable(DEFAULT_LOCALE)).andStubReturn(TextToSpeech.LANG_AVAILABLE);
|
||||||
// First, start
|
expect(tts.setLanguage(DEFAULT_LOCALE)).andReturn(TextToSpeech.LANG_AVAILABLE);
|
||||||
doStart();
|
expect(tts.setSpeechRate(AnnouncementPeriodicTask.TTS_SPEECH_RATE)).andReturn(
|
||||||
AndroidMock.verify(mockTask);
|
TextToSpeech.SUCCESS);
|
||||||
AndroidMock.reset(mockTask);
|
expect(tts.setOnUtteranceCompletedListener((OnUtteranceCompletedListener) EasyMock.anyObject()))
|
||||||
|
.andReturn(0);
|
||||||
|
|
||||||
// Then, shut down
|
// Expect actual announcement call
|
||||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
expect(
|
||||||
mockTask.listenToPhoneState(
|
tts.speak(eq(ANNOUNCEMENT), eq(TextToSpeech.QUEUE_FLUSH),
|
||||||
same(phoneListener), eq(PhoneStateListener.LISTEN_NONE));
|
eq(AnnouncementPeriodicTask.SPEECH_PARAMS))).andReturn(0);
|
||||||
tts.shutdown();
|
|
||||||
AndroidMock.replay(mockTask, tts);
|
|
||||||
task.shutdown();
|
|
||||||
AndroidMock.verify(mockTask, tts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRun() throws Exception {
|
// Run the announcement
|
||||||
// Expect service data calls
|
AndroidMock.replay(tts);
|
||||||
TripStatistics stats = new TripStatistics();
|
task.announce(stats);
|
||||||
|
AndroidMock.verify(mockTask, tts);
|
||||||
|
}
|
||||||
|
|
||||||
// Expect announcement building call
|
public void testRun_notReady() throws Exception {
|
||||||
expect(mockTask.getAnnouncement(same(stats))).andStubReturn(ANNOUNCEMENT);
|
// Put task in "not ready" state
|
||||||
|
startTask(TextToSpeech.ERROR);
|
||||||
|
|
||||||
// Put task in "ready" state
|
// Run the announcement
|
||||||
startTask(TextToSpeech.SUCCESS);
|
AndroidMock.replay(tts);
|
||||||
|
task.run(null);
|
||||||
|
AndroidMock.verify(mockTask, tts);
|
||||||
|
}
|
||||||
|
|
||||||
expect(tts.isLanguageAvailable(DEFAULT_LOCALE)).andStubReturn(TextToSpeech.LANG_AVAILABLE);
|
public void testRun_duringCall() throws Exception {
|
||||||
expect(tts.setLanguage(DEFAULT_LOCALE)).andReturn(TextToSpeech.LANG_AVAILABLE);
|
startTask(TextToSpeech.SUCCESS);
|
||||||
expect(tts.setSpeechRate(AnnouncementPeriodicTask.TTS_SPEECH_RATE)).andReturn(
|
|
||||||
TextToSpeech.SUCCESS);
|
|
||||||
expect(tts.setOnUtteranceCompletedListener((OnUtteranceCompletedListener) EasyMock.anyObject()))
|
|
||||||
.andReturn(0);
|
|
||||||
|
|
||||||
// Expect actual announcement call
|
expect(tts.isSpeaking()).andStubReturn(false);
|
||||||
expect(
|
|
||||||
tts.speak(eq(ANNOUNCEMENT), eq(TextToSpeech.QUEUE_FLUSH),
|
|
||||||
eq(AnnouncementPeriodicTask.SPEECH_PARAMS))).andReturn(0);
|
|
||||||
|
|
||||||
// Run the announcement
|
// Run the announcement
|
||||||
AndroidMock.replay(tts);
|
AndroidMock.replay(tts);
|
||||||
task.announce(stats);
|
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||||
AndroidMock.verify(mockTask, tts);
|
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null);
|
||||||
}
|
task.run(null);
|
||||||
|
AndroidMock.verify(mockTask, tts);
|
||||||
|
}
|
||||||
|
|
||||||
public void testRun_notReady() throws Exception {
|
public void testRun_ringWhileSpeaking() throws Exception {
|
||||||
// Put task in "not ready" state
|
startTask(TextToSpeech.SUCCESS);
|
||||||
startTask(TextToSpeech.ERROR);
|
|
||||||
|
|
||||||
// Run the announcement
|
expect(tts.isSpeaking()).andStubReturn(true);
|
||||||
AndroidMock.replay(tts);
|
expect(tts.stop()).andReturn(TextToSpeech.SUCCESS);
|
||||||
task.run(null);
|
|
||||||
AndroidMock.verify(mockTask, tts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRun_duringCall() throws Exception {
|
AndroidMock.replay(tts);
|
||||||
startTask(TextToSpeech.SUCCESS);
|
|
||||||
|
|
||||||
expect(tts.isSpeaking()).andStubReturn(false);
|
// Update the state to ringing - this should stop the current announcement.
|
||||||
|
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||||
|
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||||
|
|
||||||
// Run the announcement
|
// Run the announcement - this should do nothing.
|
||||||
AndroidMock.replay(tts);
|
task.run(null);
|
||||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
|
||||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null);
|
|
||||||
task.run(null);
|
|
||||||
AndroidMock.verify(mockTask, tts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRun_ringWhileSpeaking() throws Exception {
|
AndroidMock.verify(mockTask, tts);
|
||||||
startTask(TextToSpeech.SUCCESS);
|
}
|
||||||
|
|
||||||
expect(tts.isSpeaking()).andStubReturn(true);
|
public void testRun_whileRinging() throws Exception {
|
||||||
expect(tts.stop()).andReturn(TextToSpeech.SUCCESS);
|
startTask(TextToSpeech.SUCCESS);
|
||||||
|
|
||||||
AndroidMock.replay(tts);
|
expect(tts.isSpeaking()).andStubReturn(false);
|
||||||
|
|
||||||
// Update the state to ringing - this should stop the current announcement.
|
// Run the announcement
|
||||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
AndroidMock.replay(tts);
|
||||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||||
|
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||||
|
task.run(null);
|
||||||
|
AndroidMock.verify(mockTask, tts);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the announcement - this should do nothing.
|
public void testRun_noService() throws Exception {
|
||||||
task.run(null);
|
startTask(TextToSpeech.SUCCESS);
|
||||||
|
|
||||||
AndroidMock.verify(mockTask, tts);
|
// Run the announcement
|
||||||
}
|
AndroidMock.replay(tts);
|
||||||
|
task.run(null);
|
||||||
|
AndroidMock.verify(mockTask, tts);
|
||||||
|
}
|
||||||
|
|
||||||
public void testRun_whileRinging() throws Exception {
|
public void testRun_noStats() throws Exception {
|
||||||
startTask(TextToSpeech.SUCCESS);
|
// Expect service data calls
|
||||||
|
|
||||||
expect(tts.isSpeaking()).andStubReturn(false);
|
startTask(TextToSpeech.SUCCESS);
|
||||||
|
|
||||||
// Run the announcement
|
// Run the announcement
|
||||||
AndroidMock.replay(tts);
|
AndroidMock.replay(tts);
|
||||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
task.run(null);
|
||||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
AndroidMock.verify(mockTask, tts);
|
||||||
task.run(null);
|
}
|
||||||
AndroidMock.verify(mockTask, tts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRun_noService() throws Exception {
|
/**
|
||||||
startTask(TextToSpeech.SUCCESS);
|
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time zero.
|
||||||
|
*/
|
||||||
|
public void testGetAnnounceTime_time_zero() {
|
||||||
|
long time = 0; // 0 seconds
|
||||||
|
assertEquals("0 minutes 0 seconds", task.getAnnounceTime(time));
|
||||||
|
}
|
||||||
|
|
||||||
// Run the announcement
|
/**
|
||||||
AndroidMock.replay(tts);
|
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time one.
|
||||||
task.run(null);
|
*/
|
||||||
AndroidMock.verify(mockTask, tts);
|
public void testGetAnnounceTime_time_one() {
|
||||||
}
|
long time = 1 * 1000; // 1 second
|
||||||
|
assertEquals("0 minutes 1 second", task.getAnnounceTime(time));
|
||||||
|
}
|
||||||
|
|
||||||
public void testRun_noStats() throws Exception {
|
/**
|
||||||
// Expect service data calls
|
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
||||||
|
* numbers with the hour unit.
|
||||||
|
*/
|
||||||
|
public void testGetAnnounceTime_singular_has_hour() {
|
||||||
|
long time = (1 * 60 * 60 * 1000) + (1 * 60 * 1000) + (1 * 1000); // 1 hour 1 minute 1 second
|
||||||
|
assertEquals("1 hour 1 minute 1 second", task.getAnnounceTime(time));
|
||||||
|
}
|
||||||
|
|
||||||
startTask(TextToSpeech.SUCCESS);
|
/**
|
||||||
|
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
||||||
|
* with the hour unit.
|
||||||
|
*/
|
||||||
|
public void testGetAnnounceTime_plural_has_hour() {
|
||||||
|
long time = (2 * 60 * 60 * 1000) + (2 * 60 * 1000) + (2 * 1000); // 2 hours 2 minutes 2 seconds
|
||||||
|
assertEquals("2 hours 2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||||
|
}
|
||||||
|
|
||||||
// Run the announcement
|
/**
|
||||||
AndroidMock.replay(tts);
|
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
||||||
task.run(null);
|
* numbers without the hour unit.
|
||||||
AndroidMock.verify(mockTask, tts);
|
*/
|
||||||
}
|
public void testGetAnnounceTime_singular_no_hour() {
|
||||||
|
long time = (1 * 60 * 1000) + (1 * 1000); // 1 minute 1 second
|
||||||
|
assertEquals("1 minute 1 second", task.getAnnounceTime(time));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time zero.
|
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
||||||
*/
|
* without the hour unit.
|
||||||
public void testGetAnnounceTime_time_zero() {
|
*/
|
||||||
long time = 0; // 0 seconds
|
public void testGetAnnounceTime_plural_no_hour() {
|
||||||
assertEquals("0 minutes 0 seconds", task.getAnnounceTime(time));
|
long time = (2 * 60 * 1000) + (2 * 1000); // 2 minutes 2 seconds
|
||||||
}
|
assertEquals("2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private void startTask(int state) {
|
||||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time one.
|
AndroidMock.resetToNice(tts);
|
||||||
*/
|
AndroidMock.replay(tts);
|
||||||
public void testGetAnnounceTime_time_one() {
|
doStart();
|
||||||
long time = 1 * 1000; // 1 second
|
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||||
assertEquals("0 minutes 1 second", task.getAnnounceTime(time));
|
ttsInitListener.onInit(state);
|
||||||
}
|
AndroidMock.resetToDefault(tts);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private void doStart() {
|
||||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
mockTask.listenToPhoneState(capture(phoneListenerCapture),
|
||||||
* numbers with the hour unit.
|
eq(PhoneStateListener.LISTEN_CALL_STATE));
|
||||||
*/
|
expect(mockTask.newTextToSpeech(
|
||||||
public void testGetAnnounceTime_singular_has_hour() {
|
same(getContext()), capture(initListenerCapture)))
|
||||||
long time = (1 * 60 * 60 * 1000) + (1 * 60 * 1000) + (1 * 1000); // 1 hour 1 minute 1 second
|
.andStubReturn(ttsDelegate);
|
||||||
assertEquals("1 hour 1 minute 1 second", task.getAnnounceTime(time));
|
AndroidMock.replay(mockTask);
|
||||||
}
|
task.start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
* Mockable interface that we delegate TTS calls to.
|
||||||
* with the hour unit.
|
*/
|
||||||
*/
|
interface TextToSpeechInterface {
|
||||||
public void testGetAnnounceTime_plural_has_hour() {
|
int addEarcon(String earcon, String packagename, int resourceId);
|
||||||
long time = (2 * 60 * 60 * 1000) + (2 * 60 * 1000) + (2 * 1000); // 2 hours 2 minutes 2 seconds
|
|
||||||
assertEquals("2 hours 2 minutes 2 seconds", task.getAnnounceTime(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
int addEarcon(String earcon, String filename);
|
||||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
|
||||||
* numbers without the hour unit.
|
|
||||||
*/
|
|
||||||
public void testGetAnnounceTime_singular_no_hour() {
|
|
||||||
long time = (1 * 60 * 1000) + (1 * 1000); // 1 minute 1 second
|
|
||||||
assertEquals("1 minute 1 second", task.getAnnounceTime(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
int addSpeech(String text, String packagename, int resourceId);
|
||||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
|
||||||
* without the hour unit.
|
|
||||||
*/
|
|
||||||
public void testGetAnnounceTime_plural_no_hour() {
|
|
||||||
long time = (2 * 60 * 1000) + (2 * 1000); // 2 minutes 2 seconds
|
|
||||||
assertEquals("2 minutes 2 seconds", task.getAnnounceTime(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startTask(int state) {
|
int addSpeech(String text, String filename);
|
||||||
AndroidMock.resetToNice(tts);
|
|
||||||
AndroidMock.replay(tts);
|
|
||||||
doStart();
|
|
||||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
|
||||||
ttsInitListener.onInit(state);
|
|
||||||
AndroidMock.resetToDefault(tts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doStart() {
|
boolean areDefaultsEnforced();
|
||||||
mockTask.listenToPhoneState(capture(phoneListenerCapture),
|
|
||||||
eq(PhoneStateListener.LISTEN_CALL_STATE));
|
String getDefaultEngine();
|
||||||
expect(mockTask.newTextToSpeech(
|
|
||||||
same(getContext()), capture(initListenerCapture)))
|
Locale getLanguage();
|
||||||
.andStubReturn(ttsDelegate);
|
|
||||||
AndroidMock.replay(mockTask);
|
int isLanguageAvailable(Locale loc);
|
||||||
task.start();
|
|
||||||
}
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-68
@@ -25,81 +25,81 @@ import junit.framework.TestCase;
|
|||||||
*/
|
*/
|
||||||
public class TripStatisticsTest extends TestCase {
|
public class TripStatisticsTest extends TestCase {
|
||||||
|
|
||||||
private TripStatistics statistics;
|
private TripStatistics statistics;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
|
||||||
statistics = new TripStatistics();
|
statistics = new TripStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSetBounds() {
|
public void testSetBounds() {
|
||||||
// This is not a trivial setter, conversion happens in it
|
// This is not a trivial setter, conversion happens in it
|
||||||
statistics.setBounds(12345, -34567, 56789, -98765);
|
statistics.setBounds(12345, -34567, 56789, -98765);
|
||||||
assertEquals(12345, statistics.getLeft());
|
assertEquals(12345, statistics.getLeft());
|
||||||
assertEquals(-34567, statistics.getTop());
|
assertEquals(-34567, statistics.getTop());
|
||||||
assertEquals(56789, statistics.getRight());
|
assertEquals(56789, statistics.getRight());
|
||||||
assertEquals(-98765, statistics.getBottom());
|
assertEquals(-98765, statistics.getBottom());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMerge() {
|
public void testMerge() {
|
||||||
TripStatistics statistics2 = new TripStatistics();
|
TripStatistics statistics2 = new TripStatistics();
|
||||||
statistics.setStartTime(1000L); // Resulting start time
|
statistics.setStartTime(1000L); // Resulting start time
|
||||||
statistics.setStopTime(2500L);
|
statistics.setStopTime(2500L);
|
||||||
statistics2.setStartTime(3000L);
|
statistics2.setStartTime(3000L);
|
||||||
statistics2.setStopTime(4000L); // Resulting stop time
|
statistics2.setStopTime(4000L); // Resulting stop time
|
||||||
statistics.setTotalTime(1500L);
|
statistics.setTotalTime(1500L);
|
||||||
statistics2.setTotalTime(1000L); // Result: 1500+1000
|
statistics2.setTotalTime(1000L); // Result: 1500+1000
|
||||||
statistics.setMovingTime(700L);
|
statistics.setMovingTime(700L);
|
||||||
statistics2.setMovingTime(600L); // Result: 700+600
|
statistics2.setMovingTime(600L); // Result: 700+600
|
||||||
statistics.setTotalDistance(750.0);
|
statistics.setTotalDistance(750.0);
|
||||||
statistics2.setTotalDistance(350.0); // Result: 750+350
|
statistics2.setTotalDistance(350.0); // Result: 750+350
|
||||||
statistics.setTotalElevationGain(50.0);
|
statistics.setTotalElevationGain(50.0);
|
||||||
statistics2.setTotalElevationGain(850.0); // Result: 850+50
|
statistics2.setTotalElevationGain(850.0); // Result: 850+50
|
||||||
statistics.setMaxSpeed(60.0); // Resulting max speed
|
statistics.setMaxSpeed(60.0); // Resulting max speed
|
||||||
statistics2.setMaxSpeed(30.0);
|
statistics2.setMaxSpeed(30.0);
|
||||||
statistics.setMaxElevation(1250.0);
|
statistics.setMaxElevation(1250.0);
|
||||||
statistics.setMinElevation(1200.0); // Resulting min elevation
|
statistics.setMinElevation(1200.0); // Resulting min elevation
|
||||||
statistics2.setMaxElevation(3575.0); // Resulting max elevation
|
statistics2.setMaxElevation(3575.0); // Resulting max elevation
|
||||||
statistics2.setMinElevation(2800.0);
|
statistics2.setMinElevation(2800.0);
|
||||||
statistics.setMaxGrade(15.0);
|
statistics.setMaxGrade(15.0);
|
||||||
statistics.setMinGrade(-25.0); // Resulting min grade
|
statistics.setMinGrade(-25.0); // Resulting min grade
|
||||||
statistics2.setMaxGrade(35.0); // Resulting max grade
|
statistics2.setMaxGrade(35.0); // Resulting max grade
|
||||||
statistics2.setMinGrade(0.0);
|
statistics2.setMinGrade(0.0);
|
||||||
|
|
||||||
// Resulting bounds: -10000, 35000, 30000, -40000
|
// Resulting bounds: -10000, 35000, 30000, -40000
|
||||||
statistics.setBounds(-10000, 20000, 30000, -40000);
|
statistics.setBounds(-10000, 20000, 30000, -40000);
|
||||||
statistics2.setBounds(-5000, 35000, 0, 20000);
|
statistics2.setBounds(-5000, 35000, 0, 20000);
|
||||||
|
|
||||||
statistics.merge(statistics2);
|
statistics.merge(statistics2);
|
||||||
|
|
||||||
assertEquals(1000L, statistics.getStartTime());
|
assertEquals(1000L, statistics.getStartTime());
|
||||||
assertEquals(4000L, statistics.getStopTime());
|
assertEquals(4000L, statistics.getStopTime());
|
||||||
assertEquals(2500L, statistics.getTotalTime());
|
assertEquals(2500L, statistics.getTotalTime());
|
||||||
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());
|
||||||
assertEquals(-40000, statistics.getBottom());
|
assertEquals(-40000, statistics.getBottom());
|
||||||
assertEquals(1200.0, statistics.getMinElevation());
|
assertEquals(1200.0, statistics.getMinElevation());
|
||||||
assertEquals(3575.0, statistics.getMaxElevation());
|
assertEquals(3575.0, statistics.getMaxElevation());
|
||||||
assertEquals(-25.0, statistics.getMinGrade());
|
assertEquals(-25.0, statistics.getMinGrade());
|
||||||
assertEquals(35.0, statistics.getMaxGrade());
|
assertEquals(35.0, statistics.getMaxGrade());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetAverageSpeed() {
|
public void testGetAverageSpeed() {
|
||||||
statistics.setTotalDistance(1000.0);
|
statistics.setTotalDistance(1000.0);
|
||||||
statistics.setTotalTime(50000); // in milliseconds
|
statistics.setTotalTime(50000); // in milliseconds
|
||||||
assertEquals(20.0, statistics.getAverageSpeed());
|
assertEquals(20.0, statistics.getAverageSpeed());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetAverageMovingSpeed() {
|
public void testGetAverageMovingSpeed() {
|
||||||
statistics.setTotalDistance(1000.0);
|
statistics.setTotalDistance(1000.0);
|
||||||
statistics.setMovingTime(20000); // in milliseconds
|
statistics.setMovingTime(20000); // in milliseconds
|
||||||
assertEquals(50.0, statistics.getAverageMovingSpeed());
|
assertEquals(50.0, statistics.getAverageMovingSpeed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}.
|
||||||
*
|
*
|
||||||
@@ -28,94 +28,94 @@ import junit.framework.TestCase;
|
|||||||
*/
|
*/
|
||||||
public class FileUtilsTest extends TestCase {
|
public class FileUtilsTest extends TestCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#getPath(String...)}.
|
* Tests {@link FileUtils#getPath(String...)}.
|
||||||
*/
|
*/
|
||||||
public void testBuildExternalDirectoryPath() {
|
public void testBuildExternalDirectoryPath() {
|
||||||
String expectedName = Environment.getExternalStorageDirectory() + File.separator
|
String expectedName = Environment.getExternalStorageDirectory() + File.separator
|
||||||
+ FileUtils.SDCARD_TOP_DIR + File.separator + "a" + File.separator + "b" + File.separator
|
+ FileUtils.SDCARD_TOP_DIR + File.separator + "a" + File.separator + "b" + File.separator
|
||||||
+ "c";
|
+ "c";
|
||||||
String dirName = FileUtils.getPath("a", "b", "c");
|
String dirName = FileUtils.getPath("a", "b", "c");
|
||||||
assertEquals(expectedName, dirName);
|
assertEquals(expectedName, dirName);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the
|
|
||||||
* file is new.
|
|
||||||
*/
|
|
||||||
public void testBuildUniqueFileName_new() {
|
|
||||||
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
|
||||||
assertEquals("Filename.ext", filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the
|
|
||||||
* file exists already.
|
|
||||||
*/
|
|
||||||
public void testBuildUniqueFileName_exist() {
|
|
||||||
// Expect "/default.prop" to exist on the phone/emulator
|
|
||||||
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
|
||||||
assertEquals("default(1).prop", filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with special characters.
|
|
||||||
* Verifies that they are sanitized.
|
|
||||||
*/
|
|
||||||
public void testSanitizeFileName() {
|
|
||||||
String name = "Swim\10ming-^across:/the/ pacific (ocean).";
|
|
||||||
String expected = "Swim_ming-^across_the_ pacific (ocean)_";
|
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with i18n characters (in
|
|
||||||
* Chinese and Russian). Verifies that they are allowed.
|
|
||||||
*/
|
|
||||||
public void testSanitizeFileName_i18n() {
|
|
||||||
String name = "您好-привет";
|
|
||||||
String expected = "您好-привет";
|
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with special FAT32
|
|
||||||
* characters. Verifies that they are allowed.
|
|
||||||
*/
|
|
||||||
public void testSanitizeFileName_special_characters() {
|
|
||||||
String name = "$%'-_@~`!(){}^#&+,;=[] ";
|
|
||||||
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
|
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with multiple escaped
|
|
||||||
* characters in a row. Verifies that they are collapsed into one underscore.
|
|
||||||
*/
|
|
||||||
public void testSanitizeFileName_collapse() {
|
|
||||||
String name = "hello//there";
|
|
||||||
String expected = "hello_there";
|
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link FileUtils#truncateFileName(File, String, String)}. Verifies
|
|
||||||
* the a long file name is truncated.
|
|
||||||
*/
|
|
||||||
public void testTruncateFileName() {
|
|
||||||
File directory = new File("/dir1/dir2/");
|
|
||||||
String suffix = ".gpx";
|
|
||||||
char[] name = new char[FileUtils.MAX_FAT32_PATH_LENGTH];
|
|
||||||
for (int i = 0; i < name.length; i++) {
|
|
||||||
name[i] = 'a';
|
|
||||||
}
|
}
|
||||||
String nameString = new String(name);
|
|
||||||
String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
|
|
||||||
|
|
||||||
for (int i = 0; i < truncated.length(); i++) {
|
/**
|
||||||
assertEquals('a', truncated.charAt(i));
|
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the
|
||||||
|
* file is new.
|
||||||
|
*/
|
||||||
|
public void testBuildUniqueFileName_new() {
|
||||||
|
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
||||||
|
assertEquals("Filename.ext", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the
|
||||||
|
* file exists already.
|
||||||
|
*/
|
||||||
|
public void testBuildUniqueFileName_exist() {
|
||||||
|
// Expect "/default.prop" to exist on the phone/emulator
|
||||||
|
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
||||||
|
assertEquals("default(1).prop", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link FileUtils#sanitizeFileName(String)} with special characters.
|
||||||
|
* Verifies that they are sanitized.
|
||||||
|
*/
|
||||||
|
public void testSanitizeFileName() {
|
||||||
|
String name = "Swim\10ming-^across:/the/ pacific (ocean).";
|
||||||
|
String expected = "Swim_ming-^across_the_ pacific (ocean)_";
|
||||||
|
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link FileUtils#sanitizeFileName(String)} with i18n characters (in
|
||||||
|
* Chinese and Russian). Verifies that they are allowed.
|
||||||
|
*/
|
||||||
|
public void testSanitizeFileName_i18n() {
|
||||||
|
String name = "您好-привет";
|
||||||
|
String expected = "您好-привет";
|
||||||
|
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link FileUtils#sanitizeFileName(String)} with special FAT32
|
||||||
|
* characters. Verifies that they are allowed.
|
||||||
|
*/
|
||||||
|
public void testSanitizeFileName_special_characters() {
|
||||||
|
String name = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||||
|
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||||
|
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link FileUtils#sanitizeFileName(String)} with multiple escaped
|
||||||
|
* characters in a row. Verifies that they are collapsed into one underscore.
|
||||||
|
*/
|
||||||
|
public void testSanitizeFileName_collapse() {
|
||||||
|
String name = "hello//there";
|
||||||
|
String expected = "hello_there";
|
||||||
|
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link FileUtils#truncateFileName(File, String, String)}. Verifies
|
||||||
|
* the a long file name is truncated.
|
||||||
|
*/
|
||||||
|
public void testTruncateFileName() {
|
||||||
|
File directory = new File("/dir1/dir2/");
|
||||||
|
String suffix = ".gpx";
|
||||||
|
char[] name = new char[FileUtils.MAX_FAT32_PATH_LENGTH];
|
||||||
|
for (int i = 0; i < name.length; i++) {
|
||||||
|
name[i] = 'a';
|
||||||
|
}
|
||||||
|
String nameString = new String(name);
|
||||||
|
String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
|
||||||
|
|
||||||
|
for (int i = 0; i < truncated.length(); i++) {
|
||||||
|
assertEquals('a', truncated.charAt(i));
|
||||||
|
}
|
||||||
|
assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH,
|
||||||
|
new File(directory, truncated + suffix).getPath().length());
|
||||||
}
|
}
|
||||||
assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH,
|
|
||||||
new File(directory, truncated + suffix).getPath().length());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,156 +33,156 @@ import java.util.TimeZone;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class StringUtilsTest {
|
public class StringUtilsTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatDateTimeIso8601(long)}.
|
* Tests {@link StringUtils#formatDateTimeIso8601(long)}.
|
||||||
*/
|
*/
|
||||||
public void testFormatDateTimeIso8601() {
|
public void testFormatDateTimeIso8601() {
|
||||||
Assert.assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
Assert.assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatElapsedTime(long)}.
|
* Tests {@link StringUtils#formatElapsedTime(long)}.
|
||||||
*/
|
*/
|
||||||
public void testformatElapsedTime() {
|
public void testformatElapsedTime() {
|
||||||
// 1 second
|
// 1 second
|
||||||
Assert.assertEquals("00:01", StringUtils.formatElapsedTime(1000));
|
Assert.assertEquals("00:01", StringUtils.formatElapsedTime(1000));
|
||||||
// 10 seconds
|
// 10 seconds
|
||||||
Assert.assertEquals("00:10", StringUtils.formatElapsedTime(10000));
|
Assert.assertEquals("00:10", StringUtils.formatElapsedTime(10000));
|
||||||
// 1 minute
|
// 1 minute
|
||||||
Assert.assertEquals("01:00", StringUtils.formatElapsedTime(60000));
|
Assert.assertEquals("01:00", StringUtils.formatElapsedTime(60000));
|
||||||
// 10 minutes
|
// 10 minutes
|
||||||
Assert.assertEquals("10:00", StringUtils.formatElapsedTime(600000));
|
Assert.assertEquals("10:00", StringUtils.formatElapsedTime(600000));
|
||||||
// 1 hour
|
// 1 hour
|
||||||
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
|
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
|
||||||
// 10 hours
|
// 10 hours
|
||||||
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
|
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
|
||||||
// 100 hours
|
// 100 hours
|
||||||
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
|
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatElapsedTimeWithHour(long)}.
|
* Tests {@link StringUtils#formatElapsedTimeWithHour(long)}.
|
||||||
*/
|
*/
|
||||||
public void testformatElapsedTimeWithHour() {
|
public void testformatElapsedTimeWithHour() {
|
||||||
// 1 second
|
// 1 second
|
||||||
Assert.assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
|
Assert.assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
|
||||||
// 10 seconds
|
// 10 seconds
|
||||||
Assert.assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
|
Assert.assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
|
||||||
// 1 minute
|
// 1 minute
|
||||||
Assert.assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
|
Assert.assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
|
||||||
// 10 minutes
|
// 10 minutes
|
||||||
Assert.assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
|
Assert.assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
|
||||||
// 1 hour
|
// 1 hour
|
||||||
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
|
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
|
||||||
// 10 hours
|
// 10 hours
|
||||||
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
|
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
|
||||||
// 100 hours
|
// 100 hours
|
||||||
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatDistance(android.content.Context, double,
|
* Tests {@link StringUtils#formatDistance(android.content.Context, double,
|
||||||
* boolean)}.
|
* boolean)}.
|
||||||
*/
|
*/
|
||||||
public void testFormatDistance() {
|
public void testFormatDistance() {
|
||||||
// A large number in metric
|
// A large number in metric
|
||||||
Assert.assertEquals("5.00 km", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 5000, true));
|
Assert.assertEquals("5.00 km", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 5000, true));
|
||||||
// A large number in imperial
|
// A large number in imperial
|
||||||
Assert.assertEquals("3.11 mi", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 5000, false));
|
Assert.assertEquals("3.11 mi", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 5000, false));
|
||||||
// A small number in metric
|
// A small number in metric
|
||||||
Assert.assertEquals("100.00 m", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 100, true));
|
Assert.assertEquals("100.00 m", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 100, true));
|
||||||
// A small number in imperial
|
// A small number in imperial
|
||||||
Assert.assertEquals("328.08 ft", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 100, false));
|
Assert.assertEquals("328.08 ft", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 100, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatCData(String)}.
|
* Tests {@link StringUtils#formatCData(String)}.
|
||||||
*/
|
*/
|
||||||
public void testFormatCData() {
|
public void testFormatCData() {
|
||||||
Assert.assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
|
Assert.assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
|
||||||
Assert.assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
Assert.assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#getTime(String)}.
|
* Tests {@link StringUtils#getTime(String)}.
|
||||||
*/
|
*/
|
||||||
public void testGetTime() {
|
public void testGetTime() {
|
||||||
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#getTime(String)} with fractional seconds.
|
* Tests {@link StringUtils#getTime(String)} with fractional seconds.
|
||||||
*/
|
*/
|
||||||
public void testGetTime_fractional() {
|
public void testGetTime_fractional() {
|
||||||
assertGetTime("2010-05-04T03:02:01.3", 2010, 5, 4, 3, 2, 1, 300);
|
assertGetTime("2010-05-04T03:02:01.3", 2010, 5, 4, 3, 2, 1, 300);
|
||||||
assertGetTime("2010-05-04T03:02:01.35", 2010, 5, 4, 3, 2, 1, 350);
|
assertGetTime("2010-05-04T03:02:01.35", 2010, 5, 4, 3, 2, 1, 350);
|
||||||
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
assertGetTime("2010-05-04T03:02:01.3525Z", 2010, 5, 4, 3, 2, 1, 352);
|
assertGetTime("2010-05-04T03:02:01.3525Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#getTime(String)} with time zone.
|
* Tests {@link StringUtils#getTime(String)} with time zone.
|
||||||
*/
|
*/
|
||||||
public void testGetTime_timezone() {
|
public void testGetTime_timezone() {
|
||||||
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
assertGetTime("2010-05-04T03:02:01+00:00", 2010, 5, 4, 3, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01+00:00", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
assertGetTime("2010-05-04T03:02:01-00:00", 2010, 5, 4, 3, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01-00:00", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
assertGetTime("2010-05-04T03:02:01+01:00", 2010, 5, 4, 2, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01+01:00", 2010, 5, 4, 2, 2, 1, 0);
|
||||||
assertGetTime("2010-05-04T03:02:01+10:30", 2010, 5, 3, 16, 32, 1, 0);
|
assertGetTime("2010-05-04T03:02:01+10:30", 2010, 5, 3, 16, 32, 1, 0);
|
||||||
assertGetTime("2010-05-04T03:02:01-09:30", 2010, 5, 4, 12, 32, 1, 0);
|
assertGetTime("2010-05-04T03:02:01-09:30", 2010, 5, 4, 12, 32, 1, 0);
|
||||||
assertGetTime("2010-05-04T03:02:01-05:00", 2010, 5, 4, 8, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01-05:00", 2010, 5, 4, 8, 2, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#getTime(String)} with fractional seconds and time
|
* Tests {@link StringUtils#getTime(String)} with fractional seconds and time
|
||||||
* zone.
|
* zone.
|
||||||
*/
|
*/
|
||||||
public void testGetTime_fractionalAndTimezone() {
|
public void testGetTime_fractionalAndTimezone() {
|
||||||
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
assertGetTime("2010-05-04T03:02:01.47+00:00", 2010, 5, 4, 3, 2, 1, 470);
|
assertGetTime("2010-05-04T03:02:01.47+00:00", 2010, 5, 4, 3, 2, 1, 470);
|
||||||
assertGetTime("2010-05-04T03:02:01.5791+03:00", 2010, 5, 4, 0, 2, 1, 579);
|
assertGetTime("2010-05-04T03:02:01.5791+03:00", 2010, 5, 4, 0, 2, 1, 579);
|
||||||
assertGetTime("2010-05-04T03:02:01.8-05:30", 2010, 5, 4, 8, 32, 1, 800);
|
assertGetTime("2010-05-04T03:02:01.8-05:30", 2010, 5, 4, 8, 32, 1, 800);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts the {@link StringUtils#getTime(String)} returns the expected
|
* Asserts the {@link StringUtils#getTime(String)} returns the expected
|
||||||
* values.
|
* values.
|
||||||
*
|
*
|
||||||
* @param xmlDateTime the xml date time string
|
* @param xmlDateTime the xml date time string
|
||||||
* @param year the expected year
|
* @param year the expected year
|
||||||
* @param month the expected month
|
* @param month the expected month
|
||||||
* @param day the expected day
|
* @param day the expected day
|
||||||
* @param hour the expected hour
|
* @param hour the expected hour
|
||||||
* @param minute the expected minute
|
* @param minute the expected minute
|
||||||
* @param second the expected second
|
* @param second the expected second
|
||||||
* @param millisecond the expected milliseconds
|
* @param millisecond the expected milliseconds
|
||||||
*/
|
*/
|
||||||
private void assertGetTime(String xmlDateTime, int year, int month, int day, int hour, int minute,
|
private void assertGetTime(String xmlDateTime, int year, int month, int day, int hour, int minute,
|
||||||
int second, int millisecond) {
|
int second, int millisecond) {
|
||||||
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
calendar.set(year, month - 1, day, hour, minute, second);
|
calendar.set(year, month - 1, day, hour, minute, second);
|
||||||
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
||||||
Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.getTime(xmlDateTime));
|
Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.getTime(xmlDateTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#getTimeParts(long)} with a positive number.
|
* Tests {@link StringUtils#getTimeParts(long)} with a positive number.
|
||||||
*/
|
*/
|
||||||
public void testGetTimeParts_postive() {
|
public void testGetTimeParts_postive() {
|
||||||
int[] parts = StringUtils.getTimeParts(61000);
|
int[] parts = StringUtils.getTimeParts(61000);
|
||||||
Assert.assertEquals(1, parts[0]);
|
Assert.assertEquals(1, parts[0]);
|
||||||
Assert.assertEquals(1, parts[1]);
|
Assert.assertEquals(1, parts[1]);
|
||||||
Assert.assertEquals(0, parts[2]);
|
Assert.assertEquals(0, parts[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#getTimeParts(long)} with a negative number.
|
* Tests {@link StringUtils#getTimeParts(long)} with a negative number.
|
||||||
*/
|
*/
|
||||||
public void testGetTimeParts_negative() {
|
public void testGetTimeParts_negative() {
|
||||||
int[] parts = StringUtils.getTimeParts(-61000);
|
int[] parts = StringUtils.getTimeParts(-61000);
|
||||||
Assert.assertEquals(-1, parts[0]);
|
Assert.assertEquals(-1, parts[0]);
|
||||||
Assert.assertEquals(-1, parts[1]);
|
Assert.assertEquals(-1, parts[1]);
|
||||||
Assert.assertEquals(0, parts[2]);
|
Assert.assertEquals(0, parts[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-32
@@ -38,40 +38,40 @@ import java.util.Locale;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class TrackNameUtilsTest {
|
public class TrackNameUtilsTest {
|
||||||
|
|
||||||
private static final long TRACK_ID = 1L;
|
private static final long TRACK_ID = 1L;
|
||||||
private static final long START_TIME = 1288213406000L;
|
private static final long START_TIME = 1288213406000L;
|
||||||
|
|
||||||
private static final Context CONTEXT = InstrumentationRegistry.getInstrumentation().getContext();
|
private static final Context CONTEXT = InstrumentationRegistry.getInstrumentation().getContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests when the track_name_key is
|
* Tests when the track_name_key is
|
||||||
* settings_recording_track_name_date_local_value.
|
* settings_recording_track_name_date_local_value.
|
||||||
*/
|
*/
|
||||||
public void testTrackName_date_local() {
|
public void testTrackName_date_local() {
|
||||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
||||||
Assert.assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME),
|
Assert.assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME),
|
||||||
TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests when the track_name_key is
|
* Tests when the track_name_key is
|
||||||
* settings_recording_track_name_date_iso_8601_value.
|
* settings_recording_track_name_date_iso_8601_value.
|
||||||
*/
|
*/
|
||||||
public void testTrackName_date_iso_8601() {
|
public void testTrackName_date_iso_8601() {
|
||||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
||||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
|
||||||
TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
||||||
Assert.assertEquals(simpleDateFormat.format(new Date(START_TIME)),
|
Assert.assertEquals(simpleDateFormat.format(new Date(START_TIME)),
|
||||||
TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests when the track_name_key is
|
* Tests when the track_name_key is
|
||||||
* settings_recording_track_name_number_value.
|
* settings_recording_track_name_number_value.
|
||||||
*/
|
*/
|
||||||
public void testTrackName_number() {
|
public void testTrackName_number() {
|
||||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
||||||
Assert.assertEquals(
|
Assert.assertEquals(
|
||||||
"Track " + TRACK_ID, TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
"Track " + TRACK_ID, TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,62 +27,61 @@ 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 {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Set volume control stream for text to speech
|
// Set volume control stream for text to speech
|
||||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||||
|
|
||||||
setContentView(getLayoutResId());
|
setContentView(getLayoutResId());
|
||||||
|
|
||||||
// Configure action bar must be after setContentView
|
// Configure action bar must be after setContentView
|
||||||
if (configureActionBarHomeAsUp()) {
|
if (configureActionBarHomeAsUp()) {
|
||||||
ActionBar actionBar = this.getActionBar();
|
ActionBar actionBar = this.getActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
actionBar.setHomeButtonEnabled(true);
|
actionBar.setHomeButtonEnabled(true);
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the layout resource id.
|
* Gets the layout resource id.
|
||||||
*/
|
*/
|
||||||
protected abstract int getLayoutResId();
|
protected abstract int getLayoutResId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true to configure the action bar home button as the up button (go to previous activity).
|
* Returns true to configure the action bar home button as the up button (go to previous activity).
|
||||||
*/
|
*/
|
||||||
protected boolean configureActionBarHomeAsUp() {
|
protected boolean configureActionBarHomeAsUp() {
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
TrackIconUtils.setMenuIconColor(menu);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
// Handles actionBar's home menu item (arrow top left corner) and triggers, up navigation to previous activity.
|
|
||||||
if (item.getItemId() == android.R.id.home) {
|
|
||||||
onHomeSelected();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Callback when the home menu item is selected. E.g., setup the back stack when home is selected.
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
*/
|
TrackIconUtils.setMenuIconColor(menu);
|
||||||
protected void onHomeSelected() {
|
return super.onCreateOptionsMenu(menu);
|
||||||
finish();
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
// Handles actionBar's home menu item (arrow top left corner) and triggers, up navigation to previous activity.
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
onHomeSelected();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback when the home menu item is selected. E.g., setup the back stack when home is selected.
|
||||||
|
*/
|
||||||
|
protected void onHomeSelected() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,74 +51,65 @@ 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
|
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||||
if (requestCode == DELETE_REQUEST_CODE) {
|
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
||||||
onDeleted();
|
|
||||||
} else {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
|
@Override
|
||||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
|
// Do nothing
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
||||||
// Do nothing
|
mode.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
mode.invalidate();
|
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
|
||||||
}
|
mode.finish();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
* Gets the checked positions in a list view.
|
||||||
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
|
*
|
||||||
mode.finish();
|
* @param list the list view
|
||||||
}
|
*/
|
||||||
return true;
|
private int[] getCheckedPositions(ListView list) {
|
||||||
}
|
SparseBooleanArray positions = list.getCheckedItemPositions();
|
||||||
|
ArrayList<Integer> arrayList = new ArrayList<>();
|
||||||
/**
|
for (int i = 0; i < positions.size(); i++) {
|
||||||
* Gets the checked positions in a list view.
|
int key = positions.keyAt(i);
|
||||||
*
|
if (positions.valueAt(i)) {
|
||||||
* @param list the list view
|
arrayList.add(key);
|
||||||
*/
|
}
|
||||||
private int[] getCheckedPositions(ListView list) {
|
}
|
||||||
SparseBooleanArray positions = list.getCheckedItemPositions();
|
int[] result = new int[arrayList.size()];
|
||||||
ArrayList<Integer> arrayList = new ArrayList<>();
|
for (int i = 0; i < arrayList.size(); i++) {
|
||||||
for (int i = 0; i < positions.size(); i++) {
|
result[i] = arrayList.get(i);
|
||||||
int key = positions.keyAt(i);
|
}
|
||||||
if (positions.valueAt(i)) {
|
return result;
|
||||||
arrayList.add(key);
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
int[] result = new int[arrayList.size()];
|
|
||||||
for (int i = 0; i < arrayList.size(); i++) {
|
|
||||||
result[i] = arrayList.get(i);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void configureSearchWidget(Activity activity, final MenuItem menuItem, final TrackController trackController) {
|
public static void configureSearchWidget(Activity activity, final MenuItem menuItem, final TrackController trackController) {
|
||||||
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
||||||
@@ -166,46 +157,55 @@ public abstract class AbstractTrackActivity extends AbstractActivity implements
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Delete tracks.
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
*
|
if (requestCode == DELETE_REQUEST_CODE) {
|
||||||
* @param trackIds the track ids
|
onDeleted();
|
||||||
*/
|
} else {
|
||||||
protected void deleteTracks(long[] trackIds) {
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
ConfirmDeleteDialogFragment.newInstance(trackIds)
|
|
||||||
.show(getSupportFragmentManager(), ConfirmDeleteDialogFragment.CONFIRM_DELETE_DIALOG_TAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfirmDeleteDone(long[] trackIds) {
|
|
||||||
boolean stopRecording = false;
|
|
||||||
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
|
||||||
stopRecording = true;
|
|
||||||
} else {
|
|
||||||
long recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
|
||||||
for (long trackId : trackIds) {
|
|
||||||
if (trackId == recordingTrackId) {
|
|
||||||
stopRecording = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (stopRecording) {
|
|
||||||
TrackRecordingServiceConnectionUtils.stopRecording(this, getTrackRecordingServiceConnection(), false);
|
/**
|
||||||
|
* Delete tracks.
|
||||||
|
*
|
||||||
|
* @param trackIds the track ids
|
||||||
|
*/
|
||||||
|
protected void deleteTracks(long[] trackIds) {
|
||||||
|
ConfirmDeleteDialogFragment.newInstance(trackIds)
|
||||||
|
.show(getSupportFragmentManager(), ConfirmDeleteDialogFragment.CONFIRM_DELETE_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
Intent intent = IntentUtils.newIntent(this, DeleteActivity.class);
|
|
||||||
intent.putExtra(DeleteActivity.EXTRA_TRACK_IDS, trackIds);
|
|
||||||
startActivityForResult(intent, DELETE_REQUEST_CODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Gets the track recording service connection. For stopping the current
|
public void onConfirmDeleteDone(long[] trackIds) {
|
||||||
* recording if need to delete the current recording track.
|
boolean stopRecording = false;
|
||||||
*/
|
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
||||||
abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection();
|
stopRecording = true;
|
||||||
|
} else {
|
||||||
|
long recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
||||||
|
for (long trackId : trackIds) {
|
||||||
|
if (trackId == recordingTrackId) {
|
||||||
|
stopRecording = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stopRecording) {
|
||||||
|
TrackRecordingServiceConnectionUtils.stopRecording(this, getTrackRecordingServiceConnection(), false);
|
||||||
|
}
|
||||||
|
Intent intent = IntentUtils.newIntent(this, DeleteActivity.class);
|
||||||
|
intent.putExtra(DeleteActivity.EXTRA_TRACK_IDS, trackIds);
|
||||||
|
startActivityForResult(intent, DELETE_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after {@link DeleteActivity} returns its result.
|
* Gets the track recording service connection. For stopping the current
|
||||||
*/
|
* recording if need to delete the current recording track.
|
||||||
abstract protected void onDeleted();
|
*/
|
||||||
|
abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after {@link DeleteActivity} returns its result.
|
||||||
|
*/
|
||||||
|
abstract protected void onDeleted();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,31 +33,31 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class AggregatedStatsActivity extends AbstractActivity {
|
public class AggregatedStatsActivity extends AbstractActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
StatsUtils.setTripStatisticsValues(this, this, null, getTripStatistics(), null);
|
StatsUtils.setTripStatisticsValues(this, this, null, getTripStatistics(), null);
|
||||||
StatsUtils.setLocationValues(this, this, null, null, false);
|
StatsUtils.setLocationValues(this, this, null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getLayoutResId() {
|
protected int getLayoutResId() {
|
||||||
return R.layout.stats;
|
return R.layout.stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the aggregated trip statistics for all the recorded tracks or null if
|
* Gets the aggregated trip statistics for all the recorded tracks or null if
|
||||||
* there is no track.
|
* there is no track.
|
||||||
*/
|
*/
|
||||||
private TripStatistics getTripStatistics() {
|
private TripStatistics getTripStatistics() {
|
||||||
List<Track> tracks = ContentProviderUtils.Factory.get(this).getAllTracks();
|
List<Track> tracks = ContentProviderUtils.Factory.get(this).getAllTracks();
|
||||||
TripStatistics tripStatistics = null;
|
TripStatistics tripStatistics = null;
|
||||||
if (!tracks.isEmpty()) {
|
if (!tracks.isEmpty()) {
|
||||||
tripStatistics = new TripStatistics(tracks.iterator().next().getTripStatistics());
|
tripStatistics = new TripStatistics(tracks.iterator().next().getTripStatistics());
|
||||||
for (int i = 1; i < tracks.size(); i++) {
|
for (int i = 1; i < tracks.size(); i++) {
|
||||||
tripStatistics.merge(tracks.get(i).getTripStatistics());
|
tripStatistics.merge(tracks.get(i).getTripStatistics());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return tripStatistics;
|
||||||
}
|
}
|
||||||
return tripStatistics;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -41,17 +41,17 @@ import android.util.Log;
|
|||||||
*/
|
*/
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
public class BootReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private static final String TAG = BootReceiver.class.getSimpleName();
|
private static final String TAG = BootReceiver.class.getSimpleName();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
Log.d(TAG, "BootReceiver.onReceive: " + intent.getAction());
|
Log.d(TAG, "BootReceiver.onReceive: " + intent.getAction());
|
||||||
if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
||||||
Intent startIntent = new Intent(context, TrackRecordingService.class)
|
Intent startIntent = new Intent(context, TrackRecordingService.class)
|
||||||
.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||||
context.startService(startIntent);
|
context.startService(startIntent);
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "BootReceiver: unsupported action");
|
Log.w(TAG, "BootReceiver: unsupported action");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -35,229 +36,229 @@ import java.text.NumberFormat;
|
|||||||
*/
|
*/
|
||||||
public class ChartValueSeries {
|
public class ChartValueSeries {
|
||||||
|
|
||||||
private static final float STROKE_WIDTH = 2f;
|
private static final float STROKE_WIDTH = 2f;
|
||||||
|
|
||||||
private final int absoluteMin;
|
private final int absoluteMin;
|
||||||
private final int absoluteMax;
|
private final int absoluteMax;
|
||||||
private final int[] intervalValues;
|
private final int[] intervalValues;
|
||||||
private final int metricTitleId;
|
private final int metricTitleId;
|
||||||
private final int imperialTitleId;
|
private final int imperialTitleId;
|
||||||
private final Paint fillPaint;
|
private final Paint fillPaint;
|
||||||
private final Paint strokePaint;
|
private final Paint strokePaint;
|
||||||
private final Paint titlePaint;
|
private final Paint titlePaint;
|
||||||
private final Paint markerPaint;
|
private final Paint markerPaint;
|
||||||
private final ExtremityMonitor extremityMonitor;
|
private final ExtremityMonitor extremityMonitor;
|
||||||
private final NumberFormat numberFormat;
|
private final NumberFormat numberFormat;
|
||||||
private final Path path;
|
private final Path path;
|
||||||
|
|
||||||
private int interval = 1;
|
private int interval = 1;
|
||||||
private int minMarkerValue = 0;
|
private int minMarkerValue = 0;
|
||||||
private int maxMarkerValue = interval * ChartView.Y_AXIS_INTERVALS;
|
private int maxMarkerValue = interval * ChartView.Y_AXIS_INTERVALS;
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
* @param absoluteMin the absolute min value
|
* @param absoluteMin the absolute min value
|
||||||
* @param absoluteMax the absolute max value
|
* @param absoluteMax the absolute max value
|
||||||
* @param intervalValues the list of interval values
|
* @param intervalValues the list of interval values
|
||||||
* @param metricTitleId the metric title id
|
* @param metricTitleId the metric title id
|
||||||
* @param imperialTitleId the imperial title id
|
* @param imperialTitleId the imperial title id
|
||||||
* @param fillColor the fill color
|
* @param fillColor the fill color
|
||||||
* @param strokeColor the stroke color
|
* @param strokeColor the stroke color
|
||||||
*/
|
*/
|
||||||
public ChartValueSeries(Context context, int absoluteMin, int absoluteMax, int[] intervalValues,
|
public ChartValueSeries(Context context, int absoluteMin, int absoluteMax, int[] intervalValues,
|
||||||
int metricTitleId, int imperialTitleId, int fillColor, int strokeColor) {
|
int metricTitleId, int imperialTitleId, int fillColor, int strokeColor) {
|
||||||
this.absoluteMin = absoluteMin;
|
this.absoluteMin = absoluteMin;
|
||||||
this.absoluteMax = absoluteMax;
|
this.absoluteMax = absoluteMax;
|
||||||
this.intervalValues = intervalValues;
|
this.intervalValues = intervalValues;
|
||||||
this.metricTitleId = metricTitleId;
|
this.metricTitleId = metricTitleId;
|
||||||
this.imperialTitleId = imperialTitleId;
|
this.imperialTitleId = imperialTitleId;
|
||||||
|
|
||||||
fillPaint = new Paint();
|
fillPaint = new Paint();
|
||||||
fillPaint.setStyle(Style.FILL);
|
fillPaint.setStyle(Style.FILL);
|
||||||
fillPaint.setColor(context.getResources().getColor(fillColor));
|
fillPaint.setColor(context.getResources().getColor(fillColor));
|
||||||
fillPaint.setAntiAlias(true);
|
fillPaint.setAntiAlias(true);
|
||||||
|
|
||||||
strokePaint = new Paint();
|
strokePaint = new Paint();
|
||||||
strokePaint.setStyle(Style.STROKE);
|
strokePaint.setStyle(Style.STROKE);
|
||||||
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
||||||
strokePaint.setAntiAlias(true);
|
strokePaint.setAntiAlias(true);
|
||||||
|
|
||||||
float scale = context.getResources().getDisplayMetrics().density;
|
float scale = context.getResources().getDisplayMetrics().density;
|
||||||
|
|
||||||
// Make copies of the stroke paint with the default thickness
|
// Make copies of the stroke paint with the default thickness
|
||||||
titlePaint = new Paint(strokePaint);
|
titlePaint = new Paint(strokePaint);
|
||||||
titlePaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE * scale);
|
titlePaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE * scale);
|
||||||
titlePaint.setTextAlign(Align.CENTER);
|
titlePaint.setTextAlign(Align.CENTER);
|
||||||
|
|
||||||
markerPaint = new Paint(strokePaint);
|
markerPaint = new Paint(strokePaint);
|
||||||
markerPaint.setTextSize(ChartView.SMALL_TEXT_SIZE * scale);
|
markerPaint.setTextSize(ChartView.SMALL_TEXT_SIZE * scale);
|
||||||
markerPaint.setTextAlign(Align.RIGHT);
|
markerPaint.setTextAlign(Align.RIGHT);
|
||||||
|
|
||||||
// Set stroke paint thickness
|
// Set stroke paint thickness
|
||||||
strokePaint.setStrokeWidth(STROKE_WIDTH);
|
strokePaint.setStrokeWidth(STROKE_WIDTH);
|
||||||
|
|
||||||
extremityMonitor = new ExtremityMonitor();
|
extremityMonitor = new ExtremityMonitor();
|
||||||
numberFormat = NumberFormat.getIntegerInstance();
|
numberFormat = NumberFormat.getIntegerInstance();
|
||||||
path = new Path();
|
path = new Path();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the series enabled value.
|
|
||||||
*
|
|
||||||
* @param enabled true to enable
|
|
||||||
*/
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the series is enabled.
|
|
||||||
*/
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the series has data.
|
|
||||||
*/
|
|
||||||
public boolean hasData() {
|
|
||||||
return extremityMonitor.hasData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the series with a new value.
|
|
||||||
*
|
|
||||||
* @param value the new value
|
|
||||||
*/
|
|
||||||
public void update(double value) {
|
|
||||||
extremityMonitor.update(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the path.
|
|
||||||
*/
|
|
||||||
public Path getPath() {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the path on canvas.
|
|
||||||
*
|
|
||||||
* @param canvas the canvas
|
|
||||||
*/
|
|
||||||
public void drawPath(Canvas canvas) {
|
|
||||||
canvas.drawPath(path, fillPaint);
|
|
||||||
canvas.drawPath(path, strokePaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the y axis dimension.
|
|
||||||
*/
|
|
||||||
public void updateDimension() {
|
|
||||||
double min = hasData() ? extremityMonitor.getMin() : 0.0;
|
|
||||||
double max = hasData() ? extremityMonitor.getMax() : 1.0;
|
|
||||||
min = Math.max(min, absoluteMin);
|
|
||||||
max = Math.min(max, absoluteMax);
|
|
||||||
interval = getInterval(min, max);
|
|
||||||
minMarkerValue = getMinMarkerValue(min, interval);
|
|
||||||
maxMarkerValue = minMarkerValue + interval * ChartView.Y_AXIS_INTERVALS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the interval value.
|
|
||||||
*
|
|
||||||
* @param min the min value
|
|
||||||
* @param max the max value
|
|
||||||
*/
|
|
||||||
private int getInterval(double min, double max) {
|
|
||||||
for (int intervalValue : intervalValues) {
|
|
||||||
int minValue = getMinMarkerValue(min, intervalValue);
|
|
||||||
double targetInterval = (max - minValue) / ChartView.Y_AXIS_INTERVALS;
|
|
||||||
if (intervalValue >= targetInterval) {
|
|
||||||
return intervalValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Return the largest interval
|
|
||||||
return intervalValues[intervalValues.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the min marker value.
|
* Returns true if the series is enabled.
|
||||||
*
|
*/
|
||||||
* @param min the min series value
|
public boolean isEnabled() {
|
||||||
* @param intervalValue the interval value
|
return enabled;
|
||||||
*/
|
|
||||||
private int getMinMarkerValue(double min, int intervalValue) {
|
|
||||||
// Round down to the nearest intervalValue
|
|
||||||
int value = ((int) (min / intervalValue)) * intervalValue;
|
|
||||||
// value > min if min is negative
|
|
||||||
if (value > min) {
|
|
||||||
return value - intervalValue;
|
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the interval value.
|
* Sets the series enabled value.
|
||||||
*/
|
*
|
||||||
public int getInterval() {
|
* @param enabled true to enable
|
||||||
return interval;
|
*/
|
||||||
}
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the min marker value.
|
* Returns true if the series has data.
|
||||||
*/
|
*/
|
||||||
public int getMinMarkerValue() {
|
public boolean hasData() {
|
||||||
return minMarkerValue;
|
return extremityMonitor.hasData();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the max marker value.
|
* Updates the series with a new value.
|
||||||
*/
|
*
|
||||||
@VisibleForTesting
|
* @param value the new value
|
||||||
int getMaxMarkerValue() {
|
*/
|
||||||
return maxMarkerValue;
|
public void update(double value) {
|
||||||
}
|
extremityMonitor.update(value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the title id.
|
* Gets the path.
|
||||||
*/
|
*/
|
||||||
public int getTitleId(boolean metricUnits) {
|
public Path getPath() {
|
||||||
return metricUnits ? metricTitleId : imperialTitleId;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the title paint.
|
* Draws the path on canvas.
|
||||||
*/
|
*
|
||||||
public Paint getTitlePaint() {
|
* @param canvas the canvas
|
||||||
return titlePaint;
|
*/
|
||||||
}
|
public void drawPath(Canvas canvas) {
|
||||||
|
canvas.drawPath(path, fillPaint);
|
||||||
|
canvas.drawPath(path, strokePaint);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the marker paint.
|
* Updates the y axis dimension.
|
||||||
*/
|
*/
|
||||||
public Paint getMarkerPaint() {
|
public void updateDimension() {
|
||||||
return markerPaint;
|
double min = hasData() ? extremityMonitor.getMin() : 0.0;
|
||||||
}
|
double max = hasData() ? extremityMonitor.getMax() : 1.0;
|
||||||
|
min = Math.max(min, absoluteMin);
|
||||||
|
max = Math.min(max, absoluteMax);
|
||||||
|
interval = getInterval(min, max);
|
||||||
|
minMarkerValue = getMinMarkerValue(min, interval);
|
||||||
|
maxMarkerValue = minMarkerValue + interval * ChartView.Y_AXIS_INTERVALS;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the largest marker.
|
* Gets the interval value.
|
||||||
*/
|
*
|
||||||
public String getLargestMarker() {
|
* @param min the min value
|
||||||
String minMarker = numberFormat.format(getMinMarkerValue());
|
* @param max the max value
|
||||||
String maxMarker = numberFormat.format(getMaxMarkerValue());
|
*/
|
||||||
return minMarker.length() >= maxMarker.length() ? minMarker : maxMarker;
|
private int getInterval(double min, double max) {
|
||||||
}
|
for (int intervalValue : intervalValues) {
|
||||||
|
int minValue = getMinMarkerValue(min, intervalValue);
|
||||||
|
double targetInterval = (max - minValue) / ChartView.Y_AXIS_INTERVALS;
|
||||||
|
if (intervalValue >= targetInterval) {
|
||||||
|
return intervalValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return the largest interval
|
||||||
|
return intervalValues[intervalValues.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a marker value.
|
* Gets the min marker value.
|
||||||
*
|
*
|
||||||
* @param value the value
|
* @param min the min series value
|
||||||
*/
|
* @param intervalValue the interval value
|
||||||
public String formatMarker(int value) {
|
*/
|
||||||
return numberFormat.format(value);
|
private int getMinMarkerValue(double min, int intervalValue) {
|
||||||
}
|
// Round down to the nearest intervalValue
|
||||||
|
int value = ((int) (min / intervalValue)) * intervalValue;
|
||||||
|
// value > min if min is negative
|
||||||
|
if (value > min) {
|
||||||
|
return value - intervalValue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the interval value.
|
||||||
|
*/
|
||||||
|
public int getInterval() {
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the min marker value.
|
||||||
|
*/
|
||||||
|
public int getMinMarkerValue() {
|
||||||
|
return minMarkerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the max marker value.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
int getMaxMarkerValue() {
|
||||||
|
return maxMarkerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title id.
|
||||||
|
*/
|
||||||
|
public int getTitleId(boolean metricUnits) {
|
||||||
|
return metricUnits ? metricTitleId : imperialTitleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title paint.
|
||||||
|
*/
|
||||||
|
public Paint getTitlePaint() {
|
||||||
|
return titlePaint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the marker paint.
|
||||||
|
*/
|
||||||
|
public Paint getMarkerPaint() {
|
||||||
|
return markerPaint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the largest marker.
|
||||||
|
*/
|
||||||
|
public String getLargestMarker() {
|
||||||
|
String minMarker = numberFormat.format(getMinMarkerValue());
|
||||||
|
String maxMarker = numberFormat.format(getMaxMarkerValue());
|
||||||
|
return minMarker.length() >= maxMarker.length() ? minMarker : maxMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a marker value.
|
||||||
|
*
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
public String formatMarker(int value) {
|
||||||
|
return numberFormat.format(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||||
|
*/
|
||||||
|
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
|
||||||
|
/**
|
||||||
|
* The settings file name.
|
||||||
|
*/
|
||||||
|
public static final String SETTINGS_NAME = "SettingsActivity";
|
||||||
|
|
||||||
/**
|
private Constants() {
|
||||||
* Maximum number of waypoints that will be loaded at one time.
|
}
|
||||||
*/
|
|
||||||
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The settings file name.
|
|
||||||
*/
|
|
||||||
public static final String SETTINGS_NAME = "SettingsActivity";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,22 +25,22 @@ import android.view.Menu;
|
|||||||
*/
|
*/
|
||||||
public interface ContextualActionModeCallback {
|
public interface ContextualActionModeCallback {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to prepare the menu for the selected items.
|
* Invoked to prepare the menu for the selected items.
|
||||||
*
|
*
|
||||||
* @param menu the menu
|
* @param menu the menu
|
||||||
* @param positions the selected items' positions
|
* @param positions the selected items' positions
|
||||||
* @param ids the selected items' ids, if available
|
* @param ids the selected items' ids, if available
|
||||||
* @param showSelectAll true to show select all
|
* @param showSelectAll true to show select all
|
||||||
*/
|
*/
|
||||||
void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll);
|
void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when items are selected.
|
* Invoked when items are selected.
|
||||||
*
|
*
|
||||||
* @param itemId the context menu item id
|
* @param itemId the context menu item id
|
||||||
* @param positions the selected items' positions
|
* @param positions the selected items' positions
|
||||||
* @param ids the selected items' ids, if available
|
* @param ids the selected items' ids, if available
|
||||||
*/
|
*/
|
||||||
boolean onClick(int itemId, int[] positions, long[] ids);
|
boolean onClick(int itemId, int[] positions, long[] ids);
|
||||||
}
|
}
|
||||||
@@ -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.
|
||||||
*
|
*
|
||||||
@@ -32,66 +32,66 @@ import android.os.Bundle;
|
|||||||
*/
|
*/
|
||||||
public class DeleteActivity extends Activity {
|
public class DeleteActivity extends Activity {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_IDS = "track_ids";
|
public static final String EXTRA_TRACK_IDS = "track_ids";
|
||||||
|
|
||||||
private static final int DIALOG_PROGRESS_ID = 0;
|
private static final int DIALOG_PROGRESS_ID = 0;
|
||||||
|
|
||||||
private DeleteAsyncTask deleteAsyncTask;
|
private DeleteAsyncTask deleteAsyncTask;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
long[] trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
long[] trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
||||||
|
|
||||||
Object retained = getLastNonConfigurationInstance();
|
Object retained = getLastNonConfigurationInstance();
|
||||||
if (retained instanceof DeleteAsyncTask) {
|
if (retained instanceof DeleteAsyncTask) {
|
||||||
deleteAsyncTask = (DeleteAsyncTask) retained;
|
deleteAsyncTask = (DeleteAsyncTask) retained;
|
||||||
deleteAsyncTask.setActivity(this);
|
deleteAsyncTask.setActivity(this);
|
||||||
} else {
|
} else {
|
||||||
deleteAsyncTask = new DeleteAsyncTask(this, trackIds);
|
deleteAsyncTask = new DeleteAsyncTask(this, trackIds);
|
||||||
deleteAsyncTask.execute();
|
deleteAsyncTask.execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object onRetainNonConfigurationInstance() {
|
public Object onRetainNonConfigurationInstance() {
|
||||||
deleteAsyncTask.setActivity(null);
|
deleteAsyncTask.setActivity(null);
|
||||||
return deleteAsyncTask;
|
return deleteAsyncTask;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
if (id != DIALOG_PROGRESS_ID) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return DialogUtils.createSpinnerProgressDialog(
|
|
||||||
this, R.string.track_delete_progress_message, new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
deleteAsyncTask.cancel(true);
|
|
||||||
dialog.dismiss();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Invokes when the associated AsyncTask completes.
|
protected Dialog onCreateDialog(int id) {
|
||||||
*/
|
if (id != DIALOG_PROGRESS_ID) {
|
||||||
public void onAsyncTaskCompleted() {
|
return null;
|
||||||
removeDialog(DIALOG_PROGRESS_ID);
|
}
|
||||||
setResult(RESULT_OK);
|
return DialogUtils.createSpinnerProgressDialog(
|
||||||
finish();
|
this, R.string.track_delete_progress_message, new DialogInterface.OnCancelListener() {
|
||||||
}
|
@Override
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
deleteAsyncTask.cancel(true);
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the progress dialog.
|
* Invokes when the associated AsyncTask completes.
|
||||||
*/
|
*/
|
||||||
public void showProgressDialog() {
|
public void onAsyncTaskCompleted() {
|
||||||
showDialog(DIALOG_PROGRESS_ID);
|
removeDialog(DIALOG_PROGRESS_ID);
|
||||||
}
|
setResult(RESULT_OK);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the progress dialog.
|
||||||
|
*/
|
||||||
|
public void showProgressDialog() {
|
||||||
|
showDialog(DIALOG_PROGRESS_ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,68 +28,67 @@ 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
|
||||||
|
private boolean completed;
|
||||||
|
|
||||||
// true if the AsyncTask has completed
|
/**
|
||||||
private boolean completed;
|
* Creates an AsyncTask.
|
||||||
|
*
|
||||||
/**
|
* @param deleteActivity the activity currently associated with this task
|
||||||
* Creates an AsyncTask.
|
* @param trackIds the track ids to delete. To delete all, set to size 1 with
|
||||||
*
|
* trackIds[0] == -1L
|
||||||
* @param deleteActivity the activity currently associated with this task
|
*/
|
||||||
* @param trackIds the track ids to delete. To delete all, set to size 1 with
|
public DeleteAsyncTask(DeleteActivity deleteActivity, long[] trackIds) {
|
||||||
* trackIds[0] == -1L
|
this.deleteActivity = deleteActivity;
|
||||||
*/
|
this.trackIds = trackIds;
|
||||||
public DeleteAsyncTask(DeleteActivity deleteActivity, long[] trackIds) {
|
context = deleteActivity.getApplicationContext();
|
||||||
this.deleteActivity = deleteActivity;
|
completed = false;
|
||||||
this.trackIds = trackIds;
|
|
||||||
context = deleteActivity.getApplicationContext();
|
|
||||||
completed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current activity associated with this AyncTask.
|
|
||||||
*
|
|
||||||
* @param deleteActivity the current activity, can be null
|
|
||||||
*/
|
|
||||||
public void setActivity(DeleteActivity deleteActivity) {
|
|
||||||
this.deleteActivity = deleteActivity;
|
|
||||||
if (completed && deleteActivity != null) {
|
|
||||||
deleteActivity.onAsyncTaskCompleted();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void onPreExecute() {
|
* Sets the current activity associated with this AyncTask.
|
||||||
if (deleteActivity != null) {
|
*
|
||||||
deleteActivity.showProgressDialog();
|
* @param deleteActivity the current activity, can be null
|
||||||
}
|
*/
|
||||||
}
|
public void setActivity(DeleteActivity deleteActivity) {
|
||||||
|
this.deleteActivity = deleteActivity;
|
||||||
@Override
|
if (completed && deleteActivity != null) {
|
||||||
protected Boolean doInBackground(Void... params) {
|
deleteActivity.onAsyncTaskCompleted();
|
||||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
|
||||||
|
|
||||||
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
|
||||||
contentProviderUtils.deleteAllTracks(context);
|
|
||||||
} else {
|
|
||||||
for (long id : trackIds) {
|
|
||||||
if (isCancelled()) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
contentProviderUtils.deleteTrack(context, id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPreExecute() {
|
||||||
completed = true;
|
if (deleteActivity != null) {
|
||||||
if (deleteActivity != null) {
|
deleteActivity.showProgressDialog();
|
||||||
deleteActivity.onAsyncTaskCompleted();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||||
|
|
||||||
|
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
||||||
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
|
} else {
|
||||||
|
for (long id : trackIds) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
contentProviderUtils.deleteTrack(context, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
completed = true;
|
||||||
|
if (deleteActivity != null) {
|
||||||
|
deleteActivity.onAsyncTaskCompleted();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,87 +40,87 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public class MarkerDetailActivity extends AbstractActivity implements DeleteMarkerCaller {
|
public class MarkerDetailActivity extends AbstractActivity implements DeleteMarkerCaller {
|
||||||
|
|
||||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||||
private static final String TAG = MarkerDetailActivity.class.getSimpleName();
|
private static final String TAG = MarkerDetailActivity.class.getSimpleName();
|
||||||
|
|
||||||
private ArrayList<Long> markerIds;
|
private ArrayList<Long> markerIds;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle bundle) {
|
protected void onCreate(Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
long markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
long markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||||
if (markerId == -1L) {
|
if (markerId == -1L) {
|
||||||
Log.d(TAG, "invalid marker id");
|
Log.d(TAG, "invalid marker id");
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||||
|
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
|
||||||
|
|
||||||
|
markerIds = new ArrayList<>();
|
||||||
|
int markerIndex = -1;
|
||||||
|
|
||||||
|
try (Cursor cursor = contentProviderUtils.getWaypointCursor(waypoint.getTrackId(), -1L, -1)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
/*
|
||||||
|
* Yes, this will skip the first waypoint and that is intentional as the
|
||||||
|
* first waypoint holds the stats for the track.
|
||||||
|
*/
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
Waypoint current = contentProviderUtils.createWaypoint(cursor);
|
||||||
|
|
||||||
|
markerIds.add(current.getId());
|
||||||
|
if (current.getId() == markerId) {
|
||||||
|
markerIndex = markerIds.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewPager viewPager = findViewById(R.id.maker_detail_activity_view_pager);
|
||||||
|
viewPager.setAdapter(new MarkerDetailPagerAdapter(getSupportFragmentManager()));
|
||||||
|
if (markerIndex != -1) {
|
||||||
|
viewPager.setCurrentItem(markerIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
@Override
|
||||||
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
|
protected int getLayoutResId() {
|
||||||
|
return R.layout.marker_detail_activity;
|
||||||
markerIds = new ArrayList<>();
|
|
||||||
int markerIndex = -1;
|
|
||||||
|
|
||||||
try (Cursor cursor = contentProviderUtils.getWaypointCursor(waypoint.getTrackId(), -1L, -1)) {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
/*
|
|
||||||
* Yes, this will skip the first waypoint and that is intentional as the
|
|
||||||
* first waypoint holds the stats for the track.
|
|
||||||
*/
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
Waypoint current = contentProviderUtils.createWaypoint(cursor);
|
|
||||||
|
|
||||||
markerIds.add(current.getId());
|
|
||||||
if (current.getId() == markerId) {
|
|
||||||
markerIndex = markerIds.size() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewPager viewPager = findViewById(R.id.maker_detail_activity_view_pager);
|
|
||||||
viewPager.setAdapter(new MarkerDetailPagerAdapter(getSupportFragmentManager()));
|
|
||||||
if (markerIndex != -1) {
|
|
||||||
viewPager.setCurrentItem(markerIndex);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getLayoutResId() {
|
public void onDeleteMarkerDone() {
|
||||||
return R.layout.marker_detail_activity;
|
runOnUiThread(new Runnable() {
|
||||||
}
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker detail pager adapter.
|
||||||
|
*
|
||||||
|
* @author Jimmy Shih
|
||||||
|
*/
|
||||||
|
private class MarkerDetailPagerAdapter extends FragmentStatePagerAdapter {
|
||||||
|
|
||||||
|
public MarkerDetailPagerAdapter(FragmentManager fragmentManager) {
|
||||||
|
super(fragmentManager);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDeleteMarkerDone() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public Fragment getItem(int position) {
|
||||||
finish();
|
String title = getString(R.string.marker_title, position + 1, getCount());
|
||||||
}
|
return MarkerDetailFragment.newInstance(markerIds.get(position), title);
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Marker detail pager adapter.
|
public int getCount() {
|
||||||
*
|
return markerIds.size();
|
||||||
* @author Jimmy Shih
|
}
|
||||||
*/
|
|
||||||
private class MarkerDetailPagerAdapter extends FragmentStatePagerAdapter {
|
|
||||||
|
|
||||||
public MarkerDetailPagerAdapter(FragmentManager fragmentManager) {
|
|
||||||
super(fragmentManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Fragment getItem(int position) {
|
|
||||||
String title = getString(R.string.marker_title, position + 1, getCount());
|
|
||||||
return MarkerDetailFragment.newInstance(markerIds.get(position), title);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return markerIds.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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,152 +40,150 @@ 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_MARKER_ID = "marker_id";
|
||||||
|
private static final String TAG = MarkerEditActivity.class.getSimpleName();
|
||||||
|
private long trackId;
|
||||||
|
private long markerId;
|
||||||
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
|
private Waypoint waypoint;
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
// UI elements
|
||||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
private View statisticsSection;
|
||||||
|
private EditText statisticsName;
|
||||||
|
private View waypointSection;
|
||||||
|
private EditText waypointName;
|
||||||
|
private AutoCompleteTextView waypointMarkerType;
|
||||||
|
private EditText waypointDescription;
|
||||||
|
private Button done;
|
||||||
|
|
||||||
private long trackId;
|
@Override
|
||||||
private long markerId;
|
protected void onCreate(Bundle bundle) {
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
super.onCreate(bundle);
|
||||||
private Waypoint waypoint;
|
|
||||||
|
|
||||||
// UI elements
|
trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||||
private View statisticsSection;
|
markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||||
private EditText statisticsName;
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||||
private View waypointSection;
|
|
||||||
private EditText waypointName;
|
|
||||||
private AutoCompleteTextView waypointMarkerType;
|
|
||||||
private EditText waypointDescription;
|
|
||||||
private Button done;
|
|
||||||
|
|
||||||
@Override
|
// Setup UI elements
|
||||||
protected void onCreate(Bundle bundle) {
|
statisticsSection = findViewById(R.id.marker_edit_statistics_section);
|
||||||
super.onCreate(bundle);
|
statisticsName = findViewById(R.id.marker_edit_statistics_name);
|
||||||
|
|
||||||
trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
waypointSection = findViewById(R.id.marker_edit_waypoint_section);
|
||||||
markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type);
|
||||||
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||||
|
this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
|
||||||
|
waypointMarkerType.setAdapter(adapter);
|
||||||
|
waypointDescription = findViewById(R.id.marker_edit_waypoint_description);
|
||||||
|
|
||||||
// Setup UI elements
|
Button cancel = findViewById(R.id.marker_edit_cancel);
|
||||||
statisticsSection = findViewById(R.id.marker_edit_statistics_section);
|
cancel.setOnClickListener(new OnClickListener() {
|
||||||
statisticsName = findViewById(R.id.marker_edit_statistics_name);
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
done = findViewById(R.id.marker_edit_done);
|
||||||
|
updateUiByMarkerId();
|
||||||
|
}
|
||||||
|
|
||||||
waypointSection = findViewById(R.id.marker_edit_waypoint_section);
|
@Override
|
||||||
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
protected void onStart() {
|
||||||
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type);
|
super.onStart();
|
||||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||||
this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
|
}
|
||||||
waypointMarkerType.setAdapter(adapter);
|
|
||||||
waypointDescription = findViewById(R.id.marker_edit_waypoint_description);
|
|
||||||
|
|
||||||
Button cancel = findViewById(R.id.marker_edit_cancel);
|
@Override
|
||||||
cancel.setOnClickListener(new OnClickListener() {
|
protected void onStop() {
|
||||||
@Override
|
super.onStop();
|
||||||
public void onClick(View v) {
|
trackRecordingServiceConnection.unbind();
|
||||||
finish();
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
done = findViewById(R.id.marker_edit_done);
|
|
||||||
updateUiByMarkerId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected int getLayoutResId() {
|
||||||
super.onStart();
|
return R.layout.marker_edit;
|
||||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void onStop() {
|
* Updates the UI based on the marker id.
|
||||||
super.onStop();
|
*/
|
||||||
trackRecordingServiceConnection.unbind();
|
private void updateUiByMarkerId() {
|
||||||
}
|
final boolean newMarker = markerId == -1L;
|
||||||
|
|
||||||
@Override
|
setTitle(newMarker ? R.string.menu_insert_marker : R.string.menu_edit);
|
||||||
protected int getLayoutResId() {
|
done.setText(newMarker ? R.string.generic_add : R.string.generic_save);
|
||||||
return R.layout.marker_edit;
|
done.setOnClickListener(new View.OnClickListener() {
|
||||||
}
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (newMarker) {
|
||||||
|
addMarker();
|
||||||
|
} else {
|
||||||
|
saveMarker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the UI based on the marker id.
|
|
||||||
*/
|
|
||||||
private void updateUiByMarkerId() {
|
|
||||||
final boolean newMarker = markerId == -1L;
|
|
||||||
|
|
||||||
setTitle(newMarker ? R.string.menu_insert_marker : R.string.menu_edit);
|
|
||||||
done.setText(newMarker ? R.string.generic_add : R.string.generic_save);
|
|
||||||
done.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (newMarker) {
|
if (newMarker) {
|
||||||
addMarker();
|
statisticsSection.setVisibility(View.GONE);
|
||||||
|
waypointSection.setVisibility(View.VISIBLE);
|
||||||
|
int nextWaypointNumber = trackId == -1L ? -1
|
||||||
|
: ContentProviderUtils.Factory.get(this).getNextWaypointNumber(trackId, WaypointType.WAYPOINT);
|
||||||
|
if (nextWaypointNumber == -1) {
|
||||||
|
nextWaypointNumber = 0;
|
||||||
|
}
|
||||||
|
waypointName.setText(getString(R.string.marker_name_format, nextWaypointNumber));
|
||||||
|
waypointName.selectAll();
|
||||||
|
waypointMarkerType.setText("");
|
||||||
|
waypointDescription.setText("");
|
||||||
} else {
|
} else {
|
||||||
saveMarker();
|
waypoint = ContentProviderUtils.Factory.get(this).getWaypoint(markerId);
|
||||||
|
if (waypoint == null) {
|
||||||
|
Log.d(TAG, "waypoint is null");
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||||
|
statisticsSection.setVisibility(statistics ? View.VISIBLE : View.GONE);
|
||||||
|
waypointSection.setVisibility(statistics ? View.GONE : View.VISIBLE);
|
||||||
|
if (statistics) {
|
||||||
|
statisticsName.setText(waypoint.getName());
|
||||||
|
} else {
|
||||||
|
waypointName.setText(waypoint.getName());
|
||||||
|
waypointMarkerType.setText(waypoint.getCategory());
|
||||||
|
waypointDescription.setText(waypoint.getDescription());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (newMarker) {
|
|
||||||
statisticsSection.setVisibility(View.GONE);
|
|
||||||
waypointSection.setVisibility(View.VISIBLE);
|
|
||||||
int nextWaypointNumber = trackId == -1L ? -1
|
|
||||||
: ContentProviderUtils.Factory.get(this).getNextWaypointNumber(trackId, WaypointType.WAYPOINT);
|
|
||||||
if (nextWaypointNumber == -1) {
|
|
||||||
nextWaypointNumber = 0;
|
|
||||||
}
|
|
||||||
waypointName.setText(getString(R.string.marker_name_format, nextWaypointNumber));
|
|
||||||
waypointName.selectAll();
|
|
||||||
waypointMarkerType.setText("");
|
|
||||||
waypointDescription.setText("");
|
|
||||||
} else {
|
|
||||||
waypoint = ContentProviderUtils.Factory.get(this).getWaypoint(markerId);
|
|
||||||
if (waypoint == null) {
|
|
||||||
Log.d(TAG, "waypoint is null");
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
|
||||||
statisticsSection.setVisibility(statistics ? View.VISIBLE : View.GONE);
|
|
||||||
waypointSection.setVisibility(statistics ? View.GONE : View.VISIBLE);
|
|
||||||
if (statistics) {
|
|
||||||
statisticsName.setText(waypoint.getName());
|
|
||||||
} else {
|
|
||||||
waypointName.setText(waypoint.getName());
|
|
||||||
waypointMarkerType.setText(waypoint.getCategory());
|
|
||||||
waypointDescription.setText(waypoint.getDescription());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a marker.
|
* Adds a marker.
|
||||||
*/
|
*/
|
||||||
private void addMarker() {
|
private void addMarker() {
|
||||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(
|
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(
|
||||||
WaypointType.WAYPOINT,
|
WaypointType.WAYPOINT,
|
||||||
false,
|
false,
|
||||||
waypointName.getText().toString(),
|
waypointName.getText().toString(),
|
||||||
waypointMarkerType.getText().toString(),
|
waypointMarkerType.getText().toString(),
|
||||||
waypointDescription.getText().toString(),
|
waypointDescription.getText().toString(),
|
||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
TrackRecordingServiceConnectionUtils.addMarker(
|
TrackRecordingServiceConnectionUtils.addMarker(
|
||||||
this, trackRecordingServiceConnection, waypointCreationRequest);
|
this, trackRecordingServiceConnection, waypointCreationRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a marker.
|
* Saves a marker.
|
||||||
*/
|
*/
|
||||||
private void saveMarker() {
|
private void saveMarker() {
|
||||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||||
if (statistics) {
|
if (statistics) {
|
||||||
waypoint.setName(statisticsName.getText().toString());
|
waypoint.setName(statisticsName.getText().toString());
|
||||||
} else {
|
} else {
|
||||||
waypoint.setName(waypointName.getText().toString());
|
waypoint.setName(waypointName.getText().toString());
|
||||||
waypoint.setCategory(waypointMarkerType.getText().toString());
|
waypoint.setCategory(waypointMarkerType.getText().toString());
|
||||||
waypoint.setDescription(waypointDescription.getText().toString());
|
waypoint.setDescription(waypointDescription.getText().toString());
|
||||||
|
}
|
||||||
|
ContentProviderUtils.Factory.get(this).updateWaypoint(waypoint);
|
||||||
}
|
}
|
||||||
ContentProviderUtils.Factory.get(this).updateWaypoint(waypoint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,279 +58,273 @@ import com.google.android.maps.mytracks.R;
|
|||||||
*/
|
*/
|
||||||
public class MarkerListActivity extends AbstractActivity implements DeleteMarkerCaller {
|
public class MarkerListActivity extends AbstractActivity implements DeleteMarkerCaller {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
|
|
||||||
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;
|
||||||
|
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||||
|
/*
|
||||||
|
* Note that sharedPreferenceChangeListener 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) {
|
||||||
|
// Note that the key can be null
|
||||||
|
if (key == null || key.equals(PreferencesUtils.getKey(MarkerListActivity.this, R.string.recording_track_id_key))) {
|
||||||
|
recordingTrackId = PreferencesUtils.getLong(MarkerListActivity.this, R.string.recording_track_id_key);
|
||||||
|
}
|
||||||
|
if (key == null || key.equals(PreferencesUtils.getKey(MarkerListActivity.this, R.string.recording_track_paused_key))) {
|
||||||
|
recordingTrackPaused = PreferencesUtils.getBoolean(MarkerListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||||
|
}
|
||||||
|
if (key != null) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
MarkerListActivity.this.invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private Track track;
|
||||||
|
private ResourceCursorAdapter resourceCursorAdapter;
|
||||||
|
// UI elements
|
||||||
|
private ListView listView;
|
||||||
|
// Callback when an item is selected in the contextual action mode
|
||||||
|
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||||
boolean isSingleSelection = ids.length == 1;
|
boolean isSingleSelection = ids.length == 1;
|
||||||
|
|
||||||
menu.findItem(R.id.list_context_menu_share).setVisible(false);
|
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_show_on_map).setVisible(isSingleSelection);
|
||||||
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
||||||
menu.findItem(R.id.list_context_menu_delete).setVisible(true);
|
menu.findItem(R.id.list_context_menu_delete).setVisible(true);
|
||||||
/*
|
/*
|
||||||
* Set select all to the same visibility as delete since delete is the
|
* Set select all to the same visibility as delete since delete is the
|
||||||
* only action that can be applied to multiple markers.
|
* only action that can be applied to multiple markers.
|
||||||
*/
|
*/
|
||||||
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
|
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
||||||
return handleContextItem(itemId, ids);
|
return handleContextItem(itemId, ids);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that sharedPreferenceChangeListener 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) {
|
|
||||||
// Note that the key can be null
|
|
||||||
if (key == null || key.equals(PreferencesUtils.getKey(MarkerListActivity.this, R.string.recording_track_id_key))) {
|
|
||||||
recordingTrackId = PreferencesUtils.getLong(MarkerListActivity.this, R.string.recording_track_id_key);
|
|
||||||
}
|
|
||||||
if (key == null || key.equals(PreferencesUtils.getKey(MarkerListActivity.this, R.string.recording_track_paused_key))) {
|
|
||||||
recordingTrackPaused = PreferencesUtils.getBoolean(MarkerListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
|
||||||
}
|
|
||||||
if (key != null) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
MarkerListActivity.this.invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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 ResourceCursorAdapter resourceCursorAdapter;
|
|
||||||
|
|
||||||
// UI elements
|
|
||||||
private ListView listView;
|
|
||||||
private MenuItem insertMarkerMenuItem;
|
|
||||||
private MenuItem searchMenuItem;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
|
||||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
|
||||||
track = trackId != -1L ? contentProviderUtils.getTrack(trackId) : null;
|
|
||||||
final long trackFirstWaypointId = trackId != -1 ? contentProviderUtils.getFirstWaypointId(trackId) : -1;
|
|
||||||
|
|
||||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
|
||||||
|
|
||||||
listView = findViewById(R.id.marker_list);
|
|
||||||
listView.setEmptyView(findViewById(R.id.marker_list_empty));
|
|
||||||
listView.setOnItemClickListener(new OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class)
|
|
||||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, id);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
int typeIndex = cursor.getColumnIndex(WaypointsColumns.TYPE);
|
|
||||||
int nameIndex = cursor.getColumnIndex(WaypointsColumns.NAME);
|
|
||||||
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
|
||||||
int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY);
|
|
||||||
int descriptionIndex = cursor.getColumnIndex(WaypointsColumns.DESCRIPTION);
|
|
||||||
int photoUrlIndex = cursor.getColumnIndex(WaypointsColumns.PHOTOURL);
|
|
||||||
int latitudeIndex = cursor.getColumnIndex(WaypointsColumns.LATITUDE);
|
|
||||||
int longitudeIndex = cursor.getColumnIndex(WaypointsColumns.LONGITUDE);
|
|
||||||
|
|
||||||
boolean statistics = WaypointType.values()[cursor.getInt(typeIndex)] == WaypointType.STATISTICS;
|
|
||||||
int iconId = statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin;
|
|
||||||
String name = cursor.getString(nameIndex);
|
|
||||||
long time = cursor.getLong(timeIndex);
|
|
||||||
String category = statistics ? null : cursor.getString(categoryIndex);
|
|
||||||
String description = statistics ? null : cursor.getString(descriptionIndex);
|
|
||||||
String photoUrl = cursor.getString(photoUrlIndex);
|
|
||||||
double latitude = cursor.getDouble(latitudeIndex);
|
|
||||||
double longitude = cursor.getDouble(longitudeIndex);
|
|
||||||
|
|
||||||
ListItemUtils.setListItem(MarkerListActivity.this, view, false, true, iconId, R.string.image_marker, name, null, null, 0, time, false, category, description, photoUrl);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
listView.setAdapter(resourceCursorAdapter);
|
private MenuItem insertMarkerMenuItem;
|
||||||
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
private MenuItem searchMenuItem;
|
||||||
|
|
||||||
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
@Override
|
||||||
@NonNull
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@Override
|
super.onCreate(savedInstanceState);
|
||||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
|
||||||
if (track != null) {
|
|
||||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
|
||||||
WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + "!=?",
|
|
||||||
new String[] { String.valueOf(track.getId()), String.valueOf(trackFirstWaypointId) }, null);
|
|
||||||
} else {
|
|
||||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
|
||||||
WaypointsColumns.STARTTIME + " IS NULL",null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||||
resourceCursorAdapter.swapCursor(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
track = trackId != -1L ? contentProviderUtils.getTrack(trackId) : null;
|
||||||
resourceCursorAdapter.swapCursor(null);
|
final long trackFirstWaypointId = trackId != -1 ? contentProviderUtils.getFirstWaypointId(trackId) : -1;
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
listView = findViewById(R.id.marker_list);
|
||||||
protected void onResume() {
|
listView.setEmptyView(findViewById(R.id.marker_list_empty));
|
||||||
super.onResume();
|
listView.setOnItemClickListener(new OnItemClickListener() {
|
||||||
this.invalidateOptionsMenu();
|
@Override
|
||||||
}
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class)
|
||||||
|
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, id);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
int typeIndex = cursor.getColumnIndex(WaypointsColumns.TYPE);
|
||||||
|
int nameIndex = cursor.getColumnIndex(WaypointsColumns.NAME);
|
||||||
|
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
||||||
|
int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY);
|
||||||
|
int descriptionIndex = cursor.getColumnIndex(WaypointsColumns.DESCRIPTION);
|
||||||
|
int photoUrlIndex = cursor.getColumnIndex(WaypointsColumns.PHOTOURL);
|
||||||
|
int latitudeIndex = cursor.getColumnIndex(WaypointsColumns.LATITUDE);
|
||||||
|
int longitudeIndex = cursor.getColumnIndex(WaypointsColumns.LONGITUDE);
|
||||||
|
|
||||||
@Override
|
boolean statistics = WaypointType.values()[cursor.getInt(typeIndex)] == WaypointType.STATISTICS;
|
||||||
protected void onStop() {
|
int iconId = statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin;
|
||||||
super.onStop();
|
String name = cursor.getString(nameIndex);
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
long time = cursor.getLong(timeIndex);
|
||||||
}
|
String category = statistics ? null : cursor.getString(categoryIndex);
|
||||||
|
String description = statistics ? null : cursor.getString(descriptionIndex);
|
||||||
|
String photoUrl = cursor.getString(photoUrlIndex);
|
||||||
|
double latitude = cursor.getDouble(latitudeIndex);
|
||||||
|
double longitude = cursor.getDouble(longitudeIndex);
|
||||||
|
|
||||||
@Override
|
ListItemUtils.setListItem(MarkerListActivity.this, view, false, true, iconId, R.string.image_marker, name, null, null, 0, time, false, category, description, photoUrl);
|
||||||
protected int getLayoutResId() {
|
}
|
||||||
return R.layout.marker_list;
|
};
|
||||||
}
|
listView.setAdapter(resourceCursorAdapter);
|
||||||
|
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||||
|
|
||||||
@Override
|
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
@NonNull
|
||||||
getMenuInflater().inflate(R.menu.marker_list, menu);
|
@Override
|
||||||
|
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||||
|
if (track != null) {
|
||||||
|
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||||
|
WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + "!=?",
|
||||||
|
new String[]{String.valueOf(track.getId()), String.valueOf(trackFirstWaypointId)}, null);
|
||||||
|
} else {
|
||||||
|
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||||
|
WaypointsColumns.STARTTIME + " IS NULL", null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
insertMarkerMenuItem = menu.findItem(R.id.marker_list_insert_marker);
|
@Override
|
||||||
|
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
||||||
|
resourceCursorAdapter.swapCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
searchMenuItem = menu.findItem(R.id.marker_list_search);
|
@Override
|
||||||
AbstractTrackActivity.configureSearchWidget(this, searchMenuItem, null);
|
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||||
|
resourceCursorAdapter.swapCursor(null);
|
||||||
return super.onCreateOptionsMenu(menu);
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
||||||
insertMarkerMenuItem.setVisible(track != null && track.getId() == recordingTrackId && !recordingTrackPaused);
|
|
||||||
return super.onPrepareOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (track != null && item.getItemId() == R.id.marker_list_insert_marker) {
|
|
||||||
Intent intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
|
||||||
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, track.getId());
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
protected void onStart() {
|
||||||
super.onCreateContextMenu(menu, v, menuInfo);
|
super.onStart();
|
||||||
getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
|
||||||
contextualActionModeCallback.onPrepare(menu, new int[] { info.position }, new long[] { info.id }, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onContextItemSelected(MenuItem item) {
|
|
||||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
|
|
||||||
if (handleContextItem(item.getItemId(), new long[] {info.id})) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return super.onContextItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Handles a context item selection.
|
protected void onResume() {
|
||||||
*
|
super.onResume();
|
||||||
* @param itemId the menu item id
|
this.invalidateOptionsMenu();
|
||||||
* @param markerIds the marker ids
|
|
||||||
* @return true if handled.
|
|
||||||
*/
|
|
||||||
private boolean handleContextItem(int itemId, long[] markerIds) {
|
|
||||||
Intent intent;
|
|
||||||
switch (itemId) {
|
|
||||||
case R.id.list_context_menu_show_on_map:
|
|
||||||
if (markerIds.length == 1) {
|
|
||||||
intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
|
|
||||||
.putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerIds[0]);
|
|
||||||
//TODO Use IntentUtils.newShowOnMapIntent()
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.list_context_menu_edit:
|
|
||||||
if (markerIds.length == 1) {
|
|
||||||
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
|
||||||
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerIds[0]);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.list_context_menu_delete:
|
|
||||||
if (markerIds.length > 1 && markerIds.length == listView.getCount()) {
|
|
||||||
markerIds = new long[] { -1L };
|
|
||||||
}
|
|
||||||
DeleteMarkerDialogFragment.newInstance(markerIds)
|
|
||||||
.show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
|
||||||
return true;
|
|
||||||
case R.id.list_context_menu_select_all:
|
|
||||||
int size = listView.getCount();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
listView.setItemChecked(i, true);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
protected void onStop() {
|
||||||
if (keyCode == KeyEvent.KEYCODE_SEARCH && searchMenuItem != null) {
|
super.onStop();
|
||||||
return true;
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
}
|
}
|
||||||
return super.onKeyUp(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeleteMarkerDone() {
|
protected int getLayoutResId() {
|
||||||
// Do nothing
|
return R.layout.marker_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.marker_list, menu);
|
||||||
|
|
||||||
|
insertMarkerMenuItem = menu.findItem(R.id.marker_list_insert_marker);
|
||||||
|
|
||||||
|
searchMenuItem = menu.findItem(R.id.marker_list_search);
|
||||||
|
AbstractTrackActivity.configureSearchWidget(this, searchMenuItem, null);
|
||||||
|
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
|
insertMarkerMenuItem.setVisible(track != null && track.getId() == recordingTrackId && !recordingTrackPaused);
|
||||||
|
return super.onPrepareOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (track != null && item.getItemId() == R.id.marker_list_insert_marker) {
|
||||||
|
Intent intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, track.getId());
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||||
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||||||
|
getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||||
|
|
||||||
|
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||||
|
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
|
||||||
|
if (handleContextItem(item.getItemId(), new long[]{info.id})) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onContextItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a context item selection.
|
||||||
|
*
|
||||||
|
* @param itemId the menu item id
|
||||||
|
* @param markerIds the marker ids
|
||||||
|
* @return true if handled.
|
||||||
|
*/
|
||||||
|
private boolean handleContextItem(int itemId, long[] markerIds) {
|
||||||
|
Intent intent;
|
||||||
|
switch (itemId) {
|
||||||
|
case R.id.list_context_menu_show_on_map:
|
||||||
|
if (markerIds.length == 1) {
|
||||||
|
intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
|
||||||
|
.putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerIds[0]);
|
||||||
|
//TODO Use IntentUtils.newShowOnMapIntent()
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_edit:
|
||||||
|
if (markerIds.length == 1) {
|
||||||
|
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerIds[0]);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_delete:
|
||||||
|
if (markerIds.length > 1 && markerIds.length == listView.getCount()) {
|
||||||
|
markerIds = new long[]{-1L};
|
||||||
|
}
|
||||||
|
DeleteMarkerDialogFragment.newInstance(markerIds)
|
||||||
|
.show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_select_all:
|
||||||
|
int size = listView.getCount();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
listView.setItemChecked(i, true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_SEARCH && searchMenuItem != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onKeyUp(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteMarkerDone() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,444 +70,441 @@ 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 {
|
||||||
|
|
||||||
private static final String TAG = SearchListActivity.class.getSimpleName();
|
private static final String TAG = SearchListActivity.class.getSimpleName();
|
||||||
|
|
||||||
private static final String IS_RECORDING_FIELD = "isRecording";
|
private static final String IS_RECORDING_FIELD = "isRecording";
|
||||||
private static final String IS_PAUSED_FIELD = "isPaused";
|
private static final String IS_PAUSED_FIELD = "isPaused";
|
||||||
private static final String ICON_ID_FIELD = "icon";
|
private static final String ICON_ID_FIELD = "icon";
|
||||||
private static final String ICON_CONTENT_DESCRIPTION_ID_FIELD = "iconContentDescription";
|
private static final String ICON_CONTENT_DESCRIPTION_ID_FIELD = "iconContentDescription";
|
||||||
private static final String NAME_FIELD = "name";
|
private static final String NAME_FIELD = "name";
|
||||||
private static final String TOTAL_TIME_FIELD = "totalTime";
|
private static final String TOTAL_TIME_FIELD = "totalTime";
|
||||||
private static final String TOTAL_DISTANCE_FIELD = "totalDistance";
|
private static final String TOTAL_DISTANCE_FIELD = "totalDistance";
|
||||||
private static final String MARKER_COUNT_FIELD = "markerCount";
|
private static final String MARKER_COUNT_FIELD = "markerCount";
|
||||||
private static final String START_TIME_FIELD = "startTime";
|
private static final String START_TIME_FIELD = "startTime";
|
||||||
private static final String CATEGORY_FIELD = "category";
|
private static final String CATEGORY_FIELD = "category";
|
||||||
private static final String DESCRIPTION_FIELD = "description";
|
private static final String DESCRIPTION_FIELD = "description";
|
||||||
private static final String PHOTO_URL_FIELD = "photoUrl";
|
private static final String PHOTO_URL_FIELD = "photoUrl";
|
||||||
private static final String MARKER_LATITUDE_FIELD = "latitude";
|
private static final String MARKER_LATITUDE_FIELD = "latitude";
|
||||||
private static final String MARKER_LONGITUDE_FIELD = "longitude";
|
private static final String MARKER_LONGITUDE_FIELD = "longitude";
|
||||||
|
|
||||||
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))) {
|
// Callback when an item is selected in the contextual action mode
|
||||||
recordingTrackId = PreferencesUtils.getLong(SearchListActivity.this, R.string.recording_track_id_key);
|
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
}
|
|
||||||
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
|
|
||||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
|
||||||
@Override
|
|
||||||
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
|
||||||
return handleContextItem(itemId, positions);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
|
||||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
|
||||||
boolean isSingleSelection = positions.length == 1;
|
|
||||||
boolean isSingleSelectionTrack;
|
|
||||||
if (isSingleSelection) {
|
|
||||||
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
|
|
||||||
Long trackId = (Long) item.get(TRACK_ID_FIELD);
|
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
|
||||||
|
|
||||||
isSingleSelectionTrack = item.get(MARKER_ID_FIELD) == null;
|
|
||||||
} else {
|
|
||||||
isSingleSelectionTrack = false;
|
|
||||||
}
|
|
||||||
// Not recording, one item, item is a track
|
|
||||||
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection && isSingleSelectionTrack);
|
|
||||||
//TODO Setup shareIntent.
|
|
||||||
|
|
||||||
// One item, item is a marker
|
|
||||||
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection && !isSingleSelectionTrack);
|
|
||||||
// One item, can be a track or a marker
|
|
||||||
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
|
||||||
// One item. If track, no restriction.
|
|
||||||
menu.findItem(R.id.list_context_menu_delete).setVisible(isSingleSelection && (isSingleSelectionTrack));
|
|
||||||
// Disable select all, no action is available for multiple selection
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
|
||||||
|
|
||||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
|
||||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
|
||||||
searchEngine = new SearchEngine(contentProviderUtils);
|
|
||||||
searchRecentSuggestions = SearchEngineProvider.newHelper(this);
|
|
||||||
|
|
||||||
arrayAdapter = new ArrayAdapter<Map<String, Object>>(this, R.layout.list_item, R.id.list_item_name) {
|
|
||||||
@NonNull
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, @NonNull android.view.ViewGroup parent) {
|
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
||||||
View view;
|
return handleContextItem(itemId, positions);
|
||||||
if (convertView == null) {
|
|
||||||
view = getLayoutInflater().inflate(R.layout.list_item, parent, false);
|
|
||||||
} else {
|
|
||||||
view = convertView;
|
|
||||||
}
|
}
|
||||||
Map<String, Object> resultMap = getItem(position);
|
|
||||||
boolean isRecording = (boolean) resultMap.get(IS_RECORDING_FIELD);
|
|
||||||
boolean isPaused = (boolean) resultMap.get(IS_PAUSED_FIELD);
|
|
||||||
int iconId = (int) resultMap.get(ICON_ID_FIELD);
|
|
||||||
int iconContentDescriptionId = (int) resultMap.get(ICON_CONTENT_DESCRIPTION_ID_FIELD);
|
|
||||||
String name = (String) resultMap.get(NAME_FIELD);
|
|
||||||
String totalTime = (String) resultMap.get(TOTAL_TIME_FIELD);
|
|
||||||
String totalDistance = (String) resultMap.get(TOTAL_DISTANCE_FIELD);
|
|
||||||
int markerCount = (int) resultMap.get(MARKER_COUNT_FIELD);
|
|
||||||
long startTime = (long) resultMap.get(START_TIME_FIELD);
|
|
||||||
String category = (String) resultMap.get(CATEGORY_FIELD);
|
|
||||||
String description = (String) resultMap.get(DESCRIPTION_FIELD);
|
|
||||||
String photoUrl = (String) resultMap.get(PHOTO_URL_FIELD);
|
|
||||||
|
|
||||||
ListItemUtils.setListItem(SearchListActivity.this, view, isRecording, isPaused, iconId,
|
@Override
|
||||||
iconContentDescriptionId, name, totalTime, totalDistance, markerCount,
|
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||||
startTime, false, category, description, photoUrl);
|
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
return view;
|
boolean isSingleSelection = positions.length == 1;
|
||||||
}
|
boolean isSingleSelectionTrack;
|
||||||
|
if (isSingleSelection) {
|
||||||
|
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
|
||||||
|
Long trackId = (Long) item.get(TRACK_ID_FIELD);
|
||||||
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
|
isSingleSelectionTrack = item.get(MARKER_ID_FIELD) == null;
|
||||||
|
} else {
|
||||||
|
isSingleSelectionTrack = false;
|
||||||
|
}
|
||||||
|
// Not recording, one item, item is a track
|
||||||
|
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection && isSingleSelectionTrack);
|
||||||
|
//TODO Setup shareIntent.
|
||||||
|
|
||||||
|
// One item, item is a marker
|
||||||
|
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection && !isSingleSelectionTrack);
|
||||||
|
// One item, can be a track or a marker
|
||||||
|
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
||||||
|
// One item. If track, no restriction.
|
||||||
|
menu.findItem(R.id.list_context_menu_delete).setVisible(isSingleSelection && (isSingleSelectionTrack));
|
||||||
|
// Disable select all, no action is available for multiple selection
|
||||||
|
menu.findItem(R.id.list_context_menu_select_all).setVisible(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// UI elements
|
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||||
ListView listView = findViewById(R.id.search_list);
|
private final OnSharedPreferenceChangeListener
|
||||||
listView.setAdapter(arrayAdapter);
|
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
listView.setEmptyView(findViewById(R.id.search_list_empty));
|
@Override
|
||||||
listView.setOnItemClickListener(new OnItemClickListener() {
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
@Override
|
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.stats_units_key))) {
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
metricUnits = PreferencesUtils.isMetricUnits(SearchListActivity.this);
|
||||||
Map<String, Object> item = arrayAdapter.getItem(position);
|
}
|
||||||
|
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
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||||
|
|
||||||
|
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||||
|
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||||
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||||
|
searchEngine = new SearchEngine(contentProviderUtils);
|
||||||
|
searchRecentSuggestions = SearchEngineProvider.newHelper(this);
|
||||||
|
|
||||||
|
arrayAdapter = new ArrayAdapter<Map<String, Object>>(this, R.layout.list_item, R.id.list_item_name) {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, @NonNull android.view.ViewGroup parent) {
|
||||||
|
View view;
|
||||||
|
if (convertView == null) {
|
||||||
|
view = getLayoutInflater().inflate(R.layout.list_item, parent, false);
|
||||||
|
} else {
|
||||||
|
view = convertView;
|
||||||
|
}
|
||||||
|
Map<String, Object> resultMap = getItem(position);
|
||||||
|
boolean isRecording = (boolean) resultMap.get(IS_RECORDING_FIELD);
|
||||||
|
boolean isPaused = (boolean) resultMap.get(IS_PAUSED_FIELD);
|
||||||
|
int iconId = (int) resultMap.get(ICON_ID_FIELD);
|
||||||
|
int iconContentDescriptionId = (int) resultMap.get(ICON_CONTENT_DESCRIPTION_ID_FIELD);
|
||||||
|
String name = (String) resultMap.get(NAME_FIELD);
|
||||||
|
String totalTime = (String) resultMap.get(TOTAL_TIME_FIELD);
|
||||||
|
String totalDistance = (String) resultMap.get(TOTAL_DISTANCE_FIELD);
|
||||||
|
int markerCount = (int) resultMap.get(MARKER_COUNT_FIELD);
|
||||||
|
long startTime = (long) resultMap.get(START_TIME_FIELD);
|
||||||
|
String category = (String) resultMap.get(CATEGORY_FIELD);
|
||||||
|
String description = (String) resultMap.get(DESCRIPTION_FIELD);
|
||||||
|
String photoUrl = (String) resultMap.get(PHOTO_URL_FIELD);
|
||||||
|
|
||||||
|
ListItemUtils.setListItem(SearchListActivity.this, view, isRecording, isPaused, iconId,
|
||||||
|
iconContentDescriptionId, name, totalTime, totalDistance, markerCount,
|
||||||
|
startTime, false, category, description, photoUrl);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// UI elements
|
||||||
|
ListView listView = findViewById(R.id.search_list);
|
||||||
|
listView.setAdapter(arrayAdapter);
|
||||||
|
listView.setEmptyView(findViewById(R.id.search_list_empty));
|
||||||
|
listView.setOnItemClickListener(new OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
Map<String, Object> item = arrayAdapter.getItem(position);
|
||||||
|
Long trackId = (Long) item.get(TRACK_ID_FIELD);
|
||||||
|
Long markerId = (Long) item.get(MARKER_ID_FIELD);
|
||||||
|
Intent intent;
|
||||||
|
if (markerId != null) {
|
||||||
|
intent = IntentUtils.newIntent(SearchListActivity.this, TrackDetailActivity.class)
|
||||||
|
.putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerId);
|
||||||
|
} else {
|
||||||
|
intent = IntentUtils.newIntent(SearchListActivity.this, TrackDetailActivity.class)
|
||||||
|
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
}
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||||
|
handleIntent(getIntent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||||
|
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
arrayAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
trackRecordingServiceConnection.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getLayoutResId() {
|
||||||
|
return R.layout.search_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewIntent(Intent intent) {
|
||||||
|
setIntent(intent);
|
||||||
|
handleIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.search_list, menu);
|
||||||
|
menu.findItem(R.id.search_list_search).setVisible(false);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.search_list_search) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||||
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||||||
|
getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||||
|
|
||||||
|
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||||
|
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
|
||||||
|
if (handleContextItem(item.getItemId(), new int[]{info.position})) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onContextItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onKeyUp(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a context item selection.
|
||||||
|
*
|
||||||
|
* @param itemId the menu item id
|
||||||
|
* @param positions the positions of the selected rows
|
||||||
|
* @return true if handled.
|
||||||
|
*/
|
||||||
|
private boolean handleContextItem(int itemId, int[] positions) {
|
||||||
|
if (positions.length != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
|
||||||
Long trackId = (Long) item.get(TRACK_ID_FIELD);
|
Long trackId = (Long) item.get(TRACK_ID_FIELD);
|
||||||
Long markerId = (Long) item.get(MARKER_ID_FIELD);
|
Long markerId = (Long) item.get(MARKER_ID_FIELD);
|
||||||
Intent intent;
|
Intent intent;
|
||||||
if (markerId != null) {
|
switch (itemId) {
|
||||||
intent = IntentUtils.newIntent(SearchListActivity.this, TrackDetailActivity.class)
|
case R.id.list_context_menu_share:
|
||||||
.putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerId);
|
//TODO
|
||||||
} else {
|
Log.e(TAG, "Not implemented");
|
||||||
intent = IntentUtils.newIntent(SearchListActivity.this, TrackDetailActivity.class)
|
return true;
|
||||||
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId);
|
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) + "");
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_edit:
|
||||||
|
if (markerId != null) {
|
||||||
|
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
|
||||||
|
} else {
|
||||||
|
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
|
||||||
|
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
}
|
||||||
|
startActivity(intent);
|
||||||
|
|
||||||
|
// Close the search result since its content can change after edit.
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_delete:
|
||||||
|
if (markerId != null) {
|
||||||
|
DeleteMarkerDialogFragment.newInstance(new long[]{markerId}).show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
||||||
|
} else {
|
||||||
|
deleteTracks(new long[]{trackId});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
|
||||||
handleIntent(getIntent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
|
||||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
arrayAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
trackRecordingServiceConnection.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getLayoutResId() {
|
|
||||||
return R.layout.search_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewIntent(Intent intent) {
|
|
||||||
setIntent(intent);
|
|
||||||
handleIntent(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.search_list, menu);
|
|
||||||
menu.findItem(R.id.search_list_search).setVisible(false);
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.search_list_search) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
|
||||||
super.onCreateContextMenu(menu, v, menuInfo);
|
|
||||||
getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
|
||||||
|
|
||||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
|
||||||
contextualActionModeCallback.onPrepare(menu, new int[] { info.position }, new long[] { info.id }, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onContextItemSelected(MenuItem item) {
|
|
||||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
|
|
||||||
if (handleContextItem(item.getItemId(), new int[] {info.position})) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onContextItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onKeyUp(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a context item selection.
|
|
||||||
*
|
|
||||||
* @param itemId the menu item id
|
|
||||||
* @param positions the positions of the selected rows
|
|
||||||
* @return true if handled.
|
|
||||||
*/
|
|
||||||
private boolean handleContextItem(int itemId, int[] positions) {
|
|
||||||
if (positions.length != 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
|
|
||||||
Long trackId = (Long) item.get(TRACK_ID_FIELD);
|
|
||||||
Long markerId = (Long) item.get(MARKER_ID_FIELD);
|
|
||||||
Intent intent;
|
|
||||||
switch (itemId) {
|
|
||||||
case R.id.list_context_menu_share:
|
|
||||||
//TODO
|
|
||||||
Log.e(TAG, "Not implemented");
|
|
||||||
return true;
|
|
||||||
case R.id.list_context_menu_show_on_map:
|
|
||||||
intent = IntentUtils.newShowOnMapIntent((double)item.get(MARKER_LATITUDE_FIELD), (double)item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + "");
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
case R.id.list_context_menu_edit:
|
|
||||||
if (markerId != null) {
|
|
||||||
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
|
||||||
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
|
|
||||||
} else {
|
|
||||||
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
|
|
||||||
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
|
|
||||||
}
|
|
||||||
startActivity(intent);
|
|
||||||
|
|
||||||
// Close the search result since its content can change after edit.
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
case R.id.list_context_menu_delete:
|
|
||||||
if (markerId != null) {
|
|
||||||
DeleteMarkerDialogFragment.newInstance(new long[] { markerId }).show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
|
||||||
} else {
|
|
||||||
deleteTracks(new long[] { trackId });
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the intent.
|
|
||||||
*
|
|
||||||
* @param intent the intent
|
|
||||||
*/
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
if (!Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
|
||||||
Log.e(TAG, "Invalid intent action: " + intent);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String textQuery = intent.getStringExtra(SearchManager.QUERY);
|
/**
|
||||||
setTitle(textQuery);
|
* Handles the intent.
|
||||||
|
*
|
||||||
doSearch(textQuery);
|
* @param intent the intent
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do the search.
|
|
||||||
*
|
|
||||||
* @param textQuery the query
|
|
||||||
*/
|
|
||||||
private void doSearch(String textQuery) {
|
|
||||||
SearchQuery query = new SearchQuery(textQuery, null, -1L, System.currentTimeMillis());
|
|
||||||
SortedSet<ScoredResult> scoredResults = searchEngine.search(query);
|
|
||||||
final List<Map<String, Object>> displayResults = prepareResultsforDisplay(scoredResults);
|
|
||||||
|
|
||||||
// Use the UI thread to display the results
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
arrayAdapter.clear();
|
|
||||||
arrayAdapter.addAll(displayResults);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Save the query as a suggestion for the future
|
|
||||||
searchRecentSuggestions.saveRecentQuery(query.textQuery, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the result for display.
|
|
||||||
*
|
|
||||||
* @param scoredResults a list of score results
|
|
||||||
* @return a list of result maps
|
|
||||||
*/
|
|
||||||
private List<Map<String, Object>> prepareResultsforDisplay(Collection<ScoredResult> scoredResults) {
|
|
||||||
//TODO Replace use of map<string, object>, but rather provide Track or Waypoint directly.
|
|
||||||
ArrayList<Map<String, Object>> output = new ArrayList<>(scoredResults.size());
|
|
||||||
for (ScoredResult result : scoredResults) {
|
|
||||||
Map<String, Object> resultMap = new HashMap<>();
|
|
||||||
if (result.track != null) {
|
|
||||||
prepareTrackForDisplay(result.track, resultMap);
|
|
||||||
} else {
|
|
||||||
prepareMarkerForDisplay(result.waypoint, resultMap);
|
|
||||||
}
|
|
||||||
output.add(resultMap);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares a marker for display by filling in a result map.
|
|
||||||
*
|
|
||||||
* @param waypoint the marker
|
|
||||||
* @param resultMap the result map
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
private void prepareMarkerForDisplay(Waypoint waypoint, Map<String, Object> resultMap) {
|
|
||||||
/*
|
|
||||||
* TODO: It may be more appropriate to obtain the track name as a join in
|
|
||||||
* the retrieval phase of the search.
|
|
||||||
*/
|
*/
|
||||||
String trackName = null;
|
private void handleIntent(Intent intent) {
|
||||||
long trackId = waypoint.getTrackId();
|
if (!Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||||
if (trackId != -1L) {
|
Log.e(TAG, "Invalid intent action: " + intent);
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
finish();
|
||||||
if (track != null) {
|
return;
|
||||||
trackName = track.getName();
|
}
|
||||||
}
|
|
||||||
|
final String textQuery = intent.getStringExtra(SearchManager.QUERY);
|
||||||
|
setTitle(textQuery);
|
||||||
|
|
||||||
|
doSearch(textQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
/**
|
||||||
|
* Do the search.
|
||||||
|
*
|
||||||
|
* @param textQuery the query
|
||||||
|
*/
|
||||||
|
private void doSearch(String textQuery) {
|
||||||
|
SearchQuery query = new SearchQuery(textQuery, null, -1L, System.currentTimeMillis());
|
||||||
|
SortedSet<ScoredResult> scoredResults = searchEngine.search(query);
|
||||||
|
final List<Map<String, Object>> displayResults = prepareResultsforDisplay(scoredResults);
|
||||||
|
|
||||||
resultMap.put(IS_RECORDING_FIELD, false);
|
// Use the UI thread to display the results
|
||||||
resultMap.put(IS_PAUSED_FIELD, true);
|
runOnUiThread(new Runnable() {
|
||||||
resultMap.put(ICON_ID_FIELD, statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin);
|
@Override
|
||||||
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_marker);
|
public void run() {
|
||||||
resultMap.put(NAME_FIELD, waypoint.getName());
|
arrayAdapter.clear();
|
||||||
// Display the marker's track name in the total time field
|
arrayAdapter.addAll(displayResults);
|
||||||
resultMap.put(TOTAL_TIME_FIELD, trackName == null ? null : getString(R.string.search_list_marker_track_location, trackName));
|
}
|
||||||
resultMap.put(TOTAL_DISTANCE_FIELD, null);
|
});
|
||||||
resultMap.put(MARKER_COUNT_FIELD, 0);
|
|
||||||
resultMap.put(START_TIME_FIELD, waypoint.getLocation().getTime());
|
|
||||||
resultMap.put(CATEGORY_FIELD, statistics ? null : waypoint.getCategory());
|
|
||||||
resultMap.put(DESCRIPTION_FIELD, statistics ? null : waypoint.getDescription());
|
|
||||||
resultMap.put(PHOTO_URL_FIELD, waypoint.getPhotoUrl());
|
|
||||||
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId());
|
|
||||||
resultMap.put(MARKER_ID_FIELD, waypoint.getId());
|
|
||||||
|
|
||||||
resultMap.put(MARKER_LATITUDE_FIELD, waypoint.getLocation().getLatitude());
|
// Save the query as a suggestion for the future
|
||||||
resultMap.put(MARKER_LONGITUDE_FIELD, waypoint.getLocation().getLongitude());
|
searchRecentSuggestions.saveRecentQuery(query.textQuery, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares a track for display by filling in a result map.
|
* Prepares the result for display.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param scoredResults a list of score results
|
||||||
* @param resultMap the result map
|
* @return a list of result maps
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
private List<Map<String, Object>> prepareResultsforDisplay(Collection<ScoredResult> scoredResults) {
|
||||||
private void prepareTrackForDisplay(Track track, Map<String, Object> resultMap) {
|
//TODO Replace use of map<string, object>, but rather provide Track or Waypoint directly.
|
||||||
TripStatistics tripStatitics = track.getTripStatistics();
|
ArrayList<Map<String, Object>> output = new ArrayList<>(scoredResults.size());
|
||||||
String icon = track.getIcon();
|
for (ScoredResult result : scoredResults) {
|
||||||
String category = icon != null && !icon.equals("") ? null : track.getCategory();
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
|
if (result.track != null) {
|
||||||
|
prepareTrackForDisplay(result.track, resultMap);
|
||||||
|
} else {
|
||||||
|
prepareMarkerForDisplay(result.waypoint, resultMap);
|
||||||
|
}
|
||||||
|
output.add(resultMap);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
resultMap.put(IS_RECORDING_FIELD, track.getId() == recordingTrackId);
|
/**
|
||||||
resultMap.put(IS_PAUSED_FIELD, recordingTrackPaused);
|
* Prepares a marker for display by filling in a result map.
|
||||||
resultMap.put(ICON_ID_FIELD, TrackIconUtils.getIconDrawable(icon));
|
*
|
||||||
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_track);
|
* @param waypoint the marker
|
||||||
resultMap.put(NAME_FIELD, track.getName());
|
* @param resultMap the result map
|
||||||
resultMap.put(TOTAL_TIME_FIELD, StringUtils.formatElapsedTime(tripStatitics.getTotalTime()));
|
*/
|
||||||
resultMap.put(TOTAL_DISTANCE_FIELD, StringUtils.formatDistance(this, tripStatitics.getTotalDistance(), metricUnits));
|
@Deprecated
|
||||||
resultMap.put(MARKER_COUNT_FIELD, contentProviderUtils.getWaypointCount(track.getId()));
|
private void prepareMarkerForDisplay(Waypoint waypoint, Map<String, Object> resultMap) {
|
||||||
resultMap.put(START_TIME_FIELD, tripStatitics.getStartTime());
|
/*
|
||||||
resultMap.put(CATEGORY_FIELD, category);
|
* TODO: It may be more appropriate to obtain the track name as a join in
|
||||||
resultMap.put(DESCRIPTION_FIELD, track.getDescription());
|
* the retrieval phase of the search.
|
||||||
resultMap.put(PHOTO_URL_FIELD, null);
|
*/
|
||||||
resultMap.put(TRACK_ID_FIELD, track.getId());
|
String trackName = null;
|
||||||
resultMap.put(MARKER_ID_FIELD, null);
|
long trackId = waypoint.getTrackId();
|
||||||
}
|
if (trackId != -1L) {
|
||||||
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
if (track != null) {
|
||||||
|
trackName = track.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||||
public void onDeleteMarkerDone() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
handleIntent(getIntent());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
resultMap.put(IS_RECORDING_FIELD, false);
|
||||||
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
|
resultMap.put(IS_PAUSED_FIELD, true);
|
||||||
return trackRecordingServiceConnection;
|
resultMap.put(ICON_ID_FIELD, statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin);
|
||||||
}
|
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_marker);
|
||||||
|
resultMap.put(NAME_FIELD, waypoint.getName());
|
||||||
|
// Display the marker's track name in the total time field
|
||||||
|
resultMap.put(TOTAL_TIME_FIELD, trackName == null ? null : getString(R.string.search_list_marker_track_location, trackName));
|
||||||
|
resultMap.put(TOTAL_DISTANCE_FIELD, null);
|
||||||
|
resultMap.put(MARKER_COUNT_FIELD, 0);
|
||||||
|
resultMap.put(START_TIME_FIELD, waypoint.getLocation().getTime());
|
||||||
|
resultMap.put(CATEGORY_FIELD, statistics ? null : waypoint.getCategory());
|
||||||
|
resultMap.put(DESCRIPTION_FIELD, statistics ? null : waypoint.getDescription());
|
||||||
|
resultMap.put(PHOTO_URL_FIELD, waypoint.getPhotoUrl());
|
||||||
|
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId());
|
||||||
|
resultMap.put(MARKER_ID_FIELD, waypoint.getId());
|
||||||
|
|
||||||
@Override
|
resultMap.put(MARKER_LATITUDE_FIELD, waypoint.getLocation().getLatitude());
|
||||||
protected void onDeleted() {
|
resultMap.put(MARKER_LONGITUDE_FIELD, waypoint.getLocation().getLongitude());
|
||||||
runOnUiThread(new Runnable() {
|
}
|
||||||
@Override
|
|
||||||
public void run() {
|
/**
|
||||||
handleIntent(getIntent());
|
* Prepares a track for display by filling in a result map.
|
||||||
}
|
*
|
||||||
});
|
* @param track the track
|
||||||
}
|
* @param resultMap the result map
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
private void prepareTrackForDisplay(Track track, Map<String, Object> resultMap) {
|
||||||
|
TripStatistics tripStatitics = track.getTripStatistics();
|
||||||
|
String icon = track.getIcon();
|
||||||
|
String category = icon != null && !icon.equals("") ? null : track.getCategory();
|
||||||
|
|
||||||
|
resultMap.put(IS_RECORDING_FIELD, track.getId() == recordingTrackId);
|
||||||
|
resultMap.put(IS_PAUSED_FIELD, recordingTrackPaused);
|
||||||
|
resultMap.put(ICON_ID_FIELD, TrackIconUtils.getIconDrawable(icon));
|
||||||
|
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_track);
|
||||||
|
resultMap.put(NAME_FIELD, track.getName());
|
||||||
|
resultMap.put(TOTAL_TIME_FIELD, StringUtils.formatElapsedTime(tripStatitics.getTotalTime()));
|
||||||
|
resultMap.put(TOTAL_DISTANCE_FIELD, StringUtils.formatDistance(this, tripStatitics.getTotalDistance(), metricUnits));
|
||||||
|
resultMap.put(MARKER_COUNT_FIELD, contentProviderUtils.getWaypointCount(track.getId()));
|
||||||
|
resultMap.put(START_TIME_FIELD, tripStatitics.getStartTime());
|
||||||
|
resultMap.put(CATEGORY_FIELD, category);
|
||||||
|
resultMap.put(DESCRIPTION_FIELD, track.getDescription());
|
||||||
|
resultMap.put(PHOTO_URL_FIELD, null);
|
||||||
|
resultMap.put(TRACK_ID_FIELD, track.getId());
|
||||||
|
resultMap.put(MARKER_ID_FIELD, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteMarkerDone() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleIntent(getIntent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
|
||||||
|
return trackRecordingServiceConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDeleted() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleIntent(getIntent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,104 +48,106 @@ import java.util.ArrayList;
|
|||||||
//TODO Rework or better remove.
|
//TODO Rework or better remove.
|
||||||
public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
|
public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final TabHost tabHost;
|
|
||||||
private final ViewPager viewPager;
|
|
||||||
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;
|
private final Context context;
|
||||||
|
private final TabHost tabHost;
|
||||||
|
private final ViewPager viewPager;
|
||||||
|
private final ArrayList<TabInfo> tabInfos = new ArrayList<>();
|
||||||
|
|
||||||
public DummyTabFactory(Context context) {
|
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) {
|
||||||
this.context = context;
|
super(activity.getSupportFragmentManager());
|
||||||
|
this.context = activity;
|
||||||
|
this.tabHost = tabHost;
|
||||||
|
this.viewPager = viewPager;
|
||||||
|
this.tabHost.setOnTabChangedListener(this);
|
||||||
|
this.viewPager.setAdapter(this);
|
||||||
|
this.viewPager.setOnPageChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle bundle) {
|
||||||
|
tabSpec.setContent(new DummyTabFactory(context));
|
||||||
|
|
||||||
|
TabInfo info = new TabInfo(clss, bundle);
|
||||||
|
|
||||||
|
tabInfos.add(info);
|
||||||
|
tabHost.addTab(tabSpec);
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createTabContent(String tag) {
|
public int getCount() {
|
||||||
View view = new View(context);
|
return tabInfos.size();
|
||||||
view.setMinimumWidth(0);
|
|
||||||
view.setMinimumHeight(0);
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) {
|
@Override
|
||||||
super(activity.getSupportFragmentManager());
|
public Fragment getItem(int position) {
|
||||||
this.context = activity;
|
TabInfo info = tabInfos.get(position);
|
||||||
this.tabHost = tabHost;
|
return Fragment.instantiate(context, info.clss.getName(), info.bundle);
|
||||||
this.viewPager = viewPager;
|
}
|
||||||
this.tabHost.setOnTabChangedListener(this);
|
|
||||||
this.viewPager.setAdapter(this);
|
|
||||||
this.viewPager.setOnPageChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle bundle) {
|
@Override
|
||||||
tabSpec.setContent(new DummyTabFactory(context));
|
public void onTabChanged(String tabId) {
|
||||||
|
int position = tabHost.getCurrentTab();
|
||||||
|
viewPager.setCurrentItem(position);
|
||||||
|
}
|
||||||
|
|
||||||
TabInfo info = new TabInfo(clss, bundle);
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
}
|
||||||
|
|
||||||
tabInfos.add(info);
|
@Override
|
||||||
tabHost.addTab(tabSpec);
|
public void onPageSelected(int position) {
|
||||||
notifyDataSetChanged();
|
/*
|
||||||
}
|
* Unfortunately when TabHost changes the current tab, it kindly also takes
|
||||||
|
* care of putting focus on it when not in touch mode. The jerk. This hack
|
||||||
|
* tries to prevent this from pulling focus out of our ViewPager.
|
||||||
|
*/
|
||||||
|
TabWidget tabWidget = tabHost.getTabWidget();
|
||||||
|
int oldFocusability = tabWidget.getDescendantFocusability();
|
||||||
|
tabWidget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||||
|
tabHost.setCurrentTab(position);
|
||||||
|
tabWidget.setDescendantFocusability(oldFocusability);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public void onPageScrollStateChanged(int state) {
|
||||||
return tabInfos.size();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Fragment getItem(int position) {
|
* An object to hold a tab's info.
|
||||||
TabInfo info = tabInfos.get(position);
|
*
|
||||||
return Fragment.instantiate(context, info.clss.getName(), info.bundle);
|
* @author Jimmy Shih
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabChanged(String tabId) {
|
|
||||||
int position = tabHost.getCurrentTab();
|
|
||||||
viewPager.setCurrentItem(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageSelected(int position) {
|
|
||||||
/*
|
|
||||||
* Unfortunately when TabHost changes the current tab, it kindly also takes
|
|
||||||
* care of putting focus on it when not in touch mode. The jerk. This hack
|
|
||||||
* tries to prevent this from pulling focus out of our ViewPager.
|
|
||||||
*/
|
*/
|
||||||
TabWidget tabWidget = tabHost.getTabWidget();
|
private static final class TabInfo {
|
||||||
int oldFocusability = tabWidget.getDescendantFocusability();
|
|
||||||
tabWidget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
|
||||||
tabHost.setCurrentTab(position);
|
|
||||||
tabWidget.setDescendantFocusability(oldFocusability);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private final Class<?> clss;
|
||||||
public void onPageScrollStateChanged(int state) {}
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,114 +36,114 @@ import com.google.android.maps.mytracks.R;
|
|||||||
*/
|
*/
|
||||||
public class TrackController {
|
public class TrackController {
|
||||||
|
|
||||||
private static final String TAG = TrackController.class.getSimpleName();
|
private static final String TAG = TrackController.class.getSimpleName();
|
||||||
|
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private final Handler handlerUpdateTotalTime;
|
private final Handler handlerUpdateTotalTime;
|
||||||
private final View containerView;
|
private final View containerView;
|
||||||
private final TextView statusTextView;
|
private final TextView statusTextView;
|
||||||
private final TextView totalTimeTextView;
|
private final TextView totalTimeTextView;
|
||||||
private final ImageButton recordImageButton;
|
private final ImageButton recordImageButton;
|
||||||
private final ImageButton stopImageButton;
|
private final ImageButton stopImageButton;
|
||||||
private final boolean alwaysShow;
|
private final boolean alwaysShow;
|
||||||
|
|
||||||
private boolean isRecording;
|
private boolean isRecording;
|
||||||
private boolean isPaused;
|
private boolean isPaused;
|
||||||
private long totalTime = 0;
|
private long totalTime = 0;
|
||||||
|
|
||||||
// the timestamp for the toal time
|
// the timestamp for the toal time
|
||||||
private long totalTimeTimestamp = 0;
|
private long totalTimeTimestamp = 0;
|
||||||
|
|
||||||
private boolean isResumed = false;
|
private boolean isResumed = false;
|
||||||
|
|
||||||
// A runnable to update the total time.
|
// A runnable to update the total time.
|
||||||
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isResumed && isRecording && !isPaused) {
|
if (isResumed && isRecording && !isPaused) {
|
||||||
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
||||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND);
|
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public TrackController(Activity activity, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
|
public TrackController(Activity activity, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
|
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
|
||||||
this.alwaysShow = alwaysShow;
|
this.alwaysShow = alwaysShow;
|
||||||
handlerUpdateTotalTime = new Handler();
|
handlerUpdateTotalTime = new Handler();
|
||||||
containerView = activity.findViewById(R.id.track_controller_container);
|
containerView = activity.findViewById(R.id.track_controller_container);
|
||||||
statusTextView = activity.findViewById(R.id.track_controller_status);
|
statusTextView = activity.findViewById(R.id.track_controller_status);
|
||||||
totalTimeTextView = activity.findViewById(R.id.track_controller_total_time);
|
totalTimeTextView = activity.findViewById(R.id.track_controller_total_time);
|
||||||
|
|
||||||
recordImageButton = activity.findViewById(R.id.track_controller_record);
|
recordImageButton = activity.findViewById(R.id.track_controller_record);
|
||||||
recordImageButton.setOnClickListener(recordListener);
|
recordImageButton.setOnClickListener(recordListener);
|
||||||
|
|
||||||
stopImageButton = activity.findViewById(R.id.track_controller_stop);
|
stopImageButton = activity.findViewById(R.id.track_controller_stop);
|
||||||
stopImageButton.setOnClickListener(stopListener);
|
stopImageButton.setOnClickListener(stopListener);
|
||||||
}
|
|
||||||
|
|
||||||
public void update(boolean recording, boolean paused) {
|
|
||||||
if (!isResumed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isRecording = recording;
|
|
||||||
isPaused = paused;
|
|
||||||
boolean visible = alwaysShow || isRecording;
|
|
||||||
containerView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
if (!visible) {
|
|
||||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.button_pause : R.drawable.button_record);
|
public void update(boolean recording, boolean paused) {
|
||||||
recordImageButton.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
|
if (!isResumed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isRecording = recording;
|
||||||
|
isPaused = paused;
|
||||||
|
boolean visible = alwaysShow || isRecording;
|
||||||
|
containerView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
stopImageButton.setImageResource(isRecording ? R.drawable.button_stop : R.drawable.ic_button_stop_disabled);
|
if (!visible) {
|
||||||
stopImageButton.setEnabled(isRecording);
|
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
statusTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.button_pause : R.drawable.button_record);
|
||||||
if (isRecording) {
|
recordImageButton.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
|
||||||
statusTextView.setTextColor(activity.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
|
|
||||||
statusTextView.setText(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
stopImageButton.setImageResource(isRecording ? R.drawable.button_stop : R.drawable.ic_button_stop_disabled);
|
||||||
|
stopImageButton.setEnabled(isRecording);
|
||||||
|
|
||||||
|
statusTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
if (isRecording) {
|
||||||
|
statusTextView.setTextColor(activity.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
|
||||||
|
statusTextView.setText(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||||
|
totalTimeTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
if (isRecording) {
|
||||||
|
totalTime = getTotalTime();
|
||||||
|
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
||||||
|
if (!isPaused) {
|
||||||
|
totalTimeTimestamp = System.currentTimeMillis();
|
||||||
|
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
public void onResume(boolean recording, boolean paused) {
|
||||||
totalTimeTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
isResumed = true;
|
||||||
if (isRecording) {
|
update(recording, paused);
|
||||||
totalTime = getTotalTime();
|
|
||||||
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
|
||||||
if (!isPaused) {
|
|
||||||
totalTimeTimestamp = System.currentTimeMillis();
|
|
||||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void onResume(boolean recording, boolean paused) {
|
public void onPause() {
|
||||||
isResumed = true;
|
isResumed = false;
|
||||||
update(recording, paused);
|
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPause() {
|
public void hide() {
|
||||||
isResumed = false;
|
containerView.setVisibility(View.GONE);
|
||||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void hide() {
|
public void show() {
|
||||||
containerView.setVisibility(View.GONE);
|
containerView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void show() {
|
/**
|
||||||
containerView.setVisibility(View.VISIBLE);
|
* Gets the total time for the current recording track.
|
||||||
}
|
*/
|
||||||
|
private long getTotalTime() {
|
||||||
/**
|
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
* Gets the total time for the current recording track.
|
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
||||||
*/
|
}
|
||||||
private long getTotalTime() {
|
|
||||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
|
||||||
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,445 +76,439 @@ 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_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 PHOTO_URI_KEY = "photo_uri_key";
|
||||||
|
private static final String HAS_PHOTO_KEY = "has_photo_key";
|
||||||
|
private static final String JPEG_EXTENSION = "jpeg";
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
private static final int CAMERA_REQUEST_CODE = 5;
|
||||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
private static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6;
|
||||||
|
|
||||||
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
|
// The following are set in onCreate
|
||||||
private static final String PHOTO_URI_KEY = "photo_uri_key";
|
private boolean hasCamera;
|
||||||
private static final String HAS_PHOTO_KEY = "has_photo_key";
|
private Uri photoUri;
|
||||||
private static final String JPEG_EXTENSION = "jpeg";
|
private boolean hasPhoto;
|
||||||
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
|
private TrackDataHub trackDataHub;
|
||||||
|
private TabHost tabHost;
|
||||||
|
private TrackController trackController;
|
||||||
|
|
||||||
private static final int CAMERA_REQUEST_CODE = 5;
|
// From intent
|
||||||
private static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6;
|
private long trackId;
|
||||||
|
|
||||||
// The following are set in onCreate
|
// Preferences
|
||||||
private boolean hasCamera;
|
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
private Uri photoUri;
|
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||||
private boolean hasPhoto;
|
private final Runnable bindChangedCallback = new Runnable() {
|
||||||
private ContentProviderUtils contentProviderUtils;
|
@Override
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
|
||||||
private TrackDataHub trackDataHub;
|
|
||||||
private TabHost tabHost;
|
|
||||||
private TrackController trackController;
|
|
||||||
|
|
||||||
// From intent
|
|
||||||
private long trackId;
|
|
||||||
|
|
||||||
// Preferences
|
|
||||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_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() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// After binding changes (is available), update the total time in
|
|
||||||
// trackController.
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
public void run() {
|
||||||
trackController.update(isRecording(), recordingTrackPaused);
|
// After binding changes (is available), update the total time in
|
||||||
if (hasPhoto && photoUri != null) {
|
// trackController.
|
||||||
hasPhoto = false;
|
runOnUiThread(new Runnable() {
|
||||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(WaypointType.WAYPOINT, false, null, null, null, null, photoUri.toString());
|
@Override
|
||||||
long markerId = TrackRecordingServiceConnectionUtils.addMarker(TrackDetailActivity.this, trackRecordingServiceConnection, waypointCreationRequest);
|
public void run() {
|
||||||
if (markerId != -1L) {
|
trackController.update(isRecording(), recordingTrackPaused);
|
||||||
FileUtils.updateMediaScanner(TrackDetailActivity.this, photoUri);
|
if (hasPhoto && photoUri != null) {
|
||||||
}
|
hasPhoto = false;
|
||||||
}
|
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(WaypointType.WAYPOINT, false, null, null, null, null, photoUri.toString());
|
||||||
|
long markerId = TrackRecordingServiceConnectionUtils.addMarker(TrackDetailActivity.this, trackRecordingServiceConnection, waypointCreationRequest);
|
||||||
|
if (markerId != -1L) {
|
||||||
|
FileUtils.updateMediaScanner(TrackDetailActivity.this, photoUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
/*
|
||||||
};
|
* Note that sharedPreferenceChangeListener cannot be an anonymous inner
|
||||||
|
* class. Anonymous inner class will get garbage collected.
|
||||||
/*
|
*/
|
||||||
* Note that sharedPreferenceChangeListener cannot be an anonymous inner
|
private final OnSharedPreferenceChangeListener
|
||||||
* class. Anonymous inner class will get garbage collected.
|
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
*/
|
@Override
|
||||||
private final OnSharedPreferenceChangeListener
|
|
||||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.recording_track_id_key))) {
|
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.recording_track_id_key))) {
|
||||||
recordingTrackId = PreferencesUtils.getLong(TrackDetailActivity.this, R.string.recording_track_id_key);
|
recordingTrackId = PreferencesUtils.getLong(TrackDetailActivity.this, R.string.recording_track_id_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.recording_track_paused_key))) {
|
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.recording_track_paused_key))) {
|
||||||
recordingTrackPaused = PreferencesUtils.getBoolean(TrackDetailActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
recordingTrackPaused = PreferencesUtils.getBoolean(TrackDetailActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == null) return;
|
if (key == null) return;
|
||||||
|
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
TrackDetailActivity.this.invalidateOptionsMenu();
|
||||||
|
boolean isRecording = isRecording();
|
||||||
|
trackController.update(isRecording, recordingTrackPaused);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private MenuItem insertMarkerMenuItem;
|
||||||
|
private MenuItem insertPhotoMenuItem;
|
||||||
|
private MenuItem markerListMenuItem;
|
||||||
|
private MenuItem shareMenuItem;
|
||||||
|
private MenuItem voiceFrequencyMenuItem;
|
||||||
|
private MenuItem splitFrequencyMenuItem;
|
||||||
|
private final OnClickListener recordListener = new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (recordingTrackPaused) {
|
||||||
|
// Paused -> Resume
|
||||||
|
updateMenuItems(false);
|
||||||
|
TrackRecordingServiceConnectionUtils.resumeTrack(trackRecordingServiceConnection);
|
||||||
|
trackController.update(true, false);
|
||||||
|
} else {
|
||||||
|
// Recording -> Paused
|
||||||
|
updateMenuItems(true);
|
||||||
|
TrackRecordingServiceConnectionUtils.pauseTrack(trackRecordingServiceConnection);
|
||||||
|
trackController.update(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final OnClickListener stopListener = new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
TrackRecordingServiceConnectionUtils.stopRecording(TrackDetailActivity.this, trackRecordingServiceConnection, true);
|
||||||
|
updateMenuItems(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
||||||
|
photoUri = savedInstanceState != null ? (Uri) savedInstanceState.getParcelable(PHOTO_URI_KEY) : null;
|
||||||
|
hasPhoto = savedInstanceState != null && savedInstanceState.getBoolean(HAS_PHOTO_KEY, false);
|
||||||
|
|
||||||
|
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||||
|
handleIntent(getIntent());
|
||||||
|
|
||||||
|
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);
|
||||||
|
trackDataHub = TrackDataHub.newInstance(this);
|
||||||
|
|
||||||
|
tabHost = findViewById(android.R.id.tabhost);
|
||||||
|
tabHost.setup();
|
||||||
|
|
||||||
|
ViewPager viewPager = findViewById(R.id.pager);
|
||||||
|
TabsAdapter tabsAdapter = new TabsAdapter(this, tabHost, viewPager);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
tabHost.setCurrentTabByTag(savedInstanceState.getString(CURRENT_TAB_TAG_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the background after all three tabs are added
|
||||||
|
for (int i = 0; i < tabHost.getTabWidget().getChildCount(); i++) {
|
||||||
|
tabHost.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_indicator_mytracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
trackController = new TrackController(this, trackRecordingServiceConnection, false, recordListener, stopListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||||
|
|
||||||
|
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||||
|
trackDataHub.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
trackDataHub.loadTrack(trackId);
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
this.invalidateOptionsMenu();
|
||||||
|
trackController.onResume(isRecording(), recordingTrackPaused);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
trackController.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
trackRecordingServiceConnection.unbind();
|
||||||
|
trackDataHub.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putString(CURRENT_TAB_TAG_KEY, tabHost.getCurrentTabTag());
|
||||||
|
if (photoUri != null) {
|
||||||
|
outState.putParcelable(PHOTO_URI_KEY, photoUri);
|
||||||
|
}
|
||||||
|
outState.putBoolean(HAS_PHOTO_KEY, hasPhoto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == CAMERA_REQUEST_CODE) {
|
||||||
|
if (resultCode == RESULT_CANCELED) {
|
||||||
|
Toast.makeText(this, R.string.marker_add_canceled, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasPhoto = resultCode == RESULT_OK;
|
||||||
|
|
||||||
|
if (hasPhoto) {
|
||||||
|
//Register photo in media scanner
|
||||||
|
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||||
|
mediaScanIntent.setData(photoUri);
|
||||||
|
this.sendBroadcast(mediaScanIntent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getLayoutResId() {
|
||||||
|
return R.layout.track_detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHomeSelected() {
|
||||||
|
/*
|
||||||
|
* TODO: Investigate
|
||||||
|
* According to
|
||||||
|
* http://developer.android.com/training/implementing-navigation
|
||||||
|
* /ancestral.html, we should use NavUtils.shouldUpRecreateTask instead of
|
||||||
|
* always creating a new back stack. However, NavUtils.shouldUpRecreateTask
|
||||||
|
* seems to always return false.
|
||||||
|
*/
|
||||||
|
TaskStackBuilder.create(this).addParentStack(TrackDetailActivity.class).startActivities();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewIntent(Intent intent) {
|
||||||
|
setIntent(intent);
|
||||||
|
handleIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.track_detail, menu);
|
||||||
|
|
||||||
|
insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker);
|
||||||
|
insertPhotoMenuItem = menu.findItem(R.id.track_detail_insert_photo);
|
||||||
|
insertPhotoMenuItem.setVisible(new Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(getPackageManager()) != null);
|
||||||
|
|
||||||
|
shareMenuItem = menu.findItem(R.id.track_detail_share);
|
||||||
|
ShareActionProvider shareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider();
|
||||||
|
//TODO: Share the actual track when track is finished? How to get the file path or create a new file?
|
||||||
|
shareActionProvider.setShareIntent(IntentUtils.newShareFileIntent(this, trackId, "", TrackFileFormat.KML));
|
||||||
|
|
||||||
|
voiceFrequencyMenuItem = menu.findItem(R.id.track_detail_voice_frequency);
|
||||||
|
splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency);
|
||||||
|
markerListMenuItem = menu.findItem(R.id.track_detail_markers);
|
||||||
|
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
|
updateMenuItems(recordingTrackPaused);
|
||||||
|
return super.onPrepareOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
Intent intent;
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.track_detail_insert_marker:
|
||||||
|
intent = IntentUtils
|
||||||
|
.newIntent(this, MarkerEditActivity.class)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
case R.id.track_detail_insert_photo:
|
||||||
|
if (!FileUtils.isExternalStorageWriteable() || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
||||||
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
takePicture();
|
||||||
|
return true;
|
||||||
|
case R.id.track_detail_markers:
|
||||||
|
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
|
||||||
|
.putExtra(MarkerListActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
case R.id.track_detail_voice_frequency:
|
||||||
|
FrequencyDialogFragment.newInstance(R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.string.menu_voice_frequency)
|
||||||
|
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
|
||||||
|
return true;
|
||||||
|
case R.id.track_detail_split_frequency:
|
||||||
|
FrequencyDialogFragment.newInstance(R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.string.menu_split_frequency)
|
||||||
|
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
|
||||||
|
return true;
|
||||||
|
case R.id.track_detail_edit:
|
||||||
|
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
|
||||||
|
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
case R.id.track_detail_delete:
|
||||||
|
deleteTracks(new long[]{trackId});
|
||||||
|
return true;
|
||||||
|
case R.id.track_detail_settings:
|
||||||
|
intent = IntentUtils.newIntent(this, SettingsActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void takePicture() {
|
||||||
|
File dir = FileUtils.getPhotoDir(trackId);
|
||||||
|
FileUtils.ensureDirectoryExists(dir);
|
||||||
|
|
||||||
|
String fileName = SimpleDateFormat.getDateTimeInstance().format(new Date());
|
||||||
|
File file = new File(dir, FileUtils.buildUniqueFileName(dir, fileName, JPEG_EXTENSION));
|
||||||
|
|
||||||
|
if (file != null) {
|
||||||
|
photoUri = FileProvider.getUriForFile(this, FileUtils.FILEPROVIDER, file);
|
||||||
|
Log.d(TAG, "Taking photo to URI: " + photoUri);
|
||||||
|
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||||
|
.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
|
||||||
|
startActivityForResult(intent, CAMERA_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
||||||
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
||||||
|
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
takePicture();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
|
||||||
|
return trackRecordingServiceConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDeleted() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
TrackDetailActivity.this.invalidateOptionsMenu();
|
finish();
|
||||||
boolean isRecording = isRecording();
|
|
||||||
trackController.update(isRecording, recordingTrackPaused);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final OnClickListener recordListener = new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (recordingTrackPaused) {
|
|
||||||
// Paused -> Resume
|
|
||||||
updateMenuItems(false);
|
|
||||||
TrackRecordingServiceConnectionUtils.resumeTrack(trackRecordingServiceConnection);
|
|
||||||
trackController.update(true, false);
|
|
||||||
} else {
|
|
||||||
// Recording -> Paused
|
|
||||||
updateMenuItems(true);
|
|
||||||
TrackRecordingServiceConnectionUtils.pauseTrack(trackRecordingServiceConnection);
|
|
||||||
trackController.update(true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final OnClickListener stopListener = new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
TrackRecordingServiceConnectionUtils.stopRecording(TrackDetailActivity.this, trackRecordingServiceConnection, true);
|
|
||||||
updateMenuItems(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
|
||||||
photoUri = savedInstanceState != null ? (Uri) savedInstanceState.getParcelable(PHOTO_URI_KEY) : null;
|
|
||||||
hasPhoto = savedInstanceState != null && savedInstanceState.getBoolean(HAS_PHOTO_KEY, false);
|
|
||||||
|
|
||||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
|
||||||
handleIntent(getIntent());
|
|
||||||
|
|
||||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);
|
|
||||||
trackDataHub = TrackDataHub.newInstance(this);
|
|
||||||
|
|
||||||
tabHost = findViewById(android.R.id.tabhost);
|
|
||||||
tabHost.setup();
|
|
||||||
|
|
||||||
ViewPager viewPager = findViewById(R.id.pager);
|
|
||||||
TabsAdapter tabsAdapter = new TabsAdapter(this, tabHost, viewPager);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
tabHost.setCurrentTabByTag(savedInstanceState.getString(CURRENT_TAB_TAG_KEY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the background after all three tabs are added
|
/**
|
||||||
for (int i = 0; i < tabHost.getTabWidget().getChildCount(); i++) {
|
* Gets the {@link TrackDataHub}.
|
||||||
tabHost.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_indicator_mytracks);
|
|
||||||
}
|
|
||||||
|
|
||||||
trackController = new TrackController(this, trackRecordingServiceConnection, false, recordListener, stopListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
|
||||||
|
|
||||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
|
||||||
trackDataHub.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
trackDataHub.loadTrack(trackId);
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
this.invalidateOptionsMenu();
|
|
||||||
trackController.onResume(isRecording(), recordingTrackPaused);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
trackController.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
trackRecordingServiceConnection.unbind();
|
|
||||||
trackDataHub.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putString(CURRENT_TAB_TAG_KEY, tabHost.getCurrentTabTag());
|
|
||||||
if (photoUri != null) {
|
|
||||||
outState.putParcelable(PHOTO_URI_KEY, photoUri);
|
|
||||||
}
|
|
||||||
outState.putBoolean(HAS_PHOTO_KEY, hasPhoto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
if (requestCode == CAMERA_REQUEST_CODE) {
|
|
||||||
if (resultCode == RESULT_CANCELED) {
|
|
||||||
Toast.makeText(this, R.string.marker_add_canceled, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
hasPhoto = resultCode == RESULT_OK;
|
|
||||||
|
|
||||||
if (hasPhoto) {
|
|
||||||
//Register photo in media scanner
|
|
||||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
|
||||||
mediaScanIntent.setData(photoUri);
|
|
||||||
this.sendBroadcast(mediaScanIntent);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getLayoutResId() {
|
|
||||||
return R.layout.track_detail;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onHomeSelected() {
|
|
||||||
/*
|
|
||||||
* TODO: Investigate
|
|
||||||
* According to
|
|
||||||
* http://developer.android.com/training/implementing-navigation
|
|
||||||
* /ancestral.html, we should use NavUtils.shouldUpRecreateTask instead of
|
|
||||||
* always creating a new back stack. However, NavUtils.shouldUpRecreateTask
|
|
||||||
* seems to always return false.
|
|
||||||
*/
|
*/
|
||||||
TaskStackBuilder.create(this).addParentStack(TrackDetailActivity.class).startActivities();
|
public TrackDataHub getTrackDataHub() {
|
||||||
finish();
|
return trackDataHub;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void onNewIntent(Intent intent) {
|
* Gets the track id.
|
||||||
setIntent(intent);
|
*/
|
||||||
handleIntent(intent);
|
public long getTrackId() {
|
||||||
}
|
return trackId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
* Handles the data in the intent.
|
||||||
getMenuInflater().inflate(R.menu.track_detail, menu);
|
*/
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker);
|
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||||
insertPhotoMenuItem = menu.findItem(R.id.track_detail_insert_photo);
|
long markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||||
insertPhotoMenuItem.setVisible(new Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(getPackageManager()) != null);
|
if (markerId != -1L) {
|
||||||
|
// Use the trackId from the marker
|
||||||
shareMenuItem = menu.findItem(R.id.track_detail_share);
|
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
|
||||||
ShareActionProvider shareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider();
|
if (waypoint == null) {
|
||||||
//TODO: Share the actual track when track is finished? How to get the file path or create a new file?
|
finish();
|
||||||
shareActionProvider.setShareIntent(IntentUtils.newShareFileIntent(this, trackId, "", TrackFileFormat.KML));
|
return;
|
||||||
|
}
|
||||||
voiceFrequencyMenuItem = menu.findItem(R.id.track_detail_voice_frequency);
|
trackId = waypoint.getTrackId();
|
||||||
splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency);
|
|
||||||
markerListMenuItem = menu.findItem(R.id.track_detail_markers);
|
|
||||||
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
||||||
updateMenuItems(recordingTrackPaused);
|
|
||||||
return super.onPrepareOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
Intent intent;
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.track_detail_insert_marker:
|
|
||||||
intent = IntentUtils
|
|
||||||
.newIntent(this, MarkerEditActivity.class)
|
|
||||||
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId);
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
case R.id.track_detail_insert_photo:
|
|
||||||
if (!FileUtils.isExternalStorageWriteable() || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
takePicture();
|
if (trackId == -1L) {
|
||||||
return true;
|
finish();
|
||||||
case R.id.track_detail_markers:
|
return;
|
||||||
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
|
}
|
||||||
.putExtra(MarkerListActivity.EXTRA_TRACK_ID, trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
startActivity(intent);
|
if (track == null) {
|
||||||
return true;
|
// Use the last track if markerId is not set
|
||||||
case R.id.track_detail_voice_frequency:
|
if (markerId == -1L) {
|
||||||
FrequencyDialogFragment.newInstance(R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.string.menu_voice_frequency)
|
track = contentProviderUtils.getLastTrack();
|
||||||
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
|
if (track != null) {
|
||||||
return true;
|
trackId = track.getId();
|
||||||
case R.id.track_detail_split_frequency:
|
return;
|
||||||
FrequencyDialogFragment.newInstance(R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.string.menu_split_frequency)
|
}
|
||||||
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
|
}
|
||||||
return true;
|
finish();
|
||||||
case R.id.track_detail_edit:
|
|
||||||
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
|
|
||||||
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
case R.id.track_detail_delete:
|
|
||||||
deleteTracks(new long[] { trackId });
|
|
||||||
return true;
|
|
||||||
case R.id.track_detail_settings:
|
|
||||||
intent = IntentUtils.newIntent(this, SettingsActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void takePicture() {
|
|
||||||
File dir = FileUtils.getPhotoDir(trackId);
|
|
||||||
FileUtils.ensureDirectoryExists(dir);
|
|
||||||
|
|
||||||
String fileName = SimpleDateFormat.getDateTimeInstance().format(new Date());
|
|
||||||
File file = new File(dir, FileUtils.buildUniqueFileName(dir, fileName, JPEG_EXTENSION));
|
|
||||||
|
|
||||||
if (file != null) {
|
|
||||||
photoUri = FileProvider.getUriForFile(this, FileUtils.FILEPROVIDER, file);
|
|
||||||
Log.d(TAG, "Taking photo to URI: " + photoUri);
|
|
||||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
|
||||||
.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
|
|
||||||
startActivityForResult(intent, CAMERA_REQUEST_CODE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
||||||
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
|
||||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
|
||||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
|
||||||
} else {
|
|
||||||
takePicture();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
|
|
||||||
return trackRecordingServiceConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDeleted() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the {@link TrackDataHub}.
|
|
||||||
*/
|
|
||||||
public TrackDataHub getTrackDataHub() {
|
|
||||||
return trackDataHub;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the track id.
|
|
||||||
*/
|
|
||||||
public long getTrackId() {
|
|
||||||
return trackId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the data in the intent.
|
|
||||||
*/
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
|
|
||||||
long markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
|
|
||||||
if (markerId != -1L) {
|
|
||||||
// Use the trackId from the marker
|
|
||||||
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
|
|
||||||
if (waypoint == null) {
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
trackId = waypoint.getTrackId();
|
|
||||||
}
|
|
||||||
if (trackId == -1L) {
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
|
||||||
if (track == null) {
|
|
||||||
// Use the last track if markerId is not set
|
|
||||||
if (markerId == -1L) {
|
|
||||||
track = contentProviderUtils.getLastTrack();
|
|
||||||
if (track != null) {
|
|
||||||
trackId = track.getId();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the menu items.
|
* Updates the menu items.
|
||||||
*/
|
*/
|
||||||
private void updateMenuItems(boolean isPaused) {
|
private void updateMenuItems(boolean isPaused) {
|
||||||
insertMarkerMenuItem.setVisible(isRecording() && !isPaused);
|
insertMarkerMenuItem.setVisible(isRecording() && !isPaused);
|
||||||
insertPhotoMenuItem.setVisible(hasCamera && isRecording() && !isPaused);
|
insertPhotoMenuItem.setVisible(hasCamera && isRecording() && !isPaused);
|
||||||
shareMenuItem.setVisible(!isRecording());
|
shareMenuItem.setVisible(!isRecording());
|
||||||
markerListMenuItem.setShowAsAction(isRecording() ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
markerListMenuItem.setShowAsAction(isRecording() ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||||
voiceFrequencyMenuItem.setVisible(isRecording());
|
voiceFrequencyMenuItem.setVisible(isRecording());
|
||||||
splitFrequencyMenuItem.setVisible(isRecording());
|
splitFrequencyMenuItem.setVisible(isRecording());
|
||||||
String title;
|
String title;
|
||||||
if (isRecording()) {
|
if (isRecording()) {
|
||||||
title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||||
} else {
|
} else {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
title = track != null ? track.getName() : "";
|
title = track != null ? track.getName() : "";
|
||||||
|
}
|
||||||
|
setTitle(title);
|
||||||
}
|
}
|
||||||
setTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void chooseActivityType(String category) {
|
public void chooseActivityType(String category) {
|
||||||
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(), ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(), ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChooseActivityTypeDone(String iconValue) {
|
public void onChooseActivityTypeDone(String iconValue) {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||||
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRecording() {
|
private boolean isRecording() {
|
||||||
return trackId == recordingTrackId;
|
return trackId == recordingTrackId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,163 +46,163 @@ import com.google.android.maps.mytracks.R;
|
|||||||
*/
|
*/
|
||||||
public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeCaller {
|
public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeCaller {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
public static final String EXTRA_NEW_TRACK = "new_track";
|
public static final String EXTRA_NEW_TRACK = "new_track";
|
||||||
|
|
||||||
private static final String TAG = TrackEditActivity.class.getSimpleName();
|
private static final String TAG = TrackEditActivity.class.getSimpleName();
|
||||||
private static final String ICON_VALUE_KEY = "icon_value_key";
|
private static final String ICON_VALUE_KEY = "icon_value_key";
|
||||||
|
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
private Track track;
|
private Track track;
|
||||||
private String iconValue;
|
private String iconValue;
|
||||||
|
|
||||||
private EditText name;
|
private EditText name;
|
||||||
private AutoCompleteTextView activityType;
|
private AutoCompleteTextView activityType;
|
||||||
private Spinner activityTypeIcon;
|
private Spinner activityTypeIcon;
|
||||||
private EditText description;
|
private EditText description;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle bundle) {
|
protected void onCreate(Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||||
Long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
Long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||||
if (trackId == -1L) {
|
if (trackId == -1L) {
|
||||||
Log.e(TAG, "invalid trackId");
|
Log.e(TAG, "invalid trackId");
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
|
||||||
track = contentProviderUtils.getTrack(trackId);
|
|
||||||
if (track == null) {
|
|
||||||
Log.e(TAG, "No track for " + trackId);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = findViewById(R.id.track_edit_name);
|
|
||||||
name.setText(track.getName());
|
|
||||||
|
|
||||||
activityType = findViewById(R.id.track_edit_activity_type);
|
|
||||||
activityType.setText(track.getCategory());
|
|
||||||
|
|
||||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
|
||||||
this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
|
||||||
activityType.setAdapter(adapter);
|
|
||||||
activityType.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
|
||||||
TrackEditActivity.this, (String) activityType.getAdapter().getItem(position)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
activityType.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
|
||||||
if (!hasFocus) {
|
|
||||||
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
|
||||||
TrackEditActivity.this, activityType.getText().toString()));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
iconValue = null;
|
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||||
if (bundle != null) {
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
iconValue = bundle.getString(ICON_VALUE_KEY);
|
if (track == null) {
|
||||||
}
|
Log.e(TAG, "No track for " + trackId);
|
||||||
if (iconValue == null) {
|
finish();
|
||||||
iconValue = track.getIcon();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = findViewById(R.id.track_edit_name);
|
||||||
|
name.setText(track.getName());
|
||||||
|
|
||||||
|
activityType = findViewById(R.id.track_edit_activity_type);
|
||||||
|
activityType.setText(track.getCategory());
|
||||||
|
|
||||||
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||||
|
this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||||
|
activityType.setAdapter(adapter);
|
||||||
|
activityType.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||||
|
TrackEditActivity.this, (String) activityType.getAdapter().getItem(position)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
activityType.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
|
if (!hasFocus) {
|
||||||
|
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||||
|
TrackEditActivity.this, activityType.getText().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
iconValue = null;
|
||||||
|
if (bundle != null) {
|
||||||
|
iconValue = bundle.getString(ICON_VALUE_KEY);
|
||||||
|
}
|
||||||
|
if (iconValue == null) {
|
||||||
|
iconValue = track.getIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
activityTypeIcon = findViewById(R.id.track_edit_activity_type_icon);
|
||||||
|
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(this, iconValue));
|
||||||
|
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
||||||
|
getSupportFragmentManager(),
|
||||||
|
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||||
|
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
||||||
|
getSupportFragmentManager(),
|
||||||
|
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
description = findViewById(R.id.track_edit_description);
|
||||||
|
description.setText(track.getDescription());
|
||||||
|
|
||||||
|
Button save = findViewById(R.id.track_edit_save);
|
||||||
|
save.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
TrackUtils.updateTrack(TrackEditActivity.this, track, name.getText().toString(),
|
||||||
|
activityType.getText().toString(), description.getText().toString(),
|
||||||
|
contentProviderUtils);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancel = findViewById(R.id.track_edit_cancel);
|
||||||
|
if (getIntent().getBooleanExtra(EXTRA_NEW_TRACK, false)) {
|
||||||
|
setTitle(R.string.track_edit_new_track_title);
|
||||||
|
cancel.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
setTitle(R.string.menu_edit);
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cancel.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activityTypeIcon = findViewById(R.id.track_edit_activity_type_icon);
|
|
||||||
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(this, iconValue));
|
|
||||||
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
|
||||||
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
|
||||||
getSupportFragmentManager(),
|
|
||||||
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
|
||||||
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
|
||||||
getSupportFragmentManager(),
|
|
||||||
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
description = findViewById(R.id.track_edit_description);
|
@Override
|
||||||
description.setText(track.getDescription());
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
Button save = findViewById(R.id.track_edit_save);
|
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||||
save.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
TrackUtils.updateTrack(TrackEditActivity.this, track, name.getText().toString(),
|
|
||||||
activityType.getText().toString(), description.getText().toString(),
|
|
||||||
contentProviderUtils);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button cancel = findViewById(R.id.track_edit_cancel);
|
|
||||||
if (getIntent().getBooleanExtra(EXTRA_NEW_TRACK, false)) {
|
|
||||||
setTitle(R.string.track_edit_new_track_title);
|
|
||||||
cancel.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
setTitle(R.string.menu_edit);
|
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cancel.setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
trackRecordingServiceConnection.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onStart();
|
super.onSaveInstanceState(outState);
|
||||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
outState.putString(ICON_VALUE_KEY, iconValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected int getLayoutResId() {
|
||||||
super.onStop();
|
return R.layout.track_edit;
|
||||||
trackRecordingServiceConnection.unbind();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private void setActivityTypeIcon(String value) {
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
iconValue = value;
|
||||||
super.onSaveInstanceState(outState);
|
TrackIconUtils.setIconSpinner(activityTypeIcon, value);
|
||||||
outState.putString(ICON_VALUE_KEY, iconValue);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getLayoutResId() {
|
public void onChooseActivityTypeDone(String value) {
|
||||||
return R.layout.track_edit;
|
setActivityTypeIcon(value);
|
||||||
}
|
activityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
||||||
|
}
|
||||||
private void setActivityTypeIcon(String value) {
|
|
||||||
iconValue = value;
|
|
||||||
TrackIconUtils.setIconSpinner(activityTypeIcon, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChooseActivityTypeDone(String value) {
|
|
||||||
setActivityTypeIcon(value);
|
|
||||||
activityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+347
-349
@@ -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;
|
||||||
|
|
||||||
@@ -34,368 +34,366 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface ContentProviderUtils {
|
public interface ContentProviderUtils {
|
||||||
|
|
||||||
/**
|
|
||||||
* The authority (the first part of the URI) for the app's content
|
|
||||||
* provider.
|
|
||||||
*/
|
|
||||||
String AUTHORITY = "com.google.android.maps.mytracks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears a track. Removes waypoints and trackpoints. Only keeps the track id.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
void clearTrack(Context context, long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link Track} from a cursor.
|
|
||||||
*
|
|
||||||
* @param cursor the cursor pointing to the track
|
|
||||||
*/
|
|
||||||
Track createTrack(Cursor cursor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes all tracks (including waypoints and track points).
|
|
||||||
*/
|
|
||||||
void deleteAllTracks(Context context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a track.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
void deleteTrack(Context context, long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the tracks. If no track exists, an empty list is returned.
|
|
||||||
* <p>
|
|
||||||
* Note that the returned tracks do not have any track points attached.
|
|
||||||
*/
|
|
||||||
List<Track> getAllTracks();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the last track. Returns null if doesn't exist.
|
|
||||||
*/
|
|
||||||
Track getLastTrack();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a track by a track id. Returns null if not found.
|
|
||||||
* <p>
|
|
||||||
* Note that the returned track doesn't have any track points attached.
|
|
||||||
*
|
|
||||||
* @param trackId the track id.
|
|
||||||
*/
|
|
||||||
Track getTrack(long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a track cursor. The caller owns the returned cursor and is responsible
|
|
||||||
* for closing it.
|
|
||||||
*
|
|
||||||
* @param selection the selection. Can be null
|
|
||||||
* @param selectionArgs the selection arguments. Can be null
|
|
||||||
* @param sortOrder the sort order. Can be null
|
|
||||||
*/
|
|
||||||
Cursor getTrackCursor(String selection, String[] selectionArgs, String sortOrder);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a track.
|
|
||||||
* <p>
|
|
||||||
* Note: This doesn't insert any track points.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
* @return the content provider URI of the inserted track.
|
|
||||||
*/
|
|
||||||
Uri insertTrack(Track track);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates a track.
|
|
||||||
* <p>
|
|
||||||
* Note: This doesn't update any track points.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
*/
|
|
||||||
void updateTrack(Track track);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a waypoint from a cursor.
|
|
||||||
*
|
|
||||||
* @param cursor the cursor pointing to the waypoint
|
|
||||||
*/
|
|
||||||
Waypoint createWaypoint(Cursor cursor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a waypoint. If deleting a statistics waypoint, this will also
|
|
||||||
* correct the next statistics waypoint after the deleted one to reflect the
|
|
||||||
* deletion. The generator is used to update the next statistics waypoint.
|
|
||||||
*
|
|
||||||
* @param waypointId the waypoint id
|
|
||||||
* @param descriptionGenerator the description generator. Can be null for
|
|
||||||
* waypoint marker
|
|
||||||
*/
|
|
||||||
void deleteWaypoint(Context context, long waypointId, DescriptionGenerator descriptionGenerator);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the first waypoint id for a track. The first waypoint is special as it
|
|
||||||
* contains the stats for the track. Returns -1L if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
long getFirstWaypointId(long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the last waypoint for a type. Returns null if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param waypointType the waypoint type
|
|
||||||
*/
|
|
||||||
Waypoint getLastWaypoint(long trackId, WaypointType waypointType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the next waypoint number for a type. Returns -1 if not able to get the
|
|
||||||
* next waypoint number.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param waypointType the waypoint type
|
|
||||||
*/
|
|
||||||
int getNextWaypointNumber(long trackId, WaypointType waypointType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a waypoint from a waypoint id. Returns null if not found.
|
|
||||||
*
|
|
||||||
* @param waypointId the waypoint id
|
|
||||||
*/
|
|
||||||
Waypoint getWaypoint(long waypointId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a waypoint cursor. The caller owns the returned cursor and is
|
|
||||||
* responsible for closing it.
|
|
||||||
*
|
|
||||||
* @param selection the selection. Can be null
|
|
||||||
* @param selectionArgs the selection arguments. Can be null
|
|
||||||
* @param sortOrder the sort order. Can be null
|
|
||||||
* @param maxWaypoints the maximum number of waypoints to return. -1 for no
|
|
||||||
* limit
|
|
||||||
*/
|
|
||||||
Cursor getWaypointCursor(String selection, String[] selectionArgs, String sortOrder, int maxWaypoints);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a waypoint cursor for a track. The caller owns the returned cursor and
|
|
||||||
* is responsible for closing it.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param minWaypointId the minimum waypoint id. -1L to ignore
|
|
||||||
* @param maxWaypoints the maximum number of waypoints to return. -1 for no
|
|
||||||
* limit
|
|
||||||
*/
|
|
||||||
Cursor getWaypointCursor(long trackId, long minWaypointId, int maxWaypoints);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of waypoints for a track.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
int getWaypointCount(long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a waypoint.
|
|
||||||
*
|
|
||||||
* @param waypoint the waypoint
|
|
||||||
* @return the content provider URI of the inserted waypoint.
|
|
||||||
*/
|
|
||||||
Uri insertWaypoint(Waypoint waypoint);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates a waypoint. Returns true if successful.
|
|
||||||
*
|
|
||||||
* @param waypoint the waypoint
|
|
||||||
*/
|
|
||||||
boolean updateWaypoint(Waypoint waypoint);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts multiple track points.
|
|
||||||
*
|
|
||||||
* @param locations an array of locations
|
|
||||||
* @param length the number of locations (from the beginning of the array) to
|
|
||||||
* insert, or -1 for all of them
|
|
||||||
* @param trackId the track id
|
|
||||||
* @return the number of points inserted
|
|
||||||
*/
|
|
||||||
int bulkInsertTrackPoint(Location[] locations, int length, long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a location object from a cursor.
|
|
||||||
*
|
|
||||||
* @param cursor the cursor pointing to the location
|
|
||||||
*/
|
|
||||||
Location createTrackPoint(Cursor cursor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the first location id for a track. Returns -1L if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
long getFirstTrackPointId(long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the last location id for a track. Returns -1L if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
long getLastTrackPointId(long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the track point id of a location.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param location the location
|
|
||||||
* @return track point id if the location is in the track. -1L otherwise.
|
|
||||||
*/
|
|
||||||
long getTrackPointId(long trackId, Location location);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the first valid location for a track. Returns null if it doesn't
|
|
||||||
* exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
Location getFirstValidTrackPoint(long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the last valid location for a track. Returns null if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
Location getLastValidTrackPoint(long trackId);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the last valid location.
|
|
||||||
*/
|
|
||||||
Location getLastValidTrackPoint();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a location cursor. The caller owns the returned cursor and is
|
|
||||||
* responsible for closing it.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param startTrackPointId the starting track point id. -1L to ignore
|
|
||||||
* @param maxLocations maximum number of locations to return. -1 for no limit
|
|
||||||
* @param descending true to sort the result in descending order (latest
|
|
||||||
* location first)
|
|
||||||
*/
|
|
||||||
Cursor getTrackPointCursor(
|
|
||||||
long trackId, long startTrackPointId, int maxLocations, boolean descending);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new read-only iterator over a given track's points. It provides a
|
|
||||||
* lightweight way of iterating over long tracks without failing due to the
|
|
||||||
* underlying cursor limitations. Since it's a read-only iterator,
|
|
||||||
* {@link Iterator#remove()} always throws
|
|
||||||
* {@link UnsupportedOperationException}. Each call to
|
|
||||||
* {@link LocationIterator#next()} may advance to the next DB record, and if
|
|
||||||
* so, the iterator calls {@link LocationFactory#createLocation()} and
|
|
||||||
* populates it with information retrieved from the record. When done with
|
|
||||||
* iteration, {@link LocationIterator#close()} must be called.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param startTrackPointId the starting track point id. -1L to ignore
|
|
||||||
* @param descending true to sort the result in descending order (latest
|
|
||||||
* location first)
|
|
||||||
* @param locationFactory the location factory
|
|
||||||
*/
|
|
||||||
LocationIterator getTrackPointLocationIterator(
|
|
||||||
long trackId, long startTrackPointId, boolean descending, LocationFactory locationFactory);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a track point.
|
|
||||||
*
|
|
||||||
* @param location the location
|
|
||||||
* @param trackId the track id
|
|
||||||
* @return the content provider URI of the inserted track point
|
|
||||||
*/
|
|
||||||
Uri insertTrackPoint(Location location, long trackId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A lightweight wrapper around the original {@link Cursor} with a method to
|
|
||||||
* clean up.
|
|
||||||
*/
|
|
||||||
interface LocationIterator extends Iterator<Location> {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the most recently retrieved track point id by {@link #next()}.
|
* The authority (the first part of the URI) for the app's content
|
||||||
|
* provider.
|
||||||
*/
|
*/
|
||||||
long getLocationId();
|
String AUTHORITY = "com.google.android.maps.mytracks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the iterator.
|
* The default {@link LocationFactory} which creates a location each time.
|
||||||
*/
|
*/
|
||||||
void close();
|
LocationFactory DEFAULT_LOCATION_FACTORY = new LocationFactory() {
|
||||||
}
|
@Override
|
||||||
|
public Location createLocation() {
|
||||||
/**
|
return new SensorDataSetLocation(LocationManager.GPS_PROVIDER);
|
||||||
* A factory for creating new {@link Location}.
|
}
|
||||||
*/
|
};
|
||||||
interface LocationFactory {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link Location}. An implementation can create new
|
* Clears a track. Removes waypoints and trackpoints. Only keeps the track id.
|
||||||
* instances or reuse existing instances for optimization.
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
* can be overridden for testing.
|
|
||||||
*/
|
|
||||||
class Factory {
|
|
||||||
|
|
||||||
private static Factory instance = new Factory();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of {@link ContentProviderUtils}.
|
|
||||||
*
|
*
|
||||||
* @param context the context
|
* @param trackId the track id
|
||||||
*/
|
*/
|
||||||
public static ContentProviderUtils get(Context context) {
|
void clearTrack(Context context, long trackId);
|
||||||
return instance.newForContext(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the factory instance.
|
* Creates a {@link Track} from a cursor.
|
||||||
*/
|
|
||||||
public static Factory getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the factory instance for testing. Don't forget to set it back
|
|
||||||
* to the original value after testing.
|
|
||||||
*
|
*
|
||||||
* @param factory the factory
|
* @param cursor the cursor pointing to the track
|
||||||
*/
|
*/
|
||||||
public static void overrideInstance(Factory factory) {
|
Track createTrack(Cursor cursor);
|
||||||
instance = factory;
|
|
||||||
|
/**
|
||||||
|
* Deletes all tracks (including waypoints and track points).
|
||||||
|
*/
|
||||||
|
void deleteAllTracks(Context context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a track.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
void deleteTrack(Context context, long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the tracks. If no track exists, an empty list is returned.
|
||||||
|
* <p>
|
||||||
|
* Note that the returned tracks do not have any track points attached.
|
||||||
|
*/
|
||||||
|
List<Track> getAllTracks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last track. Returns null if doesn't exist.
|
||||||
|
*/
|
||||||
|
Track getLastTrack();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a track by a track id. Returns null if not found.
|
||||||
|
* <p>
|
||||||
|
* Note that the returned track doesn't have any track points attached.
|
||||||
|
*
|
||||||
|
* @param trackId the track id.
|
||||||
|
*/
|
||||||
|
Track getTrack(long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a track cursor. The caller owns the returned cursor and is responsible
|
||||||
|
* for closing it.
|
||||||
|
*
|
||||||
|
* @param selection the selection. Can be null
|
||||||
|
* @param selectionArgs the selection arguments. Can be null
|
||||||
|
* @param sortOrder the sort order. Can be null
|
||||||
|
*/
|
||||||
|
Cursor getTrackCursor(String selection, String[] selectionArgs, String sortOrder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a track.
|
||||||
|
* <p>
|
||||||
|
* Note: This doesn't insert any track points.
|
||||||
|
*
|
||||||
|
* @param track the track
|
||||||
|
* @return the content provider URI of the inserted track.
|
||||||
|
*/
|
||||||
|
Uri insertTrack(Track track);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a track.
|
||||||
|
* <p>
|
||||||
|
* Note: This doesn't update any track points.
|
||||||
|
*
|
||||||
|
* @param track the track
|
||||||
|
*/
|
||||||
|
void updateTrack(Track track);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a waypoint from a cursor.
|
||||||
|
*
|
||||||
|
* @param cursor the cursor pointing to the waypoint
|
||||||
|
*/
|
||||||
|
Waypoint createWaypoint(Cursor cursor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a waypoint. If deleting a statistics waypoint, this will also
|
||||||
|
* correct the next statistics waypoint after the deleted one to reflect the
|
||||||
|
* deletion. The generator is used to update the next statistics waypoint.
|
||||||
|
*
|
||||||
|
* @param waypointId the waypoint id
|
||||||
|
* @param descriptionGenerator the description generator. Can be null for
|
||||||
|
* waypoint marker
|
||||||
|
*/
|
||||||
|
void deleteWaypoint(Context context, long waypointId, DescriptionGenerator descriptionGenerator);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the first waypoint id for a track. The first waypoint is special as it
|
||||||
|
* contains the stats for the track. Returns -1L if it doesn't exist.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
long getFirstWaypointId(long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last waypoint for a type. Returns null if it doesn't exist.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param waypointType the waypoint type
|
||||||
|
*/
|
||||||
|
Waypoint getLastWaypoint(long trackId, WaypointType waypointType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next waypoint number for a type. Returns -1 if not able to get the
|
||||||
|
* next waypoint number.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param waypointType the waypoint type
|
||||||
|
*/
|
||||||
|
int getNextWaypointNumber(long trackId, WaypointType waypointType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a waypoint from a waypoint id. Returns null if not found.
|
||||||
|
*
|
||||||
|
* @param waypointId the waypoint id
|
||||||
|
*/
|
||||||
|
Waypoint getWaypoint(long waypointId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a waypoint cursor. The caller owns the returned cursor and is
|
||||||
|
* responsible for closing it.
|
||||||
|
*
|
||||||
|
* @param selection the selection. Can be null
|
||||||
|
* @param selectionArgs the selection arguments. Can be null
|
||||||
|
* @param sortOrder the sort order. Can be null
|
||||||
|
* @param maxWaypoints the maximum number of waypoints to return. -1 for no
|
||||||
|
* limit
|
||||||
|
*/
|
||||||
|
Cursor getWaypointCursor(String selection, String[] selectionArgs, String sortOrder, int maxWaypoints);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a waypoint cursor for a track. The caller owns the returned cursor and
|
||||||
|
* is responsible for closing it.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param minWaypointId the minimum waypoint id. -1L to ignore
|
||||||
|
* @param maxWaypoints the maximum number of waypoints to return. -1 for no
|
||||||
|
* limit
|
||||||
|
*/
|
||||||
|
Cursor getWaypointCursor(long trackId, long minWaypointId, int maxWaypoints);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of waypoints for a track.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
int getWaypointCount(long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a waypoint.
|
||||||
|
*
|
||||||
|
* @param waypoint the waypoint
|
||||||
|
* @return the content provider URI of the inserted waypoint.
|
||||||
|
*/
|
||||||
|
Uri insertWaypoint(Waypoint waypoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a waypoint. Returns true if successful.
|
||||||
|
*
|
||||||
|
* @param waypoint the waypoint
|
||||||
|
*/
|
||||||
|
boolean updateWaypoint(Waypoint waypoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts multiple track points.
|
||||||
|
*
|
||||||
|
* @param locations an array of locations
|
||||||
|
* @param length the number of locations (from the beginning of the array) to
|
||||||
|
* insert, or -1 for all of them
|
||||||
|
* @param trackId the track id
|
||||||
|
* @return the number of points inserted
|
||||||
|
*/
|
||||||
|
int bulkInsertTrackPoint(Location[] locations, int length, long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a location object from a cursor.
|
||||||
|
*
|
||||||
|
* @param cursor the cursor pointing to the location
|
||||||
|
*/
|
||||||
|
Location createTrackPoint(Cursor cursor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the first location id for a track. Returns -1L if it doesn't exist.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
long getFirstTrackPointId(long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last location id for a track. Returns -1L if it doesn't exist.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
long getLastTrackPointId(long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the track point id of a location.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param location the location
|
||||||
|
* @return track point id if the location is in the track. -1L otherwise.
|
||||||
|
*/
|
||||||
|
long getTrackPointId(long trackId, Location location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the first valid location for a track. Returns null if it doesn't
|
||||||
|
* exist.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
Location getFirstValidTrackPoint(long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last valid location for a track. Returns null if it doesn't exist.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
Location getLastValidTrackPoint(long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last valid location.
|
||||||
|
*/
|
||||||
|
Location getLastValidTrackPoint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a location cursor. The caller owns the returned cursor and is
|
||||||
|
* responsible for closing it.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param startTrackPointId the starting track point id. -1L to ignore
|
||||||
|
* @param maxLocations maximum number of locations to return. -1 for no limit
|
||||||
|
* @param descending true to sort the result in descending order (latest
|
||||||
|
* location first)
|
||||||
|
*/
|
||||||
|
Cursor getTrackPointCursor(
|
||||||
|
long trackId, long startTrackPointId, int maxLocations, boolean descending);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new read-only iterator over a given track's points. It provides a
|
||||||
|
* lightweight way of iterating over long tracks without failing due to the
|
||||||
|
* underlying cursor limitations. Since it's a read-only iterator,
|
||||||
|
* {@link Iterator#remove()} always throws
|
||||||
|
* {@link UnsupportedOperationException}. Each call to
|
||||||
|
* {@link LocationIterator#next()} may advance to the next DB record, and if
|
||||||
|
* so, the iterator calls {@link LocationFactory#createLocation()} and
|
||||||
|
* populates it with information retrieved from the record. When done with
|
||||||
|
* iteration, {@link LocationIterator#close()} must be called.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param startTrackPointId the starting track point id. -1L to ignore
|
||||||
|
* @param descending true to sort the result in descending order (latest
|
||||||
|
* location first)
|
||||||
|
* @param locationFactory the location factory
|
||||||
|
*/
|
||||||
|
LocationIterator getTrackPointLocationIterator(
|
||||||
|
long trackId, long startTrackPointId, boolean descending, LocationFactory locationFactory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a track point.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param trackId the track id
|
||||||
|
* @return the content provider URI of the inserted track point
|
||||||
|
*/
|
||||||
|
Uri insertTrackPoint(Location location, long trackId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lightweight wrapper around the original {@link Cursor} with a method to
|
||||||
|
* clean up.
|
||||||
|
*/
|
||||||
|
interface LocationIterator extends Iterator<Location> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the most recently retrieved track point id by {@link #next()}.
|
||||||
|
*/
|
||||||
|
long getLocationId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator.
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of {@link ContentProviderUtils}. Allows subclasses
|
* A factory for creating new {@link Location}.
|
||||||
* to override for testing.
|
|
||||||
*
|
|
||||||
* @param context the context
|
|
||||||
*/
|
*/
|
||||||
protected ContentProviderUtils newForContext(Context context) {
|
interface LocationFactory {
|
||||||
return new ContentProviderUtilsImpl(context.getContentResolver());
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Location}. An implementation can create new
|
||||||
|
* instances or reuse existing instances for optimization.
|
||||||
|
*/
|
||||||
|
Location createLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory which can produce instances of {@link ContentProviderUtils}, and
|
||||||
|
* can be overridden for testing.
|
||||||
|
*/
|
||||||
|
class Factory {
|
||||||
|
|
||||||
|
private static Factory instance = new Factory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ContentProviderUtils}.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
*/
|
||||||
|
public static ContentProviderUtils get(Context context) {
|
||||||
|
return instance.newForContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the factory instance.
|
||||||
|
*/
|
||||||
|
public static Factory getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the factory instance for testing. Don't forget to set it back
|
||||||
|
* to the original value after testing.
|
||||||
|
*
|
||||||
|
* @param factory the factory
|
||||||
|
*/
|
||||||
|
public static void overrideInstance(Factory factory) {
|
||||||
|
instance = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ContentProviderUtils}. Allows subclasses
|
||||||
|
* to override for testing.
|
||||||
|
*
|
||||||
|
* @param context the context
|
||||||
|
*/
|
||||||
|
protected ContentProviderUtils newForContext(Context context) {
|
||||||
|
return new ContentProviderUtilsImpl(context.getContentResolver());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1044
-1044
File diff suppressed because it is too large
Load Diff
@@ -23,13 +23,13 @@ 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 LONG_TYPE_ID = 1;
|
||||||
public static final byte BOOLEAN_TYPE_ID = 0;
|
public static final byte INT_TYPE_ID = 2;
|
||||||
public static final byte LONG_TYPE_ID = 1;
|
public static final byte FLOAT_TYPE_ID = 3;
|
||||||
public static final byte INT_TYPE_ID = 2;
|
public static final byte DOUBLE_TYPE_ID = 4;
|
||||||
public static final byte FLOAT_TYPE_ID = 3;
|
public static final byte STRING_TYPE_ID = 5;
|
||||||
public static final byte DOUBLE_TYPE_ID = 4;
|
public static final byte BLOB_TYPE_ID = 6;
|
||||||
public static final byte STRING_TYPE_ID = 5;
|
private ContentTypeIds() {
|
||||||
public static final byte BLOB_TYPE_ID = 6;
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+339
-342
@@ -49,370 +49,367 @@ import java.io.FileNotFoundException;
|
|||||||
*/
|
*/
|
||||||
public class CustomContentProvider extends ContentProvider {
|
public class CustomContentProvider extends ContentProvider {
|
||||||
|
|
||||||
private static final String TAG = CustomContentProvider.class.getSimpleName();
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int DATABASE_VERSION = 23;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String DATABASE_NAME = "database.db";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Database helper for creating and upgrading the database.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
static class DatabaseHelper extends SQLiteOpenHelper {
|
|
||||||
|
|
||||||
public DatabaseHelper(Context context) {
|
|
||||||
this(context, DATABASE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public DatabaseHelper(Context context, String databaseName) {
|
static final int DATABASE_VERSION = 23;
|
||||||
super(context, databaseName, null, DATABASE_VERSION);
|
@VisibleForTesting
|
||||||
|
static final String DATABASE_NAME = "database.db";
|
||||||
|
private static final String TAG = CustomContentProvider.class.getSimpleName();
|
||||||
|
private final UriMatcher uriMatcher;
|
||||||
|
private SQLiteDatabase db;
|
||||||
|
|
||||||
|
public CustomContentProvider() {
|
||||||
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TrackPointsColumns.TABLE_NAME, UrlType.TRACKPOINTS.ordinal());
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TrackPointsColumns.TABLE_NAME + "/#", UrlType.TRACKPOINTS_ID.ordinal());
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TracksColumns.TABLE_NAME, UrlType.TRACKS.ordinal());
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TracksColumns.TABLE_NAME + "/#", UrlType.TRACKS_ID.ordinal());
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, WaypointsColumns.TABLE_NAME, UrlType.WAYPOINTS.ordinal());
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, WaypointsColumns.TABLE_NAME + "/#", UrlType.WAYPOINTS_ID.ordinal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase db) {
|
public boolean onCreate() {
|
||||||
db.execSQL(TrackPointsColumns.CREATE_TABLE);
|
return onCreate(getContext());
|
||||||
db.execSQL(TracksColumns.CREATE_TABLE);
|
}
|
||||||
db.execSQL(WaypointsColumns.CREATE_TABLE);
|
|
||||||
|
/**
|
||||||
|
* Helper method to make onCreate is testable.
|
||||||
|
*
|
||||||
|
* @param context context to creates database
|
||||||
|
* @return true means run successfully
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean onCreate(Context context) {
|
||||||
|
DatabaseHelper databaseHelper = new DatabaseHelper(context);
|
||||||
|
try {
|
||||||
|
db = databaseHelper.getWritableDatabase();
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(TAG, "Unable to open database for writing.", e);
|
||||||
|
}
|
||||||
|
return db != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public int delete(@NonNull Uri url, String where, String[] selectionArgs) {
|
||||||
}
|
String table;
|
||||||
}
|
boolean shouldVacuum = false;
|
||||||
|
switch (getUrlType(url)) {
|
||||||
/**
|
case TRACKPOINTS:
|
||||||
* Types of url.
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
*
|
break;
|
||||||
* @author Jimmy Shih
|
case TRACKS:
|
||||||
*/
|
table = TracksColumns.TABLE_NAME;
|
||||||
@VisibleForTesting
|
shouldVacuum = true;
|
||||||
enum UrlType {
|
break;
|
||||||
TRACKPOINTS, TRACKPOINTS_ID, TRACKS, TRACKS_ID, WAYPOINTS, WAYPOINTS_ID
|
case WAYPOINTS:
|
||||||
}
|
table = WaypointsColumns.TABLE_NAME;
|
||||||
|
break;
|
||||||
private final UriMatcher uriMatcher;
|
default:
|
||||||
private SQLiteDatabase db;
|
throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
|
|
||||||
public CustomContentProvider() {
|
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TrackPointsColumns.TABLE_NAME, UrlType.TRACKPOINTS.ordinal());
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TrackPointsColumns.TABLE_NAME + "/#", UrlType.TRACKPOINTS_ID.ordinal());
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TracksColumns.TABLE_NAME, UrlType.TRACKS.ordinal());
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TracksColumns.TABLE_NAME + "/#", UrlType.TRACKS_ID.ordinal());
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, WaypointsColumns.TABLE_NAME, UrlType.WAYPOINTS.ordinal());
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, WaypointsColumns.TABLE_NAME + "/#", UrlType.WAYPOINTS_ID.ordinal());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreate() {
|
|
||||||
return onCreate(getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to make onCreate is testable.
|
|
||||||
*
|
|
||||||
* @param context context to creates database
|
|
||||||
* @return true means run successfully
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean onCreate(Context context) {
|
|
||||||
DatabaseHelper databaseHelper = new DatabaseHelper(context);
|
|
||||||
try {
|
|
||||||
db = databaseHelper.getWritableDatabase();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(TAG, "Unable to open database for writing.", e);
|
|
||||||
}
|
|
||||||
return db != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int delete(@NonNull Uri url, String where, String[] selectionArgs) {
|
|
||||||
String table;
|
|
||||||
boolean shouldVacuum = false;
|
|
||||||
switch (getUrlType(url)) {
|
|
||||||
case TRACKPOINTS:
|
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
|
||||||
break;
|
|
||||||
case TRACKS:
|
|
||||||
table = TracksColumns.TABLE_NAME;
|
|
||||||
shouldVacuum = true;
|
|
||||||
break;
|
|
||||||
case WAYPOINTS:
|
|
||||||
table = WaypointsColumns.TABLE_NAME;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.w(TAG, "Deleting table " + table);
|
|
||||||
int count;
|
|
||||||
try {
|
|
||||||
db.beginTransaction();
|
|
||||||
count = db.delete(table, where, selectionArgs);
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
getContext().getContentResolver().notifyChange(url, null, false);
|
|
||||||
|
|
||||||
if (shouldVacuum) {
|
|
||||||
// If a potentially large amount of data was deleted, reclaim its space.
|
|
||||||
Log.i(TAG, "Vacuuming the database.");
|
|
||||||
db.execSQL("VACUUM");
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType(@NonNull Uri url) {
|
|
||||||
switch (getUrlType(url)) {
|
|
||||||
case TRACKPOINTS:
|
|
||||||
return TrackPointsColumns.CONTENT_TYPE;
|
|
||||||
case TRACKPOINTS_ID:
|
|
||||||
return TrackPointsColumns.CONTENT_ITEMTYPE;
|
|
||||||
case TRACKS:
|
|
||||||
return TracksColumns.CONTENT_TYPE;
|
|
||||||
case TRACKS_ID:
|
|
||||||
return TracksColumns.CONTENT_ITEMTYPE;
|
|
||||||
case WAYPOINTS:
|
|
||||||
return WaypointsColumns.CONTENT_TYPE;
|
|
||||||
case WAYPOINTS_ID:
|
|
||||||
return WaypointsColumns.CONTENT_ITEMTYPE;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri insert(@NonNull Uri url, ContentValues initialValues) {
|
|
||||||
if (initialValues == null) {
|
|
||||||
initialValues = new ContentValues();
|
|
||||||
}
|
|
||||||
Uri result;
|
|
||||||
try {
|
|
||||||
db.beginTransaction();
|
|
||||||
result = insertContentValues(url, getUrlType(url), initialValues);
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
getContext().getContentResolver().notifyChange(url, null, false);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] valuesBulk) {
|
|
||||||
int numInserted;
|
|
||||||
try {
|
|
||||||
// Use a transaction in order to make the insertions run as a single batch
|
|
||||||
db.beginTransaction();
|
|
||||||
|
|
||||||
UrlType urlType = getUrlType(url);
|
|
||||||
for (numInserted = 0; numInserted < valuesBulk.length; numInserted++) {
|
|
||||||
ContentValues contentValues = valuesBulk[numInserted];
|
|
||||||
if (contentValues == null) {
|
|
||||||
contentValues = new ContentValues();
|
|
||||||
}
|
}
|
||||||
insertContentValues(url, urlType, contentValues);
|
|
||||||
}
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
getContext().getContentResolver().notifyChange(url, null, false);
|
|
||||||
return numInserted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Log.w(TAG, "Deleting table " + table);
|
||||||
public Cursor query(@NonNull Uri url, String[] projection, String selection, String[] selectionArgs, String sort) {
|
int count;
|
||||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
try {
|
||||||
String sortOrder = null;
|
db.beginTransaction();
|
||||||
switch (getUrlType(url)) {
|
count = db.delete(table, where, selectionArgs);
|
||||||
case TRACKPOINTS:
|
db.setTransactionSuccessful();
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
} finally {
|
||||||
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
db.endTransaction();
|
||||||
break;
|
|
||||||
case TRACKPOINTS_ID:
|
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
|
||||||
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
|
||||||
break;
|
|
||||||
case TRACKS:
|
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
|
||||||
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
|
||||||
break;
|
|
||||||
case TRACKS_ID:
|
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
|
||||||
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
|
||||||
break;
|
|
||||||
case WAYPOINTS:
|
|
||||||
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
|
||||||
sortOrder = sort != null ? sort : WaypointsColumns.DEFAULT_SORT_ORDER;
|
|
||||||
break;
|
|
||||||
case WAYPOINTS_ID:
|
|
||||||
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
|
||||||
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
|
||||||
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
|
||||||
cursor.setNotificationUri(getContext().getContentResolver(), url);
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int update(@NonNull Uri url, ContentValues values, String where, String[] selectionArgs) {
|
|
||||||
String table;
|
|
||||||
String whereClause;
|
|
||||||
switch (getUrlType(url)) {
|
|
||||||
case TRACKPOINTS:
|
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
|
||||||
whereClause = where;
|
|
||||||
break;
|
|
||||||
case TRACKPOINTS_ID:
|
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
|
||||||
whereClause = TrackPointsColumns._ID + "=" + url.getPathSegments().get(1);
|
|
||||||
if (!TextUtils.isEmpty(where)) {
|
|
||||||
whereClause += " AND (" + where + ")";
|
|
||||||
}
|
}
|
||||||
break;
|
getContext().getContentResolver().notifyChange(url, null, false);
|
||||||
case TRACKS:
|
|
||||||
table = TracksColumns.TABLE_NAME;
|
if (shouldVacuum) {
|
||||||
whereClause = where;
|
// If a potentially large amount of data was deleted, reclaim its space.
|
||||||
break;
|
Log.i(TAG, "Vacuuming the database.");
|
||||||
case TRACKS_ID:
|
db.execSQL("VACUUM");
|
||||||
table = TracksColumns.TABLE_NAME;
|
|
||||||
whereClause = TracksColumns._ID + "=" + url.getPathSegments().get(1);
|
|
||||||
if (!TextUtils.isEmpty(where)) {
|
|
||||||
whereClause += " AND (" + where + ")";
|
|
||||||
}
|
}
|
||||||
break;
|
return count;
|
||||||
case WAYPOINTS:
|
}
|
||||||
table = WaypointsColumns.TABLE_NAME;
|
|
||||||
whereClause = where;
|
@Override
|
||||||
break;
|
public String getType(@NonNull Uri url) {
|
||||||
case WAYPOINTS_ID:
|
switch (getUrlType(url)) {
|
||||||
table = WaypointsColumns.TABLE_NAME;
|
case TRACKPOINTS:
|
||||||
whereClause = WaypointsColumns._ID + "=" + url.getPathSegments().get(1);
|
return TrackPointsColumns.CONTENT_TYPE;
|
||||||
if (!TextUtils.isEmpty(where)) {
|
case TRACKPOINTS_ID:
|
||||||
whereClause += " AND (" + where + ")";
|
return TrackPointsColumns.CONTENT_ITEMTYPE;
|
||||||
|
case TRACKS:
|
||||||
|
return TracksColumns.CONTENT_TYPE;
|
||||||
|
case TRACKS_ID:
|
||||||
|
return TracksColumns.CONTENT_ITEMTYPE;
|
||||||
|
case WAYPOINTS:
|
||||||
|
return WaypointsColumns.CONTENT_TYPE;
|
||||||
|
case WAYPOINTS_ID:
|
||||||
|
return WaypointsColumns.CONTENT_ITEMTYPE;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
}
|
||||||
int count;
|
|
||||||
try {
|
|
||||||
db.beginTransaction();
|
|
||||||
count = db.update(table, values, whereClause, selectionArgs);
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
getContext().getContentResolver().notifyChange(url, null, false);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
public Uri insert(@NonNull Uri url, ContentValues initialValues) {
|
||||||
try {
|
if (initialValues == null) {
|
||||||
File file = new File(uri.getPath());
|
initialValues = new ContentValues();
|
||||||
if (file.exists()) {
|
}
|
||||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
Uri result;
|
||||||
}
|
try {
|
||||||
throw new FileNotFoundException(uri.getPath());
|
db.beginTransaction();
|
||||||
} finally {
|
result = insertContentValues(url, getUrlType(url), initialValues);
|
||||||
File dir = new File(getContext().getCacheDir(), FileUtils.PLAY_TRACKS_DIR);
|
db.setTransactionSuccessful();
|
||||||
for (File file : dir.listFiles()) {
|
} finally {
|
||||||
file.delete();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
getContext().revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
getContext().getContentResolver().notifyChange(url, null, false);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Gets the {@link UrlType} for a url.
|
public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] valuesBulk) {
|
||||||
*
|
int numInserted;
|
||||||
* @param url the url
|
try {
|
||||||
*/
|
// Use a transaction in order to make the insertions run as a single batch
|
||||||
private UrlType getUrlType(Uri url) {
|
db.beginTransaction();
|
||||||
return UrlType.values()[uriMatcher.match(url)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
UrlType urlType = getUrlType(url);
|
||||||
* Inserts a content based on the url type.
|
for (numInserted = 0; numInserted < valuesBulk.length; numInserted++) {
|
||||||
*
|
ContentValues contentValues = valuesBulk[numInserted];
|
||||||
* @param url the content url
|
if (contentValues == null) {
|
||||||
* @param urlType the url type
|
contentValues = new ContentValues();
|
||||||
* @param contentValues the content values
|
}
|
||||||
*/
|
insertContentValues(url, urlType, contentValues);
|
||||||
private Uri insertContentValues(Uri url, UrlType urlType, ContentValues contentValues) {
|
}
|
||||||
switch (urlType) {
|
db.setTransactionSuccessful();
|
||||||
case TRACKPOINTS:
|
} finally {
|
||||||
return insertTrackPoint(url, contentValues);
|
db.endTransaction();
|
||||||
case TRACKS:
|
}
|
||||||
return insertTrack(url, contentValues);
|
getContext().getContentResolver().notifyChange(url, null, false);
|
||||||
case WAYPOINTS:
|
return numInserted;
|
||||||
return insertWaypoint(url, contentValues);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Inserts a track point.
|
public Cursor query(@NonNull Uri url, String[] projection, String selection, String[] selectionArgs, String sort) {
|
||||||
*
|
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||||
* @param url the content url
|
String sortOrder = null;
|
||||||
* @param values the content values
|
switch (getUrlType(url)) {
|
||||||
*/
|
case TRACKPOINTS:
|
||||||
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
boolean hasLatitude = values.containsKey(TrackPointsColumns.LATITUDE);
|
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||||
boolean hasLongitude = values.containsKey(TrackPointsColumns.LONGITUDE);
|
break;
|
||||||
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
|
case TRACKPOINTS_ID:
|
||||||
if (!hasLatitude || !hasLongitude || !hasTime) {
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
throw new IllegalArgumentException("Latitude, longitude, and time values are required.");
|
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
||||||
|
break;
|
||||||
|
case TRACKS:
|
||||||
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
|
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
||||||
|
break;
|
||||||
|
case TRACKS_ID:
|
||||||
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
|
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
||||||
|
break;
|
||||||
|
case WAYPOINTS:
|
||||||
|
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
||||||
|
sortOrder = sort != null ? sort : WaypointsColumns.DEFAULT_SORT_ORDER;
|
||||||
|
break;
|
||||||
|
case WAYPOINTS_ID:
|
||||||
|
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
||||||
|
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown url " + url);
|
||||||
|
}
|
||||||
|
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||||
|
cursor.setNotificationUri(getContext().getContentResolver(), url);
|
||||||
|
return cursor;
|
||||||
}
|
}
|
||||||
long rowId = db.insert(TrackPointsColumns.TABLE_NAME, TrackPointsColumns._ID, values);
|
|
||||||
if (rowId >= 0) {
|
|
||||||
return ContentUris.appendId(TrackPointsColumns.CONTENT_URI.buildUpon(), rowId).build();
|
|
||||||
}
|
|
||||||
throw new SQLiteException("Failed to insert a track point " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Inserts a track.
|
public int update(@NonNull Uri url, ContentValues values, String where, String[] selectionArgs) {
|
||||||
*
|
String table;
|
||||||
* @param url the content url
|
String whereClause;
|
||||||
* @param contentValues the content values
|
switch (getUrlType(url)) {
|
||||||
*/
|
case TRACKPOINTS:
|
||||||
private Uri insertTrack(Uri url, ContentValues contentValues) {
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
boolean hasStartTime = contentValues.containsKey(TracksColumns.STARTTIME);
|
whereClause = where;
|
||||||
boolean hasStartId = contentValues.containsKey(TracksColumns.STARTID);
|
break;
|
||||||
if (!hasStartTime || !hasStartId) {
|
case TRACKPOINTS_ID:
|
||||||
throw new IllegalArgumentException("Both start time and start id values are required.");
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
|
whereClause = TrackPointsColumns._ID + "=" + url.getPathSegments().get(1);
|
||||||
|
if (!TextUtils.isEmpty(where)) {
|
||||||
|
whereClause += " AND (" + where + ")";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TRACKS:
|
||||||
|
table = TracksColumns.TABLE_NAME;
|
||||||
|
whereClause = where;
|
||||||
|
break;
|
||||||
|
case TRACKS_ID:
|
||||||
|
table = TracksColumns.TABLE_NAME;
|
||||||
|
whereClause = TracksColumns._ID + "=" + url.getPathSegments().get(1);
|
||||||
|
if (!TextUtils.isEmpty(where)) {
|
||||||
|
whereClause += " AND (" + where + ")";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WAYPOINTS:
|
||||||
|
table = WaypointsColumns.TABLE_NAME;
|
||||||
|
whereClause = where;
|
||||||
|
break;
|
||||||
|
case WAYPOINTS_ID:
|
||||||
|
table = WaypointsColumns.TABLE_NAME;
|
||||||
|
whereClause = WaypointsColumns._ID + "=" + url.getPathSegments().get(1);
|
||||||
|
if (!TextUtils.isEmpty(where)) {
|
||||||
|
whereClause += " AND (" + where + ")";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown url " + url);
|
||||||
|
}
|
||||||
|
int count;
|
||||||
|
try {
|
||||||
|
db.beginTransaction();
|
||||||
|
count = db.update(table, values, whereClause, selectionArgs);
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
getContext().getContentResolver().notifyChange(url, null, false);
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
long rowId = db.insert(TracksColumns.TABLE_NAME, TracksColumns._ID, contentValues);
|
|
||||||
if (rowId >= 0) {
|
|
||||||
return ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), rowId).build();
|
|
||||||
}
|
|
||||||
throw new SQLException("Failed to insert a track " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Inserts a waypoint.
|
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||||
*
|
try {
|
||||||
* @param url the content url
|
File file = new File(uri.getPath());
|
||||||
* @param contentValues the content values
|
if (file.exists()) {
|
||||||
*/
|
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
private Uri insertWaypoint(Uri url, ContentValues contentValues) {
|
}
|
||||||
long rowId = db.insert(WaypointsColumns.TABLE_NAME, WaypointsColumns._ID, contentValues);
|
throw new FileNotFoundException(uri.getPath());
|
||||||
if (rowId >= 0) {
|
} finally {
|
||||||
return ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), rowId).build();
|
File dir = new File(getContext().getCacheDir(), FileUtils.PLAY_TRACKS_DIR);
|
||||||
|
for (File file : dir.listFiles()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
getContext().revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link UrlType} for a url.
|
||||||
|
*
|
||||||
|
* @param url the url
|
||||||
|
*/
|
||||||
|
private UrlType getUrlType(Uri url) {
|
||||||
|
return UrlType.values()[uriMatcher.match(url)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a content based on the url type.
|
||||||
|
*
|
||||||
|
* @param url the content url
|
||||||
|
* @param urlType the url type
|
||||||
|
* @param contentValues the content values
|
||||||
|
*/
|
||||||
|
private Uri insertContentValues(Uri url, UrlType urlType, ContentValues contentValues) {
|
||||||
|
switch (urlType) {
|
||||||
|
case TRACKPOINTS:
|
||||||
|
return insertTrackPoint(url, contentValues);
|
||||||
|
case TRACKS:
|
||||||
|
return insertTrack(url, contentValues);
|
||||||
|
case WAYPOINTS:
|
||||||
|
return insertWaypoint(url, contentValues);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown url " + url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a track point.
|
||||||
|
*
|
||||||
|
* @param url the content url
|
||||||
|
* @param values the content values
|
||||||
|
*/
|
||||||
|
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
||||||
|
boolean hasLatitude = values.containsKey(TrackPointsColumns.LATITUDE);
|
||||||
|
boolean hasLongitude = values.containsKey(TrackPointsColumns.LONGITUDE);
|
||||||
|
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
|
||||||
|
if (!hasLatitude || !hasLongitude || !hasTime) {
|
||||||
|
throw new IllegalArgumentException("Latitude, longitude, and time values are required.");
|
||||||
|
}
|
||||||
|
long rowId = db.insert(TrackPointsColumns.TABLE_NAME, TrackPointsColumns._ID, values);
|
||||||
|
if (rowId >= 0) {
|
||||||
|
return ContentUris.appendId(TrackPointsColumns.CONTENT_URI.buildUpon(), rowId).build();
|
||||||
|
}
|
||||||
|
throw new SQLiteException("Failed to insert a track point " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a track.
|
||||||
|
*
|
||||||
|
* @param url the content url
|
||||||
|
* @param contentValues the content values
|
||||||
|
*/
|
||||||
|
private Uri insertTrack(Uri url, ContentValues contentValues) {
|
||||||
|
boolean hasStartTime = contentValues.containsKey(TracksColumns.STARTTIME);
|
||||||
|
boolean hasStartId = contentValues.containsKey(TracksColumns.STARTID);
|
||||||
|
if (!hasStartTime || !hasStartId) {
|
||||||
|
throw new IllegalArgumentException("Both start time and start id values are required.");
|
||||||
|
}
|
||||||
|
long rowId = db.insert(TracksColumns.TABLE_NAME, TracksColumns._ID, contentValues);
|
||||||
|
if (rowId >= 0) {
|
||||||
|
return ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), rowId).build();
|
||||||
|
}
|
||||||
|
throw new SQLException("Failed to insert a track " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a waypoint.
|
||||||
|
*
|
||||||
|
* @param url the content url
|
||||||
|
* @param contentValues the content values
|
||||||
|
*/
|
||||||
|
private Uri insertWaypoint(Uri url, ContentValues contentValues) {
|
||||||
|
long rowId = db.insert(WaypointsColumns.TABLE_NAME, WaypointsColumns._ID, contentValues);
|
||||||
|
if (rowId >= 0) {
|
||||||
|
return ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), rowId).build();
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw new SQLException("Failed to insert a waypoint " + url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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.
|
||||||
*
|
*
|
||||||
@@ -32,49 +32,49 @@ import android.net.Uri;
|
|||||||
*/
|
*/
|
||||||
public class DataSource {
|
public class DataSource {
|
||||||
|
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
private final SharedPreferences sharedPreferences;
|
private final SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
public DataSource(Context context) {
|
public DataSource(Context context) {
|
||||||
contentResolver = context.getContentResolver();
|
contentResolver = context.getContentResolver();
|
||||||
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a content observer.
|
* Registers a content observer.
|
||||||
*
|
*
|
||||||
* @param uri the uri
|
* @param uri the uri
|
||||||
* @param observer the observer
|
* @param observer the observer
|
||||||
*/
|
*/
|
||||||
public void registerContentObserver(Uri uri, ContentObserver observer) {
|
public void registerContentObserver(Uri uri, ContentObserver observer) {
|
||||||
contentResolver.registerContentObserver(uri, false, observer);
|
contentResolver.registerContentObserver(uri, false, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters a content observer.
|
* Unregisters a content observer.
|
||||||
*
|
*
|
||||||
* @param observer the observer
|
* @param observer the observer
|
||||||
*/
|
*/
|
||||||
public void unregisterContentObserver(ContentObserver observer) {
|
public void unregisterContentObserver(ContentObserver observer) {
|
||||||
contentResolver.unregisterContentObserver(observer);
|
contentResolver.unregisterContentObserver(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a shared preference change listener.
|
* Registers a shared preference change listener.
|
||||||
*
|
*
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
*/
|
*/
|
||||||
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
|
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters a shared preference change listener.
|
* Unregisters a shared preference change listener.
|
||||||
*
|
*
|
||||||
* @param listener the listener
|
* @param listener the listener
|
||||||
*/
|
*/
|
||||||
public void unregisterOnSharedPreferenceChangeListener(
|
public void unregisterOnSharedPreferenceChangeListener(
|
||||||
OnSharedPreferenceChangeListener listener) {
|
OnSharedPreferenceChangeListener listener) {
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,25 +24,25 @@ package com.google.android.apps.mytracks.content;
|
|||||||
*/
|
*/
|
||||||
public interface DataSourceListener {
|
public interface DataSourceListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies when the tracks table is updated.
|
* Notifies when the tracks table is updated.
|
||||||
*/
|
*/
|
||||||
void notifyTracksTableUpdated();
|
void notifyTracksTableUpdated();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies when the waypoints table is updated.
|
* Notifies when the waypoints table is updated.
|
||||||
*/
|
*/
|
||||||
void notifyWaypointsTableUpdated();
|
void notifyWaypointsTableUpdated();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies when the track points table is updated.
|
* Notifies when the track points table is updated.
|
||||||
*/
|
*/
|
||||||
void notifyTrackPointsTableUpdated();
|
void notifyTrackPointsTableUpdated();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies when a preference changes.
|
* Notifies when a preference changes.
|
||||||
*
|
*
|
||||||
* @param key the preference key
|
* @param key the preference key
|
||||||
*/
|
*/
|
||||||
void notifyPreferenceChanged(String key);
|
void notifyPreferenceChanged(String key);
|
||||||
}
|
}
|
||||||
@@ -34,198 +34,194 @@ 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();
|
||||||
|
private final DataSource dataSource;
|
||||||
|
private final DataSourceListener dataSourceListener;
|
||||||
|
// Registered listeners
|
||||||
|
private final Set<TrackDataType> registeredListeners = EnumSet.noneOf(TrackDataType.class);
|
||||||
|
private final Handler handler;
|
||||||
|
private final TracksTableObserver tracksTableObserver;
|
||||||
|
private final WaypointsTableObserver waypointsTableObserver;
|
||||||
|
private final TrackPointsTableObserver trackPointsTableObserver;
|
||||||
|
private final PreferenceListener preferenceListener;
|
||||||
|
public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
this.dataSourceListener = dataSourceListener;
|
||||||
|
|
||||||
/**
|
handler = new Handler();
|
||||||
* Observer when the tracks table is updated.
|
tracksTableObserver = new TracksTableObserver();
|
||||||
*
|
waypointsTableObserver = new WaypointsTableObserver();
|
||||||
* @author Jimmy Shih
|
trackPointsTableObserver = new TrackPointsTableObserver();
|
||||||
*/
|
preferenceListener = new PreferenceListener();
|
||||||
private class TracksTableObserver extends ContentObserver {
|
|
||||||
|
|
||||||
public TracksTableObserver() {
|
|
||||||
super(handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void onChange(boolean selfChange) {
|
* Updates listeners with data source.
|
||||||
dataSourceListener.notifyTracksTableUpdated();
|
*
|
||||||
}
|
* @param listeners the listeners
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 DataSourceListener dataSourceListener;
|
|
||||||
|
|
||||||
// Registered listeners
|
|
||||||
private final Set<TrackDataType> registeredListeners = EnumSet.noneOf(TrackDataType.class);
|
|
||||||
|
|
||||||
private final Handler handler;
|
|
||||||
private final TracksTableObserver tracksTableObserver;
|
|
||||||
private final WaypointsTableObserver waypointsTableObserver;
|
|
||||||
private final TrackPointsTableObserver trackPointsTableObserver;
|
|
||||||
private final PreferenceListener preferenceListener;
|
|
||||||
|
|
||||||
public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
|
|
||||||
this.dataSource = dataSource;
|
|
||||||
this.dataSourceListener = dataSourceListener;
|
|
||||||
|
|
||||||
handler = new Handler();
|
|
||||||
tracksTableObserver = new TracksTableObserver();
|
|
||||||
waypointsTableObserver = new WaypointsTableObserver();
|
|
||||||
trackPointsTableObserver = new TrackPointsTableObserver();
|
|
||||||
preferenceListener = new PreferenceListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates listeners with data source.
|
|
||||||
*
|
|
||||||
* @param listeners the listeners
|
|
||||||
*/
|
|
||||||
public void updateListeners(EnumSet<TrackDataType> listeners) {
|
|
||||||
EnumSet<TrackDataType> neededListeners = EnumSet.copyOf(listeners);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to
|
|
||||||
* the same internal listener
|
|
||||||
*/
|
*/
|
||||||
if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE)) {
|
public void updateListeners(EnumSet<TrackDataType> listeners) {
|
||||||
neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE);
|
EnumSet<TrackDataType> neededListeners = EnumSet.copyOf(listeners);
|
||||||
neededListeners.add(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE);
|
|
||||||
|
/*
|
||||||
|
* Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to
|
||||||
|
* the same internal listener
|
||||||
|
*/
|
||||||
|
if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE)) {
|
||||||
|
neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE);
|
||||||
|
neededListeners.add(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Updating listeners " + neededListeners);
|
||||||
|
|
||||||
|
// Unnecessary = registered - needed
|
||||||
|
Set<TrackDataType> unnecessaryListeners = EnumSet.copyOf(registeredListeners);
|
||||||
|
unnecessaryListeners.removeAll(neededListeners);
|
||||||
|
|
||||||
|
// Missing = needed - registered
|
||||||
|
Set<TrackDataType> missingListeners = EnumSet.copyOf(neededListeners);
|
||||||
|
missingListeners.removeAll(registeredListeners);
|
||||||
|
|
||||||
|
// Remove unnecessary listeners
|
||||||
|
for (TrackDataType trackDataType : unnecessaryListeners) {
|
||||||
|
unregisterListener(trackDataType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add missing listeners
|
||||||
|
for (TrackDataType trackDataType : missingListeners) {
|
||||||
|
registerListener(trackDataType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update registered listeners
|
||||||
|
registeredListeners.clear();
|
||||||
|
registeredListeners.addAll(neededListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Updating listeners " + neededListeners);
|
/**
|
||||||
|
* Registers a listener with data source.
|
||||||
// Unnecessary = registered - needed
|
*
|
||||||
Set<TrackDataType> unnecessaryListeners = EnumSet.copyOf(registeredListeners);
|
* @param trackDataType the listener data type
|
||||||
unnecessaryListeners.removeAll(neededListeners);
|
*/
|
||||||
|
private void registerListener(TrackDataType trackDataType) {
|
||||||
// Missing = needed - registered
|
switch (trackDataType) {
|
||||||
Set<TrackDataType> missingListeners = EnumSet.copyOf(neededListeners);
|
case TRACKS_TABLE:
|
||||||
missingListeners.removeAll(registeredListeners);
|
dataSource.registerContentObserver(TracksColumns.CONTENT_URI, tracksTableObserver);
|
||||||
|
break;
|
||||||
// Remove unnecessary listeners
|
case WAYPOINTS_TABLE:
|
||||||
for (TrackDataType trackDataType : unnecessaryListeners) {
|
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
|
||||||
unregisterListener(trackDataType);
|
break;
|
||||||
|
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
||||||
|
dataSource.registerContentObserver(
|
||||||
|
TrackPointsColumns.CONTENT_URI, trackPointsTableObserver);
|
||||||
|
break;
|
||||||
|
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
||||||
|
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
||||||
|
break;
|
||||||
|
case PREFERENCE:
|
||||||
|
dataSource.registerOnSharedPreferenceChangeListener(preferenceListener);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add missing listeners
|
/**
|
||||||
for (TrackDataType trackDataType : missingListeners) {
|
* Unregisters a listener with data source.
|
||||||
registerListener(trackDataType);
|
*
|
||||||
|
* @param trackDataType listener data type
|
||||||
|
*/
|
||||||
|
private void unregisterListener(TrackDataType trackDataType) {
|
||||||
|
switch (trackDataType) {
|
||||||
|
case TRACKS_TABLE:
|
||||||
|
dataSource.unregisterContentObserver(tracksTableObserver);
|
||||||
|
break;
|
||||||
|
case WAYPOINTS_TABLE:
|
||||||
|
dataSource.unregisterContentObserver(waypointsTableObserver);
|
||||||
|
break;
|
||||||
|
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
||||||
|
dataSource.unregisterContentObserver(trackPointsTableObserver);
|
||||||
|
break;
|
||||||
|
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
||||||
|
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
||||||
|
break;
|
||||||
|
case PREFERENCE:
|
||||||
|
dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListener);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update registered listeners
|
/**
|
||||||
registeredListeners.clear();
|
* Unregisters all listeners with data source.
|
||||||
registeredListeners.addAll(neededListeners);
|
*/
|
||||||
}
|
public void unregisterAllListeners() {
|
||||||
|
for (TrackDataType trackDataType : TrackDataType.values()) {
|
||||||
/**
|
unregisterListener(trackDataType);
|
||||||
* Registers a listener with data source.
|
}
|
||||||
*
|
|
||||||
* @param trackDataType the listener data type
|
|
||||||
*/
|
|
||||||
private void registerListener(TrackDataType trackDataType) {
|
|
||||||
switch (trackDataType) {
|
|
||||||
case TRACKS_TABLE:
|
|
||||||
dataSource.registerContentObserver(TracksColumns.CONTENT_URI, tracksTableObserver);
|
|
||||||
break;
|
|
||||||
case WAYPOINTS_TABLE:
|
|
||||||
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
|
|
||||||
break;
|
|
||||||
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
|
||||||
dataSource.registerContentObserver(
|
|
||||||
TrackPointsColumns.CONTENT_URI, trackPointsTableObserver);
|
|
||||||
break;
|
|
||||||
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
|
||||||
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
|
||||||
break;
|
|
||||||
case PREFERENCE:
|
|
||||||
dataSource.registerOnSharedPreferenceChangeListener(preferenceListener);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters a listener with data source.
|
* Observer when the tracks table is updated.
|
||||||
*
|
*
|
||||||
* @param trackDataType listener data type
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
private void unregisterListener(TrackDataType trackDataType) {
|
private class TracksTableObserver extends ContentObserver {
|
||||||
switch (trackDataType) {
|
|
||||||
case TRACKS_TABLE:
|
|
||||||
dataSource.unregisterContentObserver(tracksTableObserver);
|
|
||||||
break;
|
|
||||||
case WAYPOINTS_TABLE:
|
|
||||||
dataSource.unregisterContentObserver(waypointsTableObserver);
|
|
||||||
break;
|
|
||||||
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
|
||||||
dataSource.unregisterContentObserver(trackPointsTableObserver);
|
|
||||||
break;
|
|
||||||
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
|
||||||
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
|
||||||
break;
|
|
||||||
case PREFERENCE:
|
|
||||||
dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListener);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public TracksTableObserver() {
|
||||||
* Unregisters all listeners with data source.
|
super(handler);
|
||||||
*/
|
}
|
||||||
public void unregisterAllListeners() {
|
|
||||||
for (TrackDataType trackDataType : TrackDataType.values()) {
|
@Override
|
||||||
unregisterListener(trackDataType);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,21 +27,21 @@ import java.util.Vector;
|
|||||||
*/
|
*/
|
||||||
public interface DescriptionGenerator {
|
public interface DescriptionGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a track description.
|
* Generates a track description.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @param distances a vector of distances to generate the elevation chart
|
* @param distances a vector of distances to generate the elevation chart
|
||||||
* @param elevations a vector of elevations to generate the elevation chart
|
* @param elevations a vector of elevations to generate the elevation chart
|
||||||
* @param html true to output html, false to output plain text
|
* @param html true to output html, false to output plain text
|
||||||
*/
|
*/
|
||||||
String generateTrackDescription(
|
String generateTrackDescription(
|
||||||
Track track, Vector<Double> distances, Vector<Double> elevations, boolean html);
|
Track track, Vector<Double> distances, Vector<Double> elevations, boolean html);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a waypoint description from a trip statistics.
|
* Generate a waypoint description from a trip statistics.
|
||||||
*
|
*
|
||||||
* @param tripStatistics the trip statistics
|
* @param tripStatistics the trip statistics
|
||||||
*/
|
*/
|
||||||
String generateWaypointDescription(TripStatistics tripStatistics);
|
String generateWaypointDescription(TripStatistics tripStatistics);
|
||||||
}
|
}
|
||||||
|
|||||||
+217
-216
@@ -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;
|
||||||
@@ -31,228 +32,228 @@ import java.util.Vector;
|
|||||||
*/
|
*/
|
||||||
public class DescriptionGeneratorImpl implements DescriptionGenerator {
|
public class DescriptionGeneratorImpl implements DescriptionGenerator {
|
||||||
|
|
||||||
private static final String HTML_LINE_BREAK = "<br>";
|
private static final String HTML_LINE_BREAK = "<br>";
|
||||||
private static final String HTML_PARAGRAPH_SEPARATOR = "<p>";
|
private static final String HTML_PARAGRAPH_SEPARATOR = "<p>";
|
||||||
private static final String TEXT_LINE_BREAK = "\n";
|
private static final String TEXT_LINE_BREAK = "\n";
|
||||||
private static final String TEXT_PARAGRAPH_SEPARATOR = "\n\n";
|
private static final String TEXT_PARAGRAPH_SEPARATOR = "\n\n";
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public DescriptionGeneratorImpl(Context context) {
|
public DescriptionGeneratorImpl(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String generateTrackDescription(Track track, Vector<Double> distances, Vector<Double> elevations, boolean html) {
|
|
||||||
String paragraphSeparator = html ? HTML_PARAGRAPH_SEPARATOR : TEXT_PARAGRAPH_SEPARATOR;
|
|
||||||
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
// Created by
|
|
||||||
String creator = html
|
|
||||||
? "<a href='http://" + context.getString(R.string.app_web_url) + "'>" + context.getString(R.string.app_name) + "</a>"
|
|
||||||
: context.getString(R.string.app_name);
|
|
||||||
builder.append(creator);
|
|
||||||
|
|
||||||
builder.append(paragraphSeparator);
|
|
||||||
|
|
||||||
writeString(track.getName(), builder, R.string.generic_name_line, lineBreak);
|
|
||||||
writeString(track.getCategory(), builder, R.string.description_activity_type, lineBreak);
|
|
||||||
writeString(track.getDescription(), builder, R.string.generic_description_line, lineBreak);
|
|
||||||
builder.append(generateTripStatisticsDescription(track.getTripStatistics(), html));
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
|
||||||
return generateTripStatisticsDescription(tripStatistics, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a string to a string builder.
|
|
||||||
*
|
|
||||||
* @param text the string
|
|
||||||
* @param builder the string builder
|
|
||||||
* @param resId the resource id containing one string placeholder
|
|
||||||
* @param lineBreak the line break
|
|
||||||
*/
|
|
||||||
private void writeString(String text, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
if (text == null || text.length() == 0) {
|
|
||||||
text = context.getString(R.string.value_unknown);
|
|
||||||
}
|
|
||||||
builder.append(context.getString(resId, text));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a description for a {@link TripStatistics}.
|
|
||||||
*
|
|
||||||
* @param stats the trip statistics
|
|
||||||
* @param html true to use "<br>" for line break instead of "\n"
|
|
||||||
*/
|
|
||||||
private String generateTripStatisticsDescription(TripStatistics stats, boolean html) {
|
|
||||||
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
// Total distance
|
|
||||||
writeDistance(
|
|
||||||
stats.getTotalDistance(), builder, R.string.description_total_distance, lineBreak);
|
|
||||||
|
|
||||||
// Total time
|
|
||||||
writeTime(stats.getTotalTime(), builder, R.string.description_total_time, lineBreak);
|
|
||||||
|
|
||||||
// Moving time
|
|
||||||
writeTime(stats.getMovingTime(), builder, R.string.description_moving_time, lineBreak);
|
|
||||||
|
|
||||||
// Average speed
|
|
||||||
writeSpeed(stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak);
|
|
||||||
|
|
||||||
// Average moving speed
|
|
||||||
writeSpeed(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_speed,
|
|
||||||
lineBreak);
|
|
||||||
|
|
||||||
// Max speed
|
|
||||||
writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak);
|
|
||||||
|
|
||||||
// Average pace
|
|
||||||
writePace(
|
|
||||||
stats.getAverageSpeed(), builder, R.string.description_average_pace_in_minute, lineBreak);
|
|
||||||
|
|
||||||
// Average moving pace
|
|
||||||
writePace(stats.getAverageMovingSpeed(), builder,
|
|
||||||
R.string.description_average_moving_pace_in_minute, lineBreak);
|
|
||||||
|
|
||||||
// Fastest pace
|
|
||||||
writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace_in_minute, lineBreak);
|
|
||||||
|
|
||||||
// Max elevation
|
|
||||||
writeElevation(stats.getMaxElevation(), builder, R.string.description_max_elevation, lineBreak);
|
|
||||||
|
|
||||||
// Min elevation
|
|
||||||
writeElevation(stats.getMinElevation(), builder, R.string.description_min_elevation, lineBreak);
|
|
||||||
|
|
||||||
// Elevation gain
|
|
||||||
writeElevation(
|
|
||||||
stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
|
|
||||||
|
|
||||||
// Max grade
|
|
||||||
writeGrade(stats.getMaxGrade(), builder, R.string.description_max_grade, lineBreak);
|
|
||||||
|
|
||||||
// Min grade
|
|
||||||
writeGrade(stats.getMinGrade(), builder, R.string.description_min_grade, lineBreak);
|
|
||||||
|
|
||||||
// Recorded time
|
|
||||||
builder.append(
|
|
||||||
context.getString(R.string.description_recorded_time,
|
|
||||||
StringUtils.formatDateTime(context, stats.getStartTime())));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes distance.
|
|
||||||
*
|
|
||||||
* @param distance distance in meters
|
|
||||||
* @param builder StringBuilder to append distance
|
|
||||||
* @param resId resource id of distance string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeDistance(double distance, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
double distanceInKm = distance * UnitConversions.M_TO_KM;
|
|
||||||
double distanceInMi = distanceInKm * UnitConversions.KM_TO_MI;
|
|
||||||
builder.append(context.getString(resId, distanceInKm, distanceInMi));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes time.
|
|
||||||
*
|
|
||||||
* @param time time in milliseconds.
|
|
||||||
* @param builder StringBuilder to append time
|
|
||||||
* @param resId resource id of time string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeTime(long time, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
builder.append(context.getString(resId, StringUtils.formatElapsedTime(time)));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes speed.
|
|
||||||
*
|
|
||||||
* @param speed speed in meters per second
|
|
||||||
* @param builder StringBuilder to append speed
|
|
||||||
* @param resId resource id of speed string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeSpeed(
|
|
||||||
double speed, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
double speedInKmHr = speed * UnitConversions.MS_TO_KMH;
|
|
||||||
double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI;
|
|
||||||
builder.append(context.getString(resId, speedInKmHr, speedInMiHr));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes pace.
|
|
||||||
*
|
|
||||||
* @param speed speed in meters per second
|
|
||||||
* @param builder StringBuilder to append pace
|
|
||||||
* @param resId resource id of pace string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writePace(double speed, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
String[] paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false);
|
|
||||||
String[] paceInImperial = StringUtils.getSpeedParts(context, speed, false, false);
|
|
||||||
|
|
||||||
if (paceInMetrics[0] == null) {
|
|
||||||
paceInMetrics[0] = context.getString(R.string.value_unknown);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paceInImperial[0] == null) {
|
@Override
|
||||||
paceInImperial[0] = context.getString(R.string.value_unknown);
|
public String generateTrackDescription(Track track, Vector<Double> distances, Vector<Double> elevations, boolean html) {
|
||||||
|
String paragraphSeparator = html ? HTML_PARAGRAPH_SEPARATOR : TEXT_PARAGRAPH_SEPARATOR;
|
||||||
|
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
// Created by
|
||||||
|
String creator = html
|
||||||
|
? "<a href='http://" + context.getString(R.string.app_web_url) + "'>" + context.getString(R.string.app_name) + "</a>"
|
||||||
|
: context.getString(R.string.app_name);
|
||||||
|
builder.append(creator);
|
||||||
|
|
||||||
|
builder.append(paragraphSeparator);
|
||||||
|
|
||||||
|
writeString(track.getName(), builder, R.string.generic_name_line, lineBreak);
|
||||||
|
writeString(track.getCategory(), builder, R.string.description_activity_type, lineBreak);
|
||||||
|
writeString(track.getDescription(), builder, R.string.generic_description_line, lineBreak);
|
||||||
|
builder.append(generateTripStatisticsDescription(track.getTripStatistics(), html));
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(context.getString(resId, paceInMetrics[0], paceInImperial[0]));
|
@Override
|
||||||
builder.append(lineBreak);
|
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
||||||
}
|
return generateTripStatisticsDescription(tripStatistics, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes elevation.
|
* Writes a string to a string builder.
|
||||||
*
|
*
|
||||||
* @param elevation elevation in meters
|
* @param text the string
|
||||||
* @param builder StringBuilder to append elevation
|
* @param builder the string builder
|
||||||
* @param resId resource id of elevation string
|
* @param resId the resource id containing one string placeholder
|
||||||
* @param lineBreak line break string
|
* @param lineBreak the line break
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
private void writeString(String text, StringBuilder builder, int resId, String lineBreak) {
|
||||||
void writeElevation(
|
if (text == null || text.length() == 0) {
|
||||||
double elevation, StringBuilder builder, int resId, String lineBreak) {
|
text = context.getString(R.string.value_unknown);
|
||||||
long elevationInM = Math.round(elevation);
|
}
|
||||||
long elevationInFt = Math.round(elevation * UnitConversions.M_TO_FT);
|
builder.append(context.getString(resId, text));
|
||||||
builder.append(context.getString(resId, elevationInM, elevationInFt));
|
builder.append(lineBreak);
|
||||||
builder.append(lineBreak);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes grade.
|
* Generates a description for a {@link TripStatistics}.
|
||||||
*
|
*
|
||||||
* @param grade grade in fraction
|
* @param stats the trip statistics
|
||||||
* @param builder StringBuilder to append grade
|
* @param html true to use "<br>" for line break instead of "\n"
|
||||||
* @param resId resource id grade string
|
*/
|
||||||
* @param lineBreak line break string
|
private String generateTripStatisticsDescription(TripStatistics stats, boolean html) {
|
||||||
*/
|
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
||||||
@VisibleForTesting
|
StringBuilder builder = new StringBuilder();
|
||||||
void writeGrade(double grade, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
long gradeInPercent = Double.isNaN(grade) || Double.isInfinite(grade) ? 0L
|
// Total distance
|
||||||
: Math.round(grade * 100);
|
writeDistance(
|
||||||
builder.append(context.getString(resId, gradeInPercent));
|
stats.getTotalDistance(), builder, R.string.description_total_distance, lineBreak);
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
// Total time
|
||||||
|
writeTime(stats.getTotalTime(), builder, R.string.description_total_time, lineBreak);
|
||||||
|
|
||||||
|
// Moving time
|
||||||
|
writeTime(stats.getMovingTime(), builder, R.string.description_moving_time, lineBreak);
|
||||||
|
|
||||||
|
// Average speed
|
||||||
|
writeSpeed(stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak);
|
||||||
|
|
||||||
|
// Average moving speed
|
||||||
|
writeSpeed(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_speed,
|
||||||
|
lineBreak);
|
||||||
|
|
||||||
|
// Max speed
|
||||||
|
writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak);
|
||||||
|
|
||||||
|
// Average pace
|
||||||
|
writePace(
|
||||||
|
stats.getAverageSpeed(), builder, R.string.description_average_pace_in_minute, lineBreak);
|
||||||
|
|
||||||
|
// Average moving pace
|
||||||
|
writePace(stats.getAverageMovingSpeed(), builder,
|
||||||
|
R.string.description_average_moving_pace_in_minute, lineBreak);
|
||||||
|
|
||||||
|
// Fastest pace
|
||||||
|
writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace_in_minute, lineBreak);
|
||||||
|
|
||||||
|
// Max elevation
|
||||||
|
writeElevation(stats.getMaxElevation(), builder, R.string.description_max_elevation, lineBreak);
|
||||||
|
|
||||||
|
// Min elevation
|
||||||
|
writeElevation(stats.getMinElevation(), builder, R.string.description_min_elevation, lineBreak);
|
||||||
|
|
||||||
|
// Elevation gain
|
||||||
|
writeElevation(
|
||||||
|
stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
|
||||||
|
|
||||||
|
// Max grade
|
||||||
|
writeGrade(stats.getMaxGrade(), builder, R.string.description_max_grade, lineBreak);
|
||||||
|
|
||||||
|
// Min grade
|
||||||
|
writeGrade(stats.getMinGrade(), builder, R.string.description_min_grade, lineBreak);
|
||||||
|
|
||||||
|
// Recorded time
|
||||||
|
builder.append(
|
||||||
|
context.getString(R.string.description_recorded_time,
|
||||||
|
StringUtils.formatDateTime(context, stats.getStartTime())));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes distance.
|
||||||
|
*
|
||||||
|
* @param distance distance in meters
|
||||||
|
* @param builder StringBuilder to append distance
|
||||||
|
* @param resId resource id of distance string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeDistance(double distance, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
double distanceInKm = distance * UnitConversions.M_TO_KM;
|
||||||
|
double distanceInMi = distanceInKm * UnitConversions.KM_TO_MI;
|
||||||
|
builder.append(context.getString(resId, distanceInKm, distanceInMi));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes time.
|
||||||
|
*
|
||||||
|
* @param time time in milliseconds.
|
||||||
|
* @param builder StringBuilder to append time
|
||||||
|
* @param resId resource id of time string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeTime(long time, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
builder.append(context.getString(resId, StringUtils.formatElapsedTime(time)));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes speed.
|
||||||
|
*
|
||||||
|
* @param speed speed in meters per second
|
||||||
|
* @param builder StringBuilder to append speed
|
||||||
|
* @param resId resource id of speed string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeSpeed(
|
||||||
|
double speed, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
double speedInKmHr = speed * UnitConversions.MS_TO_KMH;
|
||||||
|
double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI;
|
||||||
|
builder.append(context.getString(resId, speedInKmHr, speedInMiHr));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes pace.
|
||||||
|
*
|
||||||
|
* @param speed speed in meters per second
|
||||||
|
* @param builder StringBuilder to append pace
|
||||||
|
* @param resId resource id of pace string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writePace(double speed, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
String[] paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false);
|
||||||
|
String[] paceInImperial = StringUtils.getSpeedParts(context, speed, false, false);
|
||||||
|
|
||||||
|
if (paceInMetrics[0] == null) {
|
||||||
|
paceInMetrics[0] = context.getString(R.string.value_unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paceInImperial[0] == null) {
|
||||||
|
paceInImperial[0] = context.getString(R.string.value_unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(context.getString(resId, paceInMetrics[0], paceInImperial[0]));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes elevation.
|
||||||
|
*
|
||||||
|
* @param elevation elevation in meters
|
||||||
|
* @param builder StringBuilder to append elevation
|
||||||
|
* @param resId resource id of elevation string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeElevation(
|
||||||
|
double elevation, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
long elevationInM = Math.round(elevation);
|
||||||
|
long elevationInFt = Math.round(elevation * UnitConversions.M_TO_FT);
|
||||||
|
builder.append(context.getString(resId, elevationInM, elevationInFt));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes grade.
|
||||||
|
*
|
||||||
|
* @param grade grade in fraction
|
||||||
|
* @param builder StringBuilder to append grade
|
||||||
|
* @param resId resource id grade string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeGrade(double grade, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
long gradeInPercent = Double.isNaN(grade) || Double.isInfinite(grade) ? 0L
|
||||||
|
: Math.round(grade * 100);
|
||||||
|
builder.append(context.getString(resId, gradeInPercent));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,364 +38,383 @@ import java.util.TreeSet;
|
|||||||
*/
|
*/
|
||||||
public class SearchEngine {
|
public class SearchEngine {
|
||||||
|
|
||||||
/** WHERE query to get tracks by name. */
|
/**
|
||||||
private static final String TRACK_SELECTION_QUERY =
|
* WHERE query to get tracks by name.
|
||||||
TracksColumns.NAME + " LIKE ? OR " +
|
*/
|
||||||
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
private static final String TRACK_SELECTION_QUERY =
|
||||||
TracksColumns.CATEGORY + " LIKE ?";
|
TracksColumns.NAME + " LIKE ? OR " +
|
||||||
|
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
||||||
|
TracksColumns.CATEGORY + " LIKE ?";
|
||||||
|
|
||||||
/** WHERE query to get waypoints by name. */
|
/**
|
||||||
private static final String WAYPOINT_SELECTION_QUERY =
|
* WHERE query to get waypoints by name.
|
||||||
WaypointsColumns.NAME + " LIKE ? OR " +
|
*/
|
||||||
WaypointsColumns.DESCRIPTION + " LIKE ? OR " +
|
private static final String WAYPOINT_SELECTION_QUERY =
|
||||||
WaypointsColumns.CATEGORY + " LIKE ?";
|
WaypointsColumns.NAME + " LIKE ? OR " +
|
||||||
|
WaypointsColumns.DESCRIPTION + " LIKE ? OR " +
|
||||||
|
WaypointsColumns.CATEGORY + " LIKE ?";
|
||||||
|
|
||||||
/** Order of track results. */
|
/**
|
||||||
private static final String TRACK_SELECTION_ORDER = TracksColumns._ID + " DESC LIMIT 1000";
|
* Order of track results.
|
||||||
|
*/
|
||||||
|
private static final String TRACK_SELECTION_ORDER = TracksColumns._ID + " DESC LIMIT 1000";
|
||||||
|
|
||||||
/** Order of waypoint results. */
|
/**
|
||||||
private static final String WAYPOINT_SELECTION_ORDER = WaypointsColumns._ID + " DESC";
|
* Order of waypoint results.
|
||||||
|
*/
|
||||||
|
private static final String WAYPOINT_SELECTION_ORDER = WaypointsColumns._ID + " DESC";
|
||||||
|
|
||||||
/** How much we promote a match in the track category. */
|
/**
|
||||||
private static final double TRACK_CATEGORY_PROMOTION = 2.0;
|
* How much we promote a match in the track category.
|
||||||
|
*/
|
||||||
|
private static final double TRACK_CATEGORY_PROMOTION = 2.0;
|
||||||
|
|
||||||
/** How much we promote a match in the track description. */
|
/**
|
||||||
private static final double TRACK_DESCRIPTION_PROMOTION = 8.0;
|
* How much we promote a match in the track description.
|
||||||
|
*/
|
||||||
|
private static final double TRACK_DESCRIPTION_PROMOTION = 8.0;
|
||||||
|
|
||||||
/** How much we promote a match in the track name. */
|
/**
|
||||||
private static final double TRACK_NAME_PROMOTION = 16.0;
|
* How much we promote a match in the track name.
|
||||||
|
*/
|
||||||
|
private static final double TRACK_NAME_PROMOTION = 16.0;
|
||||||
|
|
||||||
/** 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;
|
* 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;
|
||||||
|
|
||||||
/** How much we promote a track result if it's the currently-selected track. */
|
/**
|
||||||
private static final double CURRENT_TRACK_DEMOTION = 0.5;
|
* How much we promote a track result if it's the currently-selected track.
|
||||||
|
*/
|
||||||
|
private static final double CURRENT_TRACK_DEMOTION = 0.5;
|
||||||
|
|
||||||
/** Maximum number of waypoints which will be retrieved and scored. */
|
/**
|
||||||
private static final int MAX_SCORED_WAYPOINTS = 100;
|
* Maximum number of waypoints which will be retrieved and scored.
|
||||||
|
*/
|
||||||
|
private static final int MAX_SCORED_WAYPOINTS = 100;
|
||||||
|
|
||||||
/** Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000) */
|
/**
|
||||||
private static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
|
* Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000)
|
||||||
|
*/
|
||||||
|
private static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
|
||||||
|
/**
|
||||||
|
* Comparador for scored results.
|
||||||
|
*/
|
||||||
|
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR =
|
||||||
|
new Comparator<ScoredResult>() {
|
||||||
|
@Override
|
||||||
|
public int compare(ScoredResult r1, ScoredResult r2) {
|
||||||
|
// Score ordering.
|
||||||
|
int scoreDiff = Double.compare(r2.score, r1.score);
|
||||||
|
if (scoreDiff != 0) {
|
||||||
|
return scoreDiff;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Make tracks come before waypoints.
|
||||||
* Description of a search query, along with all contextual data needed to execute it.
|
if (r1.waypoint != null && r2.track != null) {
|
||||||
*/
|
return 1;
|
||||||
public static class SearchQuery {
|
} else if (r1.track != null && r2.waypoint != null) {
|
||||||
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId,
|
return -1;
|
||||||
long currentTimestamp) {
|
}
|
||||||
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
|
|
||||||
this.currentLocation = currentLocation;
|
// Finally, use arbitrary ordering, by ID.
|
||||||
this.currentTrackId = currentTrackId;
|
long id1 = r1.track != null ? r1.track.getId() : r1.waypoint.getId();
|
||||||
this.currentTimestamp = currentTimestamp;
|
long id2 = r2.track != null ? r2.track.getId() : r2.waypoint.getId();
|
||||||
|
long idDiff = id2 - id1;
|
||||||
|
return Long.signum(idDiff);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final ContentProviderUtils providerUtils;
|
||||||
|
|
||||||
|
public SearchEngine(ContentProviderUtils providerUtils) {
|
||||||
|
this.providerUtils = providerUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String textQuery;
|
/**
|
||||||
public final Location currentLocation;
|
* Squashes a number by calculating 1 / log (1 + x).
|
||||||
public final long currentTrackId;
|
*/
|
||||||
public final long currentTimestamp;
|
private static double squash(double x) {
|
||||||
}
|
return 1.0 / Math.log1p(x);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
* Executes a search query and returns a set of sorted results.
|
||||||
this.waypoint = waypoint;
|
*
|
||||||
this.score = score;
|
* @param query the query to execute
|
||||||
|
* @return a set of results, sorted according to their score
|
||||||
|
*/
|
||||||
|
public SortedSet<ScoredResult> search(SearchQuery query) {
|
||||||
|
ArrayList<Track> tracks = new ArrayList<>();
|
||||||
|
ArrayList<Waypoint> waypoints = new ArrayList<>();
|
||||||
|
TreeSet<ScoredResult> scoredResults = new TreeSet<>(SCORED_RESULT_COMPARATOR);
|
||||||
|
|
||||||
|
retrieveTracks(query, tracks);
|
||||||
|
retrieveWaypoints(query, waypoints);
|
||||||
|
|
||||||
|
scoreTrackResults(tracks, query, scoredResults);
|
||||||
|
scoreWaypointResults(waypoints, query, scoredResults);
|
||||||
|
|
||||||
|
return scoredResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Track track;
|
/**
|
||||||
public final Waypoint waypoint;
|
* Retrieves tracks matching the given query from the database.
|
||||||
public final double score;
|
*
|
||||||
|
* @param query the query to retrieve for
|
||||||
|
* @param tracks list to fill with the resulting tracks
|
||||||
|
*/
|
||||||
|
private void retrieveTracks(SearchQuery query, ArrayList<Track> tracks) {
|
||||||
|
String queryLikeSelection = "%" + query.textQuery + "%";
|
||||||
|
String[] trackSelectionArgs = new String[]{
|
||||||
|
queryLikeSelection,
|
||||||
|
queryLikeSelection,
|
||||||
|
queryLikeSelection};
|
||||||
|
|
||||||
@NonNull
|
try (Cursor cursor = providerUtils.getTrackCursor(
|
||||||
@Override
|
TRACK_SELECTION_QUERY, trackSelectionArgs, TRACK_SELECTION_ORDER)) {
|
||||||
public String toString() {
|
if (cursor != null) {
|
||||||
return "ScoredResult ["
|
tracks.ensureCapacity(cursor.getCount());
|
||||||
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
|
while (cursor.moveToNext()) {
|
||||||
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
|
tracks.add(providerUtils.createTrack(cursor));
|
||||||
+ "score=" + score + "]";
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Comparador for scored results. */
|
/**
|
||||||
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR =
|
* Retrieves waypoints matching the given query from the database.
|
||||||
new Comparator<ScoredResult>() {
|
*
|
||||||
|
* @param query the query to retrieve for
|
||||||
|
* @param waypoints list to fill with the resulting waypoints
|
||||||
|
*/
|
||||||
|
private void retrieveWaypoints(SearchQuery query, ArrayList<Waypoint> waypoints) {
|
||||||
|
String queryLikeSelection2 = "%" + query.textQuery + "%";
|
||||||
|
String[] waypointSelectionArgs = new String[]{
|
||||||
|
queryLikeSelection2,
|
||||||
|
queryLikeSelection2,
|
||||||
|
queryLikeSelection2};
|
||||||
|
try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs,
|
||||||
|
WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) {
|
||||||
|
if (cursor != null) {
|
||||||
|
waypoints.ensureCapacity(cursor.getCount());
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
Waypoint waypoint = providerUtils.createWaypoint(cursor);
|
||||||
|
if (LocationUtils.isValidLocation(waypoint.getLocation())) {
|
||||||
|
waypoints.add(waypoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a collection of track results.
|
||||||
|
*
|
||||||
|
* @param tracks the results to score
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param output the collection to fill with scored results
|
||||||
|
*/
|
||||||
|
private void scoreTrackResults(Collection<Track> tracks, SearchQuery query, Collection<ScoredResult> output) {
|
||||||
|
for (Track track : tracks) {
|
||||||
|
// Calculate the score.
|
||||||
|
double score = scoreTrackResult(query, track);
|
||||||
|
|
||||||
|
// Add to the output.
|
||||||
|
output.add(new ScoredResult(track, score));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a single track result.
|
||||||
|
*
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param track the results to score
|
||||||
|
* @return the score for the track
|
||||||
|
*/
|
||||||
|
private double scoreTrackResult(SearchQuery query, Track track) {
|
||||||
|
double score = 1.0;
|
||||||
|
|
||||||
|
score *= getTitleBoost(query, track.getName(), track.getDescription(), track.getCategory());
|
||||||
|
|
||||||
|
TripStatistics statistics = track.getTripStatistics();
|
||||||
|
// TODO: Also boost for proximity to the currently-centered position on the map.
|
||||||
|
score *= getDistanceBoost(query, statistics.getMeanLatitude(), statistics.getMeanLongitude());
|
||||||
|
|
||||||
|
long meanTimestamp = (statistics.getStartTime() + statistics.getStopTime()) / 2L;
|
||||||
|
score *= getTimeBoost(query, meanTimestamp);
|
||||||
|
|
||||||
|
// Score the currently-selected track lower (user is already there, wouldn't be searching for it).
|
||||||
|
if (track.getId() == query.currentTrackId) {
|
||||||
|
score *= CURRENT_TRACK_DEMOTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a collection of waypoint results.
|
||||||
|
*
|
||||||
|
* @param waypoints the results to score
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param output the collection to fill with scored results
|
||||||
|
*/
|
||||||
|
private void scoreWaypointResults(Collection<Waypoint> waypoints, SearchQuery query, Collection<ScoredResult> output) {
|
||||||
|
for (Waypoint waypoint : waypoints) {
|
||||||
|
// Calculate the score.
|
||||||
|
double score = scoreWaypointResult(query, waypoint);
|
||||||
|
|
||||||
|
// Add to the output.
|
||||||
|
output.add(new ScoredResult(waypoint, score));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a single waypoint result.
|
||||||
|
*
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param waypoint the results to score
|
||||||
|
* @return the score for the waypoint
|
||||||
|
*/
|
||||||
|
private double scoreWaypointResult(SearchQuery query, Waypoint waypoint) {
|
||||||
|
double score = 1.0;
|
||||||
|
|
||||||
|
Location location = waypoint.getLocation();
|
||||||
|
score *= getTitleBoost(query, waypoint.getName(), waypoint.getDescription(), waypoint.getCategory());
|
||||||
|
// TODO: Also boost for proximity to the currently-centered position on the map.
|
||||||
|
score *= getDistanceBoost(query, location.getLatitude(), location.getLongitude());
|
||||||
|
score *= getTimeBoost(query, location.getTime());
|
||||||
|
|
||||||
|
// Score waypoints in the currently-selected track higher (searching inside the current track).
|
||||||
|
if (query.currentTrackId != -1 && waypoint.getTrackId() == query.currentTrackId) {
|
||||||
|
score *= CURRENT_TRACK_WAYPOINT_PROMOTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the boosting of the score due to the field(s) in which the match occured.
|
||||||
|
*
|
||||||
|
* @param query the query to boost for
|
||||||
|
* @param name the name of the track or waypoint
|
||||||
|
* @param description the description of the track or waypoint
|
||||||
|
* @param category the category of the track or waypoint
|
||||||
|
* @return the total boost to be applied to the result
|
||||||
|
*/
|
||||||
|
private double getTitleBoost(SearchQuery query,
|
||||||
|
String name, String description, String category) {
|
||||||
|
// Title boost: track name > description > category.
|
||||||
|
double boost = 1.0;
|
||||||
|
if (name.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
||||||
|
boost *= TRACK_NAME_PROMOTION;
|
||||||
|
}
|
||||||
|
if (description.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
||||||
|
boost *= TRACK_DESCRIPTION_PROMOTION;
|
||||||
|
}
|
||||||
|
if (category.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
||||||
|
boost *= TRACK_CATEGORY_PROMOTION;
|
||||||
|
}
|
||||||
|
return boost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the boosting of the score due to the recency of the matched entity.
|
||||||
|
*
|
||||||
|
* @param query the query to boost for
|
||||||
|
* @param timestamp the timestamp to calculate the boost for
|
||||||
|
* @return the total boost to be applied to the result
|
||||||
|
*/
|
||||||
|
private double getTimeBoost(SearchQuery query, long timestamp) {
|
||||||
|
if (timestamp < OLDEST_ALLOWED_TIMESTAMP) {
|
||||||
|
// Safety: if timestamp is too old or invalid, don't rank based on time.
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Score recent tracks higher.
|
||||||
|
long timeAgoHours = (long) ((query.currentTimestamp - timestamp) * UnitConversions.MS_TO_S
|
||||||
|
* UnitConversions.S_TO_MIN * UnitConversions.MIN_TO_HR);
|
||||||
|
if (timeAgoHours > 0L) {
|
||||||
|
return squash(timeAgoHours);
|
||||||
|
} else {
|
||||||
|
// Should rarely happen (track recorded in the last hour).
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the boosting of the score due to proximity to a location.
|
||||||
|
*
|
||||||
|
* @param query the query to boost for
|
||||||
|
* @param latitude the latitude to calculate the boost for
|
||||||
|
* @param longitude the longitude to calculate the boost for
|
||||||
|
* @return the total boost to be applied to the result
|
||||||
|
*/
|
||||||
|
private double getDistanceBoost(SearchQuery query, double latitude, double longitude) {
|
||||||
|
if (query.currentLocation == null) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] distanceResults = new float[1];
|
||||||
|
|
||||||
|
Location.distanceBetween(
|
||||||
|
latitude, longitude,
|
||||||
|
query.currentLocation.getLatitude(), query.currentLocation.getLongitude(),
|
||||||
|
distanceResults);
|
||||||
|
|
||||||
|
// Score tracks close to the current location higher.
|
||||||
|
double distanceKm = distanceResults[0] * UnitConversions.M_TO_KM;
|
||||||
|
if (distanceKm > 0.0) {
|
||||||
|
// Use the inverse of the amortized distance.
|
||||||
|
return squash(distanceKm);
|
||||||
|
} else {
|
||||||
|
// Should rarely happen (distance is exactly 0).
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of a search query, along with all contextual data needed to execute it.
|
||||||
|
*/
|
||||||
|
public static class SearchQuery {
|
||||||
|
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
|
@Override
|
||||||
public int compare(ScoredResult r1, ScoredResult r2) {
|
public String toString() {
|
||||||
// Score ordering.
|
return "ScoredResult ["
|
||||||
int scoreDiff = Double.compare(r2.score, r1.score);
|
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
|
||||||
if (scoreDiff != 0) {
|
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
|
||||||
return scoreDiff;
|
+ "score=" + score + "]";
|
||||||
}
|
|
||||||
|
|
||||||
// Make tracks come before waypoints.
|
|
||||||
if (r1.waypoint != null && r2.track != null) {
|
|
||||||
return 1;
|
|
||||||
} else if (r1.track != null && r2.waypoint != null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, use arbitrary ordering, by ID.
|
|
||||||
long id1 = r1.track != null ? r1.track.getId() : r1.waypoint.getId();
|
|
||||||
long id2 = r2.track != null ? r2.track.getId() : r2.waypoint.getId();
|
|
||||||
long idDiff = id2 - id1;
|
|
||||||
return Long.signum(idDiff);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
private final ContentProviderUtils providerUtils;
|
|
||||||
|
|
||||||
public SearchEngine(ContentProviderUtils providerUtils) {
|
|
||||||
this.providerUtils = providerUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a search query and returns a set of sorted results.
|
|
||||||
*
|
|
||||||
* @param query the query to execute
|
|
||||||
* @return a set of results, sorted according to their score
|
|
||||||
*/
|
|
||||||
public SortedSet<ScoredResult> search(SearchQuery query) {
|
|
||||||
ArrayList<Track> tracks = new ArrayList<>();
|
|
||||||
ArrayList<Waypoint> waypoints = new ArrayList<>();
|
|
||||||
TreeSet<ScoredResult> scoredResults = new TreeSet<>(SCORED_RESULT_COMPARATOR);
|
|
||||||
|
|
||||||
retrieveTracks(query, tracks);
|
|
||||||
retrieveWaypoints(query, waypoints);
|
|
||||||
|
|
||||||
scoreTrackResults(tracks, query, scoredResults);
|
|
||||||
scoreWaypointResults(waypoints, query, scoredResults);
|
|
||||||
|
|
||||||
return scoredResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves tracks matching the given query from the database.
|
|
||||||
*
|
|
||||||
* @param query the query to retrieve for
|
|
||||||
* @param tracks list to fill with the resulting tracks
|
|
||||||
*/
|
|
||||||
private void retrieveTracks(SearchQuery query, ArrayList<Track> tracks) {
|
|
||||||
String queryLikeSelection = "%" + query.textQuery + "%";
|
|
||||||
String[] trackSelectionArgs = new String[] {
|
|
||||||
queryLikeSelection,
|
|
||||||
queryLikeSelection,
|
|
||||||
queryLikeSelection };
|
|
||||||
|
|
||||||
try (Cursor cursor = providerUtils.getTrackCursor(
|
|
||||||
TRACK_SELECTION_QUERY, trackSelectionArgs, TRACK_SELECTION_ORDER)) {
|
|
||||||
if (cursor != null) {
|
|
||||||
tracks.ensureCapacity(cursor.getCount());
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
tracks.add(providerUtils.createTrack(cursor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves waypoints matching the given query from the database.
|
|
||||||
*
|
|
||||||
* @param query the query to retrieve for
|
|
||||||
* @param waypoints list to fill with the resulting waypoints
|
|
||||||
*/
|
|
||||||
private void retrieveWaypoints(SearchQuery query, ArrayList<Waypoint> waypoints) {
|
|
||||||
String queryLikeSelection2 = "%" + query.textQuery + "%";
|
|
||||||
String[] waypointSelectionArgs = new String[] {
|
|
||||||
queryLikeSelection2,
|
|
||||||
queryLikeSelection2,
|
|
||||||
queryLikeSelection2 };
|
|
||||||
try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs,
|
|
||||||
WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) {
|
|
||||||
if (cursor != null) {
|
|
||||||
waypoints.ensureCapacity(cursor.getCount());
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
Waypoint waypoint = providerUtils.createWaypoint(cursor);
|
|
||||||
if (LocationUtils.isValidLocation(waypoint.getLocation())) {
|
|
||||||
waypoints.add(waypoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scores a collection of track results.
|
|
||||||
*
|
|
||||||
* @param tracks the results to score
|
|
||||||
* @param query the query to score for
|
|
||||||
* @param output the collection to fill with scored results
|
|
||||||
*/
|
|
||||||
private void scoreTrackResults(Collection<Track> tracks, SearchQuery query, Collection<ScoredResult> output) {
|
|
||||||
for (Track track : tracks) {
|
|
||||||
// Calculate the score.
|
|
||||||
double score = scoreTrackResult(query, track);
|
|
||||||
|
|
||||||
// Add to the output.
|
|
||||||
output.add(new ScoredResult(track, score));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scores a single track result.
|
|
||||||
*
|
|
||||||
* @param query the query to score for
|
|
||||||
* @param track the results to score
|
|
||||||
* @return the score for the track
|
|
||||||
*/
|
|
||||||
private double scoreTrackResult(SearchQuery query, Track track) {
|
|
||||||
double score = 1.0;
|
|
||||||
|
|
||||||
score *= getTitleBoost(query, track.getName(), track.getDescription(), track.getCategory());
|
|
||||||
|
|
||||||
TripStatistics statistics = track.getTripStatistics();
|
|
||||||
// TODO: Also boost for proximity to the currently-centered position on the map.
|
|
||||||
score *= getDistanceBoost(query, statistics.getMeanLatitude(), statistics.getMeanLongitude());
|
|
||||||
|
|
||||||
long meanTimestamp = (statistics.getStartTime() + statistics.getStopTime()) / 2L;
|
|
||||||
score *= getTimeBoost(query, meanTimestamp);
|
|
||||||
|
|
||||||
// Score the currently-selected track lower (user is already there, wouldn't be searching for it).
|
|
||||||
if (track.getId() == query.currentTrackId) {
|
|
||||||
score *= CURRENT_TRACK_DEMOTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scores a collection of waypoint results.
|
|
||||||
*
|
|
||||||
* @param waypoints the results to score
|
|
||||||
* @param query the query to score for
|
|
||||||
* @param output the collection to fill with scored results
|
|
||||||
*/
|
|
||||||
private void scoreWaypointResults(Collection<Waypoint> waypoints, SearchQuery query, Collection<ScoredResult> output) {
|
|
||||||
for (Waypoint waypoint : waypoints) {
|
|
||||||
// Calculate the score.
|
|
||||||
double score = scoreWaypointResult(query, waypoint);
|
|
||||||
|
|
||||||
// Add to the output.
|
|
||||||
output.add(new ScoredResult(waypoint, score));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scores a single waypoint result.
|
|
||||||
*
|
|
||||||
* @param query the query to score for
|
|
||||||
* @param waypoint the results to score
|
|
||||||
* @return the score for the waypoint
|
|
||||||
*/
|
|
||||||
private double scoreWaypointResult(SearchQuery query, Waypoint waypoint) {
|
|
||||||
double score = 1.0;
|
|
||||||
|
|
||||||
Location location = waypoint.getLocation();
|
|
||||||
score *= getTitleBoost(query, waypoint.getName(), waypoint.getDescription(), waypoint.getCategory());
|
|
||||||
// TODO: Also boost for proximity to the currently-centered position on the map.
|
|
||||||
score *= getDistanceBoost(query, location.getLatitude(), location.getLongitude());
|
|
||||||
score *= getTimeBoost(query, location.getTime());
|
|
||||||
|
|
||||||
// Score waypoints in the currently-selected track higher (searching inside the current track).
|
|
||||||
if (query.currentTrackId != -1 && waypoint.getTrackId() == query.currentTrackId) {
|
|
||||||
score *= CURRENT_TRACK_WAYPOINT_PROMOTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the boosting of the score due to the field(s) in which the match occured.
|
|
||||||
*
|
|
||||||
* @param query the query to boost for
|
|
||||||
* @param name the name of the track or waypoint
|
|
||||||
* @param description the description of the track or waypoint
|
|
||||||
* @param category the category of the track or waypoint
|
|
||||||
* @return the total boost to be applied to the result
|
|
||||||
*/
|
|
||||||
private double getTitleBoost(SearchQuery query,
|
|
||||||
String name, String description, String category) {
|
|
||||||
// Title boost: track name > description > category.
|
|
||||||
double boost = 1.0;
|
|
||||||
if (name.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
|
||||||
boost *= TRACK_NAME_PROMOTION;
|
|
||||||
}
|
|
||||||
if (description.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
|
||||||
boost *= TRACK_DESCRIPTION_PROMOTION;
|
|
||||||
}
|
|
||||||
if (category.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
|
||||||
boost *= TRACK_CATEGORY_PROMOTION;
|
|
||||||
}
|
|
||||||
return boost;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the boosting of the score due to the recency of the matched entity.
|
|
||||||
*
|
|
||||||
* @param query the query to boost for
|
|
||||||
* @param timestamp the timestamp to calculate the boost for
|
|
||||||
* @return the total boost to be applied to the result
|
|
||||||
*/
|
|
||||||
private double getTimeBoost(SearchQuery query, long timestamp) {
|
|
||||||
if (timestamp < OLDEST_ALLOWED_TIMESTAMP) {
|
|
||||||
// Safety: if timestamp is too old or invalid, don't rank based on time.
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Score recent tracks higher.
|
|
||||||
long timeAgoHours = (long) ((query.currentTimestamp - timestamp) * UnitConversions.MS_TO_S
|
|
||||||
* UnitConversions.S_TO_MIN * UnitConversions.MIN_TO_HR);
|
|
||||||
if (timeAgoHours > 0L) {
|
|
||||||
return squash(timeAgoHours);
|
|
||||||
} else {
|
|
||||||
// Should rarely happen (track recorded in the last hour).
|
|
||||||
return Double.POSITIVE_INFINITY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the boosting of the score due to proximity to a location.
|
|
||||||
*
|
|
||||||
* @param query the query to boost for
|
|
||||||
* @param latitude the latitude to calculate the boost for
|
|
||||||
* @param longitude the longitude to calculate the boost for
|
|
||||||
* @return the total boost to be applied to the result
|
|
||||||
*/
|
|
||||||
private double getDistanceBoost(SearchQuery query, double latitude, double longitude) {
|
|
||||||
if (query.currentLocation == null) {
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float[] distanceResults = new float[1];
|
|
||||||
|
|
||||||
Location.distanceBetween(
|
|
||||||
latitude, longitude,
|
|
||||||
query.currentLocation.getLatitude(), query.currentLocation.getLongitude(),
|
|
||||||
distanceResults);
|
|
||||||
|
|
||||||
// Score tracks close to the current location higher.
|
|
||||||
double distanceKm = distanceResults[0] * UnitConversions.M_TO_KM;
|
|
||||||
if (distanceKm > 0.0) {
|
|
||||||
// Use the inverse of the amortized distance.
|
|
||||||
return squash(distanceKm);
|
|
||||||
} else {
|
|
||||||
// Should rarely happen (distance is exactly 0).
|
|
||||||
return Double.POSITIVE_INFINITY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Squashes a number by calculating 1 / log (1 + x).
|
|
||||||
*/
|
|
||||||
private static double squash(double x) {
|
|
||||||
return 1.0 / Math.log1p(x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,19 +26,19 @@ import android.provider.SearchRecentSuggestions;
|
|||||||
*/
|
*/
|
||||||
public class SearchEngineProvider extends SearchRecentSuggestionsProvider {
|
public class SearchEngineProvider extends SearchRecentSuggestionsProvider {
|
||||||
|
|
||||||
private static final String AUTHORITY = "com.google.android.maps.mytracks.search";
|
private static final String AUTHORITY = "com.google.android.maps.mytracks.search";
|
||||||
private static final int MODE = DATABASE_MODE_QUERIES;
|
private static final int MODE = DATABASE_MODE_QUERIES;
|
||||||
|
|
||||||
public SearchEngineProvider() {
|
public SearchEngineProvider() {
|
||||||
setupSuggestions(AUTHORITY, MODE);
|
setupSuggestions(AUTHORITY, MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Also add suggestions from the database.
|
// TODO: Also add suggestions from the database.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and returns a helper for adding recent queries or clearing the recent query history.
|
* Creates and returns a helper for adding recent queries or clearing the recent query history.
|
||||||
*/
|
*/
|
||||||
public static SearchRecentSuggestions newHelper(Context context) {
|
public static SearchRecentSuggestions newHelper(Context context) {
|
||||||
return new SearchRecentSuggestions(context, AUTHORITY, MODE);
|
return new SearchRecentSuggestions(context, AUTHORITY, MODE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,41 +26,41 @@ import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
|
|||||||
*/
|
*/
|
||||||
public class SensorDataSetLocation extends Location {
|
public class SensorDataSetLocation extends Location {
|
||||||
|
|
||||||
private SensorDataSet sensorDataSet = null;
|
private SensorDataSet sensorDataSet = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id of this location from the provider.
|
* The id of this location from the provider.
|
||||||
*/
|
*/
|
||||||
private int id = -1;
|
private int id = -1;
|
||||||
|
|
||||||
public SensorDataSetLocation(Location location, SensorDataSet sensorDataSet) {
|
public SensorDataSetLocation(Location location, SensorDataSet sensorDataSet) {
|
||||||
super(location);
|
super(location);
|
||||||
this.sensorDataSet = sensorDataSet;
|
this.sensorDataSet = sensorDataSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataSetLocation(String provider) {
|
public SensorDataSetLocation(String provider) {
|
||||||
super(provider);
|
super(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataSet getSensorDataSet() {
|
public SensorDataSet getSensorDataSet() {
|
||||||
return sensorDataSet;
|
return sensorDataSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSensorDataSet(SensorDataSet sensorDataSet) {
|
public void setSensorDataSet(SensorDataSet sensorDataSet) {
|
||||||
this.sensorDataSet = sensorDataSet;
|
this.sensorDataSet = sensorDataSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(int id) {
|
public void setId(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
super.reset();
|
super.reset();
|
||||||
sensorDataSet = null;
|
sensorDataSet = null;
|
||||||
id = -1;
|
id = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,172 +32,169 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public class Track implements Parcelable {
|
public class Track implements Parcelable {
|
||||||
|
|
||||||
private long id = -1L;
|
public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {
|
||||||
private String name = "";
|
@Override
|
||||||
private String description = "";
|
public Track createFromParcel(Parcel in) {
|
||||||
private String category = "";
|
return new Track(in);
|
||||||
private long startId = -1L;
|
}
|
||||||
private long stopId = -1L;
|
|
||||||
|
|
||||||
/*
|
@Override
|
||||||
* The number of location points (present even if the points themselves are
|
public Track[] newArray(int size) {
|
||||||
* not loaded)
|
return new Track[size];
|
||||||
*/
|
}
|
||||||
private int numberOfPoints = 0;
|
};
|
||||||
private String icon = "";
|
private long id = -1L;
|
||||||
private long modifiedTime = -1L;
|
private String name = "";
|
||||||
|
private String description = "";
|
||||||
|
private String category = "";
|
||||||
|
private long startId = -1L;
|
||||||
|
private long stopId = -1L;
|
||||||
|
/*
|
||||||
|
* The number of location points (present even if the points themselves are
|
||||||
|
* not loaded)
|
||||||
|
*/
|
||||||
|
private int numberOfPoints = 0;
|
||||||
|
private String icon = "";
|
||||||
|
private long modifiedTime = -1L;
|
||||||
|
private TripStatistics tripStatistics = new TripStatistics();
|
||||||
|
// Location points (which may not have been loaded)
|
||||||
|
private ArrayList<Location> locations = new ArrayList<>();
|
||||||
|
|
||||||
private TripStatistics tripStatistics = new TripStatistics();
|
public Track() {
|
||||||
|
|
||||||
// Location points (which may not have been loaded)
|
|
||||||
private ArrayList<Location> locations = new ArrayList<>();
|
|
||||||
|
|
||||||
public Track() {}
|
|
||||||
|
|
||||||
private Track(Parcel in) {
|
|
||||||
id = in.readLong();
|
|
||||||
name = in.readString();
|
|
||||||
description = in.readString();
|
|
||||||
category = in.readString();
|
|
||||||
startId = in.readLong();
|
|
||||||
stopId = in.readLong();
|
|
||||||
numberOfPoints = in.readInt();
|
|
||||||
icon = in.readString();
|
|
||||||
modifiedTime = in.readLong();
|
|
||||||
|
|
||||||
ClassLoader classLoader = getClass().getClassLoader();
|
|
||||||
tripStatistics = in.readParcelable(classLoader);
|
|
||||||
|
|
||||||
for (int i = 0; i < numberOfPoints; ++i) {
|
|
||||||
Location location = in.readParcelable(classLoader);
|
|
||||||
locations.add(location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeLong(id);
|
|
||||||
dest.writeString(name);
|
|
||||||
dest.writeString(description);
|
|
||||||
dest.writeString(category);
|
|
||||||
dest.writeLong(startId);
|
|
||||||
dest.writeLong(stopId);
|
|
||||||
dest.writeInt(numberOfPoints);
|
|
||||||
dest.writeString(icon);
|
|
||||||
dest.writeLong(modifiedTime);
|
|
||||||
|
|
||||||
dest.writeParcelable(tripStatistics, 0);
|
|
||||||
for (int i = 0; i < numberOfPoints; ++i) {
|
|
||||||
dest.writeParcelable(locations.get(i), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {
|
|
||||||
@Override
|
|
||||||
public Track createFromParcel(Parcel in) {
|
|
||||||
return new Track(in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Track(Parcel in) {
|
||||||
public Track[] newArray(int size) {
|
id = in.readLong();
|
||||||
return new Track[size];
|
name = in.readString();
|
||||||
|
description = in.readString();
|
||||||
|
category = in.readString();
|
||||||
|
startId = in.readLong();
|
||||||
|
stopId = in.readLong();
|
||||||
|
numberOfPoints = in.readInt();
|
||||||
|
icon = in.readString();
|
||||||
|
modifiedTime = in.readLong();
|
||||||
|
|
||||||
|
ClassLoader classLoader = getClass().getClassLoader();
|
||||||
|
tripStatistics = in.readParcelable(classLoader);
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfPoints; ++i) {
|
||||||
|
Location location = in.readParcelable(classLoader);
|
||||||
|
locations.add(location);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
public long getId() {
|
@Override
|
||||||
return id;
|
public int describeContents() {
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void setId(long id) {
|
@Override
|
||||||
this.id = id;
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
}
|
dest.writeLong(id);
|
||||||
|
dest.writeString(name);
|
||||||
|
dest.writeString(description);
|
||||||
|
dest.writeString(category);
|
||||||
|
dest.writeLong(startId);
|
||||||
|
dest.writeLong(stopId);
|
||||||
|
dest.writeInt(numberOfPoints);
|
||||||
|
dest.writeString(icon);
|
||||||
|
dest.writeLong(modifiedTime);
|
||||||
|
|
||||||
public String getName() {
|
dest.writeParcelable(tripStatistics, 0);
|
||||||
return name;
|
for (int i = 0; i < numberOfPoints; ++i) {
|
||||||
}
|
dest.writeParcelable(locations.get(i), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public long getId() {
|
||||||
this.name = name;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public void setId(long id) {
|
||||||
return description;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDescription(String description) {
|
public String getName() {
|
||||||
this.description = description;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCategory() {
|
public void setName(String name) {
|
||||||
return category;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCategory(String category) {
|
public String getDescription() {
|
||||||
this.category = category;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStartId() {
|
public void setDescription(String description) {
|
||||||
return startId;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStartId(long startId) {
|
public String getCategory() {
|
||||||
this.startId = startId;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStopId() {
|
public void setCategory(String category) {
|
||||||
return stopId;
|
this.category = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStopId(long stopId) {
|
public long getStartId() {
|
||||||
this.stopId = stopId;
|
return startId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumberOfPoints() {
|
public void setStartId(long startId) {
|
||||||
return numberOfPoints;
|
this.startId = startId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNumberOfPoints(int numberOfPoints) {
|
public long getStopId() {
|
||||||
this.numberOfPoints = numberOfPoints;
|
return stopId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIcon() {
|
public void setStopId(long stopId) {
|
||||||
return icon;
|
this.stopId = stopId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIcon(String icon) {
|
public int getNumberOfPoints() {
|
||||||
this.icon = icon;
|
return numberOfPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getModifiedTime() {
|
public void setNumberOfPoints(int numberOfPoints) {
|
||||||
return modifiedTime;
|
this.numberOfPoints = numberOfPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setModifiedTime(long modifiedTime) {
|
public String getIcon() {
|
||||||
this.modifiedTime = modifiedTime;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TripStatistics getTripStatistics() {
|
public void setIcon(String icon) {
|
||||||
return tripStatistics;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTripStatistics(TripStatistics tripStatistics) {
|
public long getModifiedTime() {
|
||||||
this.tripStatistics = tripStatistics;
|
return modifiedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLocation(Location location) {
|
public void setModifiedTime(long modifiedTime) {
|
||||||
locations.add(location);
|
this.modifiedTime = modifiedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Location> getLocations() {
|
public TripStatistics getTripStatistics() {
|
||||||
return locations;
|
return tripStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocations(ArrayList<Location> locations) {
|
public void setTripStatistics(TripStatistics tripStatistics) {
|
||||||
this.locations = locations;
|
this.tripStatistics = tripStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addLocation(Location location) {
|
||||||
|
locations.add(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Location> getLocations() {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocations(ArrayList<Location> locations) {
|
||||||
|
this.locations = locations;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -25,93 +25,93 @@ import android.location.Location;
|
|||||||
*/
|
*/
|
||||||
public interface TrackDataListener {
|
public interface TrackDataListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the track or its statistics has been updated.
|
* Called when the track or its statistics has been updated.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
*/
|
*/
|
||||||
void onTrackUpdated(Track track);
|
void onTrackUpdated(Track track);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to clear previously-sent track points.
|
* Called to clear previously-sent track points.
|
||||||
*/
|
*/
|
||||||
void clearTrackPoints();
|
void clearTrackPoints();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a sampled in track point is read.
|
* Called when a sampled in track point is read.
|
||||||
*
|
*
|
||||||
* @param location the location
|
* @param location the location
|
||||||
*/
|
*/
|
||||||
void onSampledInTrackPoint(Location location);
|
void onSampledInTrackPoint(Location location);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a sampled out track point is read.
|
* Called when a sampled out track point is read.
|
||||||
*
|
*
|
||||||
* @param location the location
|
* @param location the location
|
||||||
*/
|
*/
|
||||||
void onSampledOutTrackPoint(Location location);
|
void onSampledOutTrackPoint(Location location);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an invalid track point representing a segment split is read.
|
* Called when an invalid track point representing a segment split is read.
|
||||||
*/
|
*/
|
||||||
void onSegmentSplit(Location location);
|
void onSegmentSplit(Location location);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when finish sending new track points. This gets called after every
|
* Called when finish sending new track points. This gets called after every
|
||||||
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
||||||
* {@link #onSampledOutTrackPoint(Location)} and
|
* {@link #onSampledOutTrackPoint(Location)} and
|
||||||
* {@link #onSegmentSplit(Location)}.
|
* {@link #onSegmentSplit(Location)}.
|
||||||
*/
|
*/
|
||||||
void onNewTrackPointsDone();
|
void onNewTrackPointsDone();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to clear previously sent waypoints.
|
* Called to clear previously sent waypoints.
|
||||||
*/
|
*/
|
||||||
void clearWaypoints();
|
void clearWaypoints();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a new waypoint is read.
|
* Called when a new waypoint is read.
|
||||||
*
|
*
|
||||||
* @param waypoint the waypoint
|
* @param waypoint the waypoint
|
||||||
*/
|
*/
|
||||||
void onNewWaypoint(Waypoint waypoint);
|
void onNewWaypoint(Waypoint waypoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when finish sending new waypoints. This gets called after every
|
* Called when finish sending new waypoints. This gets called after every
|
||||||
* batch of calls to {@link #clearWaypoints()} and
|
* batch of calls to {@link #clearWaypoints()} and
|
||||||
* {@link #onNewWaypoint(Waypoint)}.
|
* {@link #onNewWaypoint(Waypoint)}.
|
||||||
*/
|
*/
|
||||||
void onNewWaypointsDone();
|
void onNewWaypointsDone();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the metric units preference value is change.
|
* Called when the metric units preference value is change.
|
||||||
*
|
*
|
||||||
* @param metricUnits true to use metric units, false to use imperial units
|
* @param metricUnits true to use metric units, false to use imperial units
|
||||||
* @return true to reload all the data, false otherwise.
|
* @return true to reload all the data, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean onMetricUnitsChanged(boolean metricUnits);
|
boolean onMetricUnitsChanged(boolean metricUnits);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the report speed preference value is changed.
|
* Called when the report speed preference value is changed.
|
||||||
*
|
*
|
||||||
* @param reportSpeed true to report speed, false to report pace
|
* @param reportSpeed true to report speed, false to report pace
|
||||||
* @return true to reload all the data, false otherwise.
|
* @return true to reload all the data, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean onReportSpeedChanged(boolean reportSpeed);
|
boolean onReportSpeedChanged(boolean reportSpeed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the recording gps accuracy preference value is changed.
|
* Called when the recording gps accuracy preference value is changed.
|
||||||
*
|
*
|
||||||
* @param recordingGpsAccuracy the new value
|
* @param recordingGpsAccuracy the new value
|
||||||
* @return true to reload all the data, false otherwise
|
* @return true to reload all the data, false otherwise
|
||||||
*/
|
*/
|
||||||
boolean onRecordingGpsAccuracy(int recordingGpsAccuracy);
|
boolean onRecordingGpsAccuracy(int recordingGpsAccuracy);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the recording distance interval preference value is changed.
|
* Called when the recording distance interval preference value is changed.
|
||||||
*
|
*
|
||||||
* @param recordingDistanceInterval the new value
|
* @param recordingDistanceInterval the new value
|
||||||
* @return true to reload all the data, false otherwise.
|
* @return true to reload all the data, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean onRecordingDistanceIntervalChanged(int recordingDistanceInterval);
|
boolean onRecordingDistanceIntervalChanged(int recordingDistanceInterval);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,91 +33,91 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class TrackDataManager {
|
public class TrackDataManager {
|
||||||
|
|
||||||
private static final String TAG = TrackDataManager.class.getSimpleName();
|
private static final String TAG = TrackDataManager.class.getSimpleName();
|
||||||
|
|
||||||
// Map of listener to its track data types
|
// Map of listener to its track data types
|
||||||
private final Map<TrackDataListener, EnumSet<TrackDataType>>
|
private final Map<TrackDataListener, EnumSet<TrackDataType>>
|
||||||
listenerToTypesMap = new HashMap<>();
|
listenerToTypesMap = new HashMap<>();
|
||||||
|
|
||||||
// Map of track data type to listeners
|
// Map of track data type to listeners
|
||||||
private final Map<TrackDataType, Set<TrackDataListener>>
|
private final Map<TrackDataType, Set<TrackDataListener>>
|
||||||
typeToListenersMap = new EnumMap<>(TrackDataType.class);
|
typeToListenersMap = new EnumMap<>(TrackDataType.class);
|
||||||
|
|
||||||
public TrackDataManager() {
|
public TrackDataManager() {
|
||||||
for (TrackDataType trackDataType : TrackDataType.values()) {
|
for (TrackDataType trackDataType : TrackDataType.values()) {
|
||||||
typeToListenersMap.put(trackDataType, new LinkedHashSet<TrackDataListener>());
|
typeToListenersMap.put(trackDataType, new LinkedHashSet<TrackDataListener>());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a listener.
|
|
||||||
*
|
|
||||||
* @param listener the listener
|
|
||||||
* @param trackDataTypes the track data types the listener is interested
|
|
||||||
*/
|
|
||||||
public void registerListener(
|
|
||||||
TrackDataListener listener, EnumSet<TrackDataType> trackDataTypes) {
|
|
||||||
if (listenerToTypesMap.containsKey(listener)) {
|
|
||||||
Log.w(TAG, "Tried to register a listener that is already registered. Ignore.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
listenerToTypesMap.put(listener, trackDataTypes);
|
|
||||||
for (TrackDataType trackDataType : trackDataTypes) {
|
|
||||||
typeToListenersMap.get(trackDataType).add(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a listener.
|
|
||||||
*
|
|
||||||
* @param listener the listener
|
|
||||||
*/
|
|
||||||
public void unregisterListener(TrackDataListener listener) {
|
|
||||||
EnumSet<TrackDataType> removedTypes = listenerToTypesMap.remove(listener);
|
|
||||||
if (removedTypes == null) {
|
|
||||||
Log.w(TAG, "Tried to unregister a listener that is not registered. Ignore.");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the listener from the typeToListenersMap
|
/**
|
||||||
for (TrackDataType trackDataType : removedTypes) {
|
* Registers a listener.
|
||||||
typeToListenersMap.get(trackDataType).remove(listener);
|
*
|
||||||
|
* @param listener the listener
|
||||||
|
* @param trackDataTypes the track data types the listener is interested
|
||||||
|
*/
|
||||||
|
public void registerListener(
|
||||||
|
TrackDataListener listener, EnumSet<TrackDataType> trackDataTypes) {
|
||||||
|
if (listenerToTypesMap.containsKey(listener)) {
|
||||||
|
Log.w(TAG, "Tried to register a listener that is already registered. Ignore.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listenerToTypesMap.put(listener, trackDataTypes);
|
||||||
|
for (TrackDataType trackDataType : trackDataTypes) {
|
||||||
|
typeToListenersMap.get(trackDataType).add(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of {@link TrackDataListener}.
|
* Unregisters a listener.
|
||||||
*/
|
*
|
||||||
public int getNumberOfListeners() {
|
* @param listener the listener
|
||||||
return listenerToTypesMap.size();
|
*/
|
||||||
}
|
public void unregisterListener(TrackDataListener listener) {
|
||||||
|
EnumSet<TrackDataType> removedTypes = listenerToTypesMap.remove(listener);
|
||||||
|
if (removedTypes == null) {
|
||||||
|
Log.w(TAG, "Tried to unregister a listener that is not registered. Ignore.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Remove the listener from the typeToListenersMap
|
||||||
* Gets the track data types for a listener.
|
for (TrackDataType trackDataType : removedTypes) {
|
||||||
*
|
typeToListenersMap.get(trackDataType).remove(listener);
|
||||||
* @param listener the listener
|
}
|
||||||
*/
|
}
|
||||||
public EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
|
|
||||||
return listenerToTypesMap.get(listener);
|
/**
|
||||||
}
|
* Gets the number of {@link TrackDataListener}.
|
||||||
|
*/
|
||||||
/**
|
public int getNumberOfListeners() {
|
||||||
* Gets the listeners for a {@link TrackDataType}.
|
return listenerToTypesMap.size();
|
||||||
*
|
}
|
||||||
* @param type the type
|
|
||||||
*/
|
/**
|
||||||
public Set<TrackDataListener> getListeners(TrackDataType type) {
|
* Gets the track data types for a listener.
|
||||||
return typeToListenersMap.get(type);
|
*
|
||||||
}
|
* @param listener the listener
|
||||||
|
*/
|
||||||
/**
|
public EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
|
||||||
* Gets all the registered {@link TrackDataType}.
|
return listenerToTypesMap.get(listener);
|
||||||
*/
|
}
|
||||||
public EnumSet<TrackDataType> getRegisteredTrackDataTypes() {
|
|
||||||
EnumSet<TrackDataType> types = EnumSet.noneOf(TrackDataType.class);
|
/**
|
||||||
for (EnumSet<TrackDataType> value : listenerToTypesMap.values()) {
|
* Gets the listeners for a {@link TrackDataType}.
|
||||||
types.addAll(value);
|
*
|
||||||
|
* @param type the type
|
||||||
|
*/
|
||||||
|
public Set<TrackDataListener> getListeners(TrackDataType type) {
|
||||||
|
return typeToListenersMap.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the registered {@link TrackDataType}.
|
||||||
|
*/
|
||||||
|
public EnumSet<TrackDataType> getRegisteredTrackDataTypes() {
|
||||||
|
EnumSet<TrackDataType> types = EnumSet.noneOf(TrackDataType.class);
|
||||||
|
for (EnumSet<TrackDataType> value : listenerToTypesMap.values()) {
|
||||||
|
types.addAll(value);
|
||||||
|
}
|
||||||
|
return types;
|
||||||
}
|
}
|
||||||
return types;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ package com.google.android.apps.mytracks.content;
|
|||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
public enum TrackDataType {
|
public enum TrackDataType {
|
||||||
TRACKS_TABLE, // tracks table changes
|
TRACKS_TABLE, // tracks table changes
|
||||||
WAYPOINTS_TABLE, // waypoints table changes
|
WAYPOINTS_TABLE, // waypoints table changes
|
||||||
SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
|
SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
|
||||||
SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
|
SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
|
||||||
PREFERENCE // preference changes
|
PREFERENCE // preference changes
|
||||||
}
|
}
|
||||||
@@ -26,36 +26,36 @@ import android.provider.BaseColumns;
|
|||||||
*/
|
*/
|
||||||
public interface TrackPointsColumns extends BaseColumns {
|
public interface TrackPointsColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "trackpoints";
|
String TABLE_NAME = "trackpoints";
|
||||||
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/trackpoints");
|
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/trackpoints");
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.trackpoint";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.trackpoint";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.trackpoint";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.trackpoint";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
|
|
||||||
// Columns
|
// Columns
|
||||||
String TRACKID = "trackid";
|
String TRACKID = "trackid";
|
||||||
String LONGITUDE = "longitude";
|
String LONGITUDE = "longitude";
|
||||||
String LATITUDE = "latitude";
|
String LATITUDE = "latitude";
|
||||||
String TIME = "time";
|
String TIME = "time";
|
||||||
String ALTITUDE = "elevation";
|
String ALTITUDE = "elevation";
|
||||||
String ACCURACY = "accuracy";
|
String ACCURACY = "accuracy";
|
||||||
String SPEED = "speed";
|
String SPEED = "speed";
|
||||||
String BEARING = "bearing";
|
String BEARING = "bearing";
|
||||||
String SENSOR_HEARTRATE = "sensor_heartrate";
|
String SENSOR_HEARTRATE = "sensor_heartrate";
|
||||||
String SENSOR_CADENCE = "sensor_cadence";
|
String SENSOR_CADENCE = "sensor_cadence";
|
||||||
String SENSOR_POWER = "sensor_power";
|
String SENSOR_POWER = "sensor_power";
|
||||||
|
|
||||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
+ TRACKID + " INTEGER, "
|
+ TRACKID + " INTEGER, "
|
||||||
+ LONGITUDE + " INTEGER, "
|
+ LONGITUDE + " INTEGER, "
|
||||||
+ LATITUDE + " INTEGER, "
|
+ LATITUDE + " INTEGER, "
|
||||||
+ TIME + " INTEGER, "
|
+ TIME + " INTEGER, "
|
||||||
+ ALTITUDE + " FLOAT, "
|
+ ALTITUDE + " FLOAT, "
|
||||||
+ ACCURACY + " FLOAT, "
|
+ ACCURACY + " FLOAT, "
|
||||||
+ SPEED + " FLOAT, "
|
+ SPEED + " FLOAT, "
|
||||||
+ BEARING + " FLOAT, "
|
+ BEARING + " FLOAT, "
|
||||||
+ SENSOR_HEARTRATE + " FLOAT, "
|
+ SENSOR_HEARTRATE + " FLOAT, "
|
||||||
+ SENSOR_CADENCE + " FLOAT, "
|
+ SENSOR_CADENCE + " FLOAT, "
|
||||||
+ SENSOR_POWER + " FLOAT);";
|
+ SENSOR_POWER + " FLOAT);";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,80 +26,80 @@ import android.provider.BaseColumns;
|
|||||||
*/
|
*/
|
||||||
public interface TracksColumns extends BaseColumns {
|
public interface TracksColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "tracks";
|
String TABLE_NAME = "tracks";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks provider uri.
|
* Tracks provider uri.
|
||||||
*/
|
*/
|
||||||
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/tracks");
|
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/tracks");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track content type.
|
* Track content type.
|
||||||
*/
|
*/
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.track";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.track";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track id content type.
|
* Track id content type.
|
||||||
*/
|
*/
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.track";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.track";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks table default sort order.
|
* Tracks table default sort order.
|
||||||
*/
|
*/
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
|
|
||||||
// Columns
|
// Columns
|
||||||
String NAME = "name"; // track name
|
String NAME = "name"; // track name
|
||||||
String DESCRIPTION = "description"; // track description
|
String DESCRIPTION = "description"; // track description
|
||||||
String CATEGORY = "category"; // track activity type
|
String CATEGORY = "category"; // track activity type
|
||||||
String STARTID = "startid"; // first track point id
|
String STARTID = "startid"; // first track point id
|
||||||
String STOPID = "stopid"; // last track point id
|
String STOPID = "stopid"; // last track point id
|
||||||
String STARTTIME = "starttime"; // track start time
|
String STARTTIME = "starttime"; // track start time
|
||||||
String STOPTIME = "stoptime"; // track stop time
|
String STOPTIME = "stoptime"; // track stop time
|
||||||
String NUMPOINTS = "numpoints"; // number of track points
|
String NUMPOINTS = "numpoints"; // number of track points
|
||||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||||
String TOTALTIME = "totaltime"; // total time
|
String TOTALTIME = "totaltime"; // total time
|
||||||
String MOVINGTIME = "movingtime"; // moving time
|
String MOVINGTIME = "movingtime"; // moving time
|
||||||
String MINLAT = "minlat"; // minimum latitude
|
String MINLAT = "minlat"; // minimum latitude
|
||||||
String MAXLAT = "maxlat"; // maximum latitude
|
String MAXLAT = "maxlat"; // maximum latitude
|
||||||
String MINLON = "minlon"; // minimum longitude
|
String MINLON = "minlon"; // minimum longitude
|
||||||
String MAXLON = "maxlon"; // maximum longitude
|
String MAXLON = "maxlon"; // maximum longitude
|
||||||
String AVGSPEED = "avgspeed"; // average speed
|
String AVGSPEED = "avgspeed"; // average speed
|
||||||
|
|
||||||
// average moving speed
|
// average moving speed
|
||||||
String AVGMOVINGSPEED = "avgmovingspeed";
|
String AVGMOVINGSPEED = "avgmovingspeed";
|
||||||
String MAXSPEED = "maxspeed"; // maximum speed
|
String MAXSPEED = "maxspeed"; // maximum speed
|
||||||
String MINELEVATION = "minelevation"; // minimum elevation
|
String MINELEVATION = "minelevation"; // minimum elevation
|
||||||
String MAXELEVATION = "maxelevation"; // maximum elevation
|
String MAXELEVATION = "maxelevation"; // maximum elevation
|
||||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
||||||
String MINGRADE = "mingrade"; // minimum grade
|
String MINGRADE = "mingrade"; // minimum grade
|
||||||
String MAXGRADE = "maxgrade"; // maximum grade
|
String MAXGRADE = "maxgrade"; // maximum grade
|
||||||
String ICON = "icon"; // track activity type icon
|
String ICON = "icon"; // track activity type icon
|
||||||
|
|
||||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" // table
|
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" // table
|
||||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // id
|
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // id
|
||||||
+ NAME + " TEXT, " // name
|
+ NAME + " TEXT, " // name
|
||||||
+ DESCRIPTION + " TEXT, " // description
|
+ DESCRIPTION + " TEXT, " // description
|
||||||
+ CATEGORY + " TEXT, " // category
|
+ CATEGORY + " TEXT, " // category
|
||||||
+ STARTID + " INTEGER, " // start id
|
+ STARTID + " INTEGER, " // start id
|
||||||
+ STOPID + " INTEGER, " // stop id
|
+ STOPID + " INTEGER, " // stop id
|
||||||
+ STARTTIME + " INTEGER, " // start time
|
+ STARTTIME + " INTEGER, " // start time
|
||||||
+ STOPTIME + " INTEGER, " // stop time
|
+ STOPTIME + " INTEGER, " // stop time
|
||||||
+ NUMPOINTS + " INTEGER, " // num points
|
+ NUMPOINTS + " INTEGER, " // num points
|
||||||
+ TOTALDISTANCE + " FLOAT, " // total distance
|
+ TOTALDISTANCE + " FLOAT, " // total distance
|
||||||
+ TOTALTIME + " INTEGER, " // total time
|
+ TOTALTIME + " INTEGER, " // total time
|
||||||
+ MOVINGTIME + " INTEGER, " // moving time
|
+ MOVINGTIME + " INTEGER, " // moving time
|
||||||
+ MINLAT + " INTEGER, " // min latitude
|
+ MINLAT + " INTEGER, " // min latitude
|
||||||
+ MAXLAT + " INTEGER, " // max latitude
|
+ MAXLAT + " INTEGER, " // max latitude
|
||||||
+ MINLON + " INTEGER, " // min longitude
|
+ MINLON + " INTEGER, " // min longitude
|
||||||
+ MAXLON + " INTEGER, " // max longitude
|
+ MAXLON + " INTEGER, " // max longitude
|
||||||
+ AVGSPEED + " FLOAT, " // average speed
|
+ AVGSPEED + " FLOAT, " // average speed
|
||||||
+ AVGMOVINGSPEED + " FLOAT, " // average moving speed
|
+ AVGMOVINGSPEED + " FLOAT, " // average moving speed
|
||||||
+ MAXSPEED + " FLOAT, " // max speed
|
+ MAXSPEED + " FLOAT, " // max speed
|
||||||
+ MINELEVATION + " FLOAT, " // min elevation
|
+ MINELEVATION + " FLOAT, " // min elevation
|
||||||
+ MAXELEVATION + " FLOAT, " // max elevation
|
+ MAXELEVATION + " FLOAT, " // max elevation
|
||||||
+ ELEVATIONGAIN + " FLOAT, " // elevation gain
|
+ ELEVATIONGAIN + " FLOAT, " // elevation gain
|
||||||
+ MINGRADE + " FLOAT, " // min grade
|
+ MINGRADE + " FLOAT, " // min grade
|
||||||
+ MAXGRADE + " FLOAT, " // max grade
|
+ MAXGRADE + " FLOAT, " // max grade
|
||||||
+ ICON + " TEXT);"; // icon
|
+ ICON + " TEXT);"; // icon
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,228 +31,228 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
private long id = -1L;
|
@Override
|
||||||
private String name = "";
|
public Waypoint[] newArray(int size) {
|
||||||
private String description = "";
|
return new Waypoint[size];
|
||||||
private String category = "";
|
}
|
||||||
private String icon = "";
|
};
|
||||||
private long trackId = -1L;
|
private long id = -1L;
|
||||||
private WaypointType type = WaypointType.WAYPOINT;
|
private String name = "";
|
||||||
private double length = 0.0;
|
private String description = "";
|
||||||
private long duration = 0;
|
private String category = "";
|
||||||
private long startId = -1L;
|
private String icon = "";
|
||||||
private long stopId = -1L;
|
private long trackId = -1L;
|
||||||
private Location location = null;
|
private WaypointType type = WaypointType.WAYPOINT;
|
||||||
private TripStatistics tripStatistics = null;
|
private double length = 0.0;
|
||||||
private String photoUrl = "";
|
private long duration = 0;
|
||||||
|
private long startId = -1L;
|
||||||
|
private long stopId = -1L;
|
||||||
|
private Location location = null;
|
||||||
|
private TripStatistics tripStatistics = null;
|
||||||
|
private String photoUrl = "";
|
||||||
|
|
||||||
public Waypoint() {}
|
public Waypoint() {
|
||||||
|
|
||||||
public Waypoint(String name, String description, String category, String icon, long trackId,
|
|
||||||
WaypointType type, double length, long duration, long startId, long stopId, Location location,
|
|
||||||
TripStatistics tripStatistics, String photoUrl) {
|
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
|
||||||
this.category = category;
|
|
||||||
this.icon = icon;
|
|
||||||
this.trackId = trackId;
|
|
||||||
this.type = type;
|
|
||||||
this.length = length;
|
|
||||||
this.duration = duration;
|
|
||||||
this.startId = startId;
|
|
||||||
this.stopId = stopId;
|
|
||||||
this.location = location;
|
|
||||||
this.tripStatistics = tripStatistics;
|
|
||||||
this.photoUrl = photoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Waypoint(Parcel source) {
|
|
||||||
id = source.readLong();
|
|
||||||
name = source.readString();
|
|
||||||
description = source.readString();
|
|
||||||
category = source.readString();
|
|
||||||
icon = source.readString();
|
|
||||||
trackId = source.readLong();
|
|
||||||
type = WaypointType.values()[source.readInt()];
|
|
||||||
length = source.readDouble();
|
|
||||||
duration = source.readLong();
|
|
||||||
startId = source.readLong();
|
|
||||||
stopId = source.readLong();
|
|
||||||
|
|
||||||
ClassLoader classLoader = getClass().getClassLoader();
|
|
||||||
byte hasLocation = source.readByte();
|
|
||||||
if (hasLocation > 0) {
|
|
||||||
location = source.readParcelable(classLoader);
|
|
||||||
}
|
|
||||||
byte hasStats = source.readByte();
|
|
||||||
if (hasStats > 0) {
|
|
||||||
tripStatistics = source.readParcelable(classLoader);
|
|
||||||
}
|
|
||||||
photoUrl = source.readString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeLong(id);
|
|
||||||
dest.writeString(name);
|
|
||||||
dest.writeString(description);
|
|
||||||
dest.writeString(category);
|
|
||||||
dest.writeString(icon);
|
|
||||||
dest.writeLong(trackId);
|
|
||||||
dest.writeInt(type.ordinal());
|
|
||||||
dest.writeDouble(length);
|
|
||||||
dest.writeLong(duration);
|
|
||||||
dest.writeLong(startId);
|
|
||||||
dest.writeLong(stopId);
|
|
||||||
dest.writeByte(location == null ? (byte) 0 : (byte) 1);
|
|
||||||
if (location != null) {
|
|
||||||
dest.writeParcelable(location, 0);
|
|
||||||
}
|
|
||||||
dest.writeByte(tripStatistics == null ? (byte) 0 : (byte) 1);
|
|
||||||
if (tripStatistics != null) {
|
|
||||||
dest.writeParcelable(tripStatistics, 0);
|
|
||||||
}
|
|
||||||
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(String name, String description, String category, String icon, long trackId,
|
||||||
public Waypoint[] newArray(int size) {
|
WaypointType type, double length, long duration, long startId, long stopId, Location location,
|
||||||
return new Waypoint[size];
|
TripStatistics tripStatistics, String photoUrl) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.category = category;
|
||||||
|
this.icon = icon;
|
||||||
|
this.trackId = trackId;
|
||||||
|
this.type = type;
|
||||||
|
this.length = length;
|
||||||
|
this.duration = duration;
|
||||||
|
this.startId = startId;
|
||||||
|
this.stopId = stopId;
|
||||||
|
this.location = location;
|
||||||
|
this.tripStatistics = tripStatistics;
|
||||||
|
this.photoUrl = photoUrl;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
public long getId() {
|
private Waypoint(Parcel source) {
|
||||||
return id;
|
id = source.readLong();
|
||||||
}
|
name = source.readString();
|
||||||
|
description = source.readString();
|
||||||
|
category = source.readString();
|
||||||
|
icon = source.readString();
|
||||||
|
trackId = source.readLong();
|
||||||
|
type = WaypointType.values()[source.readInt()];
|
||||||
|
length = source.readDouble();
|
||||||
|
duration = source.readLong();
|
||||||
|
startId = source.readLong();
|
||||||
|
stopId = source.readLong();
|
||||||
|
|
||||||
public void setId(long id) {
|
ClassLoader classLoader = getClass().getClassLoader();
|
||||||
this.id = id;
|
byte hasLocation = source.readByte();
|
||||||
}
|
if (hasLocation > 0) {
|
||||||
|
location = source.readParcelable(classLoader);
|
||||||
|
}
|
||||||
|
byte hasStats = source.readByte();
|
||||||
|
if (hasStats > 0) {
|
||||||
|
tripStatistics = source.readParcelable(classLoader);
|
||||||
|
}
|
||||||
|
photoUrl = source.readString();
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
@Override
|
||||||
return name;
|
public int describeContents() {
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
@Override
|
||||||
this.name = name;
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
}
|
dest.writeLong(id);
|
||||||
|
dest.writeString(name);
|
||||||
|
dest.writeString(description);
|
||||||
|
dest.writeString(category);
|
||||||
|
dest.writeString(icon);
|
||||||
|
dest.writeLong(trackId);
|
||||||
|
dest.writeInt(type.ordinal());
|
||||||
|
dest.writeDouble(length);
|
||||||
|
dest.writeLong(duration);
|
||||||
|
dest.writeLong(startId);
|
||||||
|
dest.writeLong(stopId);
|
||||||
|
dest.writeByte(location == null ? (byte) 0 : (byte) 1);
|
||||||
|
if (location != null) {
|
||||||
|
dest.writeParcelable(location, 0);
|
||||||
|
}
|
||||||
|
dest.writeByte(tripStatistics == null ? (byte) 0 : (byte) 1);
|
||||||
|
if (tripStatistics != null) {
|
||||||
|
dest.writeParcelable(tripStatistics, 0);
|
||||||
|
}
|
||||||
|
dest.writeString(photoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public long getId() {
|
||||||
return description;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDescription(String description) {
|
public void setId(long id) {
|
||||||
this.description = description;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCategory() {
|
public String getName() {
|
||||||
return category;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCategory(String category) {
|
public void setName(String name) {
|
||||||
this.category = category;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIcon() {
|
public String getDescription() {
|
||||||
return icon;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIcon(String icon) {
|
public void setDescription(String description) {
|
||||||
this.icon = icon;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTrackId() {
|
public String getCategory() {
|
||||||
return trackId;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTrackId(long trackId) {
|
public void setCategory(String category) {
|
||||||
this.trackId = trackId;
|
this.category = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WaypointType getType() {
|
public String getIcon() {
|
||||||
return type;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(WaypointType type) {
|
public void setIcon(String icon) {
|
||||||
this.type = type;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getLength() {
|
public long getTrackId() {
|
||||||
return length;
|
return trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLength(double length) {
|
public void setTrackId(long trackId) {
|
||||||
this.length = length;
|
this.trackId = trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDuration() {
|
public WaypointType getType() {
|
||||||
return duration;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDuration(long duration) {
|
public void setType(WaypointType type) {
|
||||||
this.duration = duration;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStartId() {
|
public double getLength() {
|
||||||
return startId;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStartId(long startId) {
|
public void setLength(double length) {
|
||||||
this.startId = startId;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStopId() {
|
public long getDuration() {
|
||||||
return stopId;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStopId(long stopId) {
|
public void setDuration(long duration) {
|
||||||
this.stopId = stopId;
|
this.duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Location getLocation() {
|
public long getStartId() {
|
||||||
return location;
|
return startId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(Location location) {
|
public void setStartId(long startId) {
|
||||||
this.location = location;
|
this.startId = startId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TripStatistics getTripStatistics() {
|
public long getStopId() {
|
||||||
return tripStatistics;
|
return stopId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTripStatistics(TripStatistics tripStatistics) {
|
public void setStopId(long stopId) {
|
||||||
this.tripStatistics = tripStatistics;
|
this.stopId = stopId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPhotoUrl() {
|
public Location getLocation() {
|
||||||
return photoUrl;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uri getPhotoURI() {
|
public void setLocation(Location location) {
|
||||||
return Uri.parse(photoUrl);
|
this.location = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPhoto() {
|
public TripStatistics getTripStatistics() {
|
||||||
return photoUrl != null && !"".equals(photoUrl);
|
return tripStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPhotoUrl(String photoUrl) {
|
public void setTripStatistics(TripStatistics tripStatistics) {
|
||||||
this.photoUrl = photoUrl;
|
this.tripStatistics = tripStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPhotoUrl() {
|
||||||
|
return photoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhotoUrl(String photoUrl) {
|
||||||
|
this.photoUrl = photoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri getPhotoURI() {
|
||||||
|
return Uri.parse(photoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPhoto() {
|
||||||
|
return photoUrl != null && !"".equals(photoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WaypointType {
|
||||||
|
WAYPOINT, STATISTICS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-80
@@ -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,99 +27,97 @@ import android.os.Parcelable;
|
|||||||
*/
|
*/
|
||||||
public class WaypointCreationRequest implements Parcelable {
|
public class WaypointCreationRequest implements Parcelable {
|
||||||
|
|
||||||
private WaypointType type;
|
public final static WaypointCreationRequest DEFAULT_WAYPOINT = new WaypointCreationRequest(
|
||||||
// true if this marker contains the track statistics
|
WaypointType.WAYPOINT, false);
|
||||||
private boolean isTrackStatistics;
|
public final static WaypointCreationRequest DEFAULT_STATISTICS = new WaypointCreationRequest(
|
||||||
private String name;
|
WaypointType.STATISTICS, false);
|
||||||
private String category;
|
public final static WaypointCreationRequest DEFAULT_START_TRACK = new WaypointCreationRequest(
|
||||||
private String description;
|
WaypointType.STATISTICS, true);
|
||||||
private String iconUrl;
|
public static final Creator CREATOR = new Creator();
|
||||||
private String photoUrl;
|
private WaypointType type;
|
||||||
|
// true if this marker contains the track statistics
|
||||||
|
private boolean isTrackStatistics;
|
||||||
|
private String name;
|
||||||
|
private String category;
|
||||||
|
private String description;
|
||||||
|
private String iconUrl;
|
||||||
|
private String photoUrl;
|
||||||
|
|
||||||
public final static WaypointCreationRequest DEFAULT_WAYPOINT = new WaypointCreationRequest(
|
private WaypointCreationRequest(WaypointType type, boolean isTrackStatistics) {
|
||||||
WaypointType.WAYPOINT, false);
|
this(type, isTrackStatistics, null, null, null, null, null);
|
||||||
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) {
|
public WaypointCreationRequest(WaypointType type, boolean isTrackStatistics, String name,
|
||||||
this(type, isTrackStatistics, null, null, null, null, null);
|
String category, String description, String iconUrl, String photoUrl) {
|
||||||
}
|
this.type = type;
|
||||||
|
this.isTrackStatistics = isTrackStatistics;
|
||||||
public WaypointCreationRequest(WaypointType type, boolean isTrackStatistics, String name,
|
this.name = name;
|
||||||
String category, String description, String iconUrl, String photoUrl) {
|
this.category = category;
|
||||||
this.type = type;
|
this.description = description;
|
||||||
this.isTrackStatistics = isTrackStatistics;
|
this.iconUrl = iconUrl;
|
||||||
this.name = name;
|
this.photoUrl = photoUrl;
|
||||||
this.category = category;
|
}
|
||||||
this.description = description;
|
|
||||||
this.iconUrl = iconUrl;
|
|
||||||
this.photoUrl = photoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Creator implements Parcelable.Creator<WaypointCreationRequest> {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WaypointCreationRequest createFromParcel(Parcel source) {
|
public int describeContents() {
|
||||||
return new WaypointCreationRequest(
|
return 0;
|
||||||
WaypointType.values()[source.readInt()],
|
|
||||||
source.readByte() == 1,
|
|
||||||
source.readString(),
|
|
||||||
source.readString(),
|
|
||||||
source.readString(),
|
|
||||||
source.readString(),
|
|
||||||
source.readString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WaypointCreationRequest[] newArray(int size) {
|
@Override
|
||||||
return new WaypointCreationRequest[size];
|
public void writeToParcel(Parcel parcel, int arg1) {
|
||||||
|
parcel.writeInt(type.ordinal());
|
||||||
|
parcel.writeByte((byte) (isTrackStatistics ? 1 : 0));
|
||||||
|
parcel.writeString(name);
|
||||||
|
parcel.writeString(category);
|
||||||
|
parcel.writeString(description);
|
||||||
|
parcel.writeString(iconUrl);
|
||||||
|
parcel.writeString(photoUrl);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static final Creator CREATOR = new Creator();
|
public WaypointType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isTrackStatistics() {
|
||||||
public int describeContents() {
|
return isTrackStatistics;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public String getName() {
|
||||||
public void writeToParcel(Parcel parcel, int arg1) {
|
return name;
|
||||||
parcel.writeInt(type.ordinal());
|
}
|
||||||
parcel.writeByte((byte) (isTrackStatistics ? 1 : 0));
|
|
||||||
parcel.writeString(name);
|
|
||||||
parcel.writeString(category);
|
|
||||||
parcel.writeString(description);
|
|
||||||
parcel.writeString(iconUrl);
|
|
||||||
parcel.writeString(photoUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WaypointType getType() {
|
public String getCategory() {
|
||||||
return type;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTrackStatistics() {
|
public String getDescription() {
|
||||||
return isTrackStatistics;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getIconUrl() {
|
||||||
return name;
|
return iconUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCategory() {
|
public String getPhotoUrl() {
|
||||||
return category;
|
return photoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public static class Creator implements Parcelable.Creator<WaypointCreationRequest> {
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIconUrl() {
|
@Override
|
||||||
return iconUrl;
|
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 String getPhotoUrl() {
|
public WaypointCreationRequest[] newArray(int size) {
|
||||||
return photoUrl;
|
return new WaypointCreationRequest[size];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -26,78 +26,78 @@ import android.provider.BaseColumns;
|
|||||||
*/
|
*/
|
||||||
public interface WaypointsColumns extends BaseColumns {
|
public interface WaypointsColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "waypoints";
|
String TABLE_NAME = "waypoints";
|
||||||
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/waypoints");
|
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/waypoints");
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.waypoint";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.waypoint";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.waypoint";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.waypoint";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
|
|
||||||
// Columns
|
// Columns
|
||||||
String NAME = "name"; // waypoint name
|
String NAME = "name"; // waypoint name
|
||||||
String DESCRIPTION = "description"; // waypoint description
|
String DESCRIPTION = "description"; // waypoint description
|
||||||
String CATEGORY = "category"; // waypoint category
|
String CATEGORY = "category"; // waypoint category
|
||||||
String ICON = "icon"; // waypoint icon
|
String ICON = "icon"; // waypoint icon
|
||||||
String TRACKID = "trackid"; // track id
|
String TRACKID = "trackid"; // track id
|
||||||
String TYPE = "type"; // type
|
String TYPE = "type"; // type
|
||||||
String LENGTH = "length"; // length of the track (without smoothing)
|
String LENGTH = "length"; // length of the track (without smoothing)
|
||||||
String DURATION = "duration"; // total duration of the track (not from last waypoint)
|
String DURATION = "duration"; // total duration of the track (not from last waypoint)
|
||||||
String STARTTIME = "starttime"; // start time of the trip statistics
|
String STARTTIME = "starttime"; // start time of the trip statistics
|
||||||
String STARTID = "startid"; // start track point id
|
String STARTID = "startid"; // start track point id
|
||||||
String STOPID = "stopid"; // stop track point id
|
String STOPID = "stopid"; // stop track point id
|
||||||
|
|
||||||
String LONGITUDE = "longitude"; // longitude
|
String LONGITUDE = "longitude"; // longitude
|
||||||
String LATITUDE = "latitude"; // latitude
|
String LATITUDE = "latitude"; // latitude
|
||||||
String TIME = "time"; // time
|
String TIME = "time"; // time
|
||||||
String ALTITUDE = "elevation"; // altitude
|
String ALTITUDE = "elevation"; // altitude
|
||||||
String ACCURACY = "accuracy"; // accuracy
|
String ACCURACY = "accuracy"; // accuracy
|
||||||
String SPEED = "speed"; // speed
|
String SPEED = "speed"; // speed
|
||||||
String BEARING = "bearing"; // bearing
|
String BEARING = "bearing"; // bearing
|
||||||
|
|
||||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||||
String TOTALTIME = "totaltime"; // total time
|
String TOTALTIME = "totaltime"; // total time
|
||||||
String MOVINGTIME = "movingtime"; // moving time
|
String MOVINGTIME = "movingtime"; // moving time
|
||||||
String AVGSPEED = "avgspeed"; // average speed
|
String AVGSPEED = "avgspeed"; // average speed
|
||||||
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
||||||
String MAXSPEED = "maxspeed"; // max speed
|
String MAXSPEED = "maxspeed"; // max speed
|
||||||
String MINELEVATION = "minelevation"; // min elevation
|
String MINELEVATION = "minelevation"; // min elevation
|
||||||
String MAXELEVATION = "maxelevation"; // max elevation
|
String MAXELEVATION = "maxelevation"; // max elevation
|
||||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
||||||
String MINGRADE = "mingrade"; // min grade
|
String MINGRADE = "mingrade"; // min grade
|
||||||
String MAXGRADE = "maxgrade"; // max grade
|
String MAXGRADE = "maxgrade"; // max grade
|
||||||
|
|
||||||
String PHOTOURL = "photoUrl"; // url for the photo
|
String PHOTOURL = "photoUrl"; // url for the photo
|
||||||
|
|
||||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
+ NAME + " TEXT, "
|
+ NAME + " TEXT, "
|
||||||
+ DESCRIPTION + " TEXT, "
|
+ DESCRIPTION + " TEXT, "
|
||||||
+ CATEGORY + " TEXT, "
|
+ CATEGORY + " TEXT, "
|
||||||
+ ICON + " TEXT, "
|
+ ICON + " TEXT, "
|
||||||
+ TRACKID + " INTEGER, "
|
+ TRACKID + " INTEGER, "
|
||||||
+ TYPE + " INTEGER, "
|
+ TYPE + " INTEGER, "
|
||||||
+ LENGTH + " FLOAT, "
|
+ LENGTH + " FLOAT, "
|
||||||
+ DURATION + " INTEGER, "
|
+ DURATION + " INTEGER, "
|
||||||
+ STARTTIME + " INTEGER, "
|
+ STARTTIME + " INTEGER, "
|
||||||
+ STARTID + " INTEGER, "
|
+ STARTID + " INTEGER, "
|
||||||
+ STOPID + " INTEGER, "
|
+ STOPID + " INTEGER, "
|
||||||
+ LONGITUDE + " INTEGER, "
|
+ LONGITUDE + " INTEGER, "
|
||||||
+ LATITUDE + " INTEGER, "
|
+ LATITUDE + " INTEGER, "
|
||||||
+ TIME + " INTEGER, "
|
+ TIME + " INTEGER, "
|
||||||
+ ALTITUDE + " FLOAT, "
|
+ ALTITUDE + " FLOAT, "
|
||||||
+ ACCURACY + " FLOAT, "
|
+ ACCURACY + " FLOAT, "
|
||||||
+ SPEED + " FLOAT, "
|
+ SPEED + " FLOAT, "
|
||||||
+ BEARING + " FLOAT, "
|
+ BEARING + " FLOAT, "
|
||||||
+ TOTALDISTANCE + " FLOAT, "
|
+ TOTALDISTANCE + " FLOAT, "
|
||||||
+ TOTALTIME + " INTEGER, "
|
+ TOTALTIME + " INTEGER, "
|
||||||
+ MOVINGTIME + " INTEGER, "
|
+ MOVINGTIME + " INTEGER, "
|
||||||
+ AVGSPEED + " FLOAT, "
|
+ AVGSPEED + " FLOAT, "
|
||||||
+ AVGMOVINGSPEED + " FLOAT, "
|
+ AVGMOVINGSPEED + " FLOAT, "
|
||||||
+ MAXSPEED + " FLOAT, "
|
+ MAXSPEED + " FLOAT, "
|
||||||
+ MINELEVATION + " FLOAT, "
|
+ MINELEVATION + " FLOAT, "
|
||||||
+ MAXELEVATION + " FLOAT, "
|
+ MAXELEVATION + " FLOAT, "
|
||||||
+ ELEVATIONGAIN + " FLOAT, "
|
+ ELEVATIONGAIN + " FLOAT, "
|
||||||
+ MINGRADE + " FLOAT, "
|
+ MINGRADE + " FLOAT, "
|
||||||
+ MAXGRADE + " FLOAT, "
|
+ MAXGRADE + " FLOAT, "
|
||||||
+ PHOTOURL + " TEXT"
|
+ PHOTOURL + " TEXT"
|
||||||
+ ");";
|
+ ");";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,33 +16,33 @@
|
|||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final Dialog dialog = createDialog();
|
final Dialog dialog = createDialog();
|
||||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShow(DialogInterface dialogInterface) {
|
public void onShow(DialogInterface dialogInterface) {
|
||||||
DialogUtils.setDialogTitleDivider(getActivity(), dialog);
|
DialogUtils.setDialogTitleDivider(getActivity(), dialog);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Dialog createDialog();
|
protected abstract Dialog createDialog();
|
||||||
}
|
}
|
||||||
@@ -56,466 +56,466 @@ import java.util.EnumSet;
|
|||||||
*/
|
*/
|
||||||
public class ChartFragment extends Fragment implements TrackDataListener {
|
public class ChartFragment extends Fragment implements TrackDataListener {
|
||||||
|
|
||||||
public static final String CHART_FRAGMENT_TAG = "chartFragment";
|
public static final String CHART_FRAGMENT_TAG = "chartFragment";
|
||||||
|
|
||||||
private final ArrayList<double[]> pendingPoints = new ArrayList<>();
|
private final ArrayList<double[]> pendingPoints = new ArrayList<>();
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
|
|
||||||
// Stats gathered from the received data
|
// Stats gathered from the received data
|
||||||
private TripStatisticsUpdater tripStatisticsUpdater;
|
private TripStatisticsUpdater tripStatisticsUpdater;
|
||||||
private long startTime;
|
private long startTime;
|
||||||
|
|
||||||
private boolean metricUnits = true;
|
private boolean metricUnits = true;
|
||||||
private boolean reportSpeed = true;
|
private boolean reportSpeed = true;
|
||||||
private int recordingDistanceInterval = PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT;
|
private int recordingDistanceInterval = PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT;
|
||||||
|
|
||||||
// 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;
|
||||||
private ZoomControls zoomControls;
|
private ZoomControls zoomControls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A runnable that will enable/disable zoom controls and orange pointer as
|
* A runnable that will enable/disable zoom controls and orange pointer as
|
||||||
* appropriate and redraw.
|
* appropriate and redraw.
|
||||||
*/
|
|
||||||
private final Runnable updateChart = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!isResumed() || trackDataHub == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
|
||||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
|
||||||
chartView.setShowPointer(isSelectedTrackRecording());
|
|
||||||
chartView.invalidate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a chartView here to store data thus won't need to reload all the
|
|
||||||
* data on every onStart or onResume.
|
|
||||||
*/
|
*/
|
||||||
chartView = new ChartView(getActivity());
|
private final Runnable updateChart = new Runnable() {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(
|
|
||||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
View view = inflater.inflate(R.layout.chart, container, false);
|
|
||||||
zoomControls = view.findViewById(R.id.chart_zoom_controls);
|
|
||||||
zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
|
||||||
zoomIn();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
zoomOut();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
ViewGroup layout = getActivity().findViewById(R.id.chart_view_layout);
|
|
||||||
LayoutParams layoutParams = new LayoutParams(
|
|
||||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
|
||||||
layout.addView(chartView, layoutParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
resumeTrackDataHub();
|
|
||||||
checkChartSettings();
|
|
||||||
getActivity().runOnUiThread(updateChart);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
pauseTrackDataHub();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
ViewGroup layout = getActivity().findViewById(R.id.chart_view_layout);
|
|
||||||
layout.removeView(chartView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTrackUpdated(Track track) {
|
|
||||||
if (isResumed()) {
|
|
||||||
if (track == null || track.getTripStatistics() == null) {
|
|
||||||
startTime = -1L;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
startTime = track.getTripStatistics().getStartTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearTrackPoints() {
|
|
||||||
if (isResumed()) {
|
|
||||||
tripStatisticsUpdater = startTime != -1L ? new TripStatisticsUpdater(startTime) : null;
|
|
||||||
pendingPoints.clear();
|
|
||||||
chartView.reset();
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isResumed()) {
|
if (!isResumed() || trackDataHub == null) {
|
||||||
chartView.resetScroll();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||||
|
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||||
|
chartView.setShowPointer(isSelectedTrackRecording());
|
||||||
|
chartView.invalidate();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledInTrackPoint(Location location) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
if (isResumed()) {
|
super.onCreate(savedInstanceState);
|
||||||
double[] data = new double[ChartView.NUM_SERIES + 1];
|
|
||||||
fillDataPoint(location, data);
|
|
||||||
pendingPoints.add(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/*
|
||||||
public void onSampledOutTrackPoint(Location location) {
|
* Create a chartView here to store data thus won't need to reload all the
|
||||||
if (isResumed()) {
|
* data on every onStart or onResume.
|
||||||
fillDataPoint(location, null);
|
*/
|
||||||
|
chartView = new ChartView(getActivity());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSegmentSplit(Location location) {
|
public View onCreateView(
|
||||||
if (isResumed()) {
|
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
fillDataPoint(location, null);
|
View view = inflater.inflate(R.layout.chart, container, false);
|
||||||
|
zoomControls = view.findViewById(R.id.chart_zoom_controls);
|
||||||
|
zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
zoomIn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
zoomOut();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return view;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewTrackPointsDone() {
|
public void onStart() {
|
||||||
if (isResumed()) {
|
super.onStart();
|
||||||
chartView.addDataPoints(pendingPoints);
|
ViewGroup layout = getActivity().findViewById(R.id.chart_view_layout);
|
||||||
pendingPoints.clear();
|
LayoutParams layoutParams = new LayoutParams(
|
||||||
runOnUiThread(updateChart);
|
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||||
|
layout.addView(chartView, layoutParams);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearWaypoints() {
|
public void onResume() {
|
||||||
if (isResumed()) {
|
super.onResume();
|
||||||
chartView.clearWaypoints();
|
resumeTrackDataHub();
|
||||||
|
checkChartSettings();
|
||||||
|
getActivity().runOnUiThread(updateChart);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewWaypoint(Waypoint waypoint) {
|
public void onPause() {
|
||||||
if (isResumed() && waypoint != null && LocationUtils.isValidLocation(waypoint.getLocation())) {
|
super.onPause();
|
||||||
chartView.addWaypoint(waypoint);
|
pauseTrackDataHub();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewWaypointsDone() {
|
public void onStop() {
|
||||||
if (isResumed()) {
|
super.onStop();
|
||||||
runOnUiThread(updateChart);
|
ViewGroup layout = getActivity().findViewById(R.id.chart_view_layout);
|
||||||
|
layout.removeView(chartView);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onMetricUnitsChanged(boolean metric) {
|
public void onTrackUpdated(Track track) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
if (metricUnits == metric) {
|
if (track == null || track.getTripStatistics() == null) {
|
||||||
|
startTime = -1L;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startTime = track.getTripStatistics().getStartTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearTrackPoints() {
|
||||||
|
if (isResumed()) {
|
||||||
|
tripStatisticsUpdater = startTime != -1L ? new TripStatisticsUpdater(startTime) : null;
|
||||||
|
pendingPoints.clear();
|
||||||
|
chartView.reset();
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
chartView.resetScroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSampledInTrackPoint(Location location) {
|
||||||
|
if (isResumed()) {
|
||||||
|
double[] data = new double[ChartView.NUM_SERIES + 1];
|
||||||
|
fillDataPoint(location, data);
|
||||||
|
pendingPoints.add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSampledOutTrackPoint(Location location) {
|
||||||
|
if (isResumed()) {
|
||||||
|
fillDataPoint(location, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSegmentSplit(Location location) {
|
||||||
|
if (isResumed()) {
|
||||||
|
fillDataPoint(location, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewTrackPointsDone() {
|
||||||
|
if (isResumed()) {
|
||||||
|
chartView.addDataPoints(pendingPoints);
|
||||||
|
pendingPoints.clear();
|
||||||
|
runOnUiThread(updateChart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearWaypoints() {
|
||||||
|
if (isResumed()) {
|
||||||
|
chartView.clearWaypoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewWaypoint(Waypoint waypoint) {
|
||||||
|
if (isResumed() && waypoint != null && LocationUtils.isValidLocation(waypoint.getLocation())) {
|
||||||
|
chartView.addWaypoint(waypoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewWaypointsDone() {
|
||||||
|
if (isResumed()) {
|
||||||
|
runOnUiThread(updateChart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMetricUnitsChanged(boolean metric) {
|
||||||
|
if (isResumed()) {
|
||||||
|
if (metricUnits == metric) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
metricUnits = metric;
|
||||||
|
chartView.setMetricUnits(metricUnits);
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
chartView.requestLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
metricUnits = metric;
|
|
||||||
chartView.setMetricUnits(metricUnits);
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
chartView.requestLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onReportSpeedChanged(boolean speed) {
|
public boolean onReportSpeedChanged(boolean speed) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
if (reportSpeed == speed) {
|
if (reportSpeed == speed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
reportSpeed = speed;
|
||||||
|
chartView.setReportSpeed(reportSpeed);
|
||||||
|
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
||||||
|
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
||||||
|
setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed);
|
||||||
|
setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed);
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
chartView.requestLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
reportSpeed = speed;
|
|
||||||
chartView.setReportSpeed(reportSpeed);
|
|
||||||
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
|
||||||
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
|
||||||
setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed);
|
|
||||||
setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed);
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
chartView.requestLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onRecordingGpsAccuracy(int minRequiredAccuracy) {
|
public boolean onRecordingGpsAccuracy(int minRequiredAccuracy) {
|
||||||
// We don't care.
|
// We don't care.
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRecordingDistanceIntervalChanged(int value) {
|
|
||||||
if (isResumed()) {
|
|
||||||
if (recordingDistanceInterval == value) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
recordingDistanceInterval = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the chart settings.
|
|
||||||
*/
|
|
||||||
private void checkChartSettings() {
|
|
||||||
boolean needUpdate = false;
|
|
||||||
if (chartByDistance != PreferencesUtils.isChartByDistance(getActivity())) {
|
|
||||||
chartByDistance = !chartByDistance;
|
|
||||||
chartView.setChartByDistance(chartByDistance);
|
|
||||||
reloadTrackDataHub();
|
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
if (setSeriesEnabled(ChartView.ELEVATION_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_elevation_key, PreferencesUtils.CHART_SHOW_ELEVATION_DEFAULT))) {
|
|
||||||
needUpdate = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean chartShowSpeed = PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
@Override
|
||||||
if (setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed)) {
|
public boolean onRecordingDistanceIntervalChanged(int value) {
|
||||||
needUpdate = true;
|
if (isResumed()) {
|
||||||
}
|
if (recordingDistanceInterval == value) {
|
||||||
if (setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed)) {
|
return false;
|
||||||
needUpdate = true;
|
}
|
||||||
}
|
recordingDistanceInterval = value;
|
||||||
if (setSeriesEnabled(ChartView.POWER_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_power_key, PreferencesUtils.CHART_SHOW_POWER_DEFAULT))) {
|
return true;
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
if (setSeriesEnabled(ChartView.CADENCE_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_cadence_key, PreferencesUtils.CHART_SHOW_CADENCE_DEFAULT))) {
|
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
if (setSeriesEnabled(ChartView.HEART_RATE_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_heart_rate_key, PreferencesUtils.CHART_SHOW_HEART_RATE_DEFAULT))) {
|
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
if (needUpdate) {
|
|
||||||
chartView.postInvalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the series enabled value.
|
|
||||||
*
|
|
||||||
* @param index the series index
|
|
||||||
* @param value the value
|
|
||||||
* @return true if changed
|
|
||||||
*/
|
|
||||||
private boolean setSeriesEnabled(int index, boolean value) {
|
|
||||||
if (chartShow[index] != value) {
|
|
||||||
chartShow[index] = value;
|
|
||||||
chartView.setChartValueSeriesEnabled(index, value);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
|
||||||
* be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized void resumeTrackDataHub() {
|
|
||||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
|
||||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
|
||||||
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
|
||||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE, TrackDataType.PREFERENCE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
|
||||||
* be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized void pauseTrackDataHub() {
|
|
||||||
trackDataHub.unregisterTrackDataListener(this);
|
|
||||||
trackDataHub = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the selected track is recording. Needs to be synchronized
|
|
||||||
* because trackDataHub can be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized boolean isSelectedTrackRecording() {
|
|
||||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can
|
|
||||||
* be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized void reloadTrackDataHub() {
|
|
||||||
if (trackDataHub != null) {
|
|
||||||
trackDataHub.reloadDataForListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To zoom in.
|
|
||||||
*/
|
|
||||||
private void zoomIn() {
|
|
||||||
chartView.zoomIn();
|
|
||||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
|
||||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To zoom out.
|
|
||||||
*/
|
|
||||||
private void zoomOut() {
|
|
||||||
chartView.zoomOut();
|
|
||||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
|
||||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs a runnable on the UI thread if possible.
|
|
||||||
*
|
|
||||||
* @param runnable the runnable
|
|
||||||
*/
|
|
||||||
private void runOnUiThread(Runnable runnable) {
|
|
||||||
FragmentActivity fragmentActivity = getActivity();
|
|
||||||
if (fragmentActivity != null) {
|
|
||||||
fragmentActivity.runOnUiThread(runnable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a location, fill in a data point, an array of double[]. <br>
|
|
||||||
* data[0] = time/distance <br>
|
|
||||||
* data[1] = elevation <br>
|
|
||||||
* data[2] = speed <br>
|
|
||||||
* data[3] = pace <br>
|
|
||||||
* data[4] = heart rate <br>
|
|
||||||
* data[5] = cadence <br>
|
|
||||||
* data[6] = power <br>
|
|
||||||
*
|
|
||||||
* @param location the location
|
|
||||||
* @param data the data point to fill in, can be null
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void fillDataPoint(Location location, double[] data) {
|
|
||||||
double timeOrDistance = Double.NaN;
|
|
||||||
double elevation = Double.NaN;
|
|
||||||
double speed = Double.NaN;
|
|
||||||
double pace = Double.NaN;
|
|
||||||
double heartRate = Double.NaN;
|
|
||||||
double cadence = Double.NaN;
|
|
||||||
double power = Double.NaN;
|
|
||||||
|
|
||||||
if (tripStatisticsUpdater != null) {
|
|
||||||
tripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
|
|
||||||
TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
|
||||||
if (chartByDistance) {
|
|
||||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
|
||||||
if (!metricUnits) {
|
|
||||||
distance *= UnitConversions.KM_TO_MI;
|
|
||||||
}
|
}
|
||||||
timeOrDistance = distance;
|
return false;
|
||||||
} else {
|
|
||||||
timeOrDistance = tripStatistics.getTotalTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
elevation = tripStatisticsUpdater.getSmoothedElevation();
|
|
||||||
if (!metricUnits) {
|
|
||||||
elevation *= UnitConversions.M_TO_FT;
|
|
||||||
}
|
|
||||||
|
|
||||||
speed = tripStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
|
|
||||||
if (!metricUnits) {
|
|
||||||
speed *= UnitConversions.KM_TO_MI;
|
|
||||||
}
|
|
||||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
|
||||||
}
|
|
||||||
if (location instanceof SensorDataSetLocation && ((SensorDataSetLocation) location).getSensorDataSet() != null) {
|
|
||||||
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
|
|
||||||
if (sensorDataSet.hasHeartRate()) {
|
|
||||||
heartRate = sensorDataSet.getHeartRate();
|
|
||||||
}
|
|
||||||
if (sensorDataSet.hasCadence()) {
|
|
||||||
cadence = sensorDataSet.getCadence();
|
|
||||||
}
|
|
||||||
if (sensorDataSet.hasPower()) {
|
|
||||||
power = sensorDataSet.getPower();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data != null) {
|
/**
|
||||||
data[0] = timeOrDistance;
|
* Checks the chart settings.
|
||||||
data[1] = elevation;
|
*/
|
||||||
data[2] = speed;
|
private void checkChartSettings() {
|
||||||
data[3] = pace;
|
boolean needUpdate = false;
|
||||||
data[4] = heartRate;
|
if (chartByDistance != PreferencesUtils.isChartByDistance(getActivity())) {
|
||||||
data[5] = cadence;
|
chartByDistance = !chartByDistance;
|
||||||
data[6] = power;
|
chartView.setChartByDistance(chartByDistance);
|
||||||
|
reloadTrackDataHub();
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (setSeriesEnabled(ChartView.ELEVATION_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_elevation_key, PreferencesUtils.CHART_SHOW_ELEVATION_DEFAULT))) {
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean chartShowSpeed = PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
||||||
|
if (setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed)) {
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed)) {
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (setSeriesEnabled(ChartView.POWER_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_power_key, PreferencesUtils.CHART_SHOW_POWER_DEFAULT))) {
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (setSeriesEnabled(ChartView.CADENCE_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_cadence_key, PreferencesUtils.CHART_SHOW_CADENCE_DEFAULT))) {
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (setSeriesEnabled(ChartView.HEART_RATE_SERIES, PreferencesUtils.getBoolean(getActivity(), R.string.chart_show_heart_rate_key, PreferencesUtils.CHART_SHOW_HEART_RATE_DEFAULT))) {
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (needUpdate) {
|
||||||
|
chartView.postInvalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
/**
|
||||||
void setTripStatisticsUpdater(long time) {
|
* Sets the series enabled value.
|
||||||
tripStatisticsUpdater = new TripStatisticsUpdater(time);
|
*
|
||||||
}
|
* @param index the series index
|
||||||
|
* @param value the value
|
||||||
|
* @return true if changed
|
||||||
|
*/
|
||||||
|
private boolean setSeriesEnabled(int index, boolean value) {
|
||||||
|
if (chartShow[index] != value) {
|
||||||
|
chartShow[index] = value;
|
||||||
|
chartView.setChartValueSeriesEnabled(index, value);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
/**
|
||||||
void setChartView(ChartView view) {
|
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
chartView = view;
|
* be accessed by multiple threads.
|
||||||
}
|
*/
|
||||||
|
private synchronized void resumeTrackDataHub() {
|
||||||
|
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||||
|
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
||||||
|
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||||
|
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE, TrackDataType.PREFERENCE));
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
/**
|
||||||
void setMetricUnits(boolean value) {
|
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
metricUnits = value;
|
* be accessed by multiple threads.
|
||||||
}
|
*/
|
||||||
|
private synchronized void pauseTrackDataHub() {
|
||||||
|
trackDataHub.unregisterTrackDataListener(this);
|
||||||
|
trackDataHub = null;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
/**
|
||||||
void setReportSpeed(boolean value) {
|
* Returns true if the selected track is recording. Needs to be synchronized
|
||||||
reportSpeed = value;
|
* because trackDataHub can be accessed by multiple threads.
|
||||||
}
|
*/
|
||||||
|
private synchronized boolean isSelectedTrackRecording() {
|
||||||
|
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
/**
|
||||||
void setChartByDistance(boolean value) {
|
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
chartByDistance = value;
|
* be accessed by multiple threads.
|
||||||
}
|
*/
|
||||||
|
private synchronized void reloadTrackDataHub() {
|
||||||
|
if (trackDataHub != null) {
|
||||||
|
trackDataHub.reloadDataForListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To zoom in.
|
||||||
|
*/
|
||||||
|
private void zoomIn() {
|
||||||
|
chartView.zoomIn();
|
||||||
|
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||||
|
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To zoom out.
|
||||||
|
*/
|
||||||
|
private void zoomOut() {
|
||||||
|
chartView.zoomOut();
|
||||||
|
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||||
|
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a runnable on the UI thread if possible.
|
||||||
|
*
|
||||||
|
* @param runnable the runnable
|
||||||
|
*/
|
||||||
|
private void runOnUiThread(Runnable runnable) {
|
||||||
|
FragmentActivity fragmentActivity = getActivity();
|
||||||
|
if (fragmentActivity != null) {
|
||||||
|
fragmentActivity.runOnUiThread(runnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a location, fill in a data point, an array of double[]. <br>
|
||||||
|
* data[0] = time/distance <br>
|
||||||
|
* data[1] = elevation <br>
|
||||||
|
* data[2] = speed <br>
|
||||||
|
* data[3] = pace <br>
|
||||||
|
* data[4] = heart rate <br>
|
||||||
|
* data[5] = cadence <br>
|
||||||
|
* data[6] = power <br>
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param data the data point to fill in, can be null
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void fillDataPoint(Location location, double[] data) {
|
||||||
|
double timeOrDistance = Double.NaN;
|
||||||
|
double elevation = Double.NaN;
|
||||||
|
double speed = Double.NaN;
|
||||||
|
double pace = Double.NaN;
|
||||||
|
double heartRate = Double.NaN;
|
||||||
|
double cadence = Double.NaN;
|
||||||
|
double power = Double.NaN;
|
||||||
|
|
||||||
|
if (tripStatisticsUpdater != null) {
|
||||||
|
tripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
|
||||||
|
TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||||
|
if (chartByDistance) {
|
||||||
|
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||||
|
if (!metricUnits) {
|
||||||
|
distance *= UnitConversions.KM_TO_MI;
|
||||||
|
}
|
||||||
|
timeOrDistance = distance;
|
||||||
|
} else {
|
||||||
|
timeOrDistance = tripStatistics.getTotalTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
elevation = tripStatisticsUpdater.getSmoothedElevation();
|
||||||
|
if (!metricUnits) {
|
||||||
|
elevation *= UnitConversions.M_TO_FT;
|
||||||
|
}
|
||||||
|
|
||||||
|
speed = tripStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
|
||||||
|
if (!metricUnits) {
|
||||||
|
speed *= UnitConversions.KM_TO_MI;
|
||||||
|
}
|
||||||
|
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||||
|
}
|
||||||
|
if (location instanceof SensorDataSetLocation && ((SensorDataSetLocation) location).getSensorDataSet() != null) {
|
||||||
|
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
|
||||||
|
if (sensorDataSet.hasHeartRate()) {
|
||||||
|
heartRate = sensorDataSet.getHeartRate();
|
||||||
|
}
|
||||||
|
if (sensorDataSet.hasCadence()) {
|
||||||
|
cadence = sensorDataSet.getCadence();
|
||||||
|
}
|
||||||
|
if (sensorDataSet.hasPower()) {
|
||||||
|
power = sensorDataSet.getPower();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null) {
|
||||||
|
data[0] = timeOrDistance;
|
||||||
|
data[1] = elevation;
|
||||||
|
data[2] = speed;
|
||||||
|
data[3] = pace;
|
||||||
|
data[4] = heartRate;
|
||||||
|
data[5] = cadence;
|
||||||
|
data[6] = power;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setTripStatisticsUpdater(long time) {
|
||||||
|
tripStatisticsUpdater = new TripStatisticsUpdater(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setChartView(ChartView view) {
|
||||||
|
chartView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setMetricUnits(boolean value) {
|
||||||
|
metricUnits = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setReportSpeed(boolean value) {
|
||||||
|
reportSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setChartByDistance(boolean value) {
|
||||||
|
chartByDistance = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-103
@@ -46,112 +46,110 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
/**
|
public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
|
||||||
* Interface for caller of this dialog fragment.
|
private static final String KEY_CATEGORY = "category";
|
||||||
*
|
private ChooseActivityTypeCaller caller;
|
||||||
* @author apoorvn
|
|
||||||
*/
|
public static ChooseActivityTypeDialogFragment newInstance(String category) {
|
||||||
public interface ChooseActivityTypeCaller {
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString(KEY_CATEGORY, category);
|
||||||
|
|
||||||
|
ChooseActivityTypeDialogFragment fragment = new ChooseActivityTypeDialogFragment();
|
||||||
|
fragment.setArguments(bundle);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dialog getDialog(final Activity activity, final String category, final ChooseActivityTypeCaller caller) {
|
||||||
|
View view = activity.getLayoutInflater().inflate(R.layout.choose_activity_type, null);
|
||||||
|
GridView gridView = view.findViewById(R.id.choose_activity_type_grid_view);
|
||||||
|
|
||||||
|
List<Integer> imageIds = new ArrayList<>();
|
||||||
|
for (String iconValue : TrackIconUtils.getAllIconValues()) {
|
||||||
|
imageIds.add(TrackIconUtils.getIconDrawable(iconValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
Options options = new BitmapFactory.Options();
|
||||||
|
options.inJustDecodeBounds = true;
|
||||||
|
BitmapFactory.decodeResource(activity.getResources(), R.drawable.ic_track_airplane, options);
|
||||||
|
int padding = 32;
|
||||||
|
int width = options.outWidth + 2 * padding;
|
||||||
|
int height = options.outHeight + 2 * padding;
|
||||||
|
gridView.setColumnWidth(width);
|
||||||
|
|
||||||
|
final ChooseActivityTypeImageAdapter imageAdapter = new ChooseActivityTypeImageAdapter(activity, imageIds, width, height, padding);
|
||||||
|
gridView.setAdapter(imageAdapter);
|
||||||
|
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(activity).setTitle(R.string.track_edit_activity_type_hint).setView(view).create();
|
||||||
|
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||||
|
@Override
|
||||||
|
public void onShow(DialogInterface dialog) {
|
||||||
|
int position = getPosition(activity, category);
|
||||||
|
if (position != -1) {
|
||||||
|
imageAdapter.setSelected(position);
|
||||||
|
imageAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
DialogUtils.setDialogTitleDivider(activity, alertDialog);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gridView.setOnItemClickListener(new OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||||
|
caller.onChooseActivityTypeDone(TrackIconUtils.getAllIconValues().get(position));
|
||||||
|
alertDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return alertDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getPosition(Activity activity, String category) {
|
||||||
|
if (category == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
String iconValue = TrackIconUtils.getIconValue(activity, category);
|
||||||
|
if (iconValue.equals("")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
List<String> iconValues = TrackIconUtils.getAllIconValues();
|
||||||
|
for (int i = 0; i < iconValues.size(); i++) {
|
||||||
|
if (iconValues.get(i).equals(iconValue)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateWeightContainer(View weightContainer, int position) {
|
||||||
|
boolean showWeight = position == 0 || position == 1 || position == 2;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when choose activity type is done.
|
* Interface for caller of this dialog fragment.
|
||||||
|
*
|
||||||
|
* @author apoorvn
|
||||||
*/
|
*/
|
||||||
void onChooseActivityTypeDone(String iconValue);
|
public interface ChooseActivityTypeCaller {
|
||||||
}
|
|
||||||
|
|
||||||
public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
|
/**
|
||||||
|
* Called when choose activity type is done.
|
||||||
private static final String KEY_CATEGORY = "category";
|
*/
|
||||||
|
void onChooseActivityTypeDone(String iconValue);
|
||||||
private ChooseActivityTypeCaller caller;
|
|
||||||
|
|
||||||
public static ChooseActivityTypeDialogFragment newInstance(String category) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString(KEY_CATEGORY, category);
|
|
||||||
|
|
||||||
ChooseActivityTypeDialogFragment fragment = new ChooseActivityTypeDialogFragment();
|
|
||||||
fragment.setArguments(bundle);
|
|
||||||
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) {
|
|
||||||
View view = activity.getLayoutInflater().inflate(R.layout.choose_activity_type, null);
|
|
||||||
GridView gridView = view.findViewById(R.id.choose_activity_type_grid_view);
|
|
||||||
|
|
||||||
List<Integer> imageIds = new ArrayList<>();
|
|
||||||
for (String iconValue : TrackIconUtils.getAllIconValues()) {
|
|
||||||
imageIds.add(TrackIconUtils.getIconDrawable(iconValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
Options options = new BitmapFactory.Options();
|
|
||||||
options.inJustDecodeBounds = true;
|
|
||||||
BitmapFactory.decodeResource(activity.getResources(), R.drawable.ic_track_airplane, options);
|
|
||||||
int padding = 32;
|
|
||||||
int width = options.outWidth + 2 * padding;
|
|
||||||
int height = options.outHeight + 2 * padding;
|
|
||||||
gridView.setColumnWidth(width);
|
|
||||||
|
|
||||||
final ChooseActivityTypeImageAdapter imageAdapter = new ChooseActivityTypeImageAdapter(activity, imageIds, width, height, padding);
|
|
||||||
gridView.setAdapter(imageAdapter);
|
|
||||||
|
|
||||||
final AlertDialog alertDialog = new AlertDialog.Builder(activity).setTitle(R.string.track_edit_activity_type_hint).setView(view).create();
|
|
||||||
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
|
||||||
@Override
|
|
||||||
public void onShow(DialogInterface dialog) {
|
|
||||||
int position = getPosition(activity, category);
|
|
||||||
if (position != -1) {
|
|
||||||
imageAdapter.setSelected(position);
|
|
||||||
imageAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
DialogUtils.setDialogTitleDivider(activity, alertDialog);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
gridView.setOnItemClickListener(new OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
|
||||||
caller.onChooseActivityTypeDone(TrackIconUtils.getAllIconValues().get(position));
|
|
||||||
alertDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return alertDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getPosition(Activity activity, String category) {
|
|
||||||
if (category == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
String iconValue = TrackIconUtils.getIconValue(activity, category);
|
|
||||||
if (iconValue.equals("")) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
List<String> iconValues = TrackIconUtils.getAllIconValues();
|
|
||||||
for (int i = 0; i < iconValues.size(); i++) {
|
|
||||||
if (iconValues.get(i).equals(iconValue)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void updateWeightContainer(View weightContainer, int position) {
|
|
||||||
boolean showWeight = position == 0 || position == 1 || position == 2;
|
|
||||||
weightContainer.setVisibility(showWeight ? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-57
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,62 +34,62 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final List<Integer> imageIds;
|
private final List<Integer> imageIds;
|
||||||
private final int width;
|
private final int width;
|
||||||
private final int height;
|
private final int height;
|
||||||
private final int padding;
|
private final int padding;
|
||||||
private int selected = -1;
|
private int selected = -1;
|
||||||
|
|
||||||
public ChooseActivityTypeImageAdapter(
|
public ChooseActivityTypeImageAdapter(
|
||||||
Context context, List<Integer> imageIds, int width, int height, int padding) {
|
Context context, List<Integer> imageIds, int width, int height, int padding) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.imageIds = imageIds;
|
this.imageIds = imageIds;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return imageIds.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getItem(int position) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSelected() {
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelected(int position) {
|
|
||||||
selected = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
ImageView imageView;
|
|
||||||
if (convertView == null) {
|
|
||||||
imageView = new ImageView(context);
|
|
||||||
} else {
|
|
||||||
imageView = (ImageView) convertView;
|
|
||||||
}
|
}
|
||||||
if (position == selected) {
|
|
||||||
imageView.setBackgroundResource(R.drawable.list_selector_background_transition_holo_dark);
|
@Override
|
||||||
} else {
|
public int getCount() {
|
||||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
return imageIds.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSelected() {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelected(int position) {
|
||||||
|
selected = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
ImageView imageView;
|
||||||
|
if (convertView == null) {
|
||||||
|
imageView = new ImageView(context);
|
||||||
|
} else {
|
||||||
|
imageView = (ImageView) convertView;
|
||||||
|
}
|
||||||
|
if (position == selected) {
|
||||||
|
imageView.setBackgroundResource(R.drawable.list_selector_background_transition_holo_dark);
|
||||||
|
} else {
|
||||||
|
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
imageView.setImageResource(imageIds.get(position));
|
||||||
|
imageView.setMinimumHeight(height);
|
||||||
|
imageView.setMinimumWidth(width);
|
||||||
|
imageView.setPadding(padding, padding, padding, padding);
|
||||||
|
return imageView;
|
||||||
}
|
}
|
||||||
imageView.setImageResource(imageIds.get(position));
|
|
||||||
imageView.setMinimumHeight(height);
|
|
||||||
imageView.setMinimumWidth(width);
|
|
||||||
imageView.setPadding(padding, padding, padding, padding);
|
|
||||||
return imageView;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-55
@@ -31,67 +31,66 @@ import com.google.android.maps.mytracks.R;
|
|||||||
*/
|
*/
|
||||||
public class ConfirmDeleteDialogFragment extends AbstractDialogFragment {
|
public class ConfirmDeleteDialogFragment extends AbstractDialogFragment {
|
||||||
|
|
||||||
/**
|
public static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
||||||
* Interface for caller of this dialog fragment.
|
private static final String KEY_TRACK_IDS = "trackIds";
|
||||||
*
|
private ConfirmDeleteCaller caller;
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public interface ConfirmDeleteCaller {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when confirm delete is done.
|
* Create a new instance.
|
||||||
*
|
*
|
||||||
* @param trackIds list of track ids to delete. To delete all, set to size 1
|
* @param trackIds list of track ids to delete. To delete all, set to size 1
|
||||||
* with trackIds[0] == -1L
|
* with trackIds[0] == -1L
|
||||||
*/
|
*/
|
||||||
void onConfirmDeleteDone(long[] trackIds);
|
public static ConfirmDeleteDialogFragment newInstance(long[] trackIds) {
|
||||||
}
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
|
||||||
|
|
||||||
public static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
|
||||||
private static final String KEY_TRACK_IDS = "trackIds";
|
deleteTrackDialogFragment.setArguments(bundle);
|
||||||
|
return deleteTrackDialogFragment;
|
||||||
/**
|
|
||||||
* Create a new instance.
|
|
||||||
*
|
|
||||||
* @param trackIds list of track ids to delete. To delete all, set to size 1
|
|
||||||
* with trackIds[0] == -1L
|
|
||||||
*/
|
|
||||||
public static ConfirmDeleteDialogFragment newInstance(long[] trackIds) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
|
|
||||||
|
|
||||||
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
|
|
||||||
deleteTrackDialogFragment.setArguments(bundle);
|
|
||||||
return deleteTrackDialogFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConfirmDeleteCaller caller;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
try {
|
|
||||||
caller = (ConfirmDeleteCaller) context;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(context + " must implement " + ConfirmDeleteCaller.class.getSimpleName());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog createDialog() {
|
public void onAttach(Context context) {
|
||||||
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
super.onAttach(context);
|
||||||
int titleId;
|
try {
|
||||||
int messageId;
|
caller = (ConfirmDeleteCaller) context;
|
||||||
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title
|
} catch (ClassCastException e) {
|
||||||
: R.string.track_delete_one_confirm_title;
|
throw new ClassCastException(context + " must implement " + ConfirmDeleteCaller.class.getSimpleName());
|
||||||
messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message
|
}
|
||||||
: R.string.track_delete_one_confirm_message;
|
}
|
||||||
return DialogUtils.createConfirmationDialog(
|
|
||||||
getActivity(), titleId, getString(messageId), new DialogInterface.OnClickListener() {
|
@Override
|
||||||
@Override
|
protected Dialog createDialog() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
||||||
caller.onConfirmDeleteDone(trackIds);
|
int titleId;
|
||||||
}
|
int messageId;
|
||||||
});
|
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title
|
||||||
}
|
: R.string.track_delete_one_confirm_title;
|
||||||
|
messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message
|
||||||
|
: R.string.track_delete_one_confirm_message;
|
||||||
|
return DialogUtils.createConfirmationDialog(
|
||||||
|
getActivity(), titleId, getString(messageId), new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
caller.onConfirmDeleteDone(trackIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+66
-67
@@ -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,75 +35,74 @@ import com.google.android.maps.mytracks.R;
|
|||||||
*/
|
*/
|
||||||
public class DeleteMarkerDialogFragment extends AbstractDialogFragment {
|
public class DeleteMarkerDialogFragment extends AbstractDialogFragment {
|
||||||
|
|
||||||
/**
|
public static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
||||||
* Interface for caller of this dialog fragment.
|
private static final String KEY_MARKER_IDS = "markerIds";
|
||||||
*
|
private DeleteMarkerCaller caller;
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
public static DeleteMarkerDialogFragment newInstance(long[] markerIds) {
|
||||||
public interface DeleteMarkerCaller {
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putLongArray(KEY_MARKER_IDS, markerIds);
|
||||||
|
|
||||||
|
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment();
|
||||||
|
deleteMarkerDialogFragment.setArguments(bundle);
|
||||||
|
return deleteMarkerDialogFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
try {
|
||||||
|
caller = (DeleteMarkerCaller) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(context + " must implement " + DeleteMarkerCaller.class.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dialog createDialog() {
|
||||||
|
final FragmentActivity fragmentActivity = getActivity();
|
||||||
|
final long[] markerIds = getArguments().getLongArray(KEY_MARKER_IDS);
|
||||||
|
int titleId;
|
||||||
|
int messageId;
|
||||||
|
if (markerIds.length == 1 && markerIds[0] == -1L) {
|
||||||
|
titleId = R.string.generic_delete_all_confirm_title;
|
||||||
|
messageId = R.string.marker_delete_all_confirm_message;
|
||||||
|
} else {
|
||||||
|
titleId = markerIds.length > 1 ? R.string.generic_delete_selected_confirm_title
|
||||||
|
: R.string.marker_delete_one_confirm_title;
|
||||||
|
messageId = markerIds.length > 1 ? R.string.marker_delete_multiple_confirm_message
|
||||||
|
: R.string.marker_delete_one_confirm_message;
|
||||||
|
}
|
||||||
|
return DialogUtils.createConfirmationDialog(
|
||||||
|
fragmentActivity, titleId, getString(messageId), new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(
|
||||||
|
fragmentActivity);
|
||||||
|
for (long markerId : markerIds) {
|
||||||
|
contentProviderUtils.deleteWaypoint(fragmentActivity,
|
||||||
|
markerId, new DescriptionGeneratorImpl(fragmentActivity));
|
||||||
|
}
|
||||||
|
caller.onDeleteMarkerDone();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when delete marker is done.
|
* Interface for caller of this dialog fragment.
|
||||||
|
*
|
||||||
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
void onDeleteMarkerDone();
|
public interface DeleteMarkerCaller {
|
||||||
}
|
|
||||||
|
|
||||||
public static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
/**
|
||||||
private static final String KEY_MARKER_IDS = "markerIds";
|
* Called when delete marker is done.
|
||||||
|
*/
|
||||||
public static DeleteMarkerDialogFragment newInstance(long[] markerIds) {
|
void onDeleteMarkerDone();
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putLongArray(KEY_MARKER_IDS, markerIds);
|
|
||||||
|
|
||||||
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment();
|
|
||||||
deleteMarkerDialogFragment.setArguments(bundle);
|
|
||||||
return deleteMarkerDialogFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DeleteMarkerCaller caller;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
try {
|
|
||||||
caller = (DeleteMarkerCaller) context;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(context + " must implement " + DeleteMarkerCaller.class.getSimpleName());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog createDialog() {
|
|
||||||
final FragmentActivity fragmentActivity = getActivity();
|
|
||||||
final long[] markerIds = getArguments().getLongArray(KEY_MARKER_IDS);
|
|
||||||
int titleId;
|
|
||||||
int messageId;
|
|
||||||
if (markerIds.length == 1 && markerIds[0] == -1L) {
|
|
||||||
titleId = R.string.generic_delete_all_confirm_title;
|
|
||||||
messageId = R.string.marker_delete_all_confirm_message;
|
|
||||||
} else {
|
|
||||||
titleId = markerIds.length > 1 ? R.string.generic_delete_selected_confirm_title
|
|
||||||
: R.string.marker_delete_one_confirm_title;
|
|
||||||
messageId = markerIds.length > 1 ? R.string.marker_delete_multiple_confirm_message
|
|
||||||
: R.string.marker_delete_one_confirm_message;
|
|
||||||
}
|
|
||||||
return DialogUtils.createConfirmationDialog(
|
|
||||||
fragmentActivity, titleId, getString(messageId), new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(
|
|
||||||
fragmentActivity);
|
|
||||||
for (long markerId : markerIds) {
|
|
||||||
contentProviderUtils.deleteWaypoint(fragmentActivity,
|
|
||||||
markerId, new DescriptionGeneratorImpl(fragmentActivity));
|
|
||||||
}
|
|
||||||
caller.onDeleteMarkerDone();
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+56
-58
@@ -34,67 +34,65 @@ import com.google.android.maps.mytracks.R;
|
|||||||
*/
|
*/
|
||||||
public class FileTypeDialogFragment extends AbstractDialogFragment {
|
public class FileTypeDialogFragment extends AbstractDialogFragment {
|
||||||
|
|
||||||
/**
|
public static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
||||||
* Interface for caller of this dialog fragment.
|
private static final String KEY_MENU_ID = "menuId";
|
||||||
*
|
private static final String KEY_TITLE_ID = "titleId";
|
||||||
* @author Jimmy Shih
|
private static final String KEY_OPTION_ID = "optionId";
|
||||||
*/
|
private FileTypeCaller caller;
|
||||||
public interface FileTypeCaller {
|
|
||||||
|
public static FileTypeDialogFragment newInstance(int menuId, int titleId, int optionId) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putInt(KEY_MENU_ID, menuId);
|
||||||
|
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||||
|
bundle.putInt(KEY_OPTION_ID, optionId);
|
||||||
|
|
||||||
|
FileTypeDialogFragment fileTypeDialogFragment = new FileTypeDialogFragment();
|
||||||
|
fileTypeDialogFragment.setArguments(bundle);
|
||||||
|
return fileTypeDialogFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
try {
|
||||||
|
caller = (FileTypeCaller) context;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(context + " must implement " + FileTypeCaller.class.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dialog createDialog() {
|
||||||
|
int optionId = getArguments().getInt(KEY_OPTION_ID);
|
||||||
|
final int titleId = getArguments().getInt(KEY_TITLE_ID);
|
||||||
|
final int menuId = getArguments().getInt(KEY_MENU_ID);
|
||||||
|
TrackFileFormat[] trackFileFormats = TrackFileFormat.values();
|
||||||
|
String[] choices = new String[trackFileFormats.length];
|
||||||
|
for (int i = 0; i < choices.length; i++) {
|
||||||
|
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
||||||
|
choices[i] = getString(optionId, trackFileFormat.name(),
|
||||||
|
FileUtils.getPathDisplayName(trackFileFormat.getExtension()));
|
||||||
|
}
|
||||||
|
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
|
||||||
|
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||||
|
caller.onFileTypeDone(menuId, TrackFileFormat.values()[position]);
|
||||||
|
}
|
||||||
|
}).setSingleChoiceItems(choices, 0, null).setTitle(titleId).create();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when file type selection is done.
|
* Interface for caller of this dialog fragment.
|
||||||
|
*
|
||||||
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat);
|
public interface FileTypeCaller {
|
||||||
}
|
|
||||||
|
|
||||||
public static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
/**
|
||||||
|
* Called when file type selection is done.
|
||||||
private static final String KEY_MENU_ID = "menuId";
|
*/
|
||||||
private static final String KEY_TITLE_ID = "titleId";
|
void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat);
|
||||||
private static final String KEY_OPTION_ID = "optionId";
|
|
||||||
|
|
||||||
public static FileTypeDialogFragment newInstance(int menuId, int titleId, int optionId) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putInt(KEY_MENU_ID, menuId);
|
|
||||||
bundle.putInt(KEY_TITLE_ID, titleId);
|
|
||||||
bundle.putInt(KEY_OPTION_ID, optionId);
|
|
||||||
|
|
||||||
FileTypeDialogFragment fileTypeDialogFragment = new FileTypeDialogFragment();
|
|
||||||
fileTypeDialogFragment.setArguments(bundle);
|
|
||||||
return fileTypeDialogFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileTypeCaller caller;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
try {
|
|
||||||
caller = (FileTypeCaller) context;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(context + " must implement " + FileTypeCaller.class.getSimpleName());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog createDialog() {
|
|
||||||
int optionId = getArguments().getInt(KEY_OPTION_ID);
|
|
||||||
final int titleId = getArguments().getInt(KEY_TITLE_ID);
|
|
||||||
final int menuId = getArguments().getInt(KEY_MENU_ID);
|
|
||||||
TrackFileFormat[] trackFileFormats = TrackFileFormat.values();
|
|
||||||
String[] choices = new String[trackFileFormats.length];
|
|
||||||
for (int i = 0; i < choices.length; i++) {
|
|
||||||
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
|
||||||
choices[i] = getString(optionId, trackFileFormat.name(),
|
|
||||||
FileUtils.getPathDisplayName(trackFileFormat.getExtension()));
|
|
||||||
}
|
|
||||||
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
|
|
||||||
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
|
||||||
caller.onFileTypeDone(menuId, TrackFileFormat.values()[position]);
|
|
||||||
}
|
|
||||||
}).setSingleChoiceItems(choices, 0, null).setTitle(titleId).create();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+70
-69
@@ -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.
|
||||||
*
|
*
|
||||||
@@ -33,74 +34,74 @@ import androidx.fragment.app.FragmentActivity;
|
|||||||
*/
|
*/
|
||||||
public class FrequencyDialogFragment extends AbstractDialogFragment {
|
public class FrequencyDialogFragment extends AbstractDialogFragment {
|
||||||
|
|
||||||
public static final String FREQUENCY_DIALOG_TAG = "frequencyDialog";
|
public static final String FREQUENCY_DIALOG_TAG = "frequencyDialog";
|
||||||
|
|
||||||
private static final String KEY_PREFERENCE_ID = "preferenceId";
|
private static final String KEY_PREFERENCE_ID = "preferenceId";
|
||||||
private static final String KEY_DEFAULT_VALUE = "defaultValue";
|
private static final String KEY_DEFAULT_VALUE = "defaultValue";
|
||||||
private static final String KEY_TITLE_ID = "titleId";
|
private static final String KEY_TITLE_ID = "titleId";
|
||||||
|
|
||||||
public static FrequencyDialogFragment newInstance(
|
public static FrequencyDialogFragment newInstance(
|
||||||
int preferenceId, int defaultValue, int titleId) {
|
int preferenceId, int defaultValue, int titleId) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putInt(KEY_PREFERENCE_ID, preferenceId);
|
bundle.putInt(KEY_PREFERENCE_ID, preferenceId);
|
||||||
bundle.putInt(KEY_DEFAULT_VALUE, defaultValue);
|
bundle.putInt(KEY_DEFAULT_VALUE, defaultValue);
|
||||||
bundle.putInt(KEY_TITLE_ID, titleId);
|
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||||
|
|
||||||
FrequencyDialogFragment frequencyDialogFragment = new FrequencyDialogFragment();
|
FrequencyDialogFragment frequencyDialogFragment = new FrequencyDialogFragment();
|
||||||
frequencyDialogFragment.setArguments(bundle);
|
frequencyDialogFragment.setArguments(bundle);
|
||||||
return frequencyDialogFragment;
|
return frequencyDialogFragment;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog createDialog() {
|
|
||||||
FragmentActivity fragmentActivity = getActivity();
|
|
||||||
|
|
||||||
final int preferenceId = getArguments().getInt(KEY_PREFERENCE_ID);
|
|
||||||
int defaultValue = getArguments().getInt(KEY_DEFAULT_VALUE);
|
|
||||||
int titleId = getArguments().getInt(KEY_TITLE_ID);
|
|
||||||
int frequencyValue = PreferencesUtils.getInt(fragmentActivity, preferenceId, defaultValue);
|
|
||||||
|
|
||||||
return new AlertDialog.Builder(fragmentActivity).setPositiveButton(
|
|
||||||
R.string.generic_ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
int listIndex = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
|
||||||
PreferencesUtils.setInt(getActivity(), preferenceId, getFrequencyValue(listIndex));
|
|
||||||
}
|
|
||||||
}).setSingleChoiceItems(
|
|
||||||
getFrequencyDisplayOptions(fragmentActivity), getListIndex(frequencyValue), null)
|
|
||||||
.setTitle(titleId).create();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the frequency display options.
|
|
||||||
*/
|
|
||||||
private String[] getFrequencyDisplayOptions(FragmentActivity fragmentActivity) {
|
|
||||||
boolean metricUnits = PreferencesUtils.isMetricUnits(fragmentActivity);
|
|
||||||
return StringUtils.getFrequencyOptions(fragmentActivity, metricUnits);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the list index for a frequency value. Returns 0 if the value is not on
|
|
||||||
* the list.
|
|
||||||
*/
|
|
||||||
private int getListIndex(int frequencyValue) {
|
|
||||||
String[] values = getResources().getStringArray(R.array.frequency_values);
|
|
||||||
for (int i = 0; i < values.length; i++) {
|
|
||||||
if (frequencyValue == Integer.parseInt(values[i])) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Gets the frequency value from a list index.
|
protected Dialog createDialog() {
|
||||||
*
|
FragmentActivity fragmentActivity = getActivity();
|
||||||
* @param listIndex the list index
|
|
||||||
*/
|
final int preferenceId = getArguments().getInt(KEY_PREFERENCE_ID);
|
||||||
private int getFrequencyValue(int listIndex) {
|
int defaultValue = getArguments().getInt(KEY_DEFAULT_VALUE);
|
||||||
String[] values = getResources().getStringArray(R.array.frequency_values);
|
int titleId = getArguments().getInt(KEY_TITLE_ID);
|
||||||
return Integer.parseInt(values[listIndex]);
|
int frequencyValue = PreferencesUtils.getInt(fragmentActivity, preferenceId, defaultValue);
|
||||||
}
|
|
||||||
|
return new AlertDialog.Builder(fragmentActivity).setPositiveButton(
|
||||||
|
R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
int listIndex = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||||
|
PreferencesUtils.setInt(getActivity(), preferenceId, getFrequencyValue(listIndex));
|
||||||
|
}
|
||||||
|
}).setSingleChoiceItems(
|
||||||
|
getFrequencyDisplayOptions(fragmentActivity), getListIndex(frequencyValue), null)
|
||||||
|
.setTitle(titleId).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the frequency display options.
|
||||||
|
*/
|
||||||
|
private String[] getFrequencyDisplayOptions(FragmentActivity fragmentActivity) {
|
||||||
|
boolean metricUnits = PreferencesUtils.isMetricUnits(fragmentActivity);
|
||||||
|
return StringUtils.getFrequencyOptions(fragmentActivity, metricUnits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list index for a frequency value. Returns 0 if the value is not on
|
||||||
|
* the list.
|
||||||
|
*/
|
||||||
|
private int getListIndex(int frequencyValue) {
|
||||||
|
String[] values = getResources().getStringArray(R.array.frequency_values);
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
if (frequencyValue == Integer.parseInt(values[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the frequency value from a list index.
|
||||||
|
*
|
||||||
|
* @param listIndex the list index
|
||||||
|
*/
|
||||||
|
private int getFrequencyValue(int listIndex) {
|
||||||
|
String[] values = getResources().getStringArray(R.array.frequency_values);
|
||||||
|
return Integer.parseInt(values[listIndex]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+209
-209
@@ -57,231 +57,231 @@ 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) {
|
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||||
Bundle bundle = new Bundle();
|
private static final String KEY_MARKER_ID = "markerId";
|
||||||
bundle.putLong(KEY_MARKER_ID, markerId);
|
private static final String KEY_TITLE = "title";
|
||||||
bundle.putString(KEY_TITLE, title);
|
private static final long HIDE_TEXT_DELAY = 4000L; // 4 seconds
|
||||||
|
private ContentProviderUtils contentProviderUtils;
|
||||||
MarkerDetailFragment fragment = new MarkerDetailFragment();
|
private Handler handler;
|
||||||
fragment.setArguments(bundle);
|
private ImageView photo;
|
||||||
return fragment;
|
private ImageView textGradient;
|
||||||
}
|
private LinearLayout waypointInfo;
|
||||||
|
private Waypoint waypoint;
|
||||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
private Runnable hideText = new Runnable() {
|
||||||
private static final String KEY_MARKER_ID = "markerId";
|
|
||||||
private static final String KEY_TITLE = "title";
|
|
||||||
private static final long HIDE_TEXT_DELAY = 4000L; // 4 seconds
|
|
||||||
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
|
||||||
private Handler handler;
|
|
||||||
private ImageView photo;
|
|
||||||
private ImageView textGradient;
|
|
||||||
private LinearLayout waypointInfo;
|
|
||||||
private Waypoint waypoint;
|
|
||||||
|
|
||||||
private Runnable hideText = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);
|
|
||||||
animation.setAnimationListener(new AnimationListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animation anim) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationRepeat(Animation anim) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animation anim) {
|
|
||||||
textGradient.setVisibility(View.GONE);
|
|
||||||
waypointInfo.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
textGradient.startAnimation(animation);
|
|
||||||
waypointInfo.startAnimation(animation);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
|
||||||
if (markerId == -1L) {
|
|
||||||
Log.d(TAG, "invalid marker id");
|
|
||||||
getFragmentManager().popBackStack();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
contentProviderUtils = ContentProviderUtils.Factory.get(getActivity());
|
|
||||||
handler = new Handler();
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
View view = inflater.inflate(R.layout.marker_detail_fragment, container, false);
|
|
||||||
|
|
||||||
getActivity().setTitle(getArguments().getString(KEY_TITLE));
|
|
||||||
|
|
||||||
photo = view.findViewById(R.id.marker_detail_waypoint_photo);
|
|
||||||
textGradient = view.findViewById(R.id.marker_detail_waypoint_text_gradient);
|
|
||||||
waypointInfo = view.findViewById(R.id.marker_detail_waypoint_info);
|
|
||||||
|
|
||||||
photo.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void run() {
|
||||||
handler.removeCallbacks(hideText);
|
Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);
|
||||||
int visibility = waypointInfo.getVisibility() == View.GONE ? View.VISIBLE : View.GONE;
|
animation.setAnimationListener(new AnimationListener() {
|
||||||
textGradient.setVisibility(visibility);
|
|
||||||
waypointInfo.setVisibility(visibility);
|
@Override
|
||||||
if (visibility == View.VISIBLE) {
|
public void onAnimationStart(Animation anim) {
|
||||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationRepeat(Animation anim) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animation anim) {
|
||||||
|
textGradient.setVisibility(View.GONE);
|
||||||
|
waypointInfo.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
textGradient.startAnimation(animation);
|
||||||
|
waypointInfo.startAnimation(animation);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
});
|
|
||||||
return view;
|
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 onResume() {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onResume();
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Need to update the waypoint in case returning after an edit
|
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
||||||
updateWaypoint(true);
|
if (markerId == -1L) {
|
||||||
updateUi();
|
Log.d(TAG, "invalid marker id");
|
||||||
}
|
getFragmentManager().popBackStack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
contentProviderUtils = ContentProviderUtils.Factory.get(getActivity());
|
||||||
|
handler = new Handler();
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
super.onPause();
|
View view = inflater.inflate(R.layout.marker_detail_fragment, container, false);
|
||||||
handler.removeCallbacks(hideText);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
getActivity().setTitle(getArguments().getString(KEY_TITLE));
|
||||||
public void setMenuVisibility(boolean menuVisible) {
|
|
||||||
super.setMenuVisibility(menuVisible);
|
photo = view.findViewById(R.id.marker_detail_waypoint_photo);
|
||||||
/*
|
textGradient = view.findViewById(R.id.marker_detail_waypoint_text_gradient);
|
||||||
* View pager caches the neighboring fragments in the resumed state. If
|
waypointInfo = view.findViewById(R.id.marker_detail_waypoint_info);
|
||||||
* becoming visible from the resumed state, update the UI to display the
|
|
||||||
* text above the image.
|
photo.setOnClickListener(new View.OnClickListener() {
|
||||||
*/
|
@Override
|
||||||
if (isResumed()) {
|
public void onClick(View v) {
|
||||||
if (menuVisible) {
|
handler.removeCallbacks(hideText);
|
||||||
|
int visibility = waypointInfo.getVisibility() == View.GONE ? View.VISIBLE : View.GONE;
|
||||||
|
textGradient.setVisibility(visibility);
|
||||||
|
waypointInfo.setVisibility(visibility);
|
||||||
|
if (visibility == View.VISIBLE) {
|
||||||
|
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
// Need to update the waypoint in case returning after an edit
|
||||||
|
updateWaypoint(true);
|
||||||
updateUi();
|
updateUi();
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
handler.removeCallbacks(hideText);
|
handler.removeCallbacks(hideText);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void setMenuVisibility(boolean menuVisible) {
|
||||||
inflater.inflate(R.menu.marker_detail, menu);
|
super.setMenuVisibility(menuVisible);
|
||||||
|
/*
|
||||||
updateWaypoint(false);
|
* View pager caches the neighboring fragments in the resumed state. If
|
||||||
|
* becoming visible from the resumed state, update the UI to display the
|
||||||
TrackIconUtils.setMenuIconColor(menu);
|
* text above the image.
|
||||||
}
|
*/
|
||||||
|
if (isResumed()) {
|
||||||
@Override
|
if (menuVisible) {
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
updateUi();
|
||||||
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
} else {
|
||||||
FragmentActivity fragmentActivity = getActivity();
|
handler.removeCallbacks(hideText);
|
||||||
Intent intent;
|
}
|
||||||
switch (item.getItemId()) {
|
}
|
||||||
case R.id.marker_detail_show_on_map:
|
|
||||||
intent = IntentUtils.newShowOnMapIntent(waypoint);
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
case R.id.marker_detail_edit:
|
|
||||||
intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class)
|
|
||||||
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
case R.id.marker_detail_delete:
|
|
||||||
DeleteMarkerDialogFragment.newInstance(new long[] { markerId })
|
|
||||||
.show(getChildFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Updates the waypoint.
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
*
|
inflater.inflate(R.menu.marker_detail, menu);
|
||||||
* @param refresh true to always update
|
|
||||||
*/
|
updateWaypoint(false);
|
||||||
private void updateWaypoint(boolean refresh) {
|
|
||||||
if (refresh || waypoint == null) {
|
TrackIconUtils.setMenuIconColor(menu);
|
||||||
waypoint = contentProviderUtils.getWaypoint(getArguments().getLong(KEY_MARKER_ID));
|
|
||||||
if (waypoint == null) {
|
|
||||||
Log.d(TAG, "waypoint is null");
|
|
||||||
getFragmentManager().popBackStack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Updates the UI.
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
*/
|
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
||||||
private void updateUi() {
|
FragmentActivity fragmentActivity = getActivity();
|
||||||
View waypointView = getView().findViewById(R.id.marker_detail_waypoint);
|
Intent intent;
|
||||||
View statisticsView = getView().findViewById(R.id.marker_detail_statistics);
|
switch (item.getItemId()) {
|
||||||
|
case R.id.marker_detail_show_on_map:
|
||||||
boolean isWaypoint = waypoint.getType() == WaypointType.WAYPOINT;
|
intent = IntentUtils.newShowOnMapIntent(waypoint);
|
||||||
waypointView.setVisibility(isWaypoint ? View.VISIBLE : View.GONE);
|
startActivity(intent);
|
||||||
statisticsView.setVisibility(isWaypoint ? View.GONE : View.VISIBLE);
|
return true;
|
||||||
|
case R.id.marker_detail_edit:
|
||||||
if (isWaypoint) {
|
intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class)
|
||||||
boolean hasPhoto = waypoint.hasPhoto();
|
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
|
||||||
photo.setVisibility(hasPhoto ? View.VISIBLE : View.GONE);
|
startActivity(intent);
|
||||||
textGradient.setVisibility(hasPhoto ? View.VISIBLE : View.GONE);
|
return true;
|
||||||
waypointInfo.setVisibility(View.VISIBLE);
|
case R.id.marker_detail_delete:
|
||||||
|
DeleteMarkerDialogFragment.newInstance(new long[]{markerId})
|
||||||
if (hasPhoto) {
|
.show(getChildFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
||||||
handler.removeCallbacks(hideText);
|
return true;
|
||||||
photo.setImageURI(waypoint.getPhotoURI());
|
default:
|
||||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
setName(R.id.marker_detail_waypoint_name, hasPhoto);
|
|
||||||
|
|
||||||
TextView category = getView().findViewById(R.id.marker_detail_waypoint_category);
|
|
||||||
ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(waypoint.getCategory()), hasPhoto);
|
|
||||||
|
|
||||||
TextView description = getView().findViewById(R.id.marker_detail_waypoint_description);
|
|
||||||
ListItemUtils.setTextView(getActivity(), description, waypoint.getDescription(), hasPhoto);
|
|
||||||
|
|
||||||
setLocation(R.id.marker_detail_waypoint_location, hasPhoto);
|
|
||||||
} else {
|
|
||||||
setName(R.id.marker_detail_statistics_name, false);
|
|
||||||
|
|
||||||
setLocation(R.id.marker_detail_statistics_location, false);
|
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(waypoint.getTrackId());
|
|
||||||
StatsUtils.setTripStatisticsValues(getActivity(), null, getView(), waypoint.getTripStatistics(), null);
|
|
||||||
StatsUtils.setLocationValues(getActivity(), null, getView(), waypoint.getLocation(), false);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void setName(int resId, boolean addShadow) {
|
/**
|
||||||
TextView textView = getView().findViewById(resId);
|
* Updates the waypoint.
|
||||||
ListItemUtils.setTextView(getActivity(), textView, waypoint.getName(), addShadow);
|
*
|
||||||
}
|
* @param refresh true to always update
|
||||||
|
*/
|
||||||
private void setLocation(int resId, boolean addShadow) {
|
private void updateWaypoint(boolean refresh) {
|
||||||
TextView textView = getView().findViewById(resId);
|
if (refresh || waypoint == null) {
|
||||||
Location location = waypoint.getLocation();
|
waypoint = contentProviderUtils.getWaypoint(getArguments().getLong(KEY_MARKER_ID));
|
||||||
String value;
|
if (waypoint == null) {
|
||||||
if (location == null) {
|
Log.d(TAG, "waypoint is null");
|
||||||
value = null;
|
getFragmentManager().popBackStack();
|
||||||
} else {
|
}
|
||||||
value = "[" + getString(R.string.stats_latitude) + " "
|
}
|
||||||
+ StringUtils.formatCoordinate(location.getLatitude()) + ", "
|
}
|
||||||
+ getString(R.string.stats_longitude) + " "
|
|
||||||
+ StringUtils.formatCoordinate(location.getLongitude()) + "]";
|
/**
|
||||||
|
* Updates the UI.
|
||||||
|
*/
|
||||||
|
private void updateUi() {
|
||||||
|
View waypointView = getView().findViewById(R.id.marker_detail_waypoint);
|
||||||
|
View statisticsView = getView().findViewById(R.id.marker_detail_statistics);
|
||||||
|
|
||||||
|
boolean isWaypoint = waypoint.getType() == WaypointType.WAYPOINT;
|
||||||
|
waypointView.setVisibility(isWaypoint ? View.VISIBLE : View.GONE);
|
||||||
|
statisticsView.setVisibility(isWaypoint ? View.GONE : View.VISIBLE);
|
||||||
|
|
||||||
|
if (isWaypoint) {
|
||||||
|
boolean hasPhoto = waypoint.hasPhoto();
|
||||||
|
photo.setVisibility(hasPhoto ? View.VISIBLE : View.GONE);
|
||||||
|
textGradient.setVisibility(hasPhoto ? View.VISIBLE : View.GONE);
|
||||||
|
waypointInfo.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (hasPhoto) {
|
||||||
|
handler.removeCallbacks(hideText);
|
||||||
|
photo.setImageURI(waypoint.getPhotoURI());
|
||||||
|
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(R.id.marker_detail_waypoint_name, hasPhoto);
|
||||||
|
|
||||||
|
TextView category = getView().findViewById(R.id.marker_detail_waypoint_category);
|
||||||
|
ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(waypoint.getCategory()), hasPhoto);
|
||||||
|
|
||||||
|
TextView description = getView().findViewById(R.id.marker_detail_waypoint_description);
|
||||||
|
ListItemUtils.setTextView(getActivity(), description, waypoint.getDescription(), hasPhoto);
|
||||||
|
|
||||||
|
setLocation(R.id.marker_detail_waypoint_location, hasPhoto);
|
||||||
|
} else {
|
||||||
|
setName(R.id.marker_detail_statistics_name, false);
|
||||||
|
|
||||||
|
setLocation(R.id.marker_detail_statistics_location, false);
|
||||||
|
|
||||||
|
Track track = contentProviderUtils.getTrack(waypoint.getTrackId());
|
||||||
|
StatsUtils.setTripStatisticsValues(getActivity(), null, getView(), waypoint.getTripStatistics(), null);
|
||||||
|
StatsUtils.setLocationValues(getActivity(), null, getView(), waypoint.getLocation(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setName(int resId, boolean addShadow) {
|
||||||
|
TextView textView = getView().findViewById(resId);
|
||||||
|
ListItemUtils.setTextView(getActivity(), textView, waypoint.getName(), addShadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLocation(int resId, boolean addShadow) {
|
||||||
|
TextView textView = getView().findViewById(resId);
|
||||||
|
Location location = waypoint.getLocation();
|
||||||
|
String value;
|
||||||
|
if (location == null) {
|
||||||
|
value = null;
|
||||||
|
} else {
|
||||||
|
value = "[" + getString(R.string.stats_latitude) + " "
|
||||||
|
+ StringUtils.formatCoordinate(location.getLatitude()) + ", "
|
||||||
|
+ getString(R.string.stats_longitude) + " "
|
||||||
|
+ StringUtils.formatCoordinate(location.getLongitude()) + "]";
|
||||||
|
}
|
||||||
|
ListItemUtils.setTextView(getActivity(), textView, value, addShadow);
|
||||||
}
|
}
|
||||||
ListItemUtils.setTextView(getActivity(), textView, value, addShadow);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,268 +59,266 @@ import java.util.EnumSet;
|
|||||||
*/
|
*/
|
||||||
public class StatsFragment extends Fragment implements TrackDataListener {
|
public class StatsFragment extends Fragment implements TrackDataListener {
|
||||||
|
|
||||||
public static final String STATS_FRAGMENT_TAG = "statsFragment";
|
public static final String STATS_FRAGMENT_TAG = "statsFragment";
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
private Handler handlerUpdateUI;
|
private Handler handlerUpdateUI;
|
||||||
|
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
|
|
||||||
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 final Runnable updateUIeachSecond = new Runnable() {
|
||||||
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
|
public void run() {
|
||||||
|
if (isResumed() && isSelectedTrackRecording()) {
|
||||||
|
if (!isSelectedTrackPaused() && lastTripStatistics != null) {
|
||||||
|
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime());
|
||||||
|
updateSensorDataUI();
|
||||||
|
}
|
||||||
|
|
||||||
private final Runnable updateUIeachSecond = new Runnable() {
|
handlerUpdateUI.postDelayed(this, UnitConversions.ONE_SECOND);
|
||||||
public void run() {
|
}
|
||||||
if (isResumed() && isSelectedTrackRecording()) {
|
|
||||||
if (!isSelectedTrackPaused() && lastTripStatistics != null) {
|
|
||||||
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime());
|
|
||||||
updateSensorDataUI();
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
private String category = "";
|
||||||
|
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
|
||||||
|
|
||||||
handlerUpdateUI.postDelayed(this, UnitConversions.ONE_SECOND);
|
@Override
|
||||||
}
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.stats, container, false);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
handlerUpdateUI = new Handler();
|
||||||
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(getContext(), null);
|
||||||
|
TrackRecordingServiceConnectionUtils.startConnection(getContext(), trackRecordingServiceConnection);
|
||||||
|
|
||||||
@Override
|
Spinner activityTypeIcon = getView().findViewById(R.id.stats_activity_type_icon);
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getActivity(), ""));
|
||||||
return inflater.inflate(R.layout.stats, container, false);
|
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
||||||
}
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
((TrackDetailActivity) getActivity()).chooseActivityType(category);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||||
|
((TrackDetailActivity) getActivity()).chooseActivityType(category);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onResume() {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onResume();
|
||||||
handlerUpdateUI = new Handler();
|
resumeTrackDataHub();
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(getContext(), null);
|
updateUi(getActivity());
|
||||||
TrackRecordingServiceConnectionUtils.startConnection(getContext(), trackRecordingServiceConnection);
|
if (isSelectedTrackRecording()) {
|
||||||
|
handlerUpdateUI.post(updateUIeachSecond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spinner activityTypeIcon = getView().findViewById(R.id.stats_activity_type_icon);
|
@Override
|
||||||
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getActivity(), ""));
|
public void onPause() {
|
||||||
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
super.onPause();
|
||||||
@Override
|
pauseTrackDataHub();
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
handlerUpdateUI.removeCallbacks(updateUIeachSecond);
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
}
|
||||||
((TrackDetailActivity) getActivity()).chooseActivityType(category);
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
trackRecordingServiceConnection.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackUpdated(final Track track) {
|
||||||
|
if (isResumed()) {
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
lastTripStatistics = track != null ? track.getTripStatistics() : null;
|
||||||
|
category = track != null ? track.getCategory() : "";
|
||||||
|
updateUi(getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearTrackPoints() {
|
||||||
|
lastLocation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSampledInTrackPoint(Location location) {
|
||||||
|
lastLocation = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSampledOutTrackPoint(Location location) {
|
||||||
|
lastLocation = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSegmentSplit(Location location) {
|
||||||
|
// We don't care.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewTrackPointsDone() {
|
||||||
|
if (isResumed()) {
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
if (!isSelectedTrackRecording() || isSelectedTrackPaused()) {
|
||||||
|
lastLocation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastLocation != null) {
|
||||||
|
boolean hasFix = !LocationUtils.isLocationOld(lastLocation);
|
||||||
|
boolean hasGoodFix = lastLocation.hasAccuracy() && lastLocation.getAccuracy() < recordingGpsAccuracy;
|
||||||
|
|
||||||
|
if (!hasFix || !hasGoodFix) {
|
||||||
|
lastLocation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatsUtils.setLocationValues(getActivity(), getActivity(), null, lastLocation, isSelectedTrackRecording());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearWaypoints() {
|
||||||
|
// We don't care.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewWaypoint(Waypoint wpt) {
|
||||||
|
// We don't care.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewWaypointsDone() {
|
||||||
|
// We don't care.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMetricUnitsChanged(final boolean metric) {
|
||||||
|
if (isResumed()) {
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
updateUi(getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
activityTypeIcon.setOnKeyListener(new View.OnKeyListener() {
|
@Override
|
||||||
@Override
|
public boolean onReportSpeedChanged(final boolean speed) {
|
||||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
if (isResumed()) {
|
||||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
((TrackDetailActivity) getActivity()).chooseActivityType(category);
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
updateUi(getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
resumeTrackDataHub();
|
|
||||||
updateUi(getActivity());
|
|
||||||
if (isSelectedTrackRecording()) {
|
|
||||||
handlerUpdateUI.post(updateUIeachSecond);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public boolean onRecordingGpsAccuracy(int newValue) {
|
||||||
super.onPause();
|
recordingGpsAccuracy = newValue;
|
||||||
pauseTrackDataHub();
|
return false;
|
||||||
handlerUpdateUI.removeCallbacks(updateUIeachSecond);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public boolean onRecordingDistanceIntervalChanged(int minRecordingDistance) {
|
||||||
super.onStop();
|
// We don't care.
|
||||||
trackRecordingServiceConnection.unbind();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void onTrackUpdated(final Track track) {
|
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
if (isResumed()) {
|
* be accessed by multiple threads.
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
*/
|
||||||
@Override
|
private synchronized void resumeTrackDataHub() {
|
||||||
public void run() {
|
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||||
if (isResumed()) {
|
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
||||||
lastTripStatistics = track != null ? track.getTripStatistics() : null;
|
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
||||||
category = track != null ? track.getCategory() : "";
|
TrackDataType.PREFERENCE));
|
||||||
updateUi(getActivity());
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
|
* be accessed by multiple threads.
|
||||||
|
*/
|
||||||
|
private synchronized void pauseTrackDataHub() {
|
||||||
|
trackDataHub.unregisterTrackDataListener(this);
|
||||||
|
trackDataHub = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the selected track is recording. Needs to be synchronized
|
||||||
|
* because trackDataHub can be accessed by multiple threads.
|
||||||
|
*/
|
||||||
|
private synchronized boolean isSelectedTrackRecording() {
|
||||||
|
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the selected track is paused. Needs to be synchronized
|
||||||
|
* because trackDataHub can be accessed by multiple threads.
|
||||||
|
*/
|
||||||
|
private synchronized boolean isSelectedTrackPaused() {
|
||||||
|
return trackDataHub != null && trackDataHub.isSelectedTrackPaused();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to fetch most recent {@link SensorDataSet} {@link com.google.android.apps.mytracks.services.sensors.SensorManager}.
|
||||||
|
*/
|
||||||
|
private void updateSensorDataUI() {
|
||||||
|
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
|
if (trackRecordingService == null) {
|
||||||
|
Log.d(STATS_FRAGMENT_TAG, "Cannot get the track recording service.");
|
||||||
|
sensorDataSet = null;
|
||||||
|
} else {
|
||||||
|
//TODO sensorState = trackRecordingService.getSensorState();
|
||||||
|
sensorDataSet = trackRecordingService.getSensorData();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
StatsUtils.setSensorData(getActivity(), getActivity(), sensorDataSet, isSelectedTrackRecording());
|
||||||
public void clearTrackPoints() {
|
|
||||||
lastLocation = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSampledInTrackPoint(Location location) {
|
|
||||||
lastLocation = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSampledOutTrackPoint(Location location) {
|
|
||||||
lastLocation = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSegmentSplit(Location location) {
|
|
||||||
// We don't care.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewTrackPointsDone() {
|
|
||||||
if (isResumed()) {
|
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
if (!isSelectedTrackRecording() || isSelectedTrackPaused()) {
|
|
||||||
lastLocation = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastLocation != null) {
|
|
||||||
boolean hasFix = !LocationUtils.isLocationOld(lastLocation);
|
|
||||||
boolean hasGoodFix = lastLocation.hasAccuracy() && lastLocation.getAccuracy() < recordingGpsAccuracy;
|
|
||||||
|
|
||||||
if (!hasFix || !hasGoodFix) {
|
|
||||||
lastLocation = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StatsUtils.setLocationValues(getActivity(), getActivity(), null, lastLocation, isSelectedTrackRecording());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearWaypoints() {
|
|
||||||
// We don't care.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewWaypoint(Waypoint wpt) {
|
|
||||||
// We don't care.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewWaypointsDone() {
|
|
||||||
// We don't care.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMetricUnitsChanged(final boolean metric) {
|
|
||||||
if (isResumed()) {
|
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
updateUi(getActivity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onReportSpeedChanged(final boolean speed) {
|
|
||||||
if (isResumed()) {
|
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
updateUi(getActivity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRecordingGpsAccuracy(int newValue) {
|
|
||||||
recordingGpsAccuracy = newValue;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRecordingDistanceIntervalChanged(int minRecordingDistance) {
|
|
||||||
// We don't care.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
|
||||||
* be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized void resumeTrackDataHub() {
|
|
||||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
|
||||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
|
||||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
|
||||||
TrackDataType.PREFERENCE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
|
||||||
* be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized void pauseTrackDataHub() {
|
|
||||||
trackDataHub.unregisterTrackDataListener(this);
|
|
||||||
trackDataHub = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the selected track is recording. Needs to be synchronized
|
|
||||||
* because trackDataHub can be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized boolean isSelectedTrackRecording() {
|
|
||||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the selected track is paused. Needs to be synchronized
|
|
||||||
* because trackDataHub can be accessed by multiple threads.
|
|
||||||
*/
|
|
||||||
private synchronized boolean isSelectedTrackPaused() {
|
|
||||||
return trackDataHub != null && trackDataHub.isSelectedTrackPaused();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to fetch most recent {@link SensorDataSet} {@link com.google.android.apps.mytracks.services.sensors.SensorManager}.
|
|
||||||
*/
|
|
||||||
private void updateSensorDataUI() {
|
|
||||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
|
||||||
if (trackRecordingService == null) {
|
|
||||||
Log.d(STATS_FRAGMENT_TAG, "Cannot get the track recording service.");
|
|
||||||
sensorDataSet = null;
|
|
||||||
} else {
|
|
||||||
//TODO sensorState = trackRecordingService.getSensorState();
|
|
||||||
sensorDataSet = trackRecordingService.getSensorData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatsUtils.setSensorData(getActivity(), getActivity(), sensorDataSet, isSelectedTrackRecording());
|
/**
|
||||||
}
|
* Updates the UI.
|
||||||
|
*/
|
||||||
/**
|
private void updateUi(FragmentActivity activity) {
|
||||||
* Updates the UI.
|
String trackIconValue = TrackIconUtils.getIconValue(activity, category);
|
||||||
*/
|
StatsUtils.setTripStatisticsValues(activity, activity, null, lastTripStatistics, trackIconValue);
|
||||||
private void updateUi(FragmentActivity activity) {
|
StatsUtils.setLocationValues(activity, activity, null, lastLocation, isSelectedTrackRecording());
|
||||||
String trackIconValue = TrackIconUtils.getIconValue(activity, category);
|
updateSensorDataUI();
|
||||||
StatsUtils.setTripStatisticsValues(activity, activity, null, lastTripStatistics, trackIconValue);
|
}
|
||||||
StatsUtils.setLocationValues(activity, activity, null, lastLocation, isSelectedTrackRecording());
|
|
||||||
updateSensorDataUI();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,75 @@
|
|||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of all possible track formats.
|
* Definition of all possible track formats.
|
||||||
*/
|
*/
|
||||||
public enum TrackFileFormat implements Parcelable {
|
public enum TrackFileFormat implements Parcelable {
|
||||||
KML {
|
KML {
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||||
return new KmlTrackWriter(context, multiple, playTrack);
|
return new KmlTrackWriter(context, multiple, playTrack);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GPX {
|
GPX {
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||||
return new GpxTrackWriter(context.getString(R.string.app_name));
|
return new GpxTrackWriter(context.getString(R.string.app_name));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
|
||||||
public int describeContents() {
|
@Override
|
||||||
return 0;
|
public TrackFileFormat createFromParcel(final Parcel source) {
|
||||||
}
|
return TrackFileFormat.values()[source.readInt()];
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(final Parcel dest, final int flags) {
|
public TrackFileFormat[] newArray(final int size) {
|
||||||
dest.writeInt(ordinal());
|
return new TrackFileFormat[size];
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
|
@Override
|
||||||
@Override
|
public int describeContents() {
|
||||||
public TrackFileFormat createFromParcel(final Parcel source) {
|
return 0;
|
||||||
return TrackFileFormat.values()[source.readInt()];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackFileFormat[] newArray(final int size) {
|
public void writeToParcel(final Parcel dest, final int flags) {
|
||||||
return new TrackFileFormat[size];
|
dest.writeInt(ordinal());
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new track writer for the format.
|
* Creates a new track writer for the format.
|
||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
* @param multiple true for writing multiple tracks
|
* @param multiple true for writing multiple tracks
|
||||||
* @param playTrack true to play track
|
* @param playTrack true to play track
|
||||||
*/
|
*/
|
||||||
public abstract TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack);
|
public abstract TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mime type for each format.
|
* Returns the mime type for each format.
|
||||||
*/
|
*/
|
||||||
public String getMimeType() {
|
public String getMimeType() {
|
||||||
return "application/" + getExtension() + "+xml";
|
return "application/" + getExtension() + "+xml";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file extension for each format.
|
* Returns the file extension for each format.
|
||||||
*/
|
*/
|
||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return this.name().toLowerCase(Locale.US);
|
return this.name().toLowerCase(Locale.US);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+192
-192
@@ -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,197 +38,197 @@ import java.io.OutputStream;
|
|||||||
*/
|
*/
|
||||||
public class FileTrackExporter implements TrackExporter {
|
public class FileTrackExporter implements TrackExporter {
|
||||||
|
|
||||||
private static final String TAG = FileTrackExporter.class.getSimpleName();
|
private static final String TAG = FileTrackExporter.class.getSimpleName();
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private final Track[] tracks;
|
private final Track[] tracks;
|
||||||
private final TrackWriter trackWriter;
|
private final TrackWriter trackWriter;
|
||||||
private final TrackExporterListener trackExporterListener;
|
private final TrackExporterListener trackExporterListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param contentProviderUtils the content provider utils
|
* @param contentProviderUtils the content provider utils
|
||||||
* @param tracks the tracks
|
* @param tracks the tracks
|
||||||
* @param trackWriter the track writer
|
* @param trackWriter the track writer
|
||||||
* @param trackExporterListener the track export listener
|
* @param trackExporterListener the track export listener
|
||||||
*/
|
|
||||||
public FileTrackExporter(ContentProviderUtils contentProviderUtils, Track[] tracks,
|
|
||||||
TrackWriter trackWriter, TrackExporterListener trackExporterListener) {
|
|
||||||
this.contentProviderUtils = contentProviderUtils;
|
|
||||||
this.tracks = tracks;
|
|
||||||
this.trackWriter = trackWriter;
|
|
||||||
this.trackExporterListener = trackExporterListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean writeTrack(OutputStream outputStream) {
|
|
||||||
try {
|
|
||||||
trackWriter.prepare(outputStream);
|
|
||||||
trackWriter.writeHeader(tracks);
|
|
||||||
for (Track track1 : tracks) {
|
|
||||||
writeWaypoints(track1);
|
|
||||||
}
|
|
||||||
trackWriter.writeBeginTracks();
|
|
||||||
long startTime = tracks[0].getTripStatistics().getStartTime();
|
|
||||||
for (Track track : tracks) {
|
|
||||||
long offset = track.getTripStatistics().getStartTime() - startTime;
|
|
||||||
writeLocations(track, offset);
|
|
||||||
}
|
|
||||||
trackWriter.writeEndTracks();
|
|
||||||
trackWriter.writeFooter();
|
|
||||||
trackWriter.close();
|
|
||||||
return true;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(TAG, "Thread interrupted", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the waypoints.
|
|
||||||
*/
|
|
||||||
private void writeWaypoints(Track track) throws InterruptedException {
|
|
||||||
/*
|
|
||||||
* TODO: Stream through the waypoints in chunks. I am leaving the number of
|
|
||||||
* waypoints very high which should not be a problem because we don't try to
|
|
||||||
* load them into objects all at the same time.
|
|
||||||
*/
|
*/
|
||||||
boolean hasWaypoints = false;
|
public FileTrackExporter(ContentProviderUtils contentProviderUtils, Track[] tracks,
|
||||||
try (Cursor cursor = contentProviderUtils.getWaypointCursor(
|
TrackWriter trackWriter, TrackExporterListener trackExporterListener) {
|
||||||
track.getId(), -1L, Constants.MAX_LOADED_WAYPOINTS_POINTS)) {
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
this.tracks = tracks;
|
||||||
/*
|
this.trackWriter = trackWriter;
|
||||||
* Yes, this will skip the first waypoint and that is intentional as the
|
this.trackExporterListener = trackExporterListener;
|
||||||
* first waypoint holds the stats for the track.
|
|
||||||
*/
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
if (!hasWaypoints) {
|
|
||||||
trackWriter.writeBeginWaypoints(track);
|
|
||||||
hasWaypoints = true;
|
|
||||||
}
|
|
||||||
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
|
|
||||||
trackWriter.writeWaypoint(waypoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (hasWaypoints) {
|
|
||||||
trackWriter.writeEndWaypoints();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the locations.
|
|
||||||
*/
|
|
||||||
private void writeLocations(Track track, long offset) throws InterruptedException {
|
|
||||||
boolean wroteTrack = false;
|
|
||||||
boolean wroteSegment = false;
|
|
||||||
boolean isLastLocationValid = false;
|
|
||||||
TrackWriterLocationFactory locationFactory = new TrackWriterLocationFactory();
|
|
||||||
int locationNumber = 0;
|
|
||||||
LocationIterator locationIterator = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
locationIterator = contentProviderUtils.getTrackPointLocationIterator(
|
|
||||||
track.getId(), -1L, false, locationFactory);
|
|
||||||
|
|
||||||
while (locationIterator.hasNext()) {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
Location location = locationIterator.next();
|
|
||||||
|
|
||||||
setLocationTime(location, offset);
|
|
||||||
locationNumber++;
|
|
||||||
|
|
||||||
boolean isLocationValid = LocationUtils.isValidLocation(location);
|
|
||||||
boolean isSegmentValid = isLocationValid && isLastLocationValid;
|
|
||||||
if (!wroteTrack && isSegmentValid) {
|
|
||||||
// Found the first two consecutive locations that are valid
|
|
||||||
trackWriter.writeBeginTrack(track, locationFactory.lastLocation);
|
|
||||||
wroteTrack = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSegmentValid) {
|
|
||||||
if (!wroteSegment) {
|
|
||||||
// Start a segment
|
|
||||||
trackWriter.writeOpenSegment();
|
|
||||||
wroteSegment = true;
|
|
||||||
|
|
||||||
// Write the previous location, which we had previously skipped
|
|
||||||
trackWriter.writeLocation(locationFactory.lastLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the current location
|
|
||||||
trackWriter.writeLocation(location);
|
|
||||||
if (trackExporterListener != null) {
|
|
||||||
trackExporterListener.onProgressUpdate(locationNumber, track.getNumberOfPoints());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (wroteSegment) {
|
|
||||||
trackWriter.writeCloseSegment();
|
|
||||||
wroteSegment = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
locationFactory.swapLocations();
|
|
||||||
isLastLocationValid = isLocationValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wroteTrack) {
|
|
||||||
Location lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId());
|
|
||||||
setLocationTime(lastValidTrackPoint, offset);
|
|
||||||
trackWriter.writeEndTrack(track, lastValidTrackPoint);
|
|
||||||
} else {
|
|
||||||
// Write an empty track
|
|
||||||
trackWriter.writeBeginTrack(track, null);
|
|
||||||
trackWriter.writeEndTrack(track, null);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (locationIterator != null) {
|
|
||||||
locationIterator.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a location time.
|
|
||||||
*
|
|
||||||
* @param location the location
|
|
||||||
* @param offset the time offset
|
|
||||||
*/
|
|
||||||
private void setLocationTime(Location location, long offset) {
|
|
||||||
if (location != null) {
|
|
||||||
location.setTime(location.getTime() - offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Track writer location factory. Keeping the last two locations.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
private class TrackWriterLocationFactory implements ContentProviderUtils.LocationFactory {
|
|
||||||
Location currentLocation;
|
|
||||||
Location lastLocation;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Location createLocation() {
|
public boolean writeTrack(OutputStream outputStream) {
|
||||||
if (currentLocation == null) {
|
try {
|
||||||
currentLocation = new SensorDataSetLocation("");
|
trackWriter.prepare(outputStream);
|
||||||
}
|
trackWriter.writeHeader(tracks);
|
||||||
return currentLocation;
|
for (Track track1 : tracks) {
|
||||||
|
writeWaypoints(track1);
|
||||||
|
}
|
||||||
|
trackWriter.writeBeginTracks();
|
||||||
|
long startTime = tracks[0].getTripStatistics().getStartTime();
|
||||||
|
for (Track track : tracks) {
|
||||||
|
long offset = track.getTripStatistics().getStartTime() - startTime;
|
||||||
|
writeLocations(track, offset);
|
||||||
|
}
|
||||||
|
trackWriter.writeEndTracks();
|
||||||
|
trackWriter.writeFooter();
|
||||||
|
trackWriter.close();
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "Thread interrupted", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swapLocations() {
|
/**
|
||||||
Location tempLocation = lastLocation;
|
* Writes the waypoints.
|
||||||
lastLocation = currentLocation;
|
*/
|
||||||
currentLocation = tempLocation;
|
private void writeWaypoints(Track track) throws InterruptedException {
|
||||||
if (currentLocation != null) {
|
/*
|
||||||
currentLocation.reset();
|
* TODO: Stream through the waypoints in chunks. I am leaving the number of
|
||||||
}
|
* waypoints very high which should not be a problem because we don't try to
|
||||||
|
* load them into objects all at the same time.
|
||||||
|
*/
|
||||||
|
boolean hasWaypoints = false;
|
||||||
|
try (Cursor cursor = contentProviderUtils.getWaypointCursor(
|
||||||
|
track.getId(), -1L, Constants.MAX_LOADED_WAYPOINTS_POINTS)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
/*
|
||||||
|
* Yes, this will skip the first waypoint and that is intentional as the
|
||||||
|
* first waypoint holds the stats for the track.
|
||||||
|
*/
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
if (!hasWaypoints) {
|
||||||
|
trackWriter.writeBeginWaypoints(track);
|
||||||
|
hasWaypoints = true;
|
||||||
|
}
|
||||||
|
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
|
||||||
|
trackWriter.writeWaypoint(waypoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasWaypoints) {
|
||||||
|
trackWriter.writeEndWaypoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the locations.
|
||||||
|
*/
|
||||||
|
private void writeLocations(Track track, long offset) throws InterruptedException {
|
||||||
|
boolean wroteTrack = false;
|
||||||
|
boolean wroteSegment = false;
|
||||||
|
boolean isLastLocationValid = false;
|
||||||
|
TrackWriterLocationFactory locationFactory = new TrackWriterLocationFactory();
|
||||||
|
int locationNumber = 0;
|
||||||
|
LocationIterator locationIterator = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
locationIterator = contentProviderUtils.getTrackPointLocationIterator(
|
||||||
|
track.getId(), -1L, false, locationFactory);
|
||||||
|
|
||||||
|
while (locationIterator.hasNext()) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
Location location = locationIterator.next();
|
||||||
|
|
||||||
|
setLocationTime(location, offset);
|
||||||
|
locationNumber++;
|
||||||
|
|
||||||
|
boolean isLocationValid = LocationUtils.isValidLocation(location);
|
||||||
|
boolean isSegmentValid = isLocationValid && isLastLocationValid;
|
||||||
|
if (!wroteTrack && isSegmentValid) {
|
||||||
|
// Found the first two consecutive locations that are valid
|
||||||
|
trackWriter.writeBeginTrack(track, locationFactory.lastLocation);
|
||||||
|
wroteTrack = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSegmentValid) {
|
||||||
|
if (!wroteSegment) {
|
||||||
|
// Start a segment
|
||||||
|
trackWriter.writeOpenSegment();
|
||||||
|
wroteSegment = true;
|
||||||
|
|
||||||
|
// Write the previous location, which we had previously skipped
|
||||||
|
trackWriter.writeLocation(locationFactory.lastLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the current location
|
||||||
|
trackWriter.writeLocation(location);
|
||||||
|
if (trackExporterListener != null) {
|
||||||
|
trackExporterListener.onProgressUpdate(locationNumber, track.getNumberOfPoints());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (wroteSegment) {
|
||||||
|
trackWriter.writeCloseSegment();
|
||||||
|
wroteSegment = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
locationFactory.swapLocations();
|
||||||
|
isLastLocationValid = isLocationValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wroteTrack) {
|
||||||
|
Location lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId());
|
||||||
|
setLocationTime(lastValidTrackPoint, offset);
|
||||||
|
trackWriter.writeEndTrack(track, lastValidTrackPoint);
|
||||||
|
} else {
|
||||||
|
// Write an empty track
|
||||||
|
trackWriter.writeBeginTrack(track, null);
|
||||||
|
trackWriter.writeEndTrack(track, null);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (locationIterator != null) {
|
||||||
|
locationIterator.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a location time.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param offset the time offset
|
||||||
|
*/
|
||||||
|
private void setLocationTime(Location location, long offset) {
|
||||||
|
if (location != null) {
|
||||||
|
location.setTime(location.getTime() - offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track writer location factory. Keeping the last two locations.
|
||||||
|
*
|
||||||
|
* @author Jimmy Shih
|
||||||
|
*/
|
||||||
|
private class TrackWriterLocationFactory implements ContentProviderUtils.LocationFactory {
|
||||||
|
Location currentLocation;
|
||||||
|
Location lastLocation;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Location createLocation() {
|
||||||
|
if (currentLocation == null) {
|
||||||
|
currentLocation = new SensorDataSetLocation("");
|
||||||
|
}
|
||||||
|
return currentLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapLocations() {
|
||||||
|
Location tempLocation = lastLocation;
|
||||||
|
lastLocation = currentLocation;
|
||||||
|
currentLocation = tempLocation;
|
||||||
|
if (currentLocation != null) {
|
||||||
|
currentLocation.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+145
-144
@@ -36,166 +36,167 @@ import java.util.Locale;
|
|||||||
//TODO Can we export Sensor.Data in GPX?
|
//TODO Can we export Sensor.Data in GPX?
|
||||||
public class GpxTrackWriter implements TrackWriter {
|
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 {
|
|
||||||
/*
|
|
||||||
* GPX readers expect to see fractional numbers with US-style punctuation.
|
|
||||||
* That is, they want periods for decimal points, rather than commas.
|
|
||||||
*/
|
|
||||||
ELEVATION_FORMAT.setMaximumFractionDigits(1);
|
|
||||||
ELEVATION_FORMAT.setGroupingUsed(false);
|
|
||||||
|
|
||||||
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
static {
|
||||||
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
/*
|
||||||
COORDINATE_FORMAT.setGroupingUsed(false);
|
* GPX readers expect to see fractional numbers with US-style punctuation.
|
||||||
}
|
* That is, they want periods for decimal points, rather than commas.
|
||||||
|
*/
|
||||||
|
ELEVATION_FORMAT.setMaximumFractionDigits(1);
|
||||||
|
ELEVATION_FORMAT.setGroupingUsed(false);
|
||||||
|
|
||||||
private final String creator;
|
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
||||||
private PrintWriter printWriter;
|
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
||||||
|
COORDINATE_FORMAT.setGroupingUsed(false);
|
||||||
public GpxTrackWriter(String creator) {
|
|
||||||
this.creator = creator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getExtension() {
|
|
||||||
return TrackFileFormat.GPX.getExtension();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void prepare(OutputStream outputStream) {
|
|
||||||
this.printWriter = new PrintWriter(outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.flush();
|
|
||||||
printWriter = null;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private final String creator;
|
||||||
public void writeHeader(Track[] tracks) {
|
private PrintWriter printWriter;
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
public GpxTrackWriter(String creator) {
|
||||||
printWriter.println("<gpx");
|
this.creator = creator;
|
||||||
printWriter.println("version=\"1.1\"");
|
|
||||||
printWriter.println(
|
|
||||||
"creator=\"" + creator + "\"");
|
|
||||||
printWriter.println("xmlns=\"http://www.topografix.com/GPX/1/1\"");
|
|
||||||
printWriter.println(
|
|
||||||
"xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/1\"");
|
|
||||||
printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
|
|
||||||
printWriter.println("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1"
|
|
||||||
+ " http://www.topografix.com/GPX/1/1/gpx.xsd"
|
|
||||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1"
|
|
||||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd\">");
|
|
||||||
printWriter.println("<metadata>");
|
|
||||||
Track track = tracks[0];
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
|
||||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
|
||||||
printWriter.println("</metadata>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeFooter() {
|
public String getExtension() {
|
||||||
if (printWriter != null) {
|
return TrackFileFormat.GPX.getExtension();
|
||||||
printWriter.println("</gpx>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeBeginWaypoints(Track track) {
|
public void prepare(OutputStream outputStream) {
|
||||||
// Do nothing
|
this.printWriter = new PrintWriter(outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeEndWaypoints() {
|
public void close() {
|
||||||
// Do nothing
|
if (printWriter != null) {
|
||||||
}
|
printWriter.flush();
|
||||||
|
printWriter = null;
|
||||||
@Override
|
|
||||||
public void writeWaypoint(Waypoint waypoint) {
|
|
||||||
if (printWriter != null) {
|
|
||||||
Location location = waypoint.getLocation();
|
|
||||||
if (location != null) {
|
|
||||||
printWriter.println("<wpt " + formatLocation(location) + ">");
|
|
||||||
if (location.hasAltitude()) {
|
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
|
||||||
}
|
}
|
||||||
printWriter.println(
|
|
||||||
"<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>");
|
|
||||||
printWriter.println("<cmt>" + StringUtils.formatCData(waypoint.getType().name()) + "</cmt>");
|
|
||||||
printWriter.println(
|
|
||||||
"<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
|
|
||||||
printWriter.println("<type>" + StringUtils.formatCData(waypoint.getCategory()) + "</type>");
|
|
||||||
printWriter.println("</wpt>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeBeginTracks() {
|
public void writeHeader(Track[] tracks) {
|
||||||
// Do nothing
|
if (printWriter != null) {
|
||||||
}
|
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
|
printWriter.println("<gpx");
|
||||||
@Override
|
printWriter.println("version=\"1.1\"");
|
||||||
public void writeEndTracks() {
|
printWriter.println(
|
||||||
// Do nothing
|
"creator=\"" + creator + "\"");
|
||||||
}
|
printWriter.println("xmlns=\"http://www.topografix.com/GPX/1/1\"");
|
||||||
|
printWriter.println(
|
||||||
@Override
|
"xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/1\"");
|
||||||
public void writeBeginTrack(Track track, Location startLocation) {
|
printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
|
||||||
if (printWriter != null) {
|
printWriter.println("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1"
|
||||||
printWriter.println("<trk>");
|
+ " http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1"
|
||||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd\">");
|
||||||
printWriter.println("<type>" + StringUtils.formatCData(track.getCategory()) + "</type>");
|
printWriter.println("<metadata>");
|
||||||
printWriter.println("<extensions><topografix:color>c0c0c0</topografix:color></extensions>");
|
Track track = tracks[0];
|
||||||
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
|
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||||
|
printWriter.println("</metadata>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeEndTrack(Track track, Location endLocation) {
|
public void writeFooter() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("</trk>");
|
printWriter.println("</gpx>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeOpenSegment() {
|
public void writeBeginWaypoints(Track track) {
|
||||||
printWriter.println("<trkseg>");
|
// Do nothing
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeCloseSegment() {
|
|
||||||
printWriter.println("</trkseg>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeLocation(Location location) {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.println("<trkpt " + formatLocation(location) + ">");
|
|
||||||
if (location.hasAltitude()) {
|
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
|
||||||
}
|
|
||||||
printWriter.println(
|
|
||||||
"<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
|
||||||
printWriter.println("</trkpt>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Formats a location with latitude and longitude coordinates.
|
public void writeEndWaypoints() {
|
||||||
*
|
// Do nothing
|
||||||
* @param location the location
|
}
|
||||||
*/
|
|
||||||
private String formatLocation(Location location) {
|
@Override
|
||||||
return "lat=\"" + COORDINATE_FORMAT.format(location.getLatitude()) + "\" lon=\""
|
public void writeWaypoint(Waypoint waypoint) {
|
||||||
+ COORDINATE_FORMAT.format(location.getLongitude()) + "\"";
|
if (printWriter != null) {
|
||||||
}
|
Location location = waypoint.getLocation();
|
||||||
|
if (location != null) {
|
||||||
|
printWriter.println("<wpt " + formatLocation(location) + ">");
|
||||||
|
if (location.hasAltitude()) {
|
||||||
|
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
||||||
|
}
|
||||||
|
printWriter.println(
|
||||||
|
"<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
||||||
|
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>");
|
||||||
|
printWriter.println("<cmt>" + StringUtils.formatCData(waypoint.getType().name()) + "</cmt>");
|
||||||
|
printWriter.println(
|
||||||
|
"<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
|
||||||
|
printWriter.println("<type>" + StringUtils.formatCData(waypoint.getCategory()) + "</type>");
|
||||||
|
printWriter.println("</wpt>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeBeginTracks() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeEndTracks() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeBeginTrack(Track track, Location startLocation) {
|
||||||
|
if (printWriter != null) {
|
||||||
|
printWriter.println("<trk>");
|
||||||
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
|
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||||
|
printWriter.println("<type>" + StringUtils.formatCData(track.getCategory()) + "</type>");
|
||||||
|
printWriter.println("<extensions><topografix:color>c0c0c0</topografix:color></extensions>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeEndTrack(Track track, Location endLocation) {
|
||||||
|
if (printWriter != null) {
|
||||||
|
printWriter.println("</trk>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeOpenSegment() {
|
||||||
|
printWriter.println("<trkseg>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeCloseSegment() {
|
||||||
|
printWriter.println("</trkseg>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeLocation(Location location) {
|
||||||
|
if (printWriter != null) {
|
||||||
|
printWriter.println("<trkpt " + formatLocation(location) + ">");
|
||||||
|
if (location.hasAltitude()) {
|
||||||
|
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
||||||
|
}
|
||||||
|
printWriter.println(
|
||||||
|
"<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
||||||
|
printWriter.println("</trkpt>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a location with latitude and longitude coordinates.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
*/
|
||||||
|
private String formatLocation(Location location) {
|
||||||
|
return "lat=\"" + COORDINATE_FORMAT.format(location.getLatitude()) + "\" lon=\""
|
||||||
|
+ COORDINATE_FORMAT.format(location.getLongitude()) + "\"";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+382
-382
@@ -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;
|
||||||
@@ -46,415 +46,415 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public class KmlTrackWriter implements TrackWriter {
|
public class KmlTrackWriter implements TrackWriter {
|
||||||
|
|
||||||
private static final String WAYPOINT_STYLE = "waypoint";
|
private static final String WAYPOINT_STYLE = "waypoint";
|
||||||
private static final String STATISTICS_STYLE = "statistics";
|
private static final String STATISTICS_STYLE = "statistics";
|
||||||
private static final String START_STYLE = "start";
|
private static final String START_STYLE = "start";
|
||||||
private static final String END_STYLE = "end";
|
private static final String END_STYLE = "end";
|
||||||
private static final String TRACK_STYLE = "track";
|
private static final String TRACK_STYLE = "track";
|
||||||
private static final String SCHEMA_ID = "schema";
|
private static final String SCHEMA_ID = "schema";
|
||||||
private static final String CADENCE = "cadence";
|
private static final String CADENCE = "cadence";
|
||||||
private static final String HEART_RATE = "heart_rate";
|
private static final String HEART_RATE = "heart_rate";
|
||||||
private static final String POWER = "power";
|
private static final String POWER = "power";
|
||||||
|
|
||||||
private static final String
|
private static final String
|
||||||
WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
||||||
private static final String
|
private static final String
|
||||||
STATISTICS_ICON = "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";
|
STATISTICS_ICON = "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";
|
||||||
private static final String
|
private static final String
|
||||||
START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
|
START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
|
||||||
private static final String
|
private static final String
|
||||||
END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
|
END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
|
||||||
private static final String
|
private static final String
|
||||||
TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final boolean multiple;
|
private final boolean multiple;
|
||||||
private final boolean playTrack;
|
private final boolean playTrack;
|
||||||
private final DescriptionGenerator descriptionGenerator;
|
private final DescriptionGenerator descriptionGenerator;
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private PrintWriter printWriter;
|
private PrintWriter printWriter;
|
||||||
private ArrayList<Float> powerList = new ArrayList<>();
|
private ArrayList<Float> powerList = new ArrayList<>();
|
||||||
private ArrayList<Float> cadenceList = new ArrayList<>();
|
private ArrayList<Float> cadenceList = new ArrayList<>();
|
||||||
private ArrayList<Float> heartRateList = new ArrayList<>();
|
private ArrayList<Float> heartRateList = new ArrayList<>();
|
||||||
private boolean hasPower;
|
private boolean hasPower;
|
||||||
private boolean hasCadence;
|
private boolean hasCadence;
|
||||||
private boolean hasHeartRate;
|
private boolean hasHeartRate;
|
||||||
|
|
||||||
public KmlTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
public KmlTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||||
this(context, multiple, playTrack, new DescriptionGeneratorImpl(context));
|
this(context, multiple, playTrack, new DescriptionGeneratorImpl(context));
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
KmlTrackWriter(Context context, boolean multiple, boolean playTrack, DescriptionGenerator descriptionGenerator) {
|
|
||||||
this.context = context;
|
|
||||||
this.multiple = multiple;
|
|
||||||
this.playTrack = playTrack;
|
|
||||||
this.descriptionGenerator = descriptionGenerator;
|
|
||||||
this.contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getExtension() {
|
|
||||||
return TrackFileFormat.KML.getExtension();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void prepare(OutputStream outputStream) {
|
|
||||||
this.printWriter = new PrintWriter(outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.flush();
|
|
||||||
printWriter = null;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@VisibleForTesting
|
||||||
public void writeHeader(Track[] tracks) {
|
KmlTrackWriter(Context context, boolean multiple, boolean playTrack, DescriptionGenerator descriptionGenerator) {
|
||||||
if (printWriter != null) {
|
this.context = context;
|
||||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
this.multiple = multiple;
|
||||||
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
|
this.playTrack = playTrack;
|
||||||
printWriter.println("xmlns:gx=\"http://www.google.com/kml/ext/2.2\"");
|
this.descriptionGenerator = descriptionGenerator;
|
||||||
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\">");
|
this.contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||||
printWriter.println("<Document>");
|
|
||||||
printWriter.println("<open>1</open>");
|
|
||||||
printWriter.println("<visibility>1</visibility>");
|
|
||||||
Track track = tracks[0];
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
|
||||||
printWriter.println("<atom:author><atom:name>"
|
|
||||||
+ StringUtils.formatCData(context.getString(R.string.app_name))
|
|
||||||
+ "</atom:name></atom:author>");
|
|
||||||
writeTrackStyle();
|
|
||||||
writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1);
|
|
||||||
writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1);
|
|
||||||
writePlacemarkerStyle(STATISTICS_STYLE, STATISTICS_ICON, 20, 2);
|
|
||||||
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
|
|
||||||
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
|
|
||||||
writeSensorStyle(POWER, context.getString(R.string.description_sensor_power));
|
|
||||||
writeSensorStyle(CADENCE, context.getString(R.string.description_sensor_cadence));
|
|
||||||
writeSensorStyle(HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
|
|
||||||
printWriter.println("</Schema>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeFooter() {
|
public String getExtension() {
|
||||||
if (printWriter != null) {
|
return TrackFileFormat.KML.getExtension();
|
||||||
printWriter.println("</Document>");
|
|
||||||
printWriter.println("</kml>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeBeginWaypoints(Track track) {
|
public void prepare(OutputStream outputStream) {
|
||||||
if (printWriter != null) {
|
this.printWriter = new PrintWriter(outputStream);
|
||||||
printWriter.println("<Folder><name>"
|
|
||||||
+ StringUtils.formatCData(context.getString(R.string.track_markers, track.getName()))
|
|
||||||
+ "</name>");
|
|
||||||
printWriter.println("<open>1</open>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeEndWaypoints() {
|
public void close() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("</Folder>");
|
printWriter.flush();
|
||||||
}
|
printWriter = null;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeWaypoint(Waypoint waypoint) {
|
|
||||||
if (printWriter != null) {
|
|
||||||
String styleName = waypoint.getType() == WaypointType.STATISTICS ? STATISTICS_STYLE : WAYPOINT_STYLE;
|
|
||||||
if (waypoint.hasPhoto()) {
|
|
||||||
float heading = getHeading(waypoint.getTrackId(), waypoint.getLocation());
|
|
||||||
writePhotoOverlay(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), styleName, waypoint.getLocation(), waypoint.getPhotoUrl(), heading);
|
|
||||||
} else {
|
|
||||||
writePlacemark(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), styleName, waypoint.getLocation());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBeginTracks() {
|
|
||||||
if (printWriter != null && multiple) {
|
|
||||||
printWriter.println("<Folder id=tour>");
|
|
||||||
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
|
|
||||||
printWriter.println("<open>1</open>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeEndTracks() {
|
|
||||||
if (printWriter != null && multiple) {
|
|
||||||
printWriter.println("</Folder>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBeginTrack(Track track, Location startLocation) {
|
|
||||||
if (printWriter != null) {
|
|
||||||
String name = context.getString(R.string.marker_label_start, track.getName());
|
|
||||||
writePlacemark(name, "", "", START_STYLE, startLocation);
|
|
||||||
printWriter.println("<Placemark>");
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
|
||||||
printWriter.println(
|
|
||||||
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
|
||||||
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
|
||||||
writeCategory(track.getCategory());
|
|
||||||
printWriter.println("<gx:MultiTrack>");
|
|
||||||
printWriter.println("<altitudeMode>absolute</altitudeMode>");
|
|
||||||
printWriter.println("<gx:interpolate>1</gx:interpolate>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeEndTrack(Track track, Location endLocation) {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.println("</gx:MultiTrack>");
|
|
||||||
printWriter.println("</Placemark>");
|
|
||||||
String name = context.getString(R.string.marker_label_end, track.getName());
|
|
||||||
String description = descriptionGenerator.generateTrackDescription(track, null, null, false);
|
|
||||||
writePlacemark(name, "", description, END_STYLE, endLocation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeOpenSegment() {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.println("<gx:Track>");
|
|
||||||
hasPower = false;
|
|
||||||
hasCadence = false;
|
|
||||||
hasHeartRate = false;
|
|
||||||
powerList.clear();
|
|
||||||
cadenceList.clear();
|
|
||||||
heartRateList.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeCloseSegment() {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.println("<ExtendedData>");
|
|
||||||
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
|
|
||||||
if (hasPower) {
|
|
||||||
writeSensorData(powerList, POWER);
|
|
||||||
}
|
|
||||||
if (hasCadence) {
|
|
||||||
writeSensorData(cadenceList, CADENCE);
|
|
||||||
}
|
|
||||||
if (hasHeartRate) {
|
|
||||||
writeSensorData(heartRateList, HEART_RATE);
|
|
||||||
}
|
|
||||||
printWriter.println("</SchemaData>");
|
|
||||||
printWriter.println("</ExtendedData>");
|
|
||||||
printWriter.println("</gx:Track>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeLocation(Location location) {
|
|
||||||
if (printWriter != null) {
|
|
||||||
printWriter.println(
|
|
||||||
"<when>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when>");
|
|
||||||
printWriter.println("<gx:coord>" + getCoordinates(location, " ") + "</gx:coord>");
|
|
||||||
if (location instanceof SensorDataSetLocation) {
|
|
||||||
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
|
|
||||||
if (sensorDataSet != null) {
|
|
||||||
if (sensorDataSet.hasHeartRate()) {
|
|
||||||
heartRateList.add(sensorDataSet.getHeartRate());
|
|
||||||
}
|
|
||||||
if (sensorDataSet.hasCadence()) {
|
|
||||||
cadenceList.add(sensorDataSet.getCadence());
|
|
||||||
}
|
|
||||||
if (sensorDataSet.hasPower()) {
|
|
||||||
powerList.add(sensorDataSet.getPower());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Writes the sensor data.
|
public void writeHeader(Track[] tracks) {
|
||||||
*
|
if (printWriter != null) {
|
||||||
* @param list a list of sensor data
|
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
* @param name the name of the sensor data
|
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
|
||||||
*/
|
printWriter.println("xmlns:gx=\"http://www.google.com/kml/ext/2.2\"");
|
||||||
private void writeSensorData(ArrayList<Float> list, String name) {
|
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\">");
|
||||||
printWriter.println("<gx:SimpleArrayData name=\"" + name + "\">");
|
printWriter.println("<Document>");
|
||||||
for (int i = 0; i < list.size(); i++) {
|
printWriter.println("<open>1</open>");
|
||||||
printWriter.println("<gx:value>" + list.get(i) + "</gx:value>");
|
printWriter.println("<visibility>1</visibility>");
|
||||||
|
Track track = tracks[0];
|
||||||
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
|
printWriter.println("<atom:author><atom:name>"
|
||||||
|
+ StringUtils.formatCData(context.getString(R.string.app_name))
|
||||||
|
+ "</atom:name></atom:author>");
|
||||||
|
writeTrackStyle();
|
||||||
|
writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1);
|
||||||
|
writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1);
|
||||||
|
writePlacemarkerStyle(STATISTICS_STYLE, STATISTICS_ICON, 20, 2);
|
||||||
|
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
|
||||||
|
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
|
||||||
|
writeSensorStyle(POWER, context.getString(R.string.description_sensor_power));
|
||||||
|
writeSensorStyle(CADENCE, context.getString(R.string.description_sensor_cadence));
|
||||||
|
writeSensorStyle(HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
|
||||||
|
printWriter.println("</Schema>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
printWriter.println("</gx:SimpleArrayData>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Writes a placemark.
|
public void writeFooter() {
|
||||||
*
|
if (printWriter != null) {
|
||||||
* @param name the name
|
printWriter.println("</Document>");
|
||||||
* @param category the category
|
printWriter.println("</kml>");
|
||||||
* @param description the description
|
}
|
||||||
* @param styleName the style name
|
|
||||||
* @param location the location
|
|
||||||
*/
|
|
||||||
private void writePlacemark(
|
|
||||||
String name, String category, String description, String styleName, Location location) {
|
|
||||||
if (location != null) {
|
|
||||||
printWriter.println("<Placemark>");
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
|
||||||
printWriter.println(
|
|
||||||
"<description>" + StringUtils.formatCData(description) + "</description>");
|
|
||||||
printWriter.println("<TimeStamp><when>"
|
|
||||||
+ StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
|
|
||||||
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
|
||||||
writeCategory(category);
|
|
||||||
printWriter.println("<Point>");
|
|
||||||
printWriter.println("<coordinates>" + getCoordinates(location, ",") + "</coordinates>");
|
|
||||||
printWriter.println("</Point>");
|
|
||||||
printWriter.println("</Placemark>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Writes a photo overlay.
|
public void writeBeginWaypoints(Track track) {
|
||||||
*
|
if (printWriter != null) {
|
||||||
* @param name the name
|
printWriter.println("<Folder><name>"
|
||||||
* @param category the category
|
+ StringUtils.formatCData(context.getString(R.string.track_markers, track.getName()))
|
||||||
* @param description the description
|
+ "</name>");
|
||||||
* @param styleName the style name
|
printWriter.println("<open>1</open>");
|
||||||
* @param location the location
|
}
|
||||||
* @param photoUrl the photo url
|
|
||||||
* @param heading the heading
|
|
||||||
*/
|
|
||||||
private void writePhotoOverlay(String name, String category, String description, String styleName,
|
|
||||||
Location location, String photoUrl, float heading) {
|
|
||||||
if (location != null) {
|
|
||||||
printWriter.println("<PhotoOverlay>");
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
|
||||||
printWriter.println(
|
|
||||||
"<description>" + StringUtils.formatCData(description) + "</description>");
|
|
||||||
printWriter.print("<Camera>");
|
|
||||||
printWriter.print("<longitude>" + location.getLongitude() + "</longitude>");
|
|
||||||
printWriter.print("<latitude>" + location.getLatitude() + "</latitude>");
|
|
||||||
printWriter.print("<altitude>20</altitude>");
|
|
||||||
printWriter.print("<heading>" + heading + "</heading>");
|
|
||||||
printWriter.print("<tilt>90</tilt>");
|
|
||||||
printWriter.println("</Camera>");
|
|
||||||
printWriter.println("<TimeStamp><when>"
|
|
||||||
+ StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
|
|
||||||
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
|
||||||
writeCategory(category);
|
|
||||||
if (playTrack) {
|
|
||||||
printWriter.println("<Icon><href>" + Uri.decode(photoUrl) + "</href></Icon>");
|
|
||||||
} else {
|
|
||||||
Uri uri = Uri.parse(photoUrl);
|
|
||||||
printWriter.println("<Icon><href>" + KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar
|
|
||||||
+ uri.getLastPathSegment() + "</href></Icon>");
|
|
||||||
}
|
|
||||||
|
|
||||||
printWriter.print("<ViewVolume>");
|
|
||||||
printWriter.print("<near>10</near>");
|
|
||||||
printWriter.print("<leftFov>-60</leftFov>");
|
|
||||||
printWriter.print("<rightFov>60</rightFov>");
|
|
||||||
printWriter.print("<bottomFov>-45</bottomFov>");
|
|
||||||
printWriter.print("<topFov>45</topFov>");
|
|
||||||
printWriter.println("</ViewVolume>");
|
|
||||||
printWriter.println("<Point>");
|
|
||||||
printWriter.println("<coordinates>" + getCoordinates(location, ",") + "</coordinates>");
|
|
||||||
printWriter.println("</Point>");
|
|
||||||
printWriter.println("</PhotoOverlay>");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Gets the heading to a location.
|
public void writeEndWaypoints() {
|
||||||
*
|
if (printWriter != null) {
|
||||||
* @param trackId the track id containing the location
|
printWriter.println("</Folder>");
|
||||||
* @param location the location
|
}
|
||||||
*/
|
|
||||||
private float getHeading(long trackId, Location location) {
|
|
||||||
long trackPointId = contentProviderUtils.getTrackPointId(trackId, location);
|
|
||||||
if (trackPointId == -1L) {
|
|
||||||
return location.getBearing();
|
|
||||||
}
|
}
|
||||||
Location viewLocation;
|
|
||||||
try (Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackPointId, 10, true)) {
|
@Override
|
||||||
if (cursor == null || cursor.getCount() == 0) {
|
public void writeWaypoint(Waypoint waypoint) {
|
||||||
return location.getBearing();
|
if (printWriter != null) {
|
||||||
}
|
String styleName = waypoint.getType() == WaypointType.STATISTICS ? STATISTICS_STYLE : WAYPOINT_STYLE;
|
||||||
cursor.moveToPosition(cursor.getCount() - 1);
|
if (waypoint.hasPhoto()) {
|
||||||
viewLocation = contentProviderUtils.createTrackPoint(cursor);
|
float heading = getHeading(waypoint.getTrackId(), waypoint.getLocation());
|
||||||
|
writePhotoOverlay(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), styleName, waypoint.getLocation(), waypoint.getPhotoUrl(), heading);
|
||||||
|
} else {
|
||||||
|
writePlacemark(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), styleName, waypoint.getLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return viewLocation.bearingTo(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCoordinates(Location location, String separator) {
|
@Override
|
||||||
StringBuilder builder = new StringBuilder();
|
public void writeBeginTracks() {
|
||||||
builder.append(location.getLongitude()).append(separator).append(location.getLatitude());
|
if (printWriter != null && multiple) {
|
||||||
if (location.hasAltitude()) {
|
printWriter.println("<Folder id=tour>");
|
||||||
builder.append(separator).append(location.getAltitude());
|
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
|
||||||
|
printWriter.println("<open>1</open>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Writes the category.
|
public void writeEndTracks() {
|
||||||
*
|
if (printWriter != null && multiple) {
|
||||||
* @param category the category
|
printWriter.println("</Folder>");
|
||||||
*/
|
}
|
||||||
private void writeCategory(String category) {
|
|
||||||
if (category == null || category.equals("")) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
printWriter.println("<ExtendedData>");
|
|
||||||
printWriter.println(
|
|
||||||
"<Data name=\"type\"><value>" + StringUtils.formatCData(category) + "</value></Data>");
|
|
||||||
printWriter.println("</ExtendedData>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Writes the track style.
|
public void writeBeginTrack(Track track, Location startLocation) {
|
||||||
*/
|
if (printWriter != null) {
|
||||||
private void writeTrackStyle() {
|
String name = context.getString(R.string.marker_label_start, track.getName());
|
||||||
printWriter.println("<Style id=\"" + TRACK_STYLE + "\">");
|
writePlacemark(name, "", "", START_STYLE, startLocation);
|
||||||
printWriter.println("<LineStyle><color>7f0000ff</color><width>4</width></LineStyle>");
|
printWriter.println("<Placemark>");
|
||||||
printWriter.println("<IconStyle>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<scale>1.3</scale>");
|
printWriter.println(
|
||||||
printWriter.println("<Icon><href>" + TRACK_ICON + "</href></Icon>");
|
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
||||||
printWriter.println("</IconStyle>");
|
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
||||||
printWriter.println("</Style>");
|
writeCategory(track.getCategory());
|
||||||
}
|
printWriter.println("<gx:MultiTrack>");
|
||||||
|
printWriter.println("<altitudeMode>absolute</altitudeMode>");
|
||||||
|
printWriter.println("<gx:interpolate>1</gx:interpolate>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Writes a placemarker style.
|
public void writeEndTrack(Track track, Location endLocation) {
|
||||||
*
|
if (printWriter != null) {
|
||||||
* @param name the name of the style
|
printWriter.println("</gx:MultiTrack>");
|
||||||
* @param url the url of the style icon
|
printWriter.println("</Placemark>");
|
||||||
* @param x the x position of the hotspot
|
String name = context.getString(R.string.marker_label_end, track.getName());
|
||||||
* @param y the y position of the hotspot
|
String description = descriptionGenerator.generateTrackDescription(track, null, null, false);
|
||||||
*/
|
writePlacemark(name, "", description, END_STYLE, endLocation);
|
||||||
private void writePlacemarkerStyle(String name, String url, int x, int y) {
|
}
|
||||||
printWriter.println("<Style id=\"" + name + "\"><IconStyle>");
|
}
|
||||||
printWriter.println("<scale>1.3</scale>");
|
|
||||||
printWriter.println("<Icon><href>" + url + "</href></Icon>");
|
|
||||||
printWriter.println(
|
|
||||||
"<hotSpot x=\"" + x + "\" y=\"" + y + "\" xunits=\"pixels\" yunits=\"pixels\"/>");
|
|
||||||
printWriter.println("</IconStyle></Style>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Writes a sensor style.
|
public void writeOpenSegment() {
|
||||||
*
|
if (printWriter != null) {
|
||||||
* @param name the name of the sesnor
|
printWriter.println("<gx:Track>");
|
||||||
* @param displayName the sensor display name
|
hasPower = false;
|
||||||
*/
|
hasCadence = false;
|
||||||
private void writeSensorStyle(String name, String displayName) {
|
hasHeartRate = false;
|
||||||
printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"int\">");
|
powerList.clear();
|
||||||
printWriter.println("<displayName>" + StringUtils.formatCData(displayName) + "</displayName>");
|
cadenceList.clear();
|
||||||
printWriter.println("</gx:SimpleArrayField>");
|
heartRateList.clear();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeCloseSegment() {
|
||||||
|
if (printWriter != null) {
|
||||||
|
printWriter.println("<ExtendedData>");
|
||||||
|
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
|
||||||
|
if (hasPower) {
|
||||||
|
writeSensorData(powerList, POWER);
|
||||||
|
}
|
||||||
|
if (hasCadence) {
|
||||||
|
writeSensorData(cadenceList, CADENCE);
|
||||||
|
}
|
||||||
|
if (hasHeartRate) {
|
||||||
|
writeSensorData(heartRateList, HEART_RATE);
|
||||||
|
}
|
||||||
|
printWriter.println("</SchemaData>");
|
||||||
|
printWriter.println("</ExtendedData>");
|
||||||
|
printWriter.println("</gx:Track>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeLocation(Location location) {
|
||||||
|
if (printWriter != null) {
|
||||||
|
printWriter.println(
|
||||||
|
"<when>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when>");
|
||||||
|
printWriter.println("<gx:coord>" + getCoordinates(location, " ") + "</gx:coord>");
|
||||||
|
if (location instanceof SensorDataSetLocation) {
|
||||||
|
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
|
||||||
|
if (sensorDataSet != null) {
|
||||||
|
if (sensorDataSet.hasHeartRate()) {
|
||||||
|
heartRateList.add(sensorDataSet.getHeartRate());
|
||||||
|
}
|
||||||
|
if (sensorDataSet.hasCadence()) {
|
||||||
|
cadenceList.add(sensorDataSet.getCadence());
|
||||||
|
}
|
||||||
|
if (sensorDataSet.hasPower()) {
|
||||||
|
powerList.add(sensorDataSet.getPower());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the sensor data.
|
||||||
|
*
|
||||||
|
* @param list a list of sensor data
|
||||||
|
* @param name the name of the sensor data
|
||||||
|
*/
|
||||||
|
private void writeSensorData(ArrayList<Float> list, String name) {
|
||||||
|
printWriter.println("<gx:SimpleArrayData name=\"" + name + "\">");
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
printWriter.println("<gx:value>" + list.get(i) + "</gx:value>");
|
||||||
|
}
|
||||||
|
printWriter.println("</gx:SimpleArrayData>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a placemark.
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param category the category
|
||||||
|
* @param description the description
|
||||||
|
* @param styleName the style name
|
||||||
|
* @param location the location
|
||||||
|
*/
|
||||||
|
private void writePlacemark(
|
||||||
|
String name, String category, String description, String styleName, Location location) {
|
||||||
|
if (location != null) {
|
||||||
|
printWriter.println("<Placemark>");
|
||||||
|
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||||
|
printWriter.println(
|
||||||
|
"<description>" + StringUtils.formatCData(description) + "</description>");
|
||||||
|
printWriter.println("<TimeStamp><when>"
|
||||||
|
+ StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
|
||||||
|
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
||||||
|
writeCategory(category);
|
||||||
|
printWriter.println("<Point>");
|
||||||
|
printWriter.println("<coordinates>" + getCoordinates(location, ",") + "</coordinates>");
|
||||||
|
printWriter.println("</Point>");
|
||||||
|
printWriter.println("</Placemark>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a photo overlay.
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @param category the category
|
||||||
|
* @param description the description
|
||||||
|
* @param styleName the style name
|
||||||
|
* @param location the location
|
||||||
|
* @param photoUrl the photo url
|
||||||
|
* @param heading the heading
|
||||||
|
*/
|
||||||
|
private void writePhotoOverlay(String name, String category, String description, String styleName,
|
||||||
|
Location location, String photoUrl, float heading) {
|
||||||
|
if (location != null) {
|
||||||
|
printWriter.println("<PhotoOverlay>");
|
||||||
|
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||||
|
printWriter.println(
|
||||||
|
"<description>" + StringUtils.formatCData(description) + "</description>");
|
||||||
|
printWriter.print("<Camera>");
|
||||||
|
printWriter.print("<longitude>" + location.getLongitude() + "</longitude>");
|
||||||
|
printWriter.print("<latitude>" + location.getLatitude() + "</latitude>");
|
||||||
|
printWriter.print("<altitude>20</altitude>");
|
||||||
|
printWriter.print("<heading>" + heading + "</heading>");
|
||||||
|
printWriter.print("<tilt>90</tilt>");
|
||||||
|
printWriter.println("</Camera>");
|
||||||
|
printWriter.println("<TimeStamp><when>"
|
||||||
|
+ StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
|
||||||
|
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
||||||
|
writeCategory(category);
|
||||||
|
if (playTrack) {
|
||||||
|
printWriter.println("<Icon><href>" + Uri.decode(photoUrl) + "</href></Icon>");
|
||||||
|
} else {
|
||||||
|
Uri uri = Uri.parse(photoUrl);
|
||||||
|
printWriter.println("<Icon><href>" + KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar
|
||||||
|
+ uri.getLastPathSegment() + "</href></Icon>");
|
||||||
|
}
|
||||||
|
|
||||||
|
printWriter.print("<ViewVolume>");
|
||||||
|
printWriter.print("<near>10</near>");
|
||||||
|
printWriter.print("<leftFov>-60</leftFov>");
|
||||||
|
printWriter.print("<rightFov>60</rightFov>");
|
||||||
|
printWriter.print("<bottomFov>-45</bottomFov>");
|
||||||
|
printWriter.print("<topFov>45</topFov>");
|
||||||
|
printWriter.println("</ViewVolume>");
|
||||||
|
printWriter.println("<Point>");
|
||||||
|
printWriter.println("<coordinates>" + getCoordinates(location, ",") + "</coordinates>");
|
||||||
|
printWriter.println("</Point>");
|
||||||
|
printWriter.println("</PhotoOverlay>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the heading to a location.
|
||||||
|
*
|
||||||
|
* @param trackId the track id containing the location
|
||||||
|
* @param location the location
|
||||||
|
*/
|
||||||
|
private float getHeading(long trackId, Location location) {
|
||||||
|
long trackPointId = contentProviderUtils.getTrackPointId(trackId, location);
|
||||||
|
if (trackPointId == -1L) {
|
||||||
|
return location.getBearing();
|
||||||
|
}
|
||||||
|
Location viewLocation;
|
||||||
|
try (Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackPointId, 10, true)) {
|
||||||
|
if (cursor == null || cursor.getCount() == 0) {
|
||||||
|
return location.getBearing();
|
||||||
|
}
|
||||||
|
cursor.moveToPosition(cursor.getCount() - 1);
|
||||||
|
viewLocation = contentProviderUtils.createTrackPoint(cursor);
|
||||||
|
}
|
||||||
|
return viewLocation.bearingTo(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCoordinates(Location location, String separator) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(location.getLongitude()).append(separator).append(location.getLatitude());
|
||||||
|
if (location.hasAltitude()) {
|
||||||
|
builder.append(separator).append(location.getAltitude());
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the category.
|
||||||
|
*
|
||||||
|
* @param category the category
|
||||||
|
*/
|
||||||
|
private void writeCategory(String category) {
|
||||||
|
if (category == null || category.equals("")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printWriter.println("<ExtendedData>");
|
||||||
|
printWriter.println(
|
||||||
|
"<Data name=\"type\"><value>" + StringUtils.formatCData(category) + "</value></Data>");
|
||||||
|
printWriter.println("</ExtendedData>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the track style.
|
||||||
|
*/
|
||||||
|
private void writeTrackStyle() {
|
||||||
|
printWriter.println("<Style id=\"" + TRACK_STYLE + "\">");
|
||||||
|
printWriter.println("<LineStyle><color>7f0000ff</color><width>4</width></LineStyle>");
|
||||||
|
printWriter.println("<IconStyle>");
|
||||||
|
printWriter.println("<scale>1.3</scale>");
|
||||||
|
printWriter.println("<Icon><href>" + TRACK_ICON + "</href></Icon>");
|
||||||
|
printWriter.println("</IconStyle>");
|
||||||
|
printWriter.println("</Style>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a placemarker style.
|
||||||
|
*
|
||||||
|
* @param name the name of the style
|
||||||
|
* @param url the url of the style icon
|
||||||
|
* @param x the x position of the hotspot
|
||||||
|
* @param y the y position of the hotspot
|
||||||
|
*/
|
||||||
|
private void writePlacemarkerStyle(String name, String url, int x, int y) {
|
||||||
|
printWriter.println("<Style id=\"" + name + "\"><IconStyle>");
|
||||||
|
printWriter.println("<scale>1.3</scale>");
|
||||||
|
printWriter.println("<Icon><href>" + url + "</href></Icon>");
|
||||||
|
printWriter.println(
|
||||||
|
"<hotSpot x=\"" + x + "\" y=\"" + y + "\" xunits=\"pixels\" yunits=\"pixels\"/>");
|
||||||
|
printWriter.println("</IconStyle></Style>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a sensor style.
|
||||||
|
*
|
||||||
|
* @param name the name of the sesnor
|
||||||
|
* @param displayName the sensor display name
|
||||||
|
*/
|
||||||
|
private void writeSensorStyle(String name, String displayName) {
|
||||||
|
printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"int\">");
|
||||||
|
printWriter.println("<displayName>" + StringUtils.formatCData(displayName) + "</displayName>");
|
||||||
|
printWriter.println("</gx:SimpleArrayField>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+130
-130
@@ -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;
|
||||||
@@ -43,143 +43,143 @@ import java.util.zip.ZipOutputStream;
|
|||||||
*/
|
*/
|
||||||
public class KmzTrackExporter implements TrackExporter {
|
public class KmzTrackExporter implements TrackExporter {
|
||||||
|
|
||||||
public static final String KMZ_EXTENSION = "kmz";
|
public static final String KMZ_EXTENSION = "kmz";
|
||||||
public static final String KMZ_IMAGES_DIR = "images";
|
public static final String KMZ_IMAGES_DIR = "images";
|
||||||
public static final String KMZ_KML_FILE = "doc.kml";
|
public static final String KMZ_KML_FILE = "doc.kml";
|
||||||
|
|
||||||
private static final String TAG = KmzTrackExporter.class.getSimpleName();
|
private static final String TAG = KmzTrackExporter.class.getSimpleName();
|
||||||
private static final int BUFFER_SIZE = 4096;
|
private static final int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private final FileTrackExporter fileTrackExporter;
|
private final FileTrackExporter fileTrackExporter;
|
||||||
private final Track[] tracks;
|
private final Track[] tracks;
|
||||||
private final long photoSize;
|
private final long photoSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param contentProviderUtils the content provider utils
|
* @param contentProviderUtils the content provider utils
|
||||||
* @param fileTrackExporter the file track exporter
|
* @param fileTrackExporter the file track exporter
|
||||||
* @param tracks the tracks to export
|
* @param tracks the tracks to export
|
||||||
* @param context the context
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
public KmzTrackExporter(ContentProviderUtils contentProviderUtils,
|
public KmzTrackExporter(ContentProviderUtils contentProviderUtils,
|
||||||
FileTrackExporter fileTrackExporter, Track[] tracks, Context context) {
|
FileTrackExporter fileTrackExporter, Track[] tracks, Context context) {
|
||||||
this.contentProviderUtils = contentProviderUtils;
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
this.fileTrackExporter = fileTrackExporter;
|
this.fileTrackExporter = fileTrackExporter;
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
this.photoSize = PreferencesUtils.getInt(
|
this.photoSize = PreferencesUtils.getInt(
|
||||||
context, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
context, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTrack(OutputStream outputStream) {
|
public boolean writeTrack(OutputStream outputStream) {
|
||||||
ZipOutputStream zipOutputStream = null;
|
ZipOutputStream zipOutputStream = null;
|
||||||
try {
|
|
||||||
zipOutputStream = new ZipOutputStream(outputStream);
|
|
||||||
|
|
||||||
// Add kml file
|
|
||||||
ZipEntry zipEntry = new ZipEntry(KMZ_KML_FILE);
|
|
||||||
zipOutputStream.putNextEntry(zipEntry);
|
|
||||||
|
|
||||||
boolean success = fileTrackExporter.writeTrack(zipOutputStream);
|
|
||||||
zipOutputStream.closeEntry();
|
|
||||||
if (!success) {
|
|
||||||
Log.e(TAG, "Unable to write kml in kmz");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add photos
|
|
||||||
addImages(zipOutputStream);
|
|
||||||
return true;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(TAG, "Unable to write track", e);
|
|
||||||
return false;
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Unable to write track", e);
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (zipOutputStream != null) {
|
|
||||||
try {
|
try {
|
||||||
zipOutputStream.close();
|
zipOutputStream = new ZipOutputStream(outputStream);
|
||||||
|
|
||||||
|
// Add kml file
|
||||||
|
ZipEntry zipEntry = new ZipEntry(KMZ_KML_FILE);
|
||||||
|
zipOutputStream.putNextEntry(zipEntry);
|
||||||
|
|
||||||
|
boolean success = fileTrackExporter.writeTrack(zipOutputStream);
|
||||||
|
zipOutputStream.closeEntry();
|
||||||
|
if (!success) {
|
||||||
|
Log.e(TAG, "Unable to write kml in kmz");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add photos
|
||||||
|
addImages(zipOutputStream);
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "Unable to write track", e);
|
||||||
|
return false;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Unable to close zip input stream", e);
|
Log.e(TAG, "Unable to write track", e);
|
||||||
}
|
return false;
|
||||||
}
|
} finally {
|
||||||
}
|
if (zipOutputStream != null) {
|
||||||
}
|
try {
|
||||||
|
zipOutputStream.close();
|
||||||
private void addImages(ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
|
} catch (IOException e) {
|
||||||
for (Track track : tracks) {
|
Log.e(TAG, "Unable to close zip input stream", e);
|
||||||
try (Cursor cursor = contentProviderUtils.getWaypointCursor(track.getId(), -1L, -1)) {
|
}
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
/*
|
|
||||||
* Yes, this will skip the first waypoint and that is intentional as
|
|
||||||
* the first waypoint holds the stats for the track.
|
|
||||||
*/
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
}
|
||||||
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
|
|
||||||
if (waypoint.hasPhoto()) {
|
|
||||||
addImage(zipOutputStream, waypoint.getPhotoUrl());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addImage(ZipOutputStream zipOutputStream, String photoUrl) throws IOException {
|
|
||||||
Uri uri = Uri.parse(photoUrl);
|
|
||||||
File file = new File(uri.getPath());
|
|
||||||
if (!file.exists()) {
|
|
||||||
Log.e(TAG, "file not found " + photoUrl);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ZipEntry zipEntry = new ZipEntry(
|
private void addImages(ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
|
||||||
KMZ_IMAGES_DIR + File.separatorChar + uri.getLastPathSegment());
|
for (Track track : tracks) {
|
||||||
zipOutputStream.putNextEntry(zipEntry);
|
try (Cursor cursor = contentProviderUtils.getWaypointCursor(track.getId(), -1L, -1)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
int sampleSize;
|
/*
|
||||||
if (photoSize == -1) {
|
* Yes, this will skip the first waypoint and that is intentional as
|
||||||
sampleSize = 1;
|
* the first waypoint holds the stats for the track.
|
||||||
} else {
|
*/
|
||||||
long size = file.length();
|
while (cursor.moveToNext()) {
|
||||||
// Convert from kilobytes to bytes.
|
if (Thread.interrupted()) {
|
||||||
long limit = photoSize * 1024;
|
throw new InterruptedException();
|
||||||
sampleSize = size > limit ? (int) Math.ceil(size / limit) : 1;
|
}
|
||||||
|
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
|
||||||
|
if (waypoint.hasPhoto()) {
|
||||||
|
addImage(zipOutputStream, waypoint.getPhotoUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sampleSize == 1) {
|
|
||||||
readFromFile(zipOutputStream, uri);
|
|
||||||
} else {
|
|
||||||
readFromScaledBitmap(zipOutputStream, uri, sampleSize);
|
|
||||||
}
|
|
||||||
zipOutputStream.closeEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readFromScaledBitmap(ZipOutputStream zipOutputStream, Uri uri, int sampleSize) {
|
private void addImage(ZipOutputStream zipOutputStream, String photoUrl) throws IOException {
|
||||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
Uri uri = Uri.parse(photoUrl);
|
||||||
options.inJustDecodeBounds = false;
|
File file = new File(uri.getPath());
|
||||||
options.inSampleSize = sampleSize;
|
if (!file.exists()) {
|
||||||
|
Log.e(TAG, "file not found " + photoUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath(), options);
|
ZipEntry zipEntry = new ZipEntry(
|
||||||
if (bitmap == null) {
|
KMZ_IMAGES_DIR + File.separatorChar + uri.getLastPathSegment());
|
||||||
return;
|
zipOutputStream.putNextEntry(zipEntry);
|
||||||
}
|
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, zipOutputStream);
|
|
||||||
bitmap.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readFromFile(ZipOutputStream zipOutputStream, Uri uri) throws IOException {
|
int sampleSize;
|
||||||
try (FileInputStream fileInputStream = new FileInputStream(new File(uri.getPath()))) {
|
if (photoSize == -1) {
|
||||||
byte[] buffer = new byte[BUFFER_SIZE];
|
sampleSize = 1;
|
||||||
int byteCount;
|
} else {
|
||||||
while ((byteCount = fileInputStream.read(buffer)) != -1) {
|
long size = file.length();
|
||||||
zipOutputStream.write(buffer, 0, byteCount);
|
// Convert from kilobytes to bytes.
|
||||||
}
|
long limit = photoSize * 1024;
|
||||||
|
sampleSize = size > limit ? (int) Math.ceil(size / limit) : 1;
|
||||||
|
}
|
||||||
|
if (sampleSize == 1) {
|
||||||
|
readFromFile(zipOutputStream, uri);
|
||||||
|
} else {
|
||||||
|
readFromScaledBitmap(zipOutputStream, uri, sampleSize);
|
||||||
|
}
|
||||||
|
zipOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFromScaledBitmap(ZipOutputStream zipOutputStream, Uri uri, int sampleSize) {
|
||||||
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
options.inJustDecodeBounds = false;
|
||||||
|
options.inSampleSize = sampleSize;
|
||||||
|
|
||||||
|
Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath(), options);
|
||||||
|
if (bitmap == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, zipOutputStream);
|
||||||
|
bitmap.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFromFile(ZipOutputStream zipOutputStream, Uri uri) throws IOException {
|
||||||
|
try (FileInputStream fileInputStream = new FileInputStream(new File(uri.getPath()))) {
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int byteCount;
|
||||||
|
while ((byteCount = fileInputStream.read(buffer)) != -1) {
|
||||||
|
zipOutputStream.write(buffer, 0, byteCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+168
-168
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,187 +41,187 @@ import java.io.File;
|
|||||||
*/
|
*/
|
||||||
public class SaveActivity extends Activity {
|
public class SaveActivity extends Activity {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
||||||
public static final String EXTRA_TRACK_IDS = "track_ids";
|
public static final String EXTRA_TRACK_IDS = "track_ids";
|
||||||
|
|
||||||
@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.
|
||||||
public static final String EXTRA_PLAY_TRACK = "play_track";
|
public static final String EXTRA_PLAY_TRACK = "play_track";
|
||||||
|
|
||||||
private static final int DIALOG_PROGRESS_ID = 0;
|
private static final int DIALOG_PROGRESS_ID = 0;
|
||||||
private static final int DIALOG_RESULT_ID = 1;
|
private static final int DIALOG_RESULT_ID = 1;
|
||||||
|
|
||||||
private TrackFileFormat trackFileFormat;
|
private TrackFileFormat trackFileFormat;
|
||||||
private long[] trackIds;
|
private long[] trackIds;
|
||||||
private boolean playTrack;
|
private boolean playTrack;
|
||||||
private String directoryDisplayName;
|
private String directoryDisplayName;
|
||||||
|
|
||||||
private SaveAsyncTask saveAsyncTask;
|
private SaveAsyncTask saveAsyncTask;
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
|
|
||||||
// the number of tracks successfully saved
|
// the number of tracks successfully saved
|
||||||
private int successCount;
|
private int successCount;
|
||||||
|
|
||||||
// the number of tracks to save
|
// the number of tracks to save
|
||||||
private int totalCount;
|
private int totalCount;
|
||||||
|
|
||||||
// the last successfully saved path
|
// the last successfully saved path
|
||||||
private String savedPath;
|
private String savedPath;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||||
trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
||||||
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
|
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
|
||||||
|
|
||||||
if (!FileUtils.isExternalStorageWriteable()) {
|
if (!FileUtils.isExternalStorageWriteable()) {
|
||||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File directory = playTrack ? new File(getCacheDir(), FileUtils.PLAY_TRACKS_DIR)
|
File directory = playTrack ? new File(getCacheDir(), FileUtils.PLAY_TRACKS_DIR)
|
||||||
: new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
: new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
||||||
if (!FileUtils.ensureDirectoryExists(directory)) {
|
if (!FileUtils.ensureDirectoryExists(directory)) {
|
||||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playTrack) {
|
if (playTrack) {
|
||||||
for (File file : directory.listFiles()) {
|
for (File file : directory.listFiles()) {
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryDisplayName = playTrack ? directory.getName()
|
directoryDisplayName = playTrack ? directory.getName()
|
||||||
: FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
: FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||||
|
|
||||||
Object retained = getLastNonConfigurationInstance();
|
Object retained = getLastNonConfigurationInstance();
|
||||||
if (retained instanceof SaveAsyncTask) {
|
if (retained instanceof SaveAsyncTask) {
|
||||||
saveAsyncTask = (SaveAsyncTask) retained;
|
saveAsyncTask = (SaveAsyncTask) retained;
|
||||||
saveAsyncTask.setActivity(this);
|
saveAsyncTask.setActivity(this);
|
||||||
} else {
|
|
||||||
saveAsyncTask = new SaveAsyncTask(this, trackIds, trackFileFormat, playTrack, directory);
|
|
||||||
saveAsyncTask.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object onRetainNonConfigurationInstance() {
|
|
||||||
saveAsyncTask.setActivity(null);
|
|
||||||
return saveAsyncTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
switch (id) {
|
|
||||||
case DIALOG_PROGRESS_ID:
|
|
||||||
progressDialog = DialogUtils.createHorizontalProgressDialog(this,
|
|
||||||
R.string.export_external_storage_progress_message,
|
|
||||||
new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
saveAsyncTask.cancel(true);
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
}, directoryDisplayName);
|
|
||||||
return progressDialog;
|
|
||||||
case DIALOG_RESULT_ID:
|
|
||||||
int iconId;
|
|
||||||
int titleId;
|
|
||||||
String message;
|
|
||||||
String totalTracks = getResources()
|
|
||||||
.getQuantityString(R.plurals.tracks, totalCount, totalCount);
|
|
||||||
if (successCount == totalCount) {
|
|
||||||
iconId = R.drawable.ic_dialog_success;
|
|
||||||
titleId = R.string.generic_success_title;
|
|
||||||
message = getString(
|
|
||||||
R.string.export_external_storage_success, totalTracks, directoryDisplayName);
|
|
||||||
} else {
|
} else {
|
||||||
iconId = android.R.drawable.ic_dialog_alert;
|
saveAsyncTask = new SaveAsyncTask(this, trackIds, trackFileFormat, playTrack, directory);
|
||||||
titleId = R.string.generic_error_title;
|
saveAsyncTask.execute();
|
||||||
message = getString(R.string.export_external_storage_error, successCount, totalTracks,
|
|
||||||
directoryDisplayName);
|
|
||||||
}
|
}
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this).setCancelable(true)
|
}
|
||||||
.setIcon(iconId).setMessage(message)
|
|
||||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
@Override
|
||||||
@Override
|
public Object onRetainNonConfigurationInstance() {
|
||||||
public void onCancel(DialogInterface dialog) {
|
saveAsyncTask.setActivity(null);
|
||||||
dialog.dismiss();
|
return saveAsyncTask;
|
||||||
finish();
|
}
|
||||||
}
|
|
||||||
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
@Override
|
||||||
@Override
|
protected Dialog onCreateDialog(int id) {
|
||||||
public void onClick(DialogInterface dialog, int arg1) {
|
switch (id) {
|
||||||
dialog.dismiss();
|
case DIALOG_PROGRESS_ID:
|
||||||
finish();
|
progressDialog = DialogUtils.createHorizontalProgressDialog(this,
|
||||||
}
|
R.string.export_external_storage_progress_message,
|
||||||
}).setTitle(titleId);
|
new DialogInterface.OnCancelListener() {
|
||||||
if (!playTrack && trackIds.length == 1 && trackIds[0] != -1L && successCount == totalCount
|
@Override
|
||||||
&& savedPath != null) {
|
public void onCancel(DialogInterface dialog) {
|
||||||
builder.setNegativeButton(
|
saveAsyncTask.cancel(true);
|
||||||
R.string.share_track_share_file, new DialogInterface.OnClickListener() {
|
dialog.dismiss();
|
||||||
@Override
|
}
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
}, directoryDisplayName);
|
||||||
Intent intent = IntentUtils.newShareFileIntent(
|
return progressDialog;
|
||||||
SaveActivity.this, trackIds[0], savedPath, trackFileFormat);
|
case DIALOG_RESULT_ID:
|
||||||
startActivity(
|
int iconId;
|
||||||
Intent.createChooser(intent, getString(R.string.share_track_picker_title)));
|
int titleId;
|
||||||
finish();
|
String message;
|
||||||
|
String totalTracks = getResources()
|
||||||
|
.getQuantityString(R.plurals.tracks, totalCount, totalCount);
|
||||||
|
if (successCount == totalCount) {
|
||||||
|
iconId = R.drawable.ic_dialog_success;
|
||||||
|
titleId = R.string.generic_success_title;
|
||||||
|
message = getString(
|
||||||
|
R.string.export_external_storage_success, totalTracks, directoryDisplayName);
|
||||||
|
} else {
|
||||||
|
iconId = android.R.drawable.ic_dialog_alert;
|
||||||
|
titleId = R.string.generic_error_title;
|
||||||
|
message = getString(R.string.export_external_storage_error, successCount, totalTracks,
|
||||||
|
directoryDisplayName);
|
||||||
}
|
}
|
||||||
});
|
AlertDialog.Builder builder = new AlertDialog.Builder(this).setCancelable(true)
|
||||||
|
.setIcon(iconId).setMessage(message)
|
||||||
|
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||||
|
@Override
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int arg1) {
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}).setTitle(titleId);
|
||||||
|
if (!playTrack && trackIds.length == 1 && trackIds[0] != -1L && successCount == totalCount
|
||||||
|
&& savedPath != null) {
|
||||||
|
builder.setNegativeButton(
|
||||||
|
R.string.share_track_share_file, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
Intent intent = IntentUtils.newShareFileIntent(
|
||||||
|
SaveActivity.this, trackIds[0], savedPath, trackFileFormat);
|
||||||
|
startActivity(
|
||||||
|
Intent.createChooser(intent, getString(R.string.share_track_picker_title)));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
final Dialog dialog = builder.create();
|
||||||
|
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onShow(DialogInterface dialogInterface) {
|
||||||
|
DialogUtils.setDialogTitleDivider(SaveActivity.this, dialog);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return dialog;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
final Dialog dialog = builder.create();
|
|
||||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onShow(DialogInterface dialogInterface) {
|
|
||||||
DialogUtils.setDialogTitleDivider(SaveActivity.this, dialog);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return dialog;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes when the associated AsyncTask completes.
|
* Invokes when the associated AsyncTask completes.
|
||||||
*
|
*
|
||||||
* @param aSuccessCount the number of tracks successfully saved
|
* @param aSuccessCount the number of tracks successfully saved
|
||||||
* @param aTotalCount the number of tracks to save
|
* @param aTotalCount the number of tracks to save
|
||||||
* @param aSavedPath the last successfully saved path
|
* @param aSavedPath the last successfully saved path
|
||||||
*/
|
*/
|
||||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, String aSavedPath) {
|
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, String aSavedPath) {
|
||||||
successCount = aSuccessCount;
|
successCount = aSuccessCount;
|
||||||
totalCount = aTotalCount;
|
totalCount = aTotalCount;
|
||||||
savedPath = aSavedPath;
|
savedPath = aSavedPath;
|
||||||
removeDialog(DIALOG_PROGRESS_ID);
|
removeDialog(DIALOG_PROGRESS_ID);
|
||||||
showDialog(DIALOG_RESULT_ID);
|
showDialog(DIALOG_RESULT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the progress dialog.
|
* Shows the progress dialog.
|
||||||
*/
|
*/
|
||||||
public void showProgressDialog() {
|
public void showProgressDialog() {
|
||||||
showDialog(DIALOG_PROGRESS_ID);
|
showDialog(DIALOG_PROGRESS_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the progress dialog value.
|
* Sets the progress dialog value.
|
||||||
*
|
*
|
||||||
* @param number the number of points saved
|
* @param number the number of points saved
|
||||||
* @param max the maximum number of points
|
* @param max the maximum number of points
|
||||||
*/
|
*/
|
||||||
public void setProgressDialogValue(int number, int max) {
|
public void setProgressDialogValue(int number, int max) {
|
||||||
if (progressDialog != null) {
|
if (progressDialog != null) {
|
||||||
progressDialog.setIndeterminate(false);
|
progressDialog.setIndeterminate(false);
|
||||||
progressDialog.setMax(max);
|
progressDialog.setMax(max);
|
||||||
progressDialog.setProgress(Math.min(number, max));
|
progressDialog.setProgress(Math.min(number, max));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+193
-195
@@ -43,215 +43,213 @@ 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 final long[] trackIds;
|
||||||
|
private final TrackFileFormat trackFileFormat;
|
||||||
|
@Deprecated //TODO Seems to be a left over from Google Earth integration and can be removed.
|
||||||
|
private final boolean playTrack;
|
||||||
|
private final File directory;
|
||||||
|
private final Context context;
|
||||||
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
private SaveActivity saveActivity;
|
||||||
|
private WakeLock wakeLock;
|
||||||
|
|
||||||
private SaveActivity saveActivity;
|
// true if the AsyncTask has completed
|
||||||
private final long[] trackIds;
|
private boolean completed;
|
||||||
private final TrackFileFormat trackFileFormat;
|
|
||||||
@Deprecated //TODO Seems to be a left over from Google Earth integration and can be removed.
|
|
||||||
private final boolean playTrack;
|
|
||||||
private final File directory;
|
|
||||||
private final Context context;
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
|
||||||
|
|
||||||
private WakeLock wakeLock;
|
// the number of tracks successfully saved
|
||||||
|
private int successCount;
|
||||||
|
|
||||||
// true if the AsyncTask has completed
|
// the number of tracks to save
|
||||||
private boolean completed;
|
private int totalCount;
|
||||||
|
|
||||||
// the number of tracks successfully saved
|
// the last successfully saved path
|
||||||
private int successCount;
|
private String savedPath;
|
||||||
|
|
||||||
// the number of tracks to save
|
/**
|
||||||
private int totalCount;
|
* Creates an AsyncTask.
|
||||||
|
*
|
||||||
|
* @param saveActivity the activity currently associated with this task
|
||||||
|
* @param trackIds the track ids to save. To save all, set to size 1 with
|
||||||
|
* trackIds[0] == -1L
|
||||||
|
* @param trackFileFormat the track file format
|
||||||
|
* @param playTrack true to play track
|
||||||
|
* @param directory the directory to write the file
|
||||||
|
*/
|
||||||
|
public SaveAsyncTask(SaveActivity saveActivity, long[] trackIds, TrackFileFormat trackFileFormat, boolean playTrack, File directory) {
|
||||||
|
this.saveActivity = saveActivity;
|
||||||
|
this.trackIds = trackIds;
|
||||||
|
this.trackFileFormat = trackFileFormat;
|
||||||
|
this.playTrack = playTrack;
|
||||||
|
this.directory = directory;
|
||||||
|
context = saveActivity.getApplicationContext();
|
||||||
|
contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||||
|
|
||||||
// the last successfully saved path
|
completed = false;
|
||||||
private String savedPath;
|
successCount = 0;
|
||||||
|
totalCount = 0;
|
||||||
/**
|
savedPath = null;
|
||||||
* Creates an AsyncTask.
|
|
||||||
*
|
|
||||||
* @param saveActivity the activity currently associated with this task
|
|
||||||
* @param trackIds the track ids to save. To save all, set to size 1 with
|
|
||||||
* trackIds[0] == -1L
|
|
||||||
* @param trackFileFormat the track file format
|
|
||||||
* @param playTrack true to play track
|
|
||||||
* @param directory the directory to write the file
|
|
||||||
*/
|
|
||||||
public SaveAsyncTask(SaveActivity saveActivity, long[] trackIds, TrackFileFormat trackFileFormat, boolean playTrack, File directory) {
|
|
||||||
this.saveActivity = saveActivity;
|
|
||||||
this.trackIds = trackIds;
|
|
||||||
this.trackFileFormat = trackFileFormat;
|
|
||||||
this.playTrack = playTrack;
|
|
||||||
this.directory = directory;
|
|
||||||
context = saveActivity.getApplicationContext();
|
|
||||||
contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
|
||||||
|
|
||||||
completed = false;
|
|
||||||
successCount = 0;
|
|
||||||
totalCount = 0;
|
|
||||||
savedPath = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current activity associated with this AsyncTask.
|
|
||||||
*
|
|
||||||
* @param saveActivity the current activity, can be null
|
|
||||||
*/
|
|
||||||
public void setActivity(SaveActivity saveActivity) {
|
|
||||||
this.saveActivity = saveActivity;
|
|
||||||
if (completed && saveActivity != null) {
|
|
||||||
saveActivity.onAsyncTaskCompleted(successCount, totalCount, savedPath);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void onPreExecute() {
|
* Sets the current activity associated with this AsyncTask.
|
||||||
if (saveActivity != null) {
|
*
|
||||||
saveActivity.showProgressDialog();
|
* @param saveActivity the current activity, can be null
|
||||||
}
|
*/
|
||||||
}
|
public void setActivity(SaveActivity saveActivity) {
|
||||||
|
this.saveActivity = saveActivity;
|
||||||
@Override
|
if (completed && saveActivity != null) {
|
||||||
protected Boolean doInBackground(Void... params) {
|
saveActivity.onAsyncTaskCompleted(successCount, totalCount, savedPath);
|
||||||
try {
|
|
||||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
|
||||||
boolean isRecording = PreferencesUtils.getLong(saveActivity, R.string.recording_track_id_key) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
|
||||||
boolean isPaused = PreferencesUtils.getBoolean(saveActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
|
||||||
// Get the wake lock if not recording or paused
|
|
||||||
if (!isRecording || isPaused) {
|
|
||||||
wakeLock = SystemUtils.acquireWakeLock(saveActivity, wakeLock);
|
|
||||||
}
|
|
||||||
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
|
||||||
return saveAllTracks();
|
|
||||||
} else {
|
|
||||||
totalCount = 1;
|
|
||||||
Track[] tracks = new Track[trackIds.length];
|
|
||||||
for (int i = 0; i < trackIds.length; i++) {
|
|
||||||
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
|
|
||||||
if (tracks[i] == null) {
|
|
||||||
Log.d(TAG, "No track for " + trackIds[i]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (saveTracks(tracks)) {
|
}
|
||||||
successCount = 1;
|
|
||||||
return true;
|
@Override
|
||||||
} else {
|
protected void onPreExecute() {
|
||||||
return false;
|
if (saveActivity != null) {
|
||||||
|
saveActivity.showProgressDialog();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (wakeLock != null && wakeLock.isHeld()) {
|
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Integer... values) {
|
|
||||||
if (saveActivity != null) {
|
|
||||||
saveActivity.setProgressDialogValue(values[0], values[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
completed = true;
|
|
||||||
if (saveActivity != null) {
|
|
||||||
saveActivity.onAsyncTaskCompleted(successCount, totalCount, savedPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCancelled() {
|
|
||||||
completed = true;
|
|
||||||
if (saveActivity != null) {
|
|
||||||
saveActivity.onAsyncTaskCompleted(successCount, totalCount, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves tracks to one file.
|
|
||||||
*
|
|
||||||
* @param tracks the tracks
|
|
||||||
*/
|
|
||||||
private Boolean saveTracks(Track[] tracks) {
|
|
||||||
if (tracks.length == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Track track = tracks[0];
|
@Override
|
||||||
boolean useKmz = trackFileFormat == TrackFileFormat.KML && !playTrack;
|
protected Boolean doInBackground(Void... params) {
|
||||||
String extension = useKmz ? KmzTrackExporter.KMZ_EXTENSION : trackFileFormat.getExtension();
|
|
||||||
FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, tracks,
|
|
||||||
trackFileFormat.newTrackWriter(context, tracks.length > 1, playTrack),
|
|
||||||
new TrackExporterListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProgressUpdate(int number, int max) {
|
|
||||||
/*
|
|
||||||
* If only saving one track, update the progress dialog once every
|
|
||||||
* 500 points
|
|
||||||
*/
|
|
||||||
if (trackIds.length == 1 && trackIds[0] != -1L && number % 500 == 0) {
|
|
||||||
publishProgress(number, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
TrackExporter trackExporter = useKmz ? new KmzTrackExporter(contentProviderUtils, fileTrackExporter, tracks, context) : fileTrackExporter;
|
|
||||||
|
|
||||||
String fileName = FileUtils.buildUniqueFileName(directory, track.getName(), extension);
|
|
||||||
File file = new File(directory, fileName);
|
|
||||||
FileOutputStream fileOutputStream = null;
|
|
||||||
try {
|
|
||||||
fileOutputStream = new FileOutputStream(file);
|
|
||||||
if (trackExporter.writeTrack(fileOutputStream)) {
|
|
||||||
savedPath = file.getAbsolutePath();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (!file.delete()) {
|
|
||||||
Log.d(TAG, "Unable to delete file");
|
|
||||||
}
|
|
||||||
Log.e(TAG, "Unable to export track");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.e(TAG, "Unable to open file " + file.getName(), e);
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (fileOutputStream != null) {
|
|
||||||
try {
|
try {
|
||||||
fileOutputStream.close();
|
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
||||||
} catch (IOException e) {
|
boolean isRecording = PreferencesUtils.getLong(saveActivity, R.string.recording_track_id_key) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
Log.e(TAG, "Unable to close file output stream", e);
|
boolean isPaused = PreferencesUtils.getBoolean(saveActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||||
|
// Get the wake lock if not recording or paused
|
||||||
|
if (!isRecording || isPaused) {
|
||||||
|
wakeLock = SystemUtils.acquireWakeLock(saveActivity, wakeLock);
|
||||||
|
}
|
||||||
|
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
||||||
|
return saveAllTracks();
|
||||||
|
} else {
|
||||||
|
totalCount = 1;
|
||||||
|
Track[] tracks = new Track[trackIds.length];
|
||||||
|
for (int i = 0; i < trackIds.length; i++) {
|
||||||
|
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
|
||||||
|
if (tracks[i] == null) {
|
||||||
|
Log.d(TAG, "No track for " + trackIds[i]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (saveTracks(tracks)) {
|
||||||
|
successCount = 1;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (wakeLock != null && wakeLock.isHeld()) {
|
||||||
|
wakeLock.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Saves all the tracks.
|
protected void onProgressUpdate(Integer... values) {
|
||||||
*/
|
if (saveActivity != null) {
|
||||||
private Boolean saveAllTracks() {
|
saveActivity.setProgressDialogValue(values[0], values[1]);
|
||||||
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
|
}
|
||||||
if (cursor == null) {
|
}
|
||||||
return false;
|
|
||||||
}
|
@Override
|
||||||
totalCount = cursor.getCount();
|
protected void onPostExecute(Boolean result) {
|
||||||
for (int i = 0; i < totalCount; i++) {
|
completed = true;
|
||||||
if (isCancelled()) {
|
if (saveActivity != null) {
|
||||||
return false;
|
saveActivity.onAsyncTaskCompleted(successCount, totalCount, savedPath);
|
||||||
}
|
}
|
||||||
cursor.moveToPosition(i);
|
}
|
||||||
Track track = contentProviderUtils.createTrack(cursor);
|
|
||||||
if (track != null && saveTracks(new Track[]{track})) {
|
@Override
|
||||||
successCount++;
|
protected void onCancelled() {
|
||||||
}
|
completed = true;
|
||||||
publishProgress(i + 1, totalCount);
|
if (saveActivity != null) {
|
||||||
}
|
saveActivity.onAsyncTaskCompleted(successCount, totalCount, null);
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Saves tracks to one file.
|
||||||
|
*
|
||||||
|
* @param tracks the tracks
|
||||||
|
*/
|
||||||
|
private Boolean saveTracks(Track[] tracks) {
|
||||||
|
if (tracks.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Track track = tracks[0];
|
||||||
|
boolean useKmz = trackFileFormat == TrackFileFormat.KML && !playTrack;
|
||||||
|
String extension = useKmz ? KmzTrackExporter.KMZ_EXTENSION : trackFileFormat.getExtension();
|
||||||
|
FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, tracks,
|
||||||
|
trackFileFormat.newTrackWriter(context, tracks.length > 1, playTrack),
|
||||||
|
new TrackExporterListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressUpdate(int number, int max) {
|
||||||
|
/*
|
||||||
|
* If only saving one track, update the progress dialog once every
|
||||||
|
* 500 points
|
||||||
|
*/
|
||||||
|
if (trackIds.length == 1 && trackIds[0] != -1L && number % 500 == 0) {
|
||||||
|
publishProgress(number, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TrackExporter trackExporter = useKmz ? new KmzTrackExporter(contentProviderUtils, fileTrackExporter, tracks, context) : fileTrackExporter;
|
||||||
|
|
||||||
|
String fileName = FileUtils.buildUniqueFileName(directory, track.getName(), extension);
|
||||||
|
File file = new File(directory, fileName);
|
||||||
|
FileOutputStream fileOutputStream = null;
|
||||||
|
try {
|
||||||
|
fileOutputStream = new FileOutputStream(file);
|
||||||
|
if (trackExporter.writeTrack(fileOutputStream)) {
|
||||||
|
savedPath = file.getAbsolutePath();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (!file.delete()) {
|
||||||
|
Log.d(TAG, "Unable to delete file");
|
||||||
|
}
|
||||||
|
Log.e(TAG, "Unable to export track");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.e(TAG, "Unable to open file " + file.getName(), e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (fileOutputStream != null) {
|
||||||
|
try {
|
||||||
|
fileOutputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Unable to close file output stream", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves all the tracks.
|
||||||
|
*/
|
||||||
|
private Boolean saveAllTracks() {
|
||||||
|
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
|
||||||
|
if (cursor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
totalCount = cursor.getCount();
|
||||||
|
for (int i = 0; i < totalCount; i++) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cursor.moveToPosition(i);
|
||||||
|
Track track = contentProviderUtils.createTrack(cursor);
|
||||||
|
if (track != null && saveTracks(new Track[]{track})) {
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
publishProgress(i + 1, totalCount);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ import java.io.OutputStream;
|
|||||||
*/
|
*/
|
||||||
public interface TrackExporter {
|
public interface TrackExporter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write track to an output stream
|
* Write track to an output stream
|
||||||
*
|
*
|
||||||
* @param outputStream the output stream
|
* @param outputStream the output stream
|
||||||
*/
|
*/
|
||||||
boolean writeTrack(OutputStream outputStream);
|
boolean writeTrack(OutputStream outputStream);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -22,12 +22,12 @@ package com.google.android.apps.mytracks.io.file.exporter;
|
|||||||
*/
|
*/
|
||||||
public interface TrackExporterListener {
|
public interface TrackExporterListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to update progress.
|
* Called to update progress.
|
||||||
*
|
*
|
||||||
* @param number the number of locations written
|
* @param number the number of locations written
|
||||||
* @param max the maximum number of locations in a track, for calculation of
|
* @param max the maximum number of locations in a track, for calculation of
|
||||||
* completion percentage
|
* completion percentage
|
||||||
*/
|
*/
|
||||||
void onProgressUpdate(int number, int max);
|
void onProgressUpdate(int number, int max);
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,94 +51,94 @@ import java.io.OutputStream;
|
|||||||
*/
|
*/
|
||||||
public interface TrackWriter {
|
public interface TrackWriter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the file extension (e.g, gpx, kml, ...).
|
* Gets the file extension (e.g, gpx, kml, ...).
|
||||||
*/
|
*/
|
||||||
String getExtension();
|
String getExtension();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares the output stream.
|
* Prepares the output stream.
|
||||||
*
|
*
|
||||||
* @param outputStream the output stream
|
* @param outputStream the output stream
|
||||||
*/
|
*/
|
||||||
void prepare(OutputStream outputStream);
|
void prepare(OutputStream outputStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the output stream.
|
* Closes the output stream.
|
||||||
*/
|
*/
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the header
|
* Writes the header
|
||||||
*
|
*
|
||||||
* @param tracks the tracks
|
* @param tracks the tracks
|
||||||
*/
|
*/
|
||||||
void writeHeader(Track[] tracks);
|
void writeHeader(Track[] tracks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the footer.
|
* Writes the footer.
|
||||||
*/
|
*/
|
||||||
void writeFooter();
|
void writeFooter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the beginning of the waypoints.
|
* Writes the beginning of the waypoints.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
*/
|
*/
|
||||||
void writeBeginWaypoints(Track track);
|
void writeBeginWaypoints(Track track);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the end of the waypoints.
|
* Writes the end of the waypoints.
|
||||||
*/
|
*/
|
||||||
void writeEndWaypoints();
|
void writeEndWaypoints();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a waypoint.
|
* Writes a waypoint.
|
||||||
*
|
*
|
||||||
* @param waypoint the waypoint
|
* @param waypoint the waypoint
|
||||||
*/
|
*/
|
||||||
void writeWaypoint(Waypoint waypoint);
|
void writeWaypoint(Waypoint waypoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the beginning of the tracks.
|
* Writes the beginning of the tracks.
|
||||||
*/
|
*/
|
||||||
void writeBeginTracks();
|
void writeBeginTracks();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the end of the tracks,
|
* Writes the end of the tracks,
|
||||||
*/
|
*/
|
||||||
void writeEndTracks();
|
void writeEndTracks();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the beginning of a track.
|
* Writes the beginning of a track.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @param startLocation the start location
|
* @param startLocation the start location
|
||||||
*/
|
*/
|
||||||
void writeBeginTrack(Track track, Location startLocation);
|
void writeBeginTrack(Track track, Location startLocation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the end of a track.
|
* Writes the end of a track.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @param endLocation the end location
|
* @param endLocation the end location
|
||||||
*/
|
*/
|
||||||
void writeEndTrack(Track track, Location endLocation);
|
void writeEndTrack(Track track, Location endLocation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes open segment.
|
* Writes open segment.
|
||||||
*/
|
*/
|
||||||
void writeOpenSegment();
|
void writeOpenSegment();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes close segment.
|
* Writes close segment.
|
||||||
*/
|
*/
|
||||||
void writeCloseSegment();
|
void writeCloseSegment();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a location.
|
* Writes a location.
|
||||||
*
|
*
|
||||||
* @param location the location
|
* @param location the location
|
||||||
*/
|
*/
|
||||||
void writeLocation(Location location);
|
void writeLocation(Location location);
|
||||||
}
|
}
|
||||||
+506
-513
File diff suppressed because it is too large
Load Diff
+150
-149
@@ -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;
|
||||||
@@ -33,158 +34,158 @@ import org.xml.sax.SAXException;
|
|||||||
*/
|
*/
|
||||||
public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||||
|
|
||||||
private static final String TAG_DESCRIPTION = "desc";
|
private static final String TAG_DESCRIPTION = "desc";
|
||||||
private static final String TAG_COMMENT = "cmt";
|
private static final String TAG_COMMENT = "cmt";
|
||||||
private static final String TAG_ELEVATION = "ele";
|
private static final String TAG_ELEVATION = "ele";
|
||||||
private static final String TAG_GPX = "gpx";
|
private static final String TAG_GPX = "gpx";
|
||||||
private static final String TAG_NAME = "name";
|
private static final String TAG_NAME = "name";
|
||||||
private static final String TAG_TIME = "time";
|
private static final String TAG_TIME = "time";
|
||||||
private static final String TAG_TRACK = "trk";
|
private static final String TAG_TRACK = "trk";
|
||||||
private static final String TAG_TRACK_POINT = "trkpt";
|
private static final String TAG_TRACK_POINT = "trkpt";
|
||||||
private static final String TAG_TRACK_SEGMENT = "trkseg";
|
private static final String TAG_TRACK_SEGMENT = "trkseg";
|
||||||
private static final String TAG_TYPE = "type";
|
private static final String TAG_TYPE = "type";
|
||||||
private static final String TAG_WAYPOINT = "wpt";
|
private static final String TAG_WAYPOINT = "wpt";
|
||||||
|
|
||||||
private static final String ATTRIBUTE_LAT = "lat";
|
private static final String ATTRIBUTE_LAT = "lat";
|
||||||
private static final String ATTRIBUTE_LON = "lon";
|
private static final String ATTRIBUTE_LON = "lon";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
public GpxFileTrackImporter(Context context) {
|
public GpxFileTrackImporter(Context context) {
|
||||||
this(context, ContentProviderUtils.Factory.get(context));
|
this(context, ContentProviderUtils.Factory.get(context));
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
GpxFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) {
|
|
||||||
super(context, -1L, contentProviderUtils);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startElement(String uri, String localName, String tag, Attributes attributes)
|
|
||||||
throws SAXException {
|
|
||||||
switch (tag) {
|
|
||||||
case TAG_WAYPOINT:
|
|
||||||
onWaypointStart(attributes);
|
|
||||||
break;
|
|
||||||
case TAG_TRACK:
|
|
||||||
onTrackStart();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK_SEGMENT:
|
|
||||||
onTrackSegmentStart();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK_POINT:
|
|
||||||
onTrackPointStart(attributes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endElement(String uri, String localName, String tag) throws SAXException {
|
|
||||||
switch (tag) {
|
|
||||||
case TAG_GPX:
|
|
||||||
onFileEnd();
|
|
||||||
break;
|
|
||||||
case TAG_WAYPOINT:
|
|
||||||
onWaypointEnd();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK:
|
|
||||||
onTrackEnd();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK_POINT:
|
|
||||||
onTrackPointEnd();
|
|
||||||
break;
|
|
||||||
case TAG_NAME:
|
|
||||||
if (content != null) {
|
|
||||||
name = content.trim();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TAG_DESCRIPTION:
|
|
||||||
if (content != null) {
|
|
||||||
description = content.trim();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TAG_TYPE:
|
|
||||||
if (content != null) {
|
|
||||||
category = content.trim();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TAG_TIME:
|
|
||||||
if (content != null) {
|
|
||||||
time = content.trim();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TAG_ELEVATION:
|
|
||||||
if (content != null) {
|
|
||||||
altitude = content.trim();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TAG_COMMENT:
|
|
||||||
if (content != null) {
|
|
||||||
waypointType = content.trim();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset element content
|
|
||||||
content = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTrackStart() throws SAXException {
|
|
||||||
super.onTrackStart();
|
|
||||||
name = null;
|
|
||||||
description = null;
|
|
||||||
category = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On track point start.
|
|
||||||
*
|
|
||||||
* @param attributes the attributes
|
|
||||||
*/
|
|
||||||
private void onTrackPointStart(Attributes attributes) {
|
|
||||||
latitude = attributes.getValue(ATTRIBUTE_LAT);
|
|
||||||
longitude = attributes.getValue(ATTRIBUTE_LON);
|
|
||||||
altitude = null;
|
|
||||||
time = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On track point end.
|
|
||||||
*/
|
|
||||||
private void onTrackPointEnd() throws SAXException {
|
|
||||||
Location location = getTrackPoint();
|
|
||||||
if (location == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
insertTrackPoint(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@VisibleForTesting
|
||||||
* On waypoint start.
|
GpxFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) {
|
||||||
*
|
super(context, -1L, contentProviderUtils);
|
||||||
* @param attributes the attributes
|
}
|
||||||
*/
|
|
||||||
private void onWaypointStart(Attributes attributes) {
|
|
||||||
name = null;
|
|
||||||
description = null;
|
|
||||||
category = null;
|
|
||||||
photoUrl = null;
|
|
||||||
latitude = attributes.getValue(ATTRIBUTE_LAT);
|
|
||||||
longitude = attributes.getValue(ATTRIBUTE_LON);
|
|
||||||
altitude = null;
|
|
||||||
time = null;
|
|
||||||
waypointType = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* On waypoint end.
|
public void startElement(String uri, String localName, String tag, Attributes attributes)
|
||||||
*/
|
throws SAXException {
|
||||||
private void onWaypointEnd() throws SAXException {
|
switch (tag) {
|
||||||
addWaypoint(WaypointType.STATISTICS.name().equals(waypointType) ? WaypointType.STATISTICS
|
case TAG_WAYPOINT:
|
||||||
: WaypointType.WAYPOINT);
|
onWaypointStart(attributes);
|
||||||
}
|
break;
|
||||||
|
case TAG_TRACK:
|
||||||
|
onTrackStart();
|
||||||
|
break;
|
||||||
|
case TAG_TRACK_SEGMENT:
|
||||||
|
onTrackSegmentStart();
|
||||||
|
break;
|
||||||
|
case TAG_TRACK_POINT:
|
||||||
|
onTrackPointStart(attributes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String tag) throws SAXException {
|
||||||
|
switch (tag) {
|
||||||
|
case TAG_GPX:
|
||||||
|
onFileEnd();
|
||||||
|
break;
|
||||||
|
case TAG_WAYPOINT:
|
||||||
|
onWaypointEnd();
|
||||||
|
break;
|
||||||
|
case TAG_TRACK:
|
||||||
|
onTrackEnd();
|
||||||
|
break;
|
||||||
|
case TAG_TRACK_POINT:
|
||||||
|
onTrackPointEnd();
|
||||||
|
break;
|
||||||
|
case TAG_NAME:
|
||||||
|
if (content != null) {
|
||||||
|
name = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TAG_DESCRIPTION:
|
||||||
|
if (content != null) {
|
||||||
|
description = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TAG_TYPE:
|
||||||
|
if (content != null) {
|
||||||
|
category = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TAG_TIME:
|
||||||
|
if (content != null) {
|
||||||
|
time = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TAG_ELEVATION:
|
||||||
|
if (content != null) {
|
||||||
|
altitude = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TAG_COMMENT:
|
||||||
|
if (content != null) {
|
||||||
|
waypointType = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset element content
|
||||||
|
content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onTrackStart() throws SAXException {
|
||||||
|
super.onTrackStart();
|
||||||
|
name = null;
|
||||||
|
description = null;
|
||||||
|
category = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On track point start.
|
||||||
|
*
|
||||||
|
* @param attributes the attributes
|
||||||
|
*/
|
||||||
|
private void onTrackPointStart(Attributes attributes) {
|
||||||
|
latitude = attributes.getValue(ATTRIBUTE_LAT);
|
||||||
|
longitude = attributes.getValue(ATTRIBUTE_LON);
|
||||||
|
altitude = null;
|
||||||
|
time = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On track point end.
|
||||||
|
*/
|
||||||
|
private void onTrackPointEnd() throws SAXException {
|
||||||
|
Location location = getTrackPoint();
|
||||||
|
if (location == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
insertTrackPoint(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On waypoint start.
|
||||||
|
*
|
||||||
|
* @param attributes the attributes
|
||||||
|
*/
|
||||||
|
private void onWaypointStart(Attributes attributes) {
|
||||||
|
name = null;
|
||||||
|
description = null;
|
||||||
|
category = null;
|
||||||
|
photoUrl = null;
|
||||||
|
latitude = attributes.getValue(ATTRIBUTE_LAT);
|
||||||
|
longitude = attributes.getValue(ATTRIBUTE_LON);
|
||||||
|
altitude = null;
|
||||||
|
time = null;
|
||||||
|
waypointType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On waypoint end.
|
||||||
|
*/
|
||||||
|
private void onWaypointEnd() throws SAXException {
|
||||||
|
addWaypoint(WaypointType.STATISTICS.name().equals(waypointType) ? WaypointType.STATISTICS
|
||||||
|
: WaypointType.WAYPOINT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+174
-174
@@ -46,194 +46,194 @@ import java.io.File;
|
|||||||
*/
|
*/
|
||||||
public class ImportActivity extends Activity {
|
public class ImportActivity extends Activity {
|
||||||
|
|
||||||
public static final String EXTRA_IMPORT_ALL = "import_all";
|
public static final String EXTRA_IMPORT_ALL = "import_all";
|
||||||
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
||||||
|
|
||||||
private static final String TAG = ImportActivity.class.getSimpleName();
|
private static final String TAG = ImportActivity.class.getSimpleName();
|
||||||
private static final int DIALOG_PROGRESS_ID = 0;
|
private static final int DIALOG_PROGRESS_ID = 0;
|
||||||
private static final int DIALOG_RESULT_ID = 1;
|
private static final int DIALOG_RESULT_ID = 1;
|
||||||
|
|
||||||
private ImportAsyncTask importAsyncTask;
|
private ImportAsyncTask importAsyncTask;
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
|
|
||||||
private boolean importAll;
|
private boolean importAll;
|
||||||
|
|
||||||
// the path on the external storage to import
|
// the path on the external storage to import
|
||||||
private String directoryDisplayName;
|
private String directoryDisplayName;
|
||||||
|
|
||||||
// the number of files successfully imported
|
// the number of files successfully imported
|
||||||
private int successCount;
|
private int successCount;
|
||||||
|
|
||||||
// the number of files to import
|
// the number of files to import
|
||||||
private int totalCount;
|
private int totalCount;
|
||||||
|
|
||||||
// the last successfully imported track id
|
// the last successfully imported track id
|
||||||
private long trackId;
|
private long trackId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
|
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
|
||||||
TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||||
if (trackFileFormat == null) {
|
if (trackFileFormat == null) {
|
||||||
trackFileFormat = TrackFileFormat.GPX;
|
trackFileFormat = TrackFileFormat.GPX;
|
||||||
}
|
|
||||||
|
|
||||||
if (!FileUtils.isExternalStorageAvailable()) {
|
|
||||||
Toast.makeText(this, R.string.external_storage_not_available, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String directoryPath;
|
|
||||||
if (importAll) {
|
|
||||||
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
|
||||||
directoryPath = FileUtils.getPath(trackFileFormat.getExtension());
|
|
||||||
if (!FileUtils.isDirectory(new File(directoryPath))) {
|
|
||||||
Toast.makeText(
|
|
||||||
this, getString(R.string.import_no_directory, directoryDisplayName), Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (!(Intent.ACTION_ATTACH_DATA.equals(action) || Intent.ACTION_VIEW.equals(action))) {
|
|
||||||
Log.d(TAG, "Invalid action: " + intent);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri data = intent.getData();
|
|
||||||
if ("file".equals(data.getScheme())) {
|
|
||||||
Log.d(TAG, "Invalid data: " + intent);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
directoryDisplayName = data.getPath();
|
|
||||||
directoryPath = data.getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
Object retained = getLastNonConfigurationInstance();
|
|
||||||
if (retained instanceof ImportAsyncTask) {
|
|
||||||
importAsyncTask = (ImportAsyncTask) retained;
|
|
||||||
importAsyncTask.setActivity(this);
|
|
||||||
} else {
|
|
||||||
importAsyncTask = new ImportAsyncTask(this, importAll, trackFileFormat, directoryPath);
|
|
||||||
importAsyncTask.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object onRetainNonConfigurationInstance() {
|
|
||||||
importAsyncTask.setActivity(null);
|
|
||||||
return importAsyncTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
switch (id) {
|
|
||||||
case DIALOG_PROGRESS_ID:
|
|
||||||
progressDialog = DialogUtils.createHorizontalProgressDialog(
|
|
||||||
this, R.string.import_progress_message, new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
importAsyncTask.cancel(true);
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
}, directoryDisplayName);
|
|
||||||
return progressDialog;
|
|
||||||
case DIALOG_RESULT_ID:
|
|
||||||
int iconId;
|
|
||||||
int titleId;
|
|
||||||
String message;
|
|
||||||
String totalFiles = getResources()
|
|
||||||
.getQuantityString(R.plurals.files, totalCount, totalCount);
|
|
||||||
if (successCount == totalCount) {
|
|
||||||
if (totalCount == 0) {
|
|
||||||
iconId = android.R.drawable.ic_dialog_info;
|
|
||||||
titleId = R.string.import_no_file_title;
|
|
||||||
message = getString(R.string.import_no_file, directoryDisplayName);
|
|
||||||
} else {
|
|
||||||
iconId = R.drawable.ic_dialog_success;
|
|
||||||
titleId = R.string.generic_success_title;
|
|
||||||
message = getString(R.string.import_success, totalFiles, directoryDisplayName);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iconId = android.R.drawable.ic_dialog_alert;
|
|
||||||
titleId = R.string.generic_error_title;
|
|
||||||
message = getString(
|
|
||||||
R.string.import_error, successCount, totalFiles, directoryDisplayName);
|
|
||||||
}
|
}
|
||||||
final Dialog dialog = new AlertDialog.Builder(this).setCancelable(true).setIcon(iconId)
|
|
||||||
.setMessage(message).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
if (!FileUtils.isExternalStorageAvailable()) {
|
||||||
@Override
|
Toast.makeText(this, R.string.external_storage_not_available, Toast.LENGTH_LONG).show();
|
||||||
public void onCancel(DialogInterface dialogInterface) {
|
finish();
|
||||||
dialogInterface.dismiss();
|
return;
|
||||||
|
}
|
||||||
|
String directoryPath;
|
||||||
|
if (importAll) {
|
||||||
|
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||||
|
directoryPath = FileUtils.getPath(trackFileFormat.getExtension());
|
||||||
|
if (!FileUtils.isDirectory(new File(directoryPath))) {
|
||||||
|
Toast.makeText(
|
||||||
|
this, getString(R.string.import_no_directory, directoryDisplayName), Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
finish();
|
finish();
|
||||||
}
|
return;
|
||||||
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
}
|
||||||
@Override
|
} else {
|
||||||
public void onClick(DialogInterface dialogInterface, int which) {
|
String action = intent.getAction();
|
||||||
if (successCount == totalCount && !importAll && trackId != -1L) {
|
if (!(Intent.ACTION_ATTACH_DATA.equals(action) || Intent.ACTION_VIEW.equals(action))) {
|
||||||
Intent intent = IntentUtils.newIntent(
|
Log.d(TAG, "Invalid action: " + intent);
|
||||||
ImportActivity.this, TrackDetailActivity.class)
|
finish();
|
||||||
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId);
|
return;
|
||||||
TaskStackBuilder.create(ImportActivity.this)
|
}
|
||||||
.addParentStack(TrackDetailActivity.class).addNextIntent(intent)
|
|
||||||
.startActivities();
|
Uri data = intent.getData();
|
||||||
|
if ("file".equals(data.getScheme())) {
|
||||||
|
Log.d(TAG, "Invalid data: " + intent);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
directoryDisplayName = data.getPath();
|
||||||
|
directoryPath = data.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object retained = getLastNonConfigurationInstance();
|
||||||
|
if (retained instanceof ImportAsyncTask) {
|
||||||
|
importAsyncTask = (ImportAsyncTask) retained;
|
||||||
|
importAsyncTask.setActivity(this);
|
||||||
|
} else {
|
||||||
|
importAsyncTask = new ImportAsyncTask(this, importAll, trackFileFormat, directoryPath);
|
||||||
|
importAsyncTask.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object onRetainNonConfigurationInstance() {
|
||||||
|
importAsyncTask.setActivity(null);
|
||||||
|
return importAsyncTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dialog onCreateDialog(int id) {
|
||||||
|
switch (id) {
|
||||||
|
case DIALOG_PROGRESS_ID:
|
||||||
|
progressDialog = DialogUtils.createHorizontalProgressDialog(
|
||||||
|
this, R.string.import_progress_message, new DialogInterface.OnCancelListener() {
|
||||||
|
@Override
|
||||||
|
public void onCancel(DialogInterface dialog) {
|
||||||
|
importAsyncTask.cancel(true);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}, directoryDisplayName);
|
||||||
|
return progressDialog;
|
||||||
|
case DIALOG_RESULT_ID:
|
||||||
|
int iconId;
|
||||||
|
int titleId;
|
||||||
|
String message;
|
||||||
|
String totalFiles = getResources()
|
||||||
|
.getQuantityString(R.plurals.files, totalCount, totalCount);
|
||||||
|
if (successCount == totalCount) {
|
||||||
|
if (totalCount == 0) {
|
||||||
|
iconId = android.R.drawable.ic_dialog_info;
|
||||||
|
titleId = R.string.import_no_file_title;
|
||||||
|
message = getString(R.string.import_no_file, directoryDisplayName);
|
||||||
|
} else {
|
||||||
|
iconId = R.drawable.ic_dialog_success;
|
||||||
|
titleId = R.string.generic_success_title;
|
||||||
|
message = getString(R.string.import_success, totalFiles, directoryDisplayName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iconId = android.R.drawable.ic_dialog_alert;
|
||||||
|
titleId = R.string.generic_error_title;
|
||||||
|
message = getString(
|
||||||
|
R.string.import_error, successCount, totalFiles, directoryDisplayName);
|
||||||
}
|
}
|
||||||
dialogInterface.dismiss();
|
final Dialog dialog = new AlertDialog.Builder(this).setCancelable(true).setIcon(iconId)
|
||||||
finish();
|
.setMessage(message).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||||
}
|
@Override
|
||||||
}).setTitle(titleId).create();
|
public void onCancel(DialogInterface dialogInterface) {
|
||||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
dialogInterface.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialogInterface, int which) {
|
||||||
|
if (successCount == totalCount && !importAll && trackId != -1L) {
|
||||||
|
Intent intent = IntentUtils.newIntent(
|
||||||
|
ImportActivity.this, TrackDetailActivity.class)
|
||||||
|
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
TaskStackBuilder.create(ImportActivity.this)
|
||||||
|
.addParentStack(TrackDetailActivity.class).addNextIntent(intent)
|
||||||
|
.startActivities();
|
||||||
|
}
|
||||||
|
dialogInterface.dismiss();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}).setTitle(titleId).create();
|
||||||
|
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShow(DialogInterface dialogInterface) {
|
public void onShow(DialogInterface dialogInterface) {
|
||||||
DialogUtils.setDialogTitleDivider(ImportActivity.this, dialog);
|
DialogUtils.setDialogTitleDivider(ImportActivity.this, dialog);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return dialog;
|
return dialog;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes when the associated AsyncTask completes.
|
* Invokes when the associated AsyncTask completes.
|
||||||
*
|
*
|
||||||
* @param aSuccessCount the number of files successfully imported
|
* @param aSuccessCount the number of files successfully imported
|
||||||
* @param aTotalCount the number of files to import
|
* @param aTotalCount the number of files to import
|
||||||
* @param aTrackId the last successfully imported track id
|
* @param aTrackId the last successfully imported track id
|
||||||
*/
|
*/
|
||||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, long aTrackId) {
|
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, long aTrackId) {
|
||||||
successCount = aSuccessCount;
|
successCount = aSuccessCount;
|
||||||
totalCount = aTotalCount;
|
totalCount = aTotalCount;
|
||||||
trackId = aTrackId;
|
trackId = aTrackId;
|
||||||
removeDialog(DIALOG_PROGRESS_ID);
|
removeDialog(DIALOG_PROGRESS_ID);
|
||||||
showDialog(DIALOG_RESULT_ID);
|
showDialog(DIALOG_RESULT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the progress dialog.
|
* Shows the progress dialog.
|
||||||
*/
|
*/
|
||||||
public void showProgressDialog() {
|
public void showProgressDialog() {
|
||||||
showDialog(DIALOG_PROGRESS_ID);
|
showDialog(DIALOG_PROGRESS_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the progress dialog value.
|
* Sets the progress dialog value.
|
||||||
*
|
*
|
||||||
* @param number the number of files imported
|
* @param number the number of files imported
|
||||||
* @param max the maximum number of files
|
* @param max the maximum number of files
|
||||||
*/
|
*/
|
||||||
public void setProgressDialogValue(int number, int max) {
|
public void setProgressDialogValue(int number, int max) {
|
||||||
if (progressDialog != null) {
|
if (progressDialog != null) {
|
||||||
progressDialog.setIndeterminate(false);
|
progressDialog.setIndeterminate(false);
|
||||||
progressDialog.setMax(max);
|
progressDialog.setMax(max);
|
||||||
progressDialog.setProgress(Math.min(number, max));
|
progressDialog.setProgress(Math.min(number, max));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+182
-183
@@ -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;
|
||||||
@@ -45,197 +45,196 @@ 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 final boolean importAll;
|
||||||
|
private final TrackFileFormat trackFileFormat;
|
||||||
|
private final String path;
|
||||||
|
private final Context context;
|
||||||
|
private ImportActivity importActivity;
|
||||||
|
private WakeLock wakeLock;
|
||||||
|
|
||||||
private ImportActivity importActivity;
|
// true if the AsyncTask has completed
|
||||||
private final boolean importAll;
|
private boolean completed;
|
||||||
private final TrackFileFormat trackFileFormat;
|
|
||||||
private final String path;
|
|
||||||
private final Context context;
|
|
||||||
private WakeLock wakeLock;
|
|
||||||
|
|
||||||
// true if the AsyncTask has completed
|
// the number of files successfully imported
|
||||||
private boolean completed;
|
private int successCount;
|
||||||
|
|
||||||
// the number of files successfully imported
|
// the number of files to import
|
||||||
private int successCount;
|
private int totalCount;
|
||||||
|
|
||||||
// the number of files to import
|
// the last successfully imported track id
|
||||||
private int totalCount;
|
private long trackId;
|
||||||
|
|
||||||
// the last successfully imported track id
|
/**
|
||||||
private long trackId;
|
* Creates an AsyncTask.
|
||||||
|
*
|
||||||
|
* @param importActivity the activity currently associated with this AsyncTask
|
||||||
|
* @param importAll true to import all GPX files
|
||||||
|
* @param trackFileFormat the track file format
|
||||||
|
* @param path path to import GPX files
|
||||||
|
*/
|
||||||
|
public ImportAsyncTask(ImportActivity importActivity, boolean importAll,
|
||||||
|
TrackFileFormat trackFileFormat, String path) {
|
||||||
|
this.importActivity = importActivity;
|
||||||
|
this.importAll = importAll;
|
||||||
|
this.trackFileFormat = trackFileFormat;
|
||||||
|
this.path = path;
|
||||||
|
context = importActivity.getApplicationContext();
|
||||||
|
|
||||||
/**
|
completed = false;
|
||||||
* Creates an AsyncTask.
|
successCount = 0;
|
||||||
*
|
totalCount = 0;
|
||||||
* @param importActivity the activity currently associated with this AsyncTask
|
trackId = -1L;
|
||||||
* @param importAll true to import all GPX files
|
|
||||||
* @param trackFileFormat the track file format
|
|
||||||
* @param path path to import GPX files
|
|
||||||
*/
|
|
||||||
public ImportAsyncTask(ImportActivity importActivity, boolean importAll,
|
|
||||||
TrackFileFormat trackFileFormat, String path) {
|
|
||||||
this.importActivity = importActivity;
|
|
||||||
this.importAll = importAll;
|
|
||||||
this.trackFileFormat = trackFileFormat;
|
|
||||||
this.path = path;
|
|
||||||
context = importActivity.getApplicationContext();
|
|
||||||
|
|
||||||
completed = false;
|
|
||||||
successCount = 0;
|
|
||||||
totalCount = 0;
|
|
||||||
trackId = -1L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current {@link ImportActivity} associated with this AyncTask.
|
|
||||||
*
|
|
||||||
* @param importActivity the current {@link ImportActivity}, can be null
|
|
||||||
*/
|
|
||||||
public void setActivity(ImportActivity importActivity) {
|
|
||||||
this.importActivity = importActivity;
|
|
||||||
if (completed && importActivity != null) {
|
|
||||||
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void onPreExecute() {
|
* Sets the current {@link ImportActivity} associated with this AyncTask.
|
||||||
if (importActivity != null) {
|
*
|
||||||
importActivity.showProgressDialog();
|
* @param importActivity the current {@link ImportActivity}, can be null
|
||||||
}
|
*/
|
||||||
}
|
public void setActivity(ImportActivity importActivity) {
|
||||||
|
this.importActivity = importActivity;
|
||||||
@Override
|
if (completed && importActivity != null) {
|
||||||
protected Boolean doInBackground(Void... params) {
|
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
||||||
try {
|
|
||||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
|
||||||
// Get the wake lock if not recording or paused
|
|
||||||
boolean isRecording =
|
|
||||||
PreferencesUtils.getLong(importActivity, R.string.recording_track_id_key)
|
|
||||||
!= PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
|
||||||
boolean isPaused = PreferencesUtils.getBoolean(importActivity,
|
|
||||||
R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
|
||||||
if (!isRecording || isPaused) {
|
|
||||||
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<File> files = getFiles();
|
|
||||||
totalCount = files.size();
|
|
||||||
if (totalCount == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < totalCount; i++) {
|
|
||||||
if (isCancelled()) {
|
|
||||||
// If cancelled, return true to show the number of files imported
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (importFile(files.get(i))) {
|
}
|
||||||
successCount++;
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
if (importActivity != null) {
|
||||||
|
importActivity.showProgressDialog();
|
||||||
}
|
}
|
||||||
publishProgress(i + 1, totalCount);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} finally {
|
|
||||||
if (wakeLock != null && wakeLock.isHeld()) {
|
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onProgressUpdate(Integer... values) {
|
protected Boolean doInBackground(Void... params) {
|
||||||
if (importActivity != null) {
|
|
||||||
importActivity.setProgressDialogValue(values[0], values[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
completed = true;
|
|
||||||
if (importActivity != null) {
|
|
||||||
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCancelled() {
|
|
||||||
completed = true;
|
|
||||||
if (importActivity != null) {
|
|
||||||
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports a file.
|
|
||||||
*
|
|
||||||
* @param file the file
|
|
||||||
*/
|
|
||||||
private boolean importFile(final File file) {
|
|
||||||
FileInputStream fileInputStream = null;
|
|
||||||
try {
|
|
||||||
TrackImporter trackImporter;
|
|
||||||
if (trackFileFormat == TrackFileFormat.KML) {
|
|
||||||
String extension = FileUtils.getExtension(file.getName());
|
|
||||||
if (TrackFileFormat.KML.getExtension().equals(extension)) {
|
|
||||||
trackImporter = new KmlFileTrackImporter(context, -1L);
|
|
||||||
} else {
|
|
||||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
|
||||||
Uri uri = contentProviderUtils.insertTrack(new Track());
|
|
||||||
long newId = Long.parseLong(uri.getLastPathSegment());
|
|
||||||
|
|
||||||
trackImporter = new KmzTrackImporter(context, newId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
trackImporter = new GpxFileTrackImporter(context);
|
|
||||||
}
|
|
||||||
fileInputStream = new FileInputStream(file);
|
|
||||||
trackId = trackImporter.importFile(fileInputStream);
|
|
||||||
return trackId != -1L;
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.e(TAG, "Unable to import file", e);
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (fileInputStream != null) {
|
|
||||||
try {
|
try {
|
||||||
fileInputStream.close();
|
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
||||||
} catch (IOException e) {
|
// Get the wake lock if not recording or paused
|
||||||
Log.e(TAG, "Unable to close file input stream", e);
|
boolean isRecording =
|
||||||
}
|
PreferencesUtils.getLong(importActivity, R.string.recording_track_id_key)
|
||||||
}
|
!= PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
}
|
boolean isPaused = PreferencesUtils.getBoolean(importActivity,
|
||||||
}
|
R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||||
|
if (!isRecording || isPaused) {
|
||||||
/**
|
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
|
||||||
* Gets a list of files. If importAll is true, returns a list of the files
|
}
|
||||||
* under the path directory. If importAll is false, returns a list containing
|
|
||||||
* just the path file.
|
List<File> files = getFiles();
|
||||||
*/
|
totalCount = files.size();
|
||||||
private List<File> getFiles() {
|
if (totalCount == 0) {
|
||||||
List<File> files = new ArrayList<>();
|
return true;
|
||||||
File file = new File(path);
|
}
|
||||||
if (importAll) {
|
|
||||||
File[] candidates = file.listFiles();
|
for (int i = 0; i < totalCount; i++) {
|
||||||
if (candidates != null) {
|
if (isCancelled()) {
|
||||||
for (File candidate : candidates) {
|
// If cancelled, return true to show the number of files imported
|
||||||
if (!FileUtils.isDirectory(candidate)) {
|
return true;
|
||||||
String extension = FileUtils.getExtension(candidate.getName());
|
}
|
||||||
if (trackFileFormat == TrackFileFormat.KML && (
|
if (importFile(files.get(i))) {
|
||||||
TrackFileFormat.KML.getExtension().equals(extension)
|
successCount++;
|
||||||
|| KmzTrackExporter.KMZ_EXTENSION.equals(extension))) {
|
}
|
||||||
files.add(candidate);
|
publishProgress(i + 1, totalCount);
|
||||||
} else if (trackFileFormat == TrackFileFormat.GPX
|
}
|
||||||
&& TrackFileFormat.GPX.getExtension().equals(extension)) {
|
return true;
|
||||||
files.add(candidate);
|
} finally {
|
||||||
|
if (wakeLock != null && wakeLock.isHeld()) {
|
||||||
|
wakeLock.release();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
files.add(file);
|
|
||||||
}
|
}
|
||||||
return files;
|
|
||||||
}
|
@Override
|
||||||
|
protected void onProgressUpdate(Integer... values) {
|
||||||
|
if (importActivity != null) {
|
||||||
|
importActivity.setProgressDialogValue(values[0], values[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
completed = true;
|
||||||
|
if (importActivity != null) {
|
||||||
|
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
completed = true;
|
||||||
|
if (importActivity != null) {
|
||||||
|
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a file.
|
||||||
|
*
|
||||||
|
* @param file the file
|
||||||
|
*/
|
||||||
|
private boolean importFile(final File file) {
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
try {
|
||||||
|
TrackImporter trackImporter;
|
||||||
|
if (trackFileFormat == TrackFileFormat.KML) {
|
||||||
|
String extension = FileUtils.getExtension(file.getName());
|
||||||
|
if (TrackFileFormat.KML.getExtension().equals(extension)) {
|
||||||
|
trackImporter = new KmlFileTrackImporter(context, -1L);
|
||||||
|
} else {
|
||||||
|
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||||
|
Uri uri = contentProviderUtils.insertTrack(new Track());
|
||||||
|
long newId = Long.parseLong(uri.getLastPathSegment());
|
||||||
|
|
||||||
|
trackImporter = new KmzTrackImporter(context, newId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trackImporter = new GpxFileTrackImporter(context);
|
||||||
|
}
|
||||||
|
fileInputStream = new FileInputStream(file);
|
||||||
|
trackId = trackImporter.importFile(fileInputStream);
|
||||||
|
return trackId != -1L;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.e(TAG, "Unable to import file", e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (fileInputStream != null) {
|
||||||
|
try {
|
||||||
|
fileInputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Unable to close file input stream", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of files. If importAll is true, returns a list of the files
|
||||||
|
* under the path directory. If importAll is false, returns a list containing
|
||||||
|
* just the path file.
|
||||||
|
*/
|
||||||
|
private List<File> getFiles() {
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
File file = new File(path);
|
||||||
|
if (importAll) {
|
||||||
|
File[] candidates = file.listFiles();
|
||||||
|
if (candidates != null) {
|
||||||
|
for (File candidate : candidates) {
|
||||||
|
if (!FileUtils.isDirectory(candidate)) {
|
||||||
|
String extension = FileUtils.getExtension(candidate.getName());
|
||||||
|
if (trackFileFormat == TrackFileFormat.KML && (
|
||||||
|
TrackFileFormat.KML.getExtension().equals(extension)
|
||||||
|
|| KmzTrackExporter.KMZ_EXTENSION.equals(extension))) {
|
||||||
|
files.add(candidate);
|
||||||
|
} else if (trackFileFormat == TrackFileFormat.GPX
|
||||||
|
&& TrackFileFormat.GPX.getExtension().equals(extension)) {
|
||||||
|
files.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
files.add(file);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+244
-244
@@ -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;
|
||||||
|
|
||||||
@@ -39,273 +39,273 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||||
|
|
||||||
private static final String CADENCE = "cadence";
|
private static final String CADENCE = "cadence";
|
||||||
private static final String HEART_RATE = "heart_rate";
|
private static final String HEART_RATE = "heart_rate";
|
||||||
private static final String POWER = "power";
|
private static final String POWER = "power";
|
||||||
|
|
||||||
private static final String STATISTICS_STYLE = "#statistics";
|
private static final String STATISTICS_STYLE = "#statistics";
|
||||||
private static final String WAYPOINT_STYLE = "#waypoint";
|
private static final String WAYPOINT_STYLE = "#waypoint";
|
||||||
|
|
||||||
private static final String TAG_COORDINATES = "coordinates";
|
private static final String TAG_COORDINATES = "coordinates";
|
||||||
private static final String TAG_DESCRIPTION = "description";
|
private static final String TAG_DESCRIPTION = "description";
|
||||||
private static final String TAG_GX_COORD = "gx:coord";
|
private static final String TAG_GX_COORD = "gx:coord";
|
||||||
private static final String TAG_GX_MULTI_TRACK = "gx:MultiTrack";
|
private static final String TAG_GX_MULTI_TRACK = "gx:MultiTrack";
|
||||||
private static final String TAG_GX_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
private static final String TAG_GX_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
||||||
private static final String TAG_GX_TRACK = "gx:Track";
|
private static final String TAG_GX_TRACK = "gx:Track";
|
||||||
private static final String TAG_GX_VALUE = "gx:value";
|
private static final String TAG_GX_VALUE = "gx:value";
|
||||||
private static final String TAG_HREF = "href";
|
private static final String TAG_HREF = "href";
|
||||||
private static final String TAG_KML = "kml";
|
private static final String TAG_KML = "kml";
|
||||||
private static final String TAG_NAME = "name";
|
private static final String TAG_NAME = "name";
|
||||||
private static final String TAG_PHOTO_OVERLAY = "PhotoOverlay";
|
private static final String TAG_PHOTO_OVERLAY = "PhotoOverlay";
|
||||||
private static final String TAG_PLACEMARK = "Placemark";
|
private static final String TAG_PLACEMARK = "Placemark";
|
||||||
private static final String TAG_STYLE_URL = "styleUrl";
|
private static final String TAG_STYLE_URL = "styleUrl";
|
||||||
private static final String TAG_VALUE = "value";
|
private static final String TAG_VALUE = "value";
|
||||||
private static final String TAG_WHEN = "when";
|
private static final String TAG_WHEN = "when";
|
||||||
|
|
||||||
private static final String ATTRIBUTE_NAME = "name";
|
private static final String ATTRIBUTE_NAME = "name";
|
||||||
|
|
||||||
private boolean trackStarted = false;
|
private boolean trackStarted = false;
|
||||||
private String sensorName;
|
private String sensorName;
|
||||||
private ArrayList<Location> locationList;
|
private ArrayList<Location> locationList;
|
||||||
private ArrayList<Integer> cadenceList;
|
private ArrayList<Integer> cadenceList;
|
||||||
private ArrayList<Integer> heartRateList;
|
private ArrayList<Integer> heartRateList;
|
||||||
private ArrayList<Integer> powerList;
|
private ArrayList<Integer> powerList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
* @param importTrackId track id to import to. -1L to import to a new track.
|
* @param importTrackId track id to import to. -1L to import to a new track.
|
||||||
*/
|
*/
|
||||||
public KmlFileTrackImporter(Context context, long importTrackId) {
|
public KmlFileTrackImporter(Context context, long importTrackId) {
|
||||||
this(context, importTrackId, ContentProviderUtils.Factory.get(context));
|
this(context, importTrackId, ContentProviderUtils.Factory.get(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
KmlFileTrackImporter(
|
KmlFileTrackImporter(
|
||||||
Context context, long importTrackId, ContentProviderUtils contentProviderUtils) {
|
Context context, long importTrackId, ContentProviderUtils contentProviderUtils) {
|
||||||
super(context, importTrackId, contentProviderUtils);
|
super(context, importTrackId, contentProviderUtils);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String tag, Attributes attributes)
|
public void startElement(String uri, String localName, String tag, Attributes attributes)
|
||||||
throws SAXException {
|
throws SAXException {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_PLACEMARK:
|
case TAG_PLACEMARK:
|
||||||
case TAG_PHOTO_OVERLAY:
|
case TAG_PHOTO_OVERLAY:
|
||||||
/*
|
/*
|
||||||
* Note that a track is contained in a Placemark, calling onWaypointStart
|
* Note that a track is contained in a Placemark, calling onWaypointStart
|
||||||
* will clear various track variables like name, category, and
|
* will clear various track variables like name, category, and
|
||||||
* description.
|
* description.
|
||||||
*/
|
*/
|
||||||
onWaypointStart();
|
onWaypointStart();
|
||||||
break;
|
break;
|
||||||
case TAG_GX_MULTI_TRACK:
|
case TAG_GX_MULTI_TRACK:
|
||||||
trackStarted = true;
|
trackStarted = true;
|
||||||
onTrackStart();
|
onTrackStart();
|
||||||
break;
|
break;
|
||||||
case TAG_GX_TRACK:
|
case TAG_GX_TRACK:
|
||||||
if (!trackStarted) {
|
if (!trackStarted) {
|
||||||
throw new SAXException("No " + TAG_GX_MULTI_TRACK);
|
throw new SAXException("No " + TAG_GX_MULTI_TRACK);
|
||||||
|
}
|
||||||
|
onTrackSegmentStart();
|
||||||
|
break;
|
||||||
|
case TAG_GX_SIMPLE_ARRAY_DATA:
|
||||||
|
onSensorDataStart(attributes);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
onTrackSegmentStart();
|
|
||||||
break;
|
|
||||||
case TAG_GX_SIMPLE_ARRAY_DATA:
|
|
||||||
onSensorDataStart(attributes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endElement(String uri, String localName, String tag) throws SAXException {
|
|
||||||
if (tag.equals(TAG_KML)) {
|
|
||||||
onFileEnd();
|
|
||||||
} else if (tag.equals(TAG_PLACEMARK) || tag.equals(TAG_PHOTO_OVERLAY)) {
|
|
||||||
/*
|
|
||||||
* Note that a track is contained in a Placemark, calling onWaypointend is
|
|
||||||
* save since waypointType is not set for a track.
|
|
||||||
*/
|
|
||||||
onWaypointEnd();
|
|
||||||
} else if (localName.equals(TAG_COORDINATES)) {
|
|
||||||
onWaypointLocationEnd();
|
|
||||||
} else if (tag.equals(TAG_GX_MULTI_TRACK)) {
|
|
||||||
onTrackEnd();
|
|
||||||
} else if (tag.equals(TAG_GX_TRACK)) {
|
|
||||||
onTrackSegmentEnd();
|
|
||||||
} else if (tag.equals(TAG_GX_COORD)) {
|
|
||||||
onTrackPointEnd();
|
|
||||||
} else if (tag.equals(TAG_GX_VALUE)) {
|
|
||||||
onSensorValueEnd();
|
|
||||||
} else if (tag.equals(TAG_NAME)) {
|
|
||||||
if (content != null) {
|
|
||||||
name = content.trim();
|
|
||||||
}
|
|
||||||
} else if (localName.equals(TAG_DESCRIPTION)) {
|
|
||||||
if (content != null) {
|
|
||||||
description = content.trim();
|
|
||||||
}
|
|
||||||
} else if (localName.equals(TAG_VALUE)) {
|
|
||||||
if (content != null) {
|
|
||||||
category = content.trim();
|
|
||||||
}
|
|
||||||
} else if (localName.equals(TAG_WHEN)) {
|
|
||||||
if (content != null) {
|
|
||||||
time = content.trim();
|
|
||||||
}
|
|
||||||
} else if (localName.equals(TAG_STYLE_URL)) {
|
|
||||||
if (content != null) {
|
|
||||||
waypointType = content.trim();
|
|
||||||
}
|
|
||||||
} else if (localName.equals(TAG_HREF)) {
|
|
||||||
if (content != null) {
|
|
||||||
photoUrl = content.trim();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset element content
|
@Override
|
||||||
content = null;
|
public void endElement(String uri, String localName, String tag) throws SAXException {
|
||||||
}
|
if (tag.equals(TAG_KML)) {
|
||||||
|
onFileEnd();
|
||||||
|
} else if (tag.equals(TAG_PLACEMARK) || tag.equals(TAG_PHOTO_OVERLAY)) {
|
||||||
|
/*
|
||||||
|
* Note that a track is contained in a Placemark, calling onWaypointend is
|
||||||
|
* save since waypointType is not set for a track.
|
||||||
|
*/
|
||||||
|
onWaypointEnd();
|
||||||
|
} else if (localName.equals(TAG_COORDINATES)) {
|
||||||
|
onWaypointLocationEnd();
|
||||||
|
} else if (tag.equals(TAG_GX_MULTI_TRACK)) {
|
||||||
|
onTrackEnd();
|
||||||
|
} else if (tag.equals(TAG_GX_TRACK)) {
|
||||||
|
onTrackSegmentEnd();
|
||||||
|
} else if (tag.equals(TAG_GX_COORD)) {
|
||||||
|
onTrackPointEnd();
|
||||||
|
} else if (tag.equals(TAG_GX_VALUE)) {
|
||||||
|
onSensorValueEnd();
|
||||||
|
} else if (tag.equals(TAG_NAME)) {
|
||||||
|
if (content != null) {
|
||||||
|
name = content.trim();
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_DESCRIPTION)) {
|
||||||
|
if (content != null) {
|
||||||
|
description = content.trim();
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_VALUE)) {
|
||||||
|
if (content != null) {
|
||||||
|
category = content.trim();
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_WHEN)) {
|
||||||
|
if (content != null) {
|
||||||
|
time = content.trim();
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_STYLE_URL)) {
|
||||||
|
if (content != null) {
|
||||||
|
waypointType = content.trim();
|
||||||
|
}
|
||||||
|
} else if (localName.equals(TAG_HREF)) {
|
||||||
|
if (content != null) {
|
||||||
|
photoUrl = content.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Reset element content
|
||||||
* On waypoint start.
|
content = null;
|
||||||
*/
|
}
|
||||||
private void onWaypointStart() {
|
|
||||||
// Reset all Placemark variables
|
|
||||||
name = null;
|
|
||||||
description = null;
|
|
||||||
category = null;
|
|
||||||
photoUrl = null;
|
|
||||||
latitude = null;
|
|
||||||
longitude = null;
|
|
||||||
altitude = null;
|
|
||||||
time = null;
|
|
||||||
waypointType = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On waypoint end.
|
* On waypoint start.
|
||||||
*/
|
*/
|
||||||
private void onWaypointEnd() throws SAXException {
|
private void onWaypointStart() {
|
||||||
// Add a waypoint if the waypointType matches
|
// Reset all Placemark variables
|
||||||
WaypointType type = null;
|
name = null;
|
||||||
if (WAYPOINT_STYLE.equals(waypointType)) {
|
description = null;
|
||||||
type = WaypointType.WAYPOINT;
|
category = null;
|
||||||
} else if (STATISTICS_STYLE.equals(waypointType)) {
|
photoUrl = null;
|
||||||
type = WaypointType.STATISTICS;
|
latitude = null;
|
||||||
|
longitude = null;
|
||||||
|
altitude = null;
|
||||||
|
time = null;
|
||||||
|
waypointType = null;
|
||||||
}
|
}
|
||||||
if (type == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (photoUrl != null) {
|
|
||||||
Uri uri = Uri.parse(photoUrl);
|
|
||||||
photoUrl = getPhotoUrl(uri.getLastPathSegment());
|
|
||||||
}
|
|
||||||
addWaypoint(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On waypoint location end.
|
* On waypoint end.
|
||||||
*/
|
*/
|
||||||
private void onWaypointLocationEnd() {
|
private void onWaypointEnd() throws SAXException {
|
||||||
if (content != null) {
|
// Add a waypoint if the waypointType matches
|
||||||
String[] parts = content.trim().split(",");
|
WaypointType type = null;
|
||||||
if (parts.length != 2 && parts.length != 3) {
|
if (WAYPOINT_STYLE.equals(waypointType)) {
|
||||||
return;
|
type = WaypointType.WAYPOINT;
|
||||||
}
|
} else if (STATISTICS_STYLE.equals(waypointType)) {
|
||||||
longitude = parts[0];
|
type = WaypointType.STATISTICS;
|
||||||
latitude = parts[1];
|
}
|
||||||
altitude = parts.length == 3 ? parts[2] : null;
|
if (type == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (photoUrl != null) {
|
||||||
|
Uri uri = Uri.parse(photoUrl);
|
||||||
|
photoUrl = getPhotoUrl(uri.getLastPathSegment());
|
||||||
|
}
|
||||||
|
addWaypoint(type);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void onTrackSegmentStart() {
|
* On waypoint location end.
|
||||||
super.onTrackSegmentStart();
|
*/
|
||||||
locationList = new ArrayList<>();
|
private void onWaypointLocationEnd() {
|
||||||
heartRateList = new ArrayList<>();
|
if (content != null) {
|
||||||
cadenceList = new ArrayList<>();
|
String[] parts = content.trim().split(",");
|
||||||
powerList = new ArrayList<>();
|
if (parts.length != 2 && parts.length != 3) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
/**
|
longitude = parts[0];
|
||||||
* On track segment end.
|
latitude = parts[1];
|
||||||
*/
|
altitude = parts.length == 3 ? parts[2] : null;
|
||||||
private void onTrackSegmentEnd() {
|
}
|
||||||
// Close a track segment by inserting the segment locations
|
|
||||||
boolean hasHeartRate = heartRateList.size() == locationList.size();
|
|
||||||
boolean hasCadence = cadenceList.size() == locationList.size();
|
|
||||||
boolean hasPower = powerList.size() == locationList.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < locationList.size(); i++) {
|
|
||||||
Location location = locationList.get(i);
|
|
||||||
|
|
||||||
if (!hasPower && !hasCadence && !hasHeartRate) {
|
|
||||||
insertTrackPoint(location);
|
|
||||||
} else {
|
|
||||||
float heartrate = hasHeartRate ? heartRateList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
|
|
||||||
float cadence = hasHeartRate ? cadenceList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
|
|
||||||
float power = hasHeartRate ? powerList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
|
|
||||||
|
|
||||||
SensorDataSetLocation sensorDataSetLocation = new SensorDataSetLocation(location, new SensorDataSet(heartrate, cadence, power, SensorDataSet.DATA_UNAVAILABLE, location.getTime()));
|
|
||||||
insertTrackPoint(sensorDataSetLocation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* On track point end. gx:coord end tag.
|
protected void onTrackSegmentStart() {
|
||||||
*/
|
super.onTrackSegmentStart();
|
||||||
private void onTrackPointEnd() throws SAXException {
|
locationList = new ArrayList<>();
|
||||||
// Add location to locationList
|
heartRateList = new ArrayList<>();
|
||||||
if (content == null) {
|
cadenceList = new ArrayList<>();
|
||||||
return;
|
powerList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
String[] parts = content.trim().split(" ");
|
|
||||||
if (parts.length != 2 && parts.length != 3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
longitude = parts[0];
|
|
||||||
latitude = parts[1];
|
|
||||||
altitude = parts.length == 3 ? parts[2] : null;
|
|
||||||
|
|
||||||
Location location = getTrackPoint();
|
/**
|
||||||
if (location == null) {
|
* On track segment end.
|
||||||
return;
|
*/
|
||||||
}
|
private void onTrackSegmentEnd() {
|
||||||
locationList.add(location);
|
// Close a track segment by inserting the segment locations
|
||||||
time = null;
|
boolean hasHeartRate = heartRateList.size() == locationList.size();
|
||||||
}
|
boolean hasCadence = cadenceList.size() == locationList.size();
|
||||||
|
boolean hasPower = powerList.size() == locationList.size();
|
||||||
|
|
||||||
/**
|
for (int i = 0; i < locationList.size(); i++) {
|
||||||
* On sensor data start. gx:SimpleArrayData start tag.
|
Location location = locationList.get(i);
|
||||||
*/
|
|
||||||
private void onSensorDataStart(Attributes attributes) {
|
|
||||||
sensorName = attributes.getValue(ATTRIBUTE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (!hasPower && !hasCadence && !hasHeartRate) {
|
||||||
* On sensor value end. gx:value end tag.
|
insertTrackPoint(location);
|
||||||
*/
|
} else {
|
||||||
private void onSensorValueEnd() throws SAXException {
|
float heartrate = hasHeartRate ? heartRateList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
|
||||||
if (content == null) {
|
float cadence = hasHeartRate ? cadenceList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
|
||||||
return;
|
float power = hasHeartRate ? powerList.get(i) : SensorDataSet.DATA_UNAVAILABLE;
|
||||||
|
|
||||||
|
SensorDataSetLocation sensorDataSetLocation = new SensorDataSetLocation(location, new SensorDataSet(heartrate, cadence, power, SensorDataSet.DATA_UNAVAILABLE, location.getTime()));
|
||||||
|
insertTrackPoint(sensorDataSetLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
content = content.trim();
|
|
||||||
if (content.equals("")) {
|
/**
|
||||||
return;
|
* On track point end. gx:coord end tag.
|
||||||
|
*/
|
||||||
|
private void onTrackPointEnd() throws SAXException {
|
||||||
|
// Add location to locationList
|
||||||
|
if (content == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] parts = content.trim().split(" ");
|
||||||
|
if (parts.length != 2 && parts.length != 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
longitude = parts[0];
|
||||||
|
latitude = parts[1];
|
||||||
|
altitude = parts.length == 3 ? parts[2] : null;
|
||||||
|
|
||||||
|
Location location = getTrackPoint();
|
||||||
|
if (location == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
locationList.add(location);
|
||||||
|
time = null;
|
||||||
}
|
}
|
||||||
int value;
|
|
||||||
try {
|
/**
|
||||||
value = Integer.parseInt(content);
|
* On sensor data start. gx:SimpleArrayData start tag.
|
||||||
} catch (NumberFormatException e) {
|
*/
|
||||||
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
|
private void onSensorDataStart(Attributes attributes) {
|
||||||
|
sensorName = attributes.getValue(ATTRIBUTE_NAME);
|
||||||
}
|
}
|
||||||
if (POWER.equals(sensorName)) {
|
|
||||||
powerList.add(value);
|
/**
|
||||||
} else if (HEART_RATE.equals(sensorName)) {
|
* On sensor value end. gx:value end tag.
|
||||||
heartRateList.add(value);
|
*/
|
||||||
} else if (CADENCE.equals(sensorName)) {
|
private void onSensorValueEnd() throws SAXException {
|
||||||
cadenceList.add(value);
|
if (content == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
content = content.trim();
|
||||||
|
if (content.equals("")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int value;
|
||||||
|
try {
|
||||||
|
value = Integer.parseInt(content);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
|
||||||
|
}
|
||||||
|
if (POWER.equals(sensorName)) {
|
||||||
|
powerList.add(value);
|
||||||
|
} else if (HEART_RATE.equals(sensorName)) {
|
||||||
|
heartRateList.add(value);
|
||||||
|
} else if (CADENCE.equals(sensorName)) {
|
||||||
|
cadenceList.add(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+144
-144
@@ -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;
|
||||||
@@ -40,159 +40,159 @@ import java.util.zip.ZipInputStream;
|
|||||||
*/
|
*/
|
||||||
public class KmzTrackImporter implements TrackImporter {
|
public class KmzTrackImporter implements TrackImporter {
|
||||||
|
|
||||||
private static final String TAG = KmzTrackImporter.class.getSimpleName();
|
private static final String TAG = KmzTrackImporter.class.getSimpleName();
|
||||||
private static final int BUFFER_SIZE = 4096;
|
private static final int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final long importTrackId;
|
private final long importTrackId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
* @param importTrackId track id to import to. This should not be -1L so that
|
* @param importTrackId track id to import to. This should not be -1L so that
|
||||||
* images in the kmz file can be imported.
|
* images in the kmz file can be imported.
|
||||||
*/
|
*/
|
||||||
public KmzTrackImporter(Context context, long importTrackId) {
|
public KmzTrackImporter(Context context, long importTrackId) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.importTrackId = importTrackId;
|
this.importTrackId = importTrackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long importFile(InputStream inputStream) {
|
public long importFile(InputStream inputStream) {
|
||||||
ZipInputStream zipInputStream = null;
|
ZipInputStream zipInputStream = null;
|
||||||
long trackId = importTrackId;
|
long trackId = importTrackId;
|
||||||
try {
|
try {
|
||||||
ZipEntry zipEntry;
|
ZipEntry zipEntry;
|
||||||
|
|
||||||
zipInputStream = new ZipInputStream(inputStream);
|
zipInputStream = new ZipInputStream(inputStream);
|
||||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
Log.d(TAG, "Thread interrupted");
|
Log.d(TAG, "Thread interrupted");
|
||||||
cleanImport(trackId);
|
cleanImport(trackId);
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
String fileName = zipEntry.getName();
|
String fileName = zipEntry.getName();
|
||||||
if (fileName.equals(KmzTrackExporter.KMZ_KML_FILE)) {
|
if (fileName.equals(KmzTrackExporter.KMZ_KML_FILE)) {
|
||||||
trackId = parseKml(zipInputStream);
|
trackId = parseKml(zipInputStream);
|
||||||
if (trackId == -1L) {
|
if (trackId == -1L) {
|
||||||
Log.d(TAG, "Unable to parse kml in kmz");
|
Log.d(TAG, "Unable to parse kml in kmz");
|
||||||
|
cleanImport(trackId);
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String prefix = KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar;
|
||||||
|
if (fileName.startsWith(prefix)) {
|
||||||
|
readImageFile(zipInputStream, fileName.substring(prefix.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zipInputStream.closeEntry();
|
||||||
|
}
|
||||||
|
return trackId;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Unable to import file", e);
|
||||||
cleanImport(trackId);
|
cleanImport(trackId);
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
} finally {
|
||||||
} else {
|
if (zipInputStream != null) {
|
||||||
String prefix = KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar;
|
try {
|
||||||
if (fileName.startsWith(prefix)) {
|
zipInputStream.close();
|
||||||
readImageFile(zipInputStream, fileName.substring(prefix.length()));
|
} catch (IOException e) {
|
||||||
}
|
Log.e(TAG, "Unable to close zip input stream", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
zipInputStream.closeEntry();
|
}
|
||||||
}
|
|
||||||
return trackId;
|
/**
|
||||||
} catch (IOException e) {
|
* Cleans up import.
|
||||||
Log.e(TAG, "Unable to import file", e);
|
*
|
||||||
cleanImport(trackId);
|
* @param trackId the trackId
|
||||||
return -1L;
|
*/
|
||||||
} finally {
|
private void cleanImport(long trackId) {
|
||||||
if (zipInputStream != null) {
|
if (trackId != -1L) {
|
||||||
|
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||||
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importTrackId != -1L) {
|
||||||
|
File dir = FileUtils.getPhotoDir(importTrackId);
|
||||||
|
if (FileUtils.isDirectory(dir)) {
|
||||||
|
for (File file : dir.listFiles()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
dir.delete();
|
||||||
|
FileUtils.updateMediaScanner(context, Uri.fromFile(dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses kml
|
||||||
|
*
|
||||||
|
* @param zipInputStream the zip input stream
|
||||||
|
* @return the imported track id or -1L
|
||||||
|
*/
|
||||||
|
private long parseKml(ZipInputStream zipInputStream) throws IOException {
|
||||||
|
ByteArrayInputStream byteArrayInputStream = null;
|
||||||
try {
|
try {
|
||||||
zipInputStream.close();
|
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, importTrackId);
|
||||||
} catch (IOException e) {
|
byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream));
|
||||||
Log.e(TAG, "Unable to close zip input stream", e);
|
return kmlFileTrackImporter.importFile(byteArrayInputStream);
|
||||||
|
} finally {
|
||||||
|
if (byteArrayInputStream != null) {
|
||||||
|
byteArrayInputStream.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans up import.
|
|
||||||
*
|
|
||||||
* @param trackId the trackId
|
|
||||||
*/
|
|
||||||
private void cleanImport(long trackId) {
|
|
||||||
if (trackId != -1L) {
|
|
||||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
|
||||||
contentProviderUtils.deleteTrack(context, trackId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importTrackId != -1L) {
|
/**
|
||||||
File dir = FileUtils.getPhotoDir(importTrackId);
|
* Gets the kml as byte array.
|
||||||
if (FileUtils.isDirectory(dir)) {
|
*
|
||||||
for (File file : dir.listFiles()) {
|
* @param zipInputStream the zip input stream
|
||||||
file.delete();
|
*/
|
||||||
|
private byte[] getKml(ZipInputStream zipInputStream) throws IOException {
|
||||||
|
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int count;
|
||||||
|
while ((count = zipInputStream.read(buffer)) != -1) {
|
||||||
|
byteArrayOutputStream.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
}
|
}
|
||||||
dir.delete();
|
|
||||||
FileUtils.updateMediaScanner(context, Uri.fromFile(dir));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses kml
|
* Reads an image file.
|
||||||
*
|
*
|
||||||
* @param zipInputStream the zip input stream
|
* @param zipInputStream the zip input stream
|
||||||
* @return the imported track id or -1L
|
* @param fileName the file name
|
||||||
*/
|
*/
|
||||||
private long parseKml(ZipInputStream zipInputStream) throws IOException {
|
private void readImageFile(ZipInputStream zipInputStream, String fileName) throws IOException {
|
||||||
ByteArrayInputStream byteArrayInputStream = null;
|
FileOutputStream fileOutputStream = null;
|
||||||
try {
|
try {
|
||||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, importTrackId);
|
if (importTrackId == -1L) {
|
||||||
byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream));
|
return;
|
||||||
return kmlFileTrackImporter.importFile(byteArrayInputStream);
|
}
|
||||||
} finally {
|
if (fileName.equals("")) {
|
||||||
if (byteArrayInputStream != null) {
|
return;
|
||||||
byteArrayInputStream.close();
|
}
|
||||||
}
|
File dir = FileUtils.getPhotoDir(importTrackId);
|
||||||
|
FileUtils.ensureDirectoryExists(dir);
|
||||||
|
|
||||||
|
File file = new File(dir, fileName);
|
||||||
|
fileOutputStream = new FileOutputStream(file);
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int count;
|
||||||
|
while ((count = zipInputStream.read(buffer)) != -1) {
|
||||||
|
fileOutputStream.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtils.updateMediaScanner(context, Uri.fromFile(file));
|
||||||
|
} finally {
|
||||||
|
if (fileOutputStream != null) {
|
||||||
|
fileOutputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the kml as byte array.
|
|
||||||
*
|
|
||||||
* @param zipInputStream the zip input stream
|
|
||||||
*/
|
|
||||||
private byte[] getKml(ZipInputStream zipInputStream) throws IOException {
|
|
||||||
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
|
|
||||||
byte[] buffer = new byte[BUFFER_SIZE];
|
|
||||||
int count;
|
|
||||||
while ((count = zipInputStream.read(buffer)) != -1) {
|
|
||||||
byteArrayOutputStream.write(buffer, 0, count);
|
|
||||||
}
|
|
||||||
return byteArrayOutputStream.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an image file.
|
|
||||||
*
|
|
||||||
* @param zipInputStream the zip input stream
|
|
||||||
* @param fileName the file name
|
|
||||||
*/
|
|
||||||
private void readImageFile(ZipInputStream zipInputStream, String fileName) throws IOException {
|
|
||||||
FileOutputStream fileOutputStream = null;
|
|
||||||
try {
|
|
||||||
if (importTrackId == -1L) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fileName.equals("")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File dir = FileUtils.getPhotoDir(importTrackId);
|
|
||||||
FileUtils.ensureDirectoryExists(dir);
|
|
||||||
|
|
||||||
File file = new File(dir, fileName);
|
|
||||||
fileOutputStream = new FileOutputStream(file);
|
|
||||||
byte[] buffer = new byte[BUFFER_SIZE];
|
|
||||||
int count;
|
|
||||||
while ((count = zipInputStream.read(buffer)) != -1) {
|
|
||||||
fileOutputStream.write(buffer, 0, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileUtils.updateMediaScanner(context, Uri.fromFile(file));
|
|
||||||
} finally {
|
|
||||||
if (fileOutputStream != null) {
|
|
||||||
fileOutputStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
public interface TrackImporter {
|
public interface TrackImporter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a file.
|
* Import a file.
|
||||||
*
|
*
|
||||||
* @param inputStream the file's input stream
|
* @param inputStream the file's input stream
|
||||||
* @return the imported track id or -1L.
|
* @return the imported track id or -1L.
|
||||||
*/
|
*/
|
||||||
long importFile(InputStream inputStream);
|
long importFile(InputStream inputStream);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-21
@@ -24,29 +24,29 @@ package com.google.android.apps.mytracks.services;
|
|||||||
*/
|
*/
|
||||||
public class AbsoluteLocationListenerPolicy implements LocationListenerPolicy {
|
public class AbsoluteLocationListenerPolicy implements LocationListenerPolicy {
|
||||||
|
|
||||||
private final long interval;
|
private final long interval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param interval the interval to request for gps signal
|
* @param interval the interval to request for gps signal
|
||||||
*/
|
*/
|
||||||
public AbsoluteLocationListenerPolicy(long interval) {
|
public AbsoluteLocationListenerPolicy(long interval) {
|
||||||
this.interval = interval;
|
this.interval = interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getDesiredPollingInterval() {
|
public long getDesiredPollingInterval() {
|
||||||
return interval;
|
return interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMinDistance() {
|
public int getMinDistance() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateIdleTime(long idleTime) {
|
public void updateIdleTime(long idleTime) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-39
@@ -26,48 +26,48 @@ package com.google.android.apps.mytracks.services;
|
|||||||
*/
|
*/
|
||||||
public class AdaptiveLocationListenerPolicy implements LocationListenerPolicy {
|
public class AdaptiveLocationListenerPolicy implements LocationListenerPolicy {
|
||||||
|
|
||||||
private final long minInterval;
|
private final long minInterval;
|
||||||
private final long maxInterval;
|
private final long maxInterval;
|
||||||
private final int minDistance;
|
private final int minDistance;
|
||||||
|
|
||||||
// The time the user has been idle at the current location, in milliseconds.
|
// The time the user has been idle at the current location, in milliseconds.
|
||||||
private long idleTime;
|
private long idleTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a policy that will be bounded by the given minInterval and
|
* Creates a policy that will be bounded by the given minInterval and
|
||||||
* maxInterval.
|
* maxInterval.
|
||||||
*
|
*
|
||||||
* @param minInterval the smallest interval this policy will dictate, in
|
* @param minInterval the smallest interval this policy will dictate, in
|
||||||
* milliseconds
|
* milliseconds
|
||||||
* @param maxInterval the largest interval this policy will dictate, in
|
* @param maxInterval the largest interval this policy will dictate, in
|
||||||
* milliseconds
|
* milliseconds
|
||||||
* @param minDistance the minimum distance in meters
|
* @param minDistance the minimum distance in meters
|
||||||
*/
|
*/
|
||||||
public AdaptiveLocationListenerPolicy(long minInterval, long maxInterval, int minDistance) {
|
public AdaptiveLocationListenerPolicy(long minInterval, long maxInterval, int minDistance) {
|
||||||
this.minInterval = minInterval;
|
this.minInterval = minInterval;
|
||||||
this.maxInterval = maxInterval;
|
this.maxInterval = maxInterval;
|
||||||
this.minDistance = minDistance;
|
this.minDistance = minDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns an interval half of the idle time, but bounded by minInteval and
|
* Returns an interval half of the idle time, but bounded by minInteval and
|
||||||
* maxInterval.
|
* maxInterval.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public long getDesiredPollingInterval() {
|
public long getDesiredPollingInterval() {
|
||||||
long desiredInterval = idleTime / 2;
|
long desiredInterval = idleTime / 2;
|
||||||
// Round to second to avoid setting the interval too often
|
// Round to second to avoid setting the interval too often
|
||||||
desiredInterval = (desiredInterval / 1000) * 1000;
|
desiredInterval = (desiredInterval / 1000) * 1000;
|
||||||
return Math.max(Math.min(maxInterval, desiredInterval), minInterval);
|
return Math.max(Math.min(maxInterval, desiredInterval), minInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMinDistance() {
|
public int getMinDistance() {
|
||||||
return minDistance;
|
return minDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateIdleTime(long newIdleTime) {
|
public void updateIdleTime(long newIdleTime) {
|
||||||
idleTime = newIdleTime;
|
idleTime = newIdleTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-83
@@ -39,95 +39,95 @@ import com.google.android.maps.mytracks.R;
|
|||||||
*/
|
*/
|
||||||
public class ControlRecordingService extends IntentService implements ServiceConnection {
|
public class ControlRecordingService extends IntentService implements ServiceConnection {
|
||||||
|
|
||||||
private static final String TAG = ControlRecordingService.class.getSimpleName();
|
private static final String TAG = ControlRecordingService.class.getSimpleName();
|
||||||
|
|
||||||
private ITrackRecordingService trackRecordingService;
|
private ITrackRecordingService trackRecordingService;
|
||||||
private boolean connected = false;
|
private boolean connected = false;
|
||||||
|
|
||||||
public ControlRecordingService() {
|
public ControlRecordingService() {
|
||||||
super(ControlRecordingService.class.getSimpleName());
|
super(ControlRecordingService.class.getSimpleName());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
Intent newIntent = new Intent(this, TrackRecordingService.class);
|
|
||||||
startService(newIntent);
|
|
||||||
bindService(newIntent, this, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
trackRecordingService = (ITrackRecordingService)service;
|
|
||||||
notifyConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
connected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies all threads that connection to {@link TrackRecordingService} is
|
|
||||||
* available.
|
|
||||||
*/
|
|
||||||
private synchronized void notifyConnected() {
|
|
||||||
connected = true;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits until the connection to {@link TrackRecordingService} is available.
|
|
||||||
*/
|
|
||||||
private synchronized void waitConnected() {
|
|
||||||
while (!connected) {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
|
|
||||||
// can safely ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onHandleIntent(Intent intent) {
|
public void onCreate() {
|
||||||
waitConnected();
|
super.onCreate();
|
||||||
onHandleIntent(intent, trackRecordingService);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Intent newIntent = new Intent(this, TrackRecordingService.class);
|
||||||
/**
|
startService(newIntent);
|
||||||
* Handles the intent to start or stop a recording.
|
bindService(newIntent, this, 0);
|
||||||
*
|
|
||||||
* @param intent to be handled
|
|
||||||
* @param service the trackRecordingService
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void onHandleIntent(Intent intent, ITrackRecordingService service) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (action != null) {
|
|
||||||
if (action.equals(getString(R.string.track_action_start))) {
|
|
||||||
service.startNewTrack();
|
|
||||||
} else if (action.equals(getString(R.string.track_action_end))) {
|
|
||||||
service.endCurrentTrack();
|
|
||||||
} else if (action.equals(getString(R.string.track_action_pause))) {
|
|
||||||
service.pauseCurrentTrack();
|
|
||||||
} else if (action.equals(getString(R.string.track_action_resume))) {
|
|
||||||
service.resumeCurrentTrack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
unbindService(this);
|
|
||||||
connected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
super.onDestroy();
|
trackRecordingService = (ITrackRecordingService) service;
|
||||||
if (connected) {
|
notifyConnected();
|
||||||
unbindService(this);
|
}
|
||||||
connected = false;
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies all threads that connection to {@link TrackRecordingService} is
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
private synchronized void notifyConnected() {
|
||||||
|
connected = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until the connection to {@link TrackRecordingService} is available.
|
||||||
|
*/
|
||||||
|
private synchronized void waitConnected() {
|
||||||
|
while (!connected) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
|
||||||
|
// can safely ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
waitConnected();
|
||||||
|
onHandleIntent(intent, trackRecordingService);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the intent to start or stop a recording.
|
||||||
|
*
|
||||||
|
* @param intent to be handled
|
||||||
|
* @param service the trackRecordingService
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void onHandleIntent(Intent intent, ITrackRecordingService service) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action != null) {
|
||||||
|
if (action.equals(getString(R.string.track_action_start))) {
|
||||||
|
service.startNewTrack();
|
||||||
|
} else if (action.equals(getString(R.string.track_action_end))) {
|
||||||
|
service.endCurrentTrack();
|
||||||
|
} else if (action.equals(getString(R.string.track_action_pause))) {
|
||||||
|
service.pauseCurrentTrack();
|
||||||
|
} else if (action.equals(getString(R.string.track_action_resume))) {
|
||||||
|
service.resumeCurrentTrack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unbindService(this);
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (connected) {
|
||||||
|
unbindService(this);
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+70
-70
@@ -27,87 +27,87 @@ import com.google.android.apps.mytracks.content.sensor.SensorState;
|
|||||||
*/
|
*/
|
||||||
public interface ITrackRecordingService {
|
public interface ITrackRecordingService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts gps.
|
* Starts gps.
|
||||||
*/
|
*/
|
||||||
void startGps();
|
void startGps();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops gps.
|
* Stops gps.
|
||||||
*/
|
*/
|
||||||
void stopGps();
|
void stopGps();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts recording a new track.
|
* Starts recording a new track.
|
||||||
*
|
*
|
||||||
* @return the track ID of the new track.
|
* @return the track ID of the new track.
|
||||||
*/
|
*/
|
||||||
long startNewTrack();
|
long startNewTrack();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses the current recording track.
|
* Pauses the current recording track.
|
||||||
*/
|
*/
|
||||||
void pauseCurrentTrack();
|
void pauseCurrentTrack();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resumes the current recording track.
|
* Resumes the current recording track.
|
||||||
*/
|
*/
|
||||||
void resumeCurrentTrack();
|
void resumeCurrentTrack();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends the current recording track.
|
* Ends the current recording track.
|
||||||
*/
|
*/
|
||||||
void endCurrentTrack();
|
void endCurrentTrack();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if currently recording a track.
|
* Returns true if currently recording a track.
|
||||||
*/
|
*/
|
||||||
boolean isRecording();
|
boolean isRecording();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the current recording track is paused. Returns true if not recording.
|
* Returns true if the current recording track is paused. Returns true if not recording.
|
||||||
*/
|
*/
|
||||||
boolean isPaused();
|
boolean isPaused();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current recording track ID. Returns -1 if not recording.
|
* Gets the current recording track ID. Returns -1 if not recording.
|
||||||
*/
|
*/
|
||||||
long getRecordingTrackId();
|
long getRecordingTrackId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the total time for the current recording track. Returns 0 if not recording.
|
* Gets the total time for the current recording track. Returns 0 if not recording.
|
||||||
*/
|
*/
|
||||||
long getTotalTime();
|
long getTotalTime();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a waypoint in the current recording track.
|
* Inserts a waypoint in the current recording track.
|
||||||
*
|
*
|
||||||
* @param request the details of the waypoint to be inserted
|
* @param request the details of the waypoint to be inserted
|
||||||
* @return the ID of the inserted waypoint
|
* @return the ID of the inserted waypoint
|
||||||
*/
|
*/
|
||||||
long insertWaypoint(WaypointCreationRequest request);
|
long insertWaypoint(WaypointCreationRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
* @param location the track point to be inserted
|
* @param location the track point to be inserted
|
||||||
*/
|
*/
|
||||||
void insertTrackPoint(Location location);
|
void insertTrackPoint(Location location);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
*/
|
||||||
|
SensorDataSet getSensorData();
|
||||||
|
|
||||||
* @return SensorDataSet object.
|
/**
|
||||||
*/
|
* Gets the current sensor manager state.
|
||||||
SensorDataSet getSensorData();
|
* <p>
|
||||||
|
* return a Sensor.SensorState enum value.
|
||||||
/**
|
*/
|
||||||
* Gets the current sensor manager state.
|
SensorState getSensorState();
|
||||||
*
|
|
||||||
* return a Sensor.SensorState enum value.
|
|
||||||
*/
|
|
||||||
SensorState getSensorState();
|
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-17
@@ -25,23 +25,23 @@ package com.google.android.apps.mytracks.services;
|
|||||||
*/
|
*/
|
||||||
public interface LocationListenerPolicy {
|
public interface LocationListenerPolicy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the polling interval this policy would like at this moment.
|
* Returns the polling interval this policy would like at this moment.
|
||||||
*
|
*
|
||||||
* @return the polling interval
|
* @return the polling interval
|
||||||
*/
|
*/
|
||||||
long getDesiredPollingInterval();
|
long getDesiredPollingInterval();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the minimum distance between updates.
|
* Returns the minimum distance between updates.
|
||||||
*/
|
*/
|
||||||
int getMinDistance();
|
int getMinDistance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the amount of time the user has been idle at his current location.
|
* Notifies the amount of time the user has been idle at his current location.
|
||||||
*
|
*
|
||||||
* @param idleTime the time that the user has been idle at his current
|
* @param idleTime the time that the user has been idle at his current
|
||||||
* location
|
* location
|
||||||
*/
|
*/
|
||||||
void updateIdleTime(long idleTime);
|
void updateIdleTime(long idleTime);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-20
@@ -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
|
||||||
|
|||||||
+112
-115
@@ -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.
|
||||||
@@ -37,127 +37,124 @@ 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() {
|
||||||
|
@Override
|
||||||
|
public void binderDied() {
|
||||||
|
Log.d(TAG, "Service died.");
|
||||||
|
setTrackRecordingService(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
|
Log.i(TAG, "Connected to the service.");
|
||||||
|
try {
|
||||||
|
service.linkToDeath(deathRecipient, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Failed to bind a death recipient.", e);
|
||||||
|
}
|
||||||
|
setTrackRecordingService((ITrackRecordingService) service);
|
||||||
|
}
|
||||||
|
|
||||||
private final DeathRecipient deathRecipient = new DeathRecipient() {
|
@Override
|
||||||
@Override
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
public void binderDied() {
|
Log.i(TAG, "Disconnected from the service.");
|
||||||
Log.d(TAG, "Service died.");
|
setTrackRecordingService(null);
|
||||||
setTrackRecordingService(null);
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
/**
|
||||||
@Override
|
* Constructor.
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
*
|
||||||
Log.i(TAG, "Connected to the service.");
|
* @param context the context
|
||||||
try {
|
* @param callback the callback to invoke when the service binding changes
|
||||||
service.linkToDeath(deathRecipient, 0);
|
*/
|
||||||
} catch (RemoteException e) {
|
public TrackRecordingServiceConnection(Context context, Runnable callback) {
|
||||||
Log.e(TAG, "Failed to bind a death recipient.", e);
|
this.context = context;
|
||||||
}
|
this.callback = callback;
|
||||||
setTrackRecordingService((ITrackRecordingService)service);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
* Starts and binds the service.
|
||||||
Log.i(TAG, "Disconnected from the service.");
|
*/
|
||||||
setTrackRecordingService(null);
|
public void startAndBind() {
|
||||||
}
|
bindService(true);
|
||||||
};
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final Runnable callback;
|
|
||||||
private ITrackRecordingService trackRecordingService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param context the context
|
|
||||||
* @param callback the callback to invoke when the service binding changes
|
|
||||||
*/
|
|
||||||
public TrackRecordingServiceConnection(Context context, Runnable callback) {
|
|
||||||
this.context = context;
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts and binds the service.
|
|
||||||
*/
|
|
||||||
public void startAndBind() {
|
|
||||||
bindService(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the service if it is started.
|
|
||||||
*/
|
|
||||||
public void bindIfStarted() {
|
|
||||||
bindService(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unbinds and stops the service.
|
|
||||||
*/
|
|
||||||
public void unbindAndStop() {
|
|
||||||
unbind();
|
|
||||||
context.stopService(new Intent(context, TrackRecordingService.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unbinds the service (but leave it running).
|
|
||||||
*/
|
|
||||||
public void unbind() {
|
|
||||||
try {
|
|
||||||
context.unbindService(serviceConnection);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// Means not bound to the service. OK to ignore.
|
|
||||||
}
|
|
||||||
setTrackRecordingService(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the track recording service if bound. Returns null otherwise
|
|
||||||
*/
|
|
||||||
public ITrackRecordingService getServiceIfBound() {
|
|
||||||
return trackRecordingService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the service if it is started.
|
|
||||||
*
|
|
||||||
* @param startIfNeeded start the service if needed
|
|
||||||
*/
|
|
||||||
private void bindService(boolean startIfNeeded) {
|
|
||||||
if (trackRecordingService != null) {
|
|
||||||
// Service is already started and bound.
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!startIfNeeded
|
/**
|
||||||
&& !TrackRecordingServiceConnectionUtils.isRecordingServiceRunning(context)) {
|
* Binds the service if it is started.
|
||||||
Log.d(TAG, "Service is not started. Not binding it.");
|
*/
|
||||||
return;
|
public void bindIfStarted() {
|
||||||
|
bindService(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startIfNeeded) {
|
/**
|
||||||
Log.i(TAG, "Starting the service.");
|
* Unbinds and stops the service.
|
||||||
context.startService(new Intent(context, TrackRecordingService.class));
|
*/
|
||||||
|
public void unbindAndStop() {
|
||||||
|
unbind();
|
||||||
|
context.stopService(new Intent(context, TrackRecordingService.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i(TAG, "Binding the service.");
|
/**
|
||||||
int flags = BuildConfig.DEBUG ? Context.BIND_DEBUG_UNBIND : 0;
|
* Unbinds the service (but leave it running).
|
||||||
context.bindService(new Intent(context, TrackRecordingService.class), serviceConnection, flags);
|
*/
|
||||||
}
|
public void unbind() {
|
||||||
|
try {
|
||||||
/**
|
context.unbindService(serviceConnection);
|
||||||
* Sets the trackRecordingService.
|
} catch (IllegalArgumentException e) {
|
||||||
*
|
// Means not bound to the service. OK to ignore.
|
||||||
* @param value the value
|
}
|
||||||
*/
|
setTrackRecordingService(null);
|
||||||
private void setTrackRecordingService(ITrackRecordingService value) {
|
}
|
||||||
trackRecordingService = value;
|
|
||||||
if (callback != null) {
|
/**
|
||||||
callback.run();
|
* Gets the track recording service if bound. Returns null otherwise
|
||||||
|
*/
|
||||||
|
public ITrackRecordingService getServiceIfBound() {
|
||||||
|
return trackRecordingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the service if it is started.
|
||||||
|
*
|
||||||
|
* @param startIfNeeded start the service if needed
|
||||||
|
*/
|
||||||
|
private void bindService(boolean startIfNeeded) {
|
||||||
|
if (trackRecordingService != null) {
|
||||||
|
// Service is already started and bound.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!startIfNeeded
|
||||||
|
&& !TrackRecordingServiceConnectionUtils.isRecordingServiceRunning(context)) {
|
||||||
|
Log.d(TAG, "Service is not started. Not binding it.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startIfNeeded) {
|
||||||
|
Log.i(TAG, "Starting the service.");
|
||||||
|
context.startService(new Intent(context, TrackRecordingService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Binding the service.");
|
||||||
|
int flags = BuildConfig.DEBUG ? Context.BIND_DEBUG_UNBIND : 0;
|
||||||
|
context.bindService(new Intent(context, TrackRecordingService.class), serviceConnection, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the trackRecordingService.
|
||||||
|
*
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
private void setTrackRecordingService(ITrackRecordingService value) {
|
||||||
|
trackRecordingService = value;
|
||||||
|
if (callback != null) {
|
||||||
|
callback.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -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;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user