mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-07 03:53:05 +02:00
Autoformatted all classes (non-testing).
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -27,62 +27,61 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public abstract class AbstractActivity extends FragmentActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Set volume control stream for text to speech
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
// Set volume control stream for text to speech
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
|
||||
setContentView(getLayoutResId());
|
||||
setContentView(getLayoutResId());
|
||||
|
||||
// Configure action bar must be after setContentView
|
||||
if (configureActionBarHomeAsUp()) {
|
||||
ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
// Configure action bar must be after setContentView
|
||||
if (configureActionBarHomeAsUp()) {
|
||||
ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the layout resource id.
|
||||
*/
|
||||
protected abstract int getLayoutResId();
|
||||
/**
|
||||
* Gets the layout resource id.
|
||||
*/
|
||||
protected abstract int getLayoutResId();
|
||||
|
||||
/**
|
||||
* Returns true to configure the action bar home button as the up button (go to previous activity).
|
||||
*/
|
||||
protected boolean configureActionBarHomeAsUp() {
|
||||
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;
|
||||
/**
|
||||
* Returns true to configure the action bar home button as the up button (go to previous activity).
|
||||
*/
|
||||
protected boolean configureActionBarHomeAsUp() {
|
||||
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();
|
||||
}
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the home menu item is selected. E.g., setup the back stack when home is selected.
|
||||
*/
|
||||
protected void onHomeSelected() {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -51,74 +51,65 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public abstract class AbstractTrackActivity extends AbstractActivity implements ConfirmDeleteCaller {
|
||||
|
||||
private static final int DELETE_REQUEST_CODE = 3;
|
||||
protected static final int GPS_REQUEST_CODE = 6;
|
||||
protected static final int GPS_REQUEST_CODE = 6;
|
||||
private static final int DELETE_REQUEST_CODE = 3;
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == DELETE_REQUEST_CODE) {
|
||||
onDeleted();
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
|
||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
||||
|
||||
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
|
||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
// Do nothing
|
||||
}
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
||||
mode.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
||||
mode.invalidate();
|
||||
}
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
|
||||
mode.finish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
|
||||
mode.finish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the checked positions in a list view.
|
||||
*
|
||||
* @param list the list view
|
||||
*/
|
||||
private int[] getCheckedPositions(ListView list) {
|
||||
SparseBooleanArray positions = list.getCheckedItemPositions();
|
||||
ArrayList<Integer> arrayList = new ArrayList<>();
|
||||
for (int i = 0; i < positions.size(); i++) {
|
||||
int key = positions.keyAt(i);
|
||||
if (positions.valueAt(i)) {
|
||||
arrayList.add(key);
|
||||
}
|
||||
}
|
||||
int[] result = new int[arrayList.size()];
|
||||
for (int i = 0; i < arrayList.size(); i++) {
|
||||
result[i] = arrayList.get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Gets the checked positions in a list view.
|
||||
*
|
||||
* @param list the list view
|
||||
*/
|
||||
private int[] getCheckedPositions(ListView list) {
|
||||
SparseBooleanArray positions = list.getCheckedItemPositions();
|
||||
ArrayList<Integer> arrayList = new ArrayList<>();
|
||||
for (int i = 0; i < positions.size(); i++) {
|
||||
int key = positions.keyAt(i);
|
||||
if (positions.valueAt(i)) {
|
||||
arrayList.add(key);
|
||||
}
|
||||
}
|
||||
int[] result = new int[arrayList.size()];
|
||||
for (int i = 0; i < arrayList.size(); i++) {
|
||||
result[i] = arrayList.get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void configureSearchWidget(Activity activity, final MenuItem menuItem, final TrackController trackController) {
|
||||
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
||||
@@ -166,46 +157,55 @@ public abstract class AbstractTrackActivity extends AbstractActivity implements
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete tracks.
|
||||
*
|
||||
* @param trackIds the track ids
|
||||
*/
|
||||
protected void deleteTracks(long[] trackIds) {
|
||||
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;
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == DELETE_REQUEST_CODE) {
|
||||
onDeleted();
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track recording service connection. For stopping the current
|
||||
* recording if need to delete the current recording track.
|
||||
*/
|
||||
abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection();
|
||||
@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);
|
||||
}
|
||||
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.
|
||||
*/
|
||||
abstract protected void onDeleted();
|
||||
/**
|
||||
* Gets the track recording service connection. For stopping the current
|
||||
* recording if need to delete the current recording track.
|
||||
*/
|
||||
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;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.apps.mytracks.content.ContentProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.StatsUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -33,31 +33,31 @@ import java.util.List;
|
||||
*/
|
||||
public class AggregatedStatsActivity extends AbstractActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatsUtils.setTripStatisticsValues(this, this, null, getTripStatistics(), null);
|
||||
StatsUtils.setLocationValues(this, this, null, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the aggregated trip statistics for all the recorded tracks or null if
|
||||
* there is no track.
|
||||
*/
|
||||
private TripStatistics getTripStatistics() {
|
||||
List<Track> tracks = ContentProviderUtils.Factory.get(this).getAllTracks();
|
||||
TripStatistics tripStatistics = null;
|
||||
if (!tracks.isEmpty()) {
|
||||
tripStatistics = new TripStatistics(tracks.iterator().next().getTripStatistics());
|
||||
for (int i = 1; i < tracks.size(); i++) {
|
||||
tripStatistics.merge(tracks.get(i).getTripStatistics());
|
||||
}
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatsUtils.setTripStatisticsValues(this, this, null, getTripStatistics(), null);
|
||||
StatsUtils.setLocationValues(this, this, null, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the aggregated trip statistics for all the recorded tracks or null if
|
||||
* there is no track.
|
||||
*/
|
||||
private TripStatistics getTripStatistics() {
|
||||
List<Track> tracks = ContentProviderUtils.Factory.get(this).getAllTracks();
|
||||
TripStatistics tripStatistics = null;
|
||||
if (!tracks.isEmpty()) {
|
||||
tripStatistics = new TripStatistics(tracks.iterator().next().getTripStatistics());
|
||||
for (int i = 1; i < tracks.size(); i++) {
|
||||
tripStatistics.merge(tracks.get(i).getTripStatistics());
|
||||
}
|
||||
}
|
||||
return tripStatistics;
|
||||
}
|
||||
return tripStatistics;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -15,43 +15,43 @@
|
||||
*/
|
||||
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.Context;
|
||||
import android.content.Intent;
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* started tracking session if the phone crashed (hopefully not), or the user
|
||||
* decided to swap the battery or some external event occurred which forced
|
||||
* a phone reboot.
|
||||
*
|
||||
* <p>
|
||||
* This class simply delegates to {@link TrackRecordingService} to make a
|
||||
* decision whether to continue with the previous track (if any), or just
|
||||
* abandon it.
|
||||
*
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = BootReceiver.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "BootReceiver.onReceive: " + intent.getAction());
|
||||
if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
||||
Intent startIntent = new Intent(context, TrackRecordingService.class)
|
||||
.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
context.startService(startIntent);
|
||||
} else {
|
||||
Log.w(TAG, "BootReceiver: unsupported action");
|
||||
private static final String TAG = BootReceiver.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "BootReceiver.onReceive: " + intent.getAction());
|
||||
if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
||||
Intent startIntent = new Intent(context, TrackRecordingService.class)
|
||||
.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
context.startService(startIntent);
|
||||
} else {
|
||||
Log.w(TAG, "BootReceiver: unsupported action");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Path;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.ExtremityMonitor;
|
||||
@@ -30,234 +31,234 @@ import java.text.NumberFormat;
|
||||
|
||||
/**
|
||||
* This class encapsulates the meta data for one series of the chart values.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ChartValueSeries {
|
||||
|
||||
private static final float STROKE_WIDTH = 2f;
|
||||
private static final float STROKE_WIDTH = 2f;
|
||||
|
||||
private final int absoluteMin;
|
||||
private final int absoluteMax;
|
||||
private final int[] intervalValues;
|
||||
private final int metricTitleId;
|
||||
private final int imperialTitleId;
|
||||
private final Paint fillPaint;
|
||||
private final Paint strokePaint;
|
||||
private final Paint titlePaint;
|
||||
private final Paint markerPaint;
|
||||
private final ExtremityMonitor extremityMonitor;
|
||||
private final NumberFormat numberFormat;
|
||||
private final Path path;
|
||||
|
||||
private int interval = 1;
|
||||
private int minMarkerValue = 0;
|
||||
private int maxMarkerValue = interval * ChartView.Y_AXIS_INTERVALS;
|
||||
private boolean enabled = true;
|
||||
private final int absoluteMin;
|
||||
private final int absoluteMax;
|
||||
private final int[] intervalValues;
|
||||
private final int metricTitleId;
|
||||
private final int imperialTitleId;
|
||||
private final Paint fillPaint;
|
||||
private final Paint strokePaint;
|
||||
private final Paint titlePaint;
|
||||
private final Paint markerPaint;
|
||||
private final ExtremityMonitor extremityMonitor;
|
||||
private final NumberFormat numberFormat;
|
||||
private final Path path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
* @param absoluteMin the absolute min value
|
||||
* @param absoluteMax the absolute max value
|
||||
* @param intervalValues the list of interval values
|
||||
* @param metricTitleId the metric title id
|
||||
* @param imperialTitleId the imperial title id
|
||||
* @param fillColor the fill color
|
||||
* @param strokeColor the stroke color
|
||||
*/
|
||||
public ChartValueSeries(Context context, int absoluteMin, int absoluteMax, int[] intervalValues,
|
||||
int metricTitleId, int imperialTitleId, int fillColor, int strokeColor) {
|
||||
this.absoluteMin = absoluteMin;
|
||||
this.absoluteMax = absoluteMax;
|
||||
this.intervalValues = intervalValues;
|
||||
this.metricTitleId = metricTitleId;
|
||||
this.imperialTitleId = imperialTitleId;
|
||||
private int interval = 1;
|
||||
private int minMarkerValue = 0;
|
||||
private int maxMarkerValue = interval * ChartView.Y_AXIS_INTERVALS;
|
||||
private boolean enabled = true;
|
||||
|
||||
fillPaint = new Paint();
|
||||
fillPaint.setStyle(Style.FILL);
|
||||
fillPaint.setColor(context.getResources().getColor(fillColor));
|
||||
fillPaint.setAntiAlias(true);
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
* @param absoluteMin the absolute min value
|
||||
* @param absoluteMax the absolute max value
|
||||
* @param intervalValues the list of interval values
|
||||
* @param metricTitleId the metric title id
|
||||
* @param imperialTitleId the imperial title id
|
||||
* @param fillColor the fill color
|
||||
* @param strokeColor the stroke color
|
||||
*/
|
||||
public ChartValueSeries(Context context, int absoluteMin, int absoluteMax, int[] intervalValues,
|
||||
int metricTitleId, int imperialTitleId, int fillColor, int strokeColor) {
|
||||
this.absoluteMin = absoluteMin;
|
||||
this.absoluteMax = absoluteMax;
|
||||
this.intervalValues = intervalValues;
|
||||
this.metricTitleId = metricTitleId;
|
||||
this.imperialTitleId = imperialTitleId;
|
||||
|
||||
strokePaint = new Paint();
|
||||
strokePaint.setStyle(Style.STROKE);
|
||||
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
||||
strokePaint.setAntiAlias(true);
|
||||
fillPaint = new Paint();
|
||||
fillPaint.setStyle(Style.FILL);
|
||||
fillPaint.setColor(context.getResources().getColor(fillColor));
|
||||
fillPaint.setAntiAlias(true);
|
||||
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
|
||||
// Make copies of the stroke paint with the default thickness
|
||||
titlePaint = new Paint(strokePaint);
|
||||
titlePaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE * scale);
|
||||
titlePaint.setTextAlign(Align.CENTER);
|
||||
strokePaint = new Paint();
|
||||
strokePaint.setStyle(Style.STROKE);
|
||||
strokePaint.setColor(context.getResources().getColor(strokeColor));
|
||||
strokePaint.setAntiAlias(true);
|
||||
|
||||
markerPaint = new Paint(strokePaint);
|
||||
markerPaint.setTextSize(ChartView.SMALL_TEXT_SIZE * scale);
|
||||
markerPaint.setTextAlign(Align.RIGHT);
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
|
||||
// Set stroke paint thickness
|
||||
strokePaint.setStrokeWidth(STROKE_WIDTH);
|
||||
// Make copies of the stroke paint with the default thickness
|
||||
titlePaint = new Paint(strokePaint);
|
||||
titlePaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE * scale);
|
||||
titlePaint.setTextAlign(Align.CENTER);
|
||||
|
||||
extremityMonitor = new ExtremityMonitor();
|
||||
numberFormat = NumberFormat.getIntegerInstance();
|
||||
path = new Path();
|
||||
}
|
||||
markerPaint = new Paint(strokePaint);
|
||||
markerPaint.setTextSize(ChartView.SMALL_TEXT_SIZE * scale);
|
||||
markerPaint.setTextAlign(Align.RIGHT);
|
||||
|
||||
/**
|
||||
* Sets the series enabled value.
|
||||
*
|
||||
* @param enabled true to enable
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
// Set stroke paint thickness
|
||||
strokePaint.setStrokeWidth(STROKE_WIDTH);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
extremityMonitor = new ExtremityMonitor();
|
||||
numberFormat = NumberFormat.getIntegerInstance();
|
||||
path = new Path();
|
||||
}
|
||||
// Return the largest interval
|
||||
return intervalValues[intervalValues.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the min marker value.
|
||||
*
|
||||
* @param min the min series value
|
||||
* @param intervalValue the interval 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;
|
||||
/**
|
||||
* Returns true if the series is enabled.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interval value.
|
||||
*/
|
||||
public int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
/**
|
||||
* Sets the series enabled value.
|
||||
*
|
||||
* @param enabled true to enable
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the min marker value.
|
||||
*/
|
||||
public int getMinMarkerValue() {
|
||||
return minMarkerValue;
|
||||
}
|
||||
/**
|
||||
* Returns true if the series has data.
|
||||
*/
|
||||
public boolean hasData() {
|
||||
return extremityMonitor.hasData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the max marker value.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
int getMaxMarkerValue() {
|
||||
return maxMarkerValue;
|
||||
}
|
||||
/**
|
||||
* Updates the series with a new value.
|
||||
*
|
||||
* @param value the new value
|
||||
*/
|
||||
public void update(double value) {
|
||||
extremityMonitor.update(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title id.
|
||||
*/
|
||||
public int getTitleId(boolean metricUnits) {
|
||||
return metricUnits ? metricTitleId : imperialTitleId;
|
||||
}
|
||||
/**
|
||||
* Gets the path.
|
||||
*/
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title paint.
|
||||
*/
|
||||
public Paint getTitlePaint() {
|
||||
return titlePaint;
|
||||
}
|
||||
/**
|
||||
* Draws the path on canvas.
|
||||
*
|
||||
* @param canvas the canvas
|
||||
*/
|
||||
public void drawPath(Canvas canvas) {
|
||||
canvas.drawPath(path, fillPaint);
|
||||
canvas.drawPath(path, strokePaint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the marker paint.
|
||||
*/
|
||||
public Paint getMarkerPaint() {
|
||||
return markerPaint;
|
||||
}
|
||||
/**
|
||||
* 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 largest marker.
|
||||
*/
|
||||
public String getLargestMarker() {
|
||||
String minMarker = numberFormat.format(getMinMarkerValue());
|
||||
String maxMarker = numberFormat.format(getMaxMarkerValue());
|
||||
return minMarker.length() >= maxMarker.length() ? minMarker : maxMarker;
|
||||
}
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a marker value.
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
public String formatMarker(int value) {
|
||||
return numberFormat.format(value);
|
||||
}
|
||||
/**
|
||||
* Gets the min marker value.
|
||||
*
|
||||
* @param min the min series value
|
||||
* @param intervalValue the interval 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
@@ -18,20 +18,20 @@ package com.google.android.apps.mytracks;
|
||||
|
||||
/**
|
||||
* Common constants.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,27 +20,27 @@ import android.view.Menu;
|
||||
|
||||
/**
|
||||
* Callback when items in the contextual action mode are selected.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ContextualActionModeCallback {
|
||||
|
||||
/**
|
||||
* Invoked to prepare the menu for the selected items.
|
||||
*
|
||||
* @param menu the menu
|
||||
* @param positions the selected items' positions
|
||||
* @param ids the selected items' ids, if available
|
||||
* @param showSelectAll true to show select all
|
||||
*/
|
||||
void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll);
|
||||
/**
|
||||
* Invoked to prepare the menu for the selected items.
|
||||
*
|
||||
* @param menu the menu
|
||||
* @param positions the selected items' positions
|
||||
* @param ids the selected items' ids, if available
|
||||
* @param showSelectAll true to show select all
|
||||
*/
|
||||
void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll);
|
||||
|
||||
/**
|
||||
* Invoked when items are selected.
|
||||
*
|
||||
* @param itemId the context menu item id
|
||||
* @param positions the selected items' positions
|
||||
* @param ids the selected items' ids, if available
|
||||
*/
|
||||
boolean onClick(int itemId, int[] positions, long[] ids);
|
||||
/**
|
||||
* Invoked when items are selected.
|
||||
*
|
||||
* @param itemId the context menu item id
|
||||
* @param positions the selected items' positions
|
||||
* @param ids the selected items' ids, if available
|
||||
*/
|
||||
boolean onClick(int itemId, int[] positions, long[] ids);
|
||||
}
|
||||
@@ -16,82 +16,82 @@
|
||||
|
||||
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.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An activity for delete tracks.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
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
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
Intent intent = getIntent();
|
||||
long[] trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof DeleteAsyncTask) {
|
||||
deleteAsyncTask = (DeleteAsyncTask) retained;
|
||||
deleteAsyncTask.setActivity(this);
|
||||
} else {
|
||||
deleteAsyncTask = new DeleteAsyncTask(this, trackIds);
|
||||
deleteAsyncTask.execute();
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
Intent intent = getIntent();
|
||||
long[] trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof DeleteAsyncTask) {
|
||||
deleteAsyncTask = (DeleteAsyncTask) retained;
|
||||
deleteAsyncTask.setActivity(this);
|
||||
} else {
|
||||
deleteAsyncTask = new DeleteAsyncTask(this, trackIds);
|
||||
deleteAsyncTask.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
deleteAsyncTask.setActivity(null);
|
||||
return deleteAsyncTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != DIALOG_PROGRESS_ID) {
|
||||
return null;
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
deleteAsyncTask.setActivity(null);
|
||||
return deleteAsyncTask;
|
||||
}
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*/
|
||||
public void onAsyncTaskCompleted() {
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
@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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*/
|
||||
public void onAsyncTaskCompleted() {
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,73 +23,72 @@ import com.google.android.apps.mytracks.content.ContentProviderUtils;
|
||||
|
||||
/**
|
||||
* Async Task to delete tracks.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private DeleteActivity deleteActivity;
|
||||
private final long[] trackIds;
|
||||
private final Context context;
|
||||
private final long[] trackIds;
|
||||
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
|
||||
* @param trackIds the track ids to delete. To delete all, set to size 1 with
|
||||
* trackIds[0] == -1L
|
||||
*/
|
||||
public DeleteAsyncTask(DeleteActivity deleteActivity, long[] trackIds) {
|
||||
this.deleteActivity = deleteActivity;
|
||||
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();
|
||||
/**
|
||||
* Creates an AsyncTask.
|
||||
*
|
||||
* @param deleteActivity the activity currently associated with this task
|
||||
* @param trackIds the track ids to delete. To delete all, set to size 1 with
|
||||
* trackIds[0] == -1L
|
||||
*/
|
||||
public DeleteAsyncTask(DeleteActivity deleteActivity, long[] trackIds) {
|
||||
this.deleteActivity = deleteActivity;
|
||||
this.trackIds = trackIds;
|
||||
context = deleteActivity.getApplicationContext();
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (deleteActivity != null) {
|
||||
deleteActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
contentProviderUtils.deleteTrack(context, id);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
completed = true;
|
||||
if (deleteActivity != null) {
|
||||
deleteActivity.onAsyncTaskCompleted();
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (deleteActivity != null) {
|
||||
deleteActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -35,92 +35,92 @@ import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An activity to display marker detail info.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class MarkerDetailActivity extends AbstractActivity implements DeleteMarkerCaller {
|
||||
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
private static final String TAG = MarkerDetailActivity.class.getSimpleName();
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
private static final String TAG = MarkerDetailActivity.class.getSimpleName();
|
||||
|
||||
private ArrayList<Long> markerIds;
|
||||
private ArrayList<Long> markerIds;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
long markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||
if (markerId == -1L) {
|
||||
Log.d(TAG, "invalid marker id");
|
||||
finish();
|
||||
return;
|
||||
long markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||
if (markerId == -1L) {
|
||||
Log.d(TAG, "invalid marker id");
|
||||
finish();
|
||||
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);
|
||||
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);
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.marker_detail_activity;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.marker_detail_activity;
|
||||
}
|
||||
@Override
|
||||
public void onDeleteMarkerDone() {
|
||||
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
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
public Fragment getItem(int position) {
|
||||
String title = getString(R.string.marker_title, position + 1, getCount());
|
||||
return MarkerDetailFragment.newInstance(markerIds.get(position), title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker detail pager adapter.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class MarkerDetailPagerAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
public MarkerDetailPagerAdapter(FragmentManager fragmentManager) {
|
||||
super(fragmentManager);
|
||||
@Override
|
||||
public int getCount() {
|
||||
return markerIds.size();
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -16,14 +16,6 @@
|
||||
|
||||
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.util.Log;
|
||||
import android.view.View;
|
||||
@@ -33,6 +25,14 @@ import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
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.
|
||||
*
|
||||
@@ -40,152 +40,150 @@ import android.widget.EditText;
|
||||
*/
|
||||
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";
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
// UI elements
|
||||
private View statisticsSection;
|
||||
private EditText statisticsName;
|
||||
private View waypointSection;
|
||||
private EditText waypointName;
|
||||
private AutoCompleteTextView waypointMarkerType;
|
||||
private EditText waypointDescription;
|
||||
private Button done;
|
||||
|
||||
private long trackId;
|
||||
private long markerId;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private Waypoint waypoint;
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
// UI elements
|
||||
private View statisticsSection;
|
||||
private EditText statisticsName;
|
||||
private View waypointSection;
|
||||
private EditText waypointName;
|
||||
private AutoCompleteTextView waypointMarkerType;
|
||||
private EditText waypointDescription;
|
||||
private Button done;
|
||||
trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
// Setup UI elements
|
||||
statisticsSection = findViewById(R.id.marker_edit_statistics_section);
|
||||
statisticsName = findViewById(R.id.marker_edit_statistics_name);
|
||||
|
||||
trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
markerId = getIntent().getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
|
||||
// Setup UI elements
|
||||
statisticsSection = findViewById(R.id.marker_edit_statistics_section);
|
||||
statisticsName = findViewById(R.id.marker_edit_statistics_name);
|
||||
|
||||
waypointSection = findViewById(R.id.marker_edit_waypoint_section);
|
||||
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
||||
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);
|
||||
waypointSection = findViewById(R.id.marker_edit_waypoint_section);
|
||||
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
||||
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);
|
||||
|
||||
Button cancel = findViewById(R.id.marker_edit_cancel);
|
||||
cancel.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
done = findViewById(R.id.marker_edit_done);
|
||||
updateUiByMarkerId();
|
||||
}
|
||||
Button cancel = findViewById(R.id.marker_edit_cancel);
|
||||
cancel.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
done = findViewById(R.id.marker_edit_done);
|
||||
updateUiByMarkerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
}
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.marker_edit;
|
||||
}
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.marker_edit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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) {
|
||||
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 {
|
||||
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.
|
||||
*/
|
||||
private void addMarker() {
|
||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(
|
||||
WaypointType.WAYPOINT,
|
||||
false,
|
||||
waypointName.getText().toString(),
|
||||
waypointMarkerType.getText().toString(),
|
||||
waypointDescription.getText().toString(),
|
||||
null,
|
||||
null);
|
||||
TrackRecordingServiceConnectionUtils.addMarker(
|
||||
this, trackRecordingServiceConnection, waypointCreationRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a marker.
|
||||
*/
|
||||
private void saveMarker() {
|
||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||
if (statistics) {
|
||||
waypoint.setName(statisticsName.getText().toString());
|
||||
} else {
|
||||
waypoint.setName(waypointName.getText().toString());
|
||||
waypoint.setCategory(waypointMarkerType.getText().toString());
|
||||
waypoint.setDescription(waypointDescription.getText().toString());
|
||||
/**
|
||||
* Adds a marker.
|
||||
*/
|
||||
private void addMarker() {
|
||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(
|
||||
WaypointType.WAYPOINT,
|
||||
false,
|
||||
waypointName.getText().toString(),
|
||||
waypointMarkerType.getText().toString(),
|
||||
waypointDescription.getText().toString(),
|
||||
null,
|
||||
null);
|
||||
TrackRecordingServiceConnectionUtils.addMarker(
|
||||
this, trackRecordingServiceConnection, waypointCreationRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a marker.
|
||||
*/
|
||||
private void saveMarker() {
|
||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||
if (statistics) {
|
||||
waypoint.setName(statisticsName.getText().toString());
|
||||
} else {
|
||||
waypoint.setName(waypointName.getText().toString());
|
||||
waypoint.setCategory(waypointMarkerType.getText().toString());
|
||||
waypoint.setDescription(waypointDescription.getText().toString());
|
||||
}
|
||||
ContentProviderUtils.Factory.get(this).updateWaypoint(waypoint);
|
||||
}
|
||||
ContentProviderUtils.Factory.get(this).updateWaypoint(waypoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,284 +53,278 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* Activity to show a list of markers in a track.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
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,
|
||||
WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY,
|
||||
WaypointsColumns.TYPE, WaypointsColumns.TIME, WaypointsColumns.PHOTOURL,
|
||||
WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE};
|
||||
|
||||
// Callback when an item is selected in the contextual action mode
|
||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
private static final String[] PROJECTION = new String[]{WaypointsColumns._ID,
|
||||
WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY,
|
||||
WaypointsColumns.TYPE, WaypointsColumns.TIME, WaypointsColumns.PHOTOURL,
|
||||
WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE};
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private SharedPreferences sharedPreferences;
|
||||
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
|
||||
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_show_on_map).setVisible(isSingleSelection);
|
||||
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
||||
menu.findItem(R.id.list_context_menu_delete).setVisible(true);
|
||||
/*
|
||||
* Set select all to the same visibility as delete since delete is the
|
||||
* only action that can be applied to multiple markers.
|
||||
*/
|
||||
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
|
||||
menu.findItem(R.id.list_context_menu_share).setVisible(false);
|
||||
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection);
|
||||
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
||||
menu.findItem(R.id.list_context_menu_delete).setVisible(true);
|
||||
/*
|
||||
* Set select all to the same visibility as delete since delete is the
|
||||
* only action that can be applied to multiple markers.
|
||||
*/
|
||||
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
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);
|
||||
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
private MenuItem insertMarkerMenuItem;
|
||||
private MenuItem searchMenuItem;
|
||||
|
||||
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
||||
@NonNull
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
||||
resourceCursorAdapter.swapCursor(cursor);
|
||||
}
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||
resourceCursorAdapter.swapCursor(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
track = trackId != -1L ? contentProviderUtils.getTrack(trackId) : null;
|
||||
final long trackFirstWaypointId = trackId != -1 ? contentProviderUtils.getFirstWaypointId(trackId) : -1;
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
}
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
this.invalidateOptionsMenu();
|
||||
}
|
||||
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);
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
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);
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.marker_list;
|
||||
}
|
||||
ListItemUtils.setListItem(MarkerListActivity.this, view, false, true, iconId, R.string.image_marker, name, null, null, 0, time, false, category, description, photoUrl);
|
||||
}
|
||||
};
|
||||
listView.setAdapter(resourceCursorAdapter);
|
||||
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.marker_list, menu);
|
||||
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
||||
@NonNull
|
||||
@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);
|
||||
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;
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||
resourceCursorAdapter.swapCursor(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
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;
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
}
|
||||
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
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
this.invalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH && searchMenuItem != null) {
|
||||
return true;
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteMarkerDone() {
|
||||
// Do nothing
|
||||
}
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,446 +68,443 @@ import java.util.SortedSet;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
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_PAUSED_FIELD = "isPaused";
|
||||
private static final String ICON_ID_FIELD = "icon";
|
||||
private static final String ICON_CONTENT_DESCRIPTION_ID_FIELD = "iconContentDescription";
|
||||
private static final String NAME_FIELD = "name";
|
||||
private static final String TOTAL_TIME_FIELD = "totalTime";
|
||||
private static final String TOTAL_DISTANCE_FIELD = "totalDistance";
|
||||
private static final String MARKER_COUNT_FIELD = "markerCount";
|
||||
private static final String START_TIME_FIELD = "startTime";
|
||||
private static final String CATEGORY_FIELD = "category";
|
||||
private static final String DESCRIPTION_FIELD = "description";
|
||||
private static final String PHOTO_URL_FIELD = "photoUrl";
|
||||
private static final String MARKER_LATITUDE_FIELD = "latitude";
|
||||
private static final String MARKER_LONGITUDE_FIELD = "longitude";
|
||||
private static final String IS_RECORDING_FIELD = "isRecording";
|
||||
private static final String IS_PAUSED_FIELD = "isPaused";
|
||||
private static final String ICON_ID_FIELD = "icon";
|
||||
private static final String ICON_CONTENT_DESCRIPTION_ID_FIELD = "iconContentDescription";
|
||||
private static final String NAME_FIELD = "name";
|
||||
private static final String TOTAL_TIME_FIELD = "totalTime";
|
||||
private static final String TOTAL_DISTANCE_FIELD = "totalDistance";
|
||||
private static final String MARKER_COUNT_FIELD = "markerCount";
|
||||
private static final String START_TIME_FIELD = "startTime";
|
||||
private static final String CATEGORY_FIELD = "category";
|
||||
private static final String DESCRIPTION_FIELD = "description";
|
||||
private static final String PHOTO_URL_FIELD = "photoUrl";
|
||||
private static final String MARKER_LATITUDE_FIELD = "latitude";
|
||||
private static final String MARKER_LONGITUDE_FIELD = "longitude";
|
||||
|
||||
private static final String TRACK_ID_FIELD = "trackId";
|
||||
private static final String MARKER_ID_FIELD = "markerId";
|
||||
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.stats_units_key))) {
|
||||
metricUnits = PreferencesUtils.isMetricUnits(SearchListActivity.this);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(SearchListActivity.this, R.string.recording_track_id_key);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_paused_key))) {
|
||||
recordingTrackPaused = PreferencesUtils.getBoolean(SearchListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
}
|
||||
if (key != null) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
arrayAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
private static final String TRACK_ID_FIELD = "trackId";
|
||||
private static final String MARKER_ID_FIELD = "markerId";
|
||||
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;
|
||||
// Callback when an item is selected in the contextual action mode
|
||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
@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;
|
||||
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);
|
||||
}
|
||||
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);
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.stats_units_key))) {
|
||||
metricUnits = PreferencesUtils.isMetricUnits(SearchListActivity.this);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(SearchListActivity.this, R.string.recording_track_id_key);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_paused_key))) {
|
||||
recordingTrackPaused = PreferencesUtils.getBoolean(SearchListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
}
|
||||
if (key != null) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
arrayAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
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 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);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
doSearch(textQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
/**
|
||||
* Handles the intent.
|
||||
*
|
||||
* @param intent the intent
|
||||
*/
|
||||
String trackName = null;
|
||||
long trackId = waypoint.getTrackId();
|
||||
if (trackId != -1L) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
if (track != null) {
|
||||
trackName = track.getName();
|
||||
}
|
||||
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);
|
||||
|
||||
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);
|
||||
resultMap.put(IS_PAUSED_FIELD, true);
|
||||
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());
|
||||
// Use the UI thread to display the results
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
arrayAdapter.clear();
|
||||
arrayAdapter.addAll(displayResults);
|
||||
}
|
||||
});
|
||||
|
||||
resultMap.put(MARKER_LATITUDE_FIELD, waypoint.getLocation().getLatitude());
|
||||
resultMap.put(MARKER_LONGITUDE_FIELD, waypoint.getLocation().getLongitude());
|
||||
}
|
||||
// Save the query as a suggestion for the future
|
||||
searchRecentSuggestions.saveRecentQuery(query.textQuery, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
long trackId = waypoint.getTrackId();
|
||||
if (trackId != -1L) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
if (track != null) {
|
||||
trackName = track.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteMarkerDone() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleIntent(getIntent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
|
||||
return trackRecordingServiceConnection;
|
||||
}
|
||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||
|
||||
@Override
|
||||
protected void onDeleted() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleIntent(getIntent());
|
||||
}
|
||||
});
|
||||
}
|
||||
resultMap.put(IS_RECORDING_FIELD, false);
|
||||
resultMap.put(IS_PAUSED_FIELD, true);
|
||||
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());
|
||||
|
||||
resultMap.put(MARKER_LATITUDE_FIELD, waypoint.getLocation().getLatitude());
|
||||
resultMap.put(MARKER_LONGITUDE_FIELD, waypoint.getLocation().getLongitude());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.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.ViewGroup;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TabHost.TabContentFactory;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -41,110 +42,112 @@ import java.util.ArrayList;
|
||||
* the ViewPager whenever the selected tab changes.
|
||||
* <p>
|
||||
* Copied from the FragmentTabsPager sample in the support library.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
//TODO Rework or better remove.
|
||||
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 TabHost tabHost;
|
||||
private final ViewPager viewPager;
|
||||
private final ArrayList<TabInfo> tabInfos = new ArrayList<>();
|
||||
|
||||
public DummyTabFactory(Context context) {
|
||||
this.context = context;
|
||||
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) {
|
||||
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
|
||||
public View createTabContent(String tag) {
|
||||
View view = new View(context);
|
||||
view.setMinimumWidth(0);
|
||||
view.setMinimumHeight(0);
|
||||
return view;
|
||||
public int getCount() {
|
||||
return tabInfos.size();
|
||||
}
|
||||
}
|
||||
|
||||
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) {
|
||||
super(activity.getSupportFragmentManager());
|
||||
this.context = activity;
|
||||
this.tabHost = tabHost;
|
||||
this.viewPager = viewPager;
|
||||
this.tabHost.setOnTabChangedListener(this);
|
||||
this.viewPager.setAdapter(this);
|
||||
this.viewPager.setOnPageChangeListener(this);
|
||||
}
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
TabInfo info = tabInfos.get(position);
|
||||
return Fragment.instantiate(context, info.clss.getName(), info.bundle);
|
||||
}
|
||||
|
||||
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle bundle) {
|
||||
tabSpec.setContent(new DummyTabFactory(context));
|
||||
@Override
|
||||
public void onTabChanged(String tabId) {
|
||||
int position = tabHost.getCurrentTab();
|
||||
viewPager.setCurrentItem(position);
|
||||
}
|
||||
|
||||
TabInfo info = new TabInfo(clss, bundle);
|
||||
|
||||
tabInfos.add(info);
|
||||
tabHost.addTab(tabSpec);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return tabInfos.size();
|
||||
}
|
||||
@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();
|
||||
int oldFocusability = tabWidget.getDescendantFocusability();
|
||||
tabWidget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||
tabHost.setCurrentTab(position);
|
||||
tabWidget.setDescendantFocusability(oldFocusability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
TabInfo info = tabInfos.get(position);
|
||||
return Fragment.instantiate(context, info.clss.getName(), info.bundle);
|
||||
}
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
|
||||
@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.
|
||||
/**
|
||||
* An object to hold a tab's info.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
TabWidget tabWidget = tabHost.getTabWidget();
|
||||
int oldFocusability = tabWidget.getDescendantFocusability();
|
||||
tabWidget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||
tabHost.setCurrentTab(position);
|
||||
tabWidget.setDescendantFocusability(oldFocusability);
|
||||
}
|
||||
private static final class TabInfo {
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {}
|
||||
private final Class<?> clss;
|
||||
private final Bundle bundle;
|
||||
|
||||
public TabInfo(Class<?> clss, Bundle bunlde) {
|
||||
this.clss = clss;
|
||||
this.bundle = bunlde;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy {@link TabContentFactory} that creates an empty view to satisfy the
|
||||
* {@link TabHost} API.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private static class DummyTabFactory implements TabHost.TabContentFactory {
|
||||
|
||||
private final Context context;
|
||||
|
||||
public DummyTabFactory(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createTabContent(String tag) {
|
||||
View view = new View(context);
|
||||
view.setMinimumWidth(0);
|
||||
view.setMinimumHeight(0);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,119 +31,119 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* Track controller for record, pause, resume, and stop.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
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 TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private final Handler handlerUpdateTotalTime;
|
||||
private final View containerView;
|
||||
private final TextView statusTextView;
|
||||
private final TextView totalTimeTextView;
|
||||
private final ImageButton recordImageButton;
|
||||
private final ImageButton stopImageButton;
|
||||
private final boolean alwaysShow;
|
||||
private final Activity activity;
|
||||
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private final Handler handlerUpdateTotalTime;
|
||||
private final View containerView;
|
||||
private final TextView statusTextView;
|
||||
private final TextView totalTimeTextView;
|
||||
private final ImageButton recordImageButton;
|
||||
private final ImageButton stopImageButton;
|
||||
private final boolean alwaysShow;
|
||||
|
||||
private boolean isRecording;
|
||||
private boolean isPaused;
|
||||
private long totalTime = 0;
|
||||
private boolean isRecording;
|
||||
private boolean isPaused;
|
||||
private long totalTime = 0;
|
||||
|
||||
// the timestamp for the toal time
|
||||
private long totalTimeTimestamp = 0;
|
||||
// the timestamp for the toal time
|
||||
private long totalTimeTimestamp = 0;
|
||||
|
||||
private boolean isResumed = false;
|
||||
private boolean isResumed = false;
|
||||
|
||||
// A runnable to update the total time.
|
||||
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed && isRecording && !isPaused) {
|
||||
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND);
|
||||
}
|
||||
}
|
||||
};
|
||||
// A runnable to update the total time.
|
||||
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed && isRecording && !isPaused) {
|
||||
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public TrackController(Activity activity, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
|
||||
this.activity = activity;
|
||||
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
|
||||
this.alwaysShow = alwaysShow;
|
||||
handlerUpdateTotalTime = new Handler();
|
||||
containerView = activity.findViewById(R.id.track_controller_container);
|
||||
statusTextView = activity.findViewById(R.id.track_controller_status);
|
||||
totalTimeTextView = activity.findViewById(R.id.track_controller_total_time);
|
||||
public TrackController(Activity activity, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
|
||||
this.activity = activity;
|
||||
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
|
||||
this.alwaysShow = alwaysShow;
|
||||
handlerUpdateTotalTime = new Handler();
|
||||
containerView = activity.findViewById(R.id.track_controller_container);
|
||||
statusTextView = activity.findViewById(R.id.track_controller_status);
|
||||
totalTimeTextView = activity.findViewById(R.id.track_controller_total_time);
|
||||
|
||||
recordImageButton = activity.findViewById(R.id.track_controller_record);
|
||||
recordImageButton.setOnClickListener(recordListener);
|
||||
recordImageButton = activity.findViewById(R.id.track_controller_record);
|
||||
recordImageButton.setOnClickListener(recordListener);
|
||||
|
||||
stopImageButton = activity.findViewById(R.id.track_controller_stop);
|
||||
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;
|
||||
stopImageButton = activity.findViewById(R.id.track_controller_stop);
|
||||
stopImageButton.setOnClickListener(stopListener);
|
||||
}
|
||||
|
||||
recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.button_pause : R.drawable.button_record);
|
||||
recordImageButton.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
|
||||
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);
|
||||
|
||||
stopImageButton.setImageResource(isRecording ? R.drawable.button_stop : R.drawable.ic_button_stop_disabled);
|
||||
stopImageButton.setEnabled(isRecording);
|
||||
if (!visible) {
|
||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.button_pause : R.drawable.button_record);
|
||||
recordImageButton.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
public void onResume(boolean recording, boolean paused) {
|
||||
isResumed = true;
|
||||
update(recording, paused);
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume(boolean recording, boolean paused) {
|
||||
isResumed = true;
|
||||
update(recording, paused);
|
||||
}
|
||||
public void onPause() {
|
||||
isResumed = false;
|
||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
isResumed = false;
|
||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||
}
|
||||
public void hide() {
|
||||
containerView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
containerView.setVisibility(View.GONE);
|
||||
}
|
||||
public void show() {
|
||||
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();
|
||||
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
||||
}
|
||||
/**
|
||||
* Gets the total time for the current recording track.
|
||||
*/
|
||||
private long getTotalTime() {
|
||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,451 +70,445 @@ import java.util.Date;
|
||||
|
||||
/**
|
||||
* An activity to show the track detail.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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";
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
private static final int CAMERA_REQUEST_CODE = 5;
|
||||
private static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6;
|
||||
|
||||
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";
|
||||
// The following are set in onCreate
|
||||
private boolean hasCamera;
|
||||
private Uri photoUri;
|
||||
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;
|
||||
private static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6;
|
||||
// From intent
|
||||
private long trackId;
|
||||
|
||||
// The following are set in onCreate
|
||||
private boolean hasCamera;
|
||||
private Uri photoUri;
|
||||
private boolean hasPhoto;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
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
|
||||
// Preferences
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
private final Runnable bindChangedCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
trackController.update(isRecording(), recordingTrackPaused);
|
||||
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);
|
||||
}
|
||||
}
|
||||
// After binding changes (is available), update the total time in
|
||||
// trackController.
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
trackController.update(isRecording(), recordingTrackPaused);
|
||||
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.
|
||||
*/
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
};
|
||||
/*
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
public void run() {
|
||||
TrackDetailActivity.this.invalidateOptionsMenu();
|
||||
boolean isRecording = isRecording();
|
||||
trackController.update(isRecording, recordingTrackPaused);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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.
|
||||
/**
|
||||
* Gets the {@link TrackDataHub}.
|
||||
*/
|
||||
TaskStackBuilder.create(this).addParentStack(TrackDetailActivity.class).startActivities();
|
||||
finish();
|
||||
}
|
||||
public TrackDataHub getTrackDataHub() {
|
||||
return trackDataHub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
setIntent(intent);
|
||||
handleIntent(intent);
|
||||
}
|
||||
/**
|
||||
* Gets the track id.
|
||||
*/
|
||||
public long getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
|
||||
@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;
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
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
|
||||
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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the menu items.
|
||||
*/
|
||||
private void updateMenuItems(boolean isPaused) {
|
||||
insertMarkerMenuItem.setVisible(isRecording() && !isPaused);
|
||||
insertPhotoMenuItem.setVisible(hasCamera && isRecording() && !isPaused);
|
||||
shareMenuItem.setVisible(!isRecording());
|
||||
markerListMenuItem.setShowAsAction(isRecording() ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
voiceFrequencyMenuItem.setVisible(isRecording());
|
||||
splitFrequencyMenuItem.setVisible(isRecording());
|
||||
String title;
|
||||
if (isRecording()) {
|
||||
title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||
} else {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
title = track != null ? track.getName() : "";
|
||||
/**
|
||||
* Updates the menu items.
|
||||
*/
|
||||
private void updateMenuItems(boolean isPaused) {
|
||||
insertMarkerMenuItem.setVisible(isRecording() && !isPaused);
|
||||
insertPhotoMenuItem.setVisible(hasCamera && isRecording() && !isPaused);
|
||||
shareMenuItem.setVisible(!isRecording());
|
||||
markerListMenuItem.setShowAsAction(isRecording() ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
voiceFrequencyMenuItem.setVisible(isRecording());
|
||||
splitFrequencyMenuItem.setVisible(isRecording());
|
||||
String title;
|
||||
if (isRecording()) {
|
||||
title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||
} else {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
title = track != null ? track.getName() : "";
|
||||
}
|
||||
setTitle(title);
|
||||
}
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
public void chooseActivityType(String category) {
|
||||
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(), ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
||||
}
|
||||
public void chooseActivityType(String category) {
|
||||
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(), ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
}
|
||||
|
||||
private boolean isRecording() {
|
||||
return trackId == recordingTrackId;
|
||||
}
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
||||
}
|
||||
|
||||
private boolean isRecording() {
|
||||
return trackId == recordingTrackId;
|
||||
}
|
||||
}
|
||||
@@ -41,168 +41,168 @@ import com.google.android.maps.mytracks.R;
|
||||
/**
|
||||
* An activity that let's the user see and edit the user editable track meta
|
||||
* data such as track name, activity type, and track description.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeCaller {
|
||||
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_NEW_TRACK = "new_track";
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_NEW_TRACK = "new_track";
|
||||
|
||||
private static final String TAG = TrackEditActivity.class.getSimpleName();
|
||||
private static final String ICON_VALUE_KEY = "icon_value_key";
|
||||
private static final String TAG = TrackEditActivity.class.getSimpleName();
|
||||
private static final String ICON_VALUE_KEY = "icon_value_key";
|
||||
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private Track track;
|
||||
private String iconValue;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private Track track;
|
||||
private String iconValue;
|
||||
|
||||
private EditText name;
|
||||
private AutoCompleteTextView activityType;
|
||||
private Spinner activityTypeIcon;
|
||||
private EditText description;
|
||||
private EditText name;
|
||||
private AutoCompleteTextView activityType;
|
||||
private Spinner activityTypeIcon;
|
||||
private EditText description;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
Long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
if (trackId == -1L) {
|
||||
Log.e(TAG, "invalid trackId");
|
||||
finish();
|
||||
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()));
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
Long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
if (trackId == -1L) {
|
||||
Log.e(TAG, "invalid trackId");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
iconValue = null;
|
||||
if (bundle != null) {
|
||||
iconValue = bundle.getString(ICON_VALUE_KEY);
|
||||
}
|
||||
if (iconValue == null) {
|
||||
iconValue = track.getIcon();
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
TrackRecordingServiceConnectionUtils.startConnection(this, trackRecordingServiceConnection);
|
||||
}
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(ICON_VALUE_KEY, iconValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
}
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.track_edit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(ICON_VALUE_KEY, iconValue);
|
||||
}
|
||||
private void setActivityTypeIcon(String value) {
|
||||
iconValue = value;
|
||||
TrackIconUtils.setIconSpinner(activityTypeIcon, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.track_edit;
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String value) {
|
||||
setActivityTypeIcon(value);
|
||||
activityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+343
-345
@@ -16,386 +16,384 @@
|
||||
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities to access data from the app's content provider.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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> {
|
||||
/**
|
||||
* The authority (the first part of the URI) for the app's content
|
||||
* provider.
|
||||
*/
|
||||
String AUTHORITY = "com.google.android.maps.mytracks";
|
||||
/**
|
||||
* The default {@link LocationFactory} which creates a location each time.
|
||||
*/
|
||||
LocationFactory DEFAULT_LOCATION_FACTORY = new LocationFactory() {
|
||||
@Override
|
||||
public Location createLocation() {
|
||||
return new SensorDataSetLocation(LocationManager.GPS_PROVIDER);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the most recently retrieved track point id by {@link #next()}.
|
||||
* Clears a track. Removes waypoints and trackpoints. Only keeps the track id.
|
||||
*
|
||||
* @param trackId the track id
|
||||
*/
|
||||
long getLocationId();
|
||||
void clearTrack(Context context, long trackId);
|
||||
|
||||
/**
|
||||
* Closes the iterator.
|
||||
* Creates a {@link Track} from a cursor.
|
||||
*
|
||||
* @param cursor the cursor pointing to the track
|
||||
*/
|
||||
void close();
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory for creating new {@link Location}.
|
||||
*/
|
||||
interface LocationFactory {
|
||||
Track createTrack(Cursor cursor);
|
||||
|
||||
/**
|
||||
* Creates a new {@link Location}. An implementation can create new
|
||||
* instances or reuse existing instances for optimization.
|
||||
* Deletes all tracks (including waypoints and track points).
|
||||
*/
|
||||
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();
|
||||
void deleteAllTracks(Context context);
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link ContentProviderUtils}.
|
||||
*
|
||||
* @param context the context
|
||||
* Deletes a track.
|
||||
*
|
||||
* @param trackId the track id
|
||||
*/
|
||||
public static ContentProviderUtils get(Context context) {
|
||||
return instance.newForContext(context);
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the factory instance.
|
||||
* A factory for creating new {@link Location}.
|
||||
*/
|
||||
public static Factory getInstance() {
|
||||
return instance;
|
||||
interface LocationFactory {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Location}. An implementation can create new
|
||||
* instances or reuse existing instances for optimization.
|
||||
*/
|
||||
Location createLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the factory instance for testing. Don't forget to set it back
|
||||
* to the original value after testing.
|
||||
*
|
||||
* @param factory the factory
|
||||
* A factory which can produce instances of {@link ContentProviderUtils}, and
|
||||
* can be overridden for testing.
|
||||
*/
|
||||
public static void overrideInstance(Factory factory) {
|
||||
instance = factory;
|
||||
}
|
||||
class 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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1045
-1045
File diff suppressed because it is too large
Load Diff
@@ -18,18 +18,18 @@ package com.google.android.apps.mytracks.content;
|
||||
|
||||
/**
|
||||
* Utilities for serializing primitive types.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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 INT_TYPE_ID = 2;
|
||||
public static final byte FLOAT_TYPE_ID = 3;
|
||||
public static final byte DOUBLE_TYPE_ID = 4;
|
||||
public static final byte STRING_TYPE_ID = 5;
|
||||
public static final byte BLOB_TYPE_ID = 6;
|
||||
public static final byte BOOLEAN_TYPE_ID = 0;
|
||||
public static final byte LONG_TYPE_ID = 1;
|
||||
public static final byte INT_TYPE_ID = 2;
|
||||
public static final byte FLOAT_TYPE_ID = 3;
|
||||
public static final byte DOUBLE_TYPE_ID = 4;
|
||||
public static final byte STRING_TYPE_ID = 5;
|
||||
public static final byte BLOB_TYPE_ID = 6;
|
||||
private ContentTypeIds() {
|
||||
}
|
||||
}
|
||||
|
||||
+340
-343
@@ -44,375 +44,372 @@ import java.io.FileNotFoundException;
|
||||
/**
|
||||
* A {@link ContentProvider} that handles access to track points, tracks, and
|
||||
* waypoints tables.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
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
|
||||
public DatabaseHelper(Context context, String databaseName) {
|
||||
super(context, databaseName, null, DATABASE_VERSION);
|
||||
static final int DATABASE_VERSION = 23;
|
||||
@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
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(TrackPointsColumns.CREATE_TABLE);
|
||||
db.execSQL(TracksColumns.CREATE_TABLE);
|
||||
db.execSQL(WaypointsColumns.CREATE_TABLE);
|
||||
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 void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of url.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@VisibleForTesting
|
||||
enum UrlType {
|
||||
TRACKPOINTS, TRACKPOINTS_ID, TRACKS, TRACKS_ID, WAYPOINTS, WAYPOINTS_ID
|
||||
}
|
||||
|
||||
private final UriMatcher uriMatcher;
|
||||
private 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
|
||||
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();
|
||||
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);
|
||||
}
|
||||
insertContentValues(url, urlType, contentValues);
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(url, null, false);
|
||||
return numInserted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri url, String[] projection, String selection, String[] selectionArgs, String sort) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
String sortOrder = null;
|
||||
switch (getUrlType(url)) {
|
||||
case TRACKPOINTS:
|
||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||
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 + ")";
|
||||
Log.w(TAG, "Deleting table " + table);
|
||||
int count;
|
||||
try {
|
||||
db.beginTransaction();
|
||||
count = db.delete(table, where, selectionArgs);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
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 + ")";
|
||||
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");
|
||||
}
|
||||
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 + ")";
|
||||
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);
|
||||
}
|
||||
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
|
||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||
try {
|
||||
File file = new File(uri.getPath());
|
||||
if (file.exists()) {
|
||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
throw new FileNotFoundException(uri.getPath());
|
||||
} finally {
|
||||
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);
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link UrlType} for a url.
|
||||
*
|
||||
* @param url the url
|
||||
*/
|
||||
private UrlType getUrlType(Uri url) {
|
||||
return UrlType.values()[uriMatcher.match(url)];
|
||||
}
|
||||
@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();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.");
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri url, String[] projection, String selection, String[] selectionArgs, String sort) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
String sortOrder = null;
|
||||
switch (getUrlType(url)) {
|
||||
case TRACKPOINTS:
|
||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||
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;
|
||||
}
|
||||
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.");
|
||||
@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;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||
try {
|
||||
File file = new File(uri.getPath());
|
||||
if (file.exists()) {
|
||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
throw new FileNotFoundException(uri.getPath());
|
||||
} finally {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -25,56 +23,58 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
|
||||
/**
|
||||
* Data source on the phone.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class DataSource {
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
public DataSource(Context context) {
|
||||
contentResolver = context.getContentResolver();
|
||||
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
private final ContentResolver contentResolver;
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
/**
|
||||
* Registers a content observer.
|
||||
*
|
||||
* @param uri the uri
|
||||
* @param observer the observer
|
||||
*/
|
||||
public void registerContentObserver(Uri uri, ContentObserver observer) {
|
||||
contentResolver.registerContentObserver(uri, false, observer);
|
||||
}
|
||||
public DataSource(Context context) {
|
||||
contentResolver = context.getContentResolver();
|
||||
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a content observer.
|
||||
*
|
||||
* @param observer the observer
|
||||
*/
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
contentResolver.unregisterContentObserver(observer);
|
||||
}
|
||||
/**
|
||||
* Registers a content observer.
|
||||
*
|
||||
* @param uri the uri
|
||||
* @param observer the observer
|
||||
*/
|
||||
public void registerContentObserver(Uri uri, ContentObserver observer) {
|
||||
contentResolver.registerContentObserver(uri, false, observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a shared preference change listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
|
||||
}
|
||||
/**
|
||||
* Unregisters a content observer.
|
||||
*
|
||||
* @param observer the observer
|
||||
*/
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
contentResolver.unregisterContentObserver(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a shared preference change listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void unregisterOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener listener) {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
|
||||
}
|
||||
/**
|
||||
* Registers a shared preference change listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a shared preference change listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void unregisterOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener listener) {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -19,30 +19,30 @@ package com.google.android.apps.mytracks.content;
|
||||
|
||||
/**
|
||||
* Listener to be invoked when {@link DataSource} changes.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface DataSourceListener {
|
||||
|
||||
/**
|
||||
* Notifies when the tracks table is updated.
|
||||
*/
|
||||
void notifyTracksTableUpdated();
|
||||
/**
|
||||
* Notifies when the tracks table is updated.
|
||||
*/
|
||||
void notifyTracksTableUpdated();
|
||||
|
||||
/**
|
||||
* Notifies when the waypoints table is updated.
|
||||
*/
|
||||
void notifyWaypointsTableUpdated();
|
||||
/**
|
||||
* Notifies when the waypoints table is updated.
|
||||
*/
|
||||
void notifyWaypointsTableUpdated();
|
||||
|
||||
/**
|
||||
* Notifies when the track points table is updated.
|
||||
*/
|
||||
void notifyTrackPointsTableUpdated();
|
||||
/**
|
||||
* Notifies when the track points table is updated.
|
||||
*/
|
||||
void notifyTrackPointsTableUpdated();
|
||||
|
||||
/**
|
||||
* Notifies when a preference changes.
|
||||
*
|
||||
* @param key the preference key
|
||||
*/
|
||||
void notifyPreferenceChanged(String key);
|
||||
/**
|
||||
* Notifies when a preference changes.
|
||||
*
|
||||
* @param key the preference key
|
||||
*/
|
||||
void notifyPreferenceChanged(String key);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -29,203 +29,199 @@ import java.util.Set;
|
||||
* Data source manager. Creates observers/listeners and manages their
|
||||
* registration with {@link DataSource}. The observers/listeners calls
|
||||
* {@link DataSourceListener} when data changes.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class DataSourceManager {
|
||||
|
||||
private static final String TAG = DataSourceManager.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Observer when the tracks table is updated.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class TracksTableObserver extends ContentObserver {
|
||||
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;
|
||||
|
||||
public TracksTableObserver() {
|
||||
super(handler);
|
||||
handler = new Handler();
|
||||
tracksTableObserver = new TracksTableObserver();
|
||||
waypointsTableObserver = new WaypointsTableObserver();
|
||||
trackPointsTableObserver = new TrackPointsTableObserver();
|
||||
preferenceListener = new PreferenceListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
dataSourceListener.notifyTracksTableUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer when the waypoints table is updated.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class WaypointsTableObserver extends ContentObserver {
|
||||
|
||||
public WaypointsTableObserver() {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
dataSourceListener.notifyWaypointsTableUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer when the track points table is updated.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class TrackPointsTableObserver extends ContentObserver {
|
||||
|
||||
public TrackPointsTableObserver() {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
dataSourceListener.notifyTrackPointsTableUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for preference changes.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class PreferenceListener implements OnSharedPreferenceChangeListener {
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
dataSourceListener.notifyPreferenceChanged(key);
|
||||
}
|
||||
}
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final 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
|
||||
/**
|
||||
* Updates listeners with data source.
|
||||
*
|
||||
* @param listeners the listeners
|
||||
*/
|
||||
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);
|
||||
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)) {
|
||||
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);
|
||||
|
||||
// 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);
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing listeners
|
||||
for (TrackDataType trackDataType : missingListeners) {
|
||||
registerListener(trackDataType);
|
||||
/**
|
||||
* Unregisters a listener with data source.
|
||||
*
|
||||
* @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();
|
||||
registeredListeners.addAll(neededListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 all listeners with data source.
|
||||
*/
|
||||
public void unregisterAllListeners() {
|
||||
for (TrackDataType trackDataType : TrackDataType.values()) {
|
||||
unregisterListener(trackDataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a listener with data source.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Observer when the tracks table is updated.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class TracksTableObserver extends ContentObserver {
|
||||
|
||||
/**
|
||||
* Unregisters all listeners with data source.
|
||||
*/
|
||||
public void unregisterAllListeners() {
|
||||
for (TrackDataType trackDataType : TrackDataType.values()) {
|
||||
unregisterListener(trackDataType);
|
||||
public TracksTableObserver() {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
dataSourceListener.notifyTracksTableUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer when the waypoints table is updated.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class WaypointsTableObserver extends ContentObserver {
|
||||
|
||||
public WaypointsTableObserver() {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
dataSourceListener.notifyWaypointsTableUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer when the track points table is updated.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class TrackPointsTableObserver extends ContentObserver {
|
||||
|
||||
public TrackPointsTableObserver() {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
dataSourceListener.notifyTrackPointsTableUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for preference changes.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private class PreferenceListener implements OnSharedPreferenceChangeListener {
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
dataSourceListener.notifyPreferenceChanged(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -27,21 +27,21 @@ import java.util.Vector;
|
||||
*/
|
||||
public interface DescriptionGenerator {
|
||||
|
||||
/**
|
||||
* Generates a track description.
|
||||
*
|
||||
* @param track the track
|
||||
* @param distances a vector of distances 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
|
||||
*/
|
||||
String generateTrackDescription(
|
||||
Track track, Vector<Double> distances, Vector<Double> elevations, boolean html);
|
||||
/**
|
||||
* Generates a track description.
|
||||
*
|
||||
* @param track the track
|
||||
* @param distances a vector of distances 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
|
||||
*/
|
||||
String generateTrackDescription(
|
||||
Track track, Vector<Double> distances, Vector<Double> elevations, boolean html);
|
||||
|
||||
/**
|
||||
* Generate a waypoint description from a trip statistics.
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
String generateWaypointDescription(TripStatistics tripStatistics);
|
||||
/**
|
||||
* Generate a waypoint description from a trip statistics.
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
String generateWaypointDescription(TripStatistics tripStatistics);
|
||||
}
|
||||
|
||||
+217
-216
@@ -17,6 +17,7 @@
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
@@ -31,228 +32,228 @@ import java.util.Vector;
|
||||
*/
|
||||
public class DescriptionGeneratorImpl implements DescriptionGenerator {
|
||||
|
||||
private static final String HTML_LINE_BREAK = "<br>";
|
||||
private static final String HTML_PARAGRAPH_SEPARATOR = "<p>";
|
||||
private static final String TEXT_LINE_BREAK = "\n";
|
||||
private static final String TEXT_PARAGRAPH_SEPARATOR = "\n\n";
|
||||
private static final String HTML_LINE_BREAK = "<br>";
|
||||
private static final String HTML_PARAGRAPH_SEPARATOR = "<p>";
|
||||
private static final String TEXT_LINE_BREAK = "\n";
|
||||
private static final String TEXT_PARAGRAPH_SEPARATOR = "\n\n";
|
||||
|
||||
private Context context;
|
||||
private Context context;
|
||||
|
||||
public DescriptionGeneratorImpl(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);
|
||||
public DescriptionGeneratorImpl(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
if (paceInImperial[0] == null) {
|
||||
paceInImperial[0] = context.getString(R.string.value_unknown);
|
||||
@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();
|
||||
}
|
||||
|
||||
builder.append(context.getString(resId, paceInMetrics[0], paceInImperial[0]));
|
||||
builder.append(lineBreak);
|
||||
}
|
||||
@Override
|
||||
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
||||
return generateTripStatisticsDescription(tripStatistics, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
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 {
|
||||
|
||||
/** WHERE query to get tracks by name. */
|
||||
private static final String TRACK_SELECTION_QUERY =
|
||||
TracksColumns.NAME + " LIKE ? OR " +
|
||||
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
||||
TracksColumns.CATEGORY + " LIKE ?";
|
||||
/**
|
||||
* WHERE query to get tracks by name.
|
||||
*/
|
||||
private static final String TRACK_SELECTION_QUERY =
|
||||
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 =
|
||||
WaypointsColumns.NAME + " LIKE ? OR " +
|
||||
WaypointsColumns.DESCRIPTION + " LIKE ? OR " +
|
||||
WaypointsColumns.CATEGORY + " LIKE ?";
|
||||
/**
|
||||
* WHERE query to get waypoints by name.
|
||||
*/
|
||||
private static final String WAYPOINT_SELECTION_QUERY =
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a search query, along with all contextual data needed to execute it.
|
||||
*/
|
||||
public static class SearchQuery {
|
||||
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId,
|
||||
long currentTimestamp) {
|
||||
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
|
||||
this.currentLocation = currentLocation;
|
||||
this.currentTrackId = currentTrackId;
|
||||
this.currentTimestamp = currentTimestamp;
|
||||
// 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;
|
||||
}
|
||||
|
||||
public final String textQuery;
|
||||
public final Location currentLocation;
|
||||
public final long currentTrackId;
|
||||
public final long currentTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a search result which has been retrieved and scored.
|
||||
*/
|
||||
public static class ScoredResult {
|
||||
ScoredResult(Track track, double score) {
|
||||
this.track = track;
|
||||
this.waypoint = null;
|
||||
this.score = score;
|
||||
/**
|
||||
* Squashes a number by calculating 1 / log (1 + x).
|
||||
*/
|
||||
private static double squash(double x) {
|
||||
return 1.0 / Math.log1p(x);
|
||||
}
|
||||
|
||||
ScoredResult(Waypoint waypoint, double score) {
|
||||
this.track = null;
|
||||
this.waypoint = waypoint;
|
||||
this.score = score;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public final Track track;
|
||||
public final Waypoint waypoint;
|
||||
public final double score;
|
||||
/**
|
||||
* 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};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ScoredResult ["
|
||||
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
|
||||
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
|
||||
+ "score=" + score + "]";
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Comparador for scored results. */
|
||||
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR =
|
||||
new Comparator<ScoredResult>() {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
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.
|
||||
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);
|
||||
public String toString() {
|
||||
return "ScoredResult ["
|
||||
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
|
||||
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
|
||||
+ "score=" + score + "]";
|
||||
}
|
||||
};
|
||||
|
||||
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 {
|
||||
|
||||
private static final String AUTHORITY = "com.google.android.maps.mytracks.search";
|
||||
private static final int MODE = DATABASE_MODE_QUERIES;
|
||||
private static final String AUTHORITY = "com.google.android.maps.mytracks.search";
|
||||
private static final int MODE = DATABASE_MODE_QUERIES;
|
||||
|
||||
public SearchEngineProvider() {
|
||||
setupSuggestions(AUTHORITY, MODE);
|
||||
}
|
||||
public SearchEngineProvider() {
|
||||
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.
|
||||
*/
|
||||
public static SearchRecentSuggestions newHelper(Context context) {
|
||||
return new SearchRecentSuggestions(context, AUTHORITY, MODE);
|
||||
}
|
||||
/**
|
||||
* Creates and returns a helper for adding recent queries or clearing the recent query history.
|
||||
*/
|
||||
public static SearchRecentSuggestions newHelper(Context context) {
|
||||
return new SearchRecentSuggestions(context, AUTHORITY, MODE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,41 +26,41 @@ import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
|
||||
*/
|
||||
public class SensorDataSetLocation extends Location {
|
||||
|
||||
private SensorDataSet sensorDataSet = null;
|
||||
private SensorDataSet sensorDataSet = null;
|
||||
|
||||
/**
|
||||
* The id of this location from the provider.
|
||||
*/
|
||||
private int id = -1;
|
||||
/**
|
||||
* The id of this location from the provider.
|
||||
*/
|
||||
private int id = -1;
|
||||
|
||||
public SensorDataSetLocation(Location location, SensorDataSet sensorDataSet) {
|
||||
super(location);
|
||||
this.sensorDataSet = sensorDataSet;
|
||||
}
|
||||
public SensorDataSetLocation(Location location, SensorDataSet sensorDataSet) {
|
||||
super(location);
|
||||
this.sensorDataSet = sensorDataSet;
|
||||
}
|
||||
|
||||
public SensorDataSetLocation(String provider) {
|
||||
super(provider);
|
||||
}
|
||||
public SensorDataSetLocation(String provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
public SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
public SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
public void setSensorDataSet(SensorDataSet sensorDataSet) {
|
||||
this.sensorDataSet = sensorDataSet;
|
||||
}
|
||||
public void setSensorDataSet(SensorDataSet sensorDataSet) {
|
||||
this.sensorDataSet = sensorDataSet;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
super.reset();
|
||||
sensorDataSet = null;
|
||||
id = -1;
|
||||
}
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
super.reset();
|
||||
sensorDataSet = null;
|
||||
id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,188 +16,185 @@
|
||||
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A track.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class Track implements Parcelable {
|
||||
|
||||
private long id = -1L;
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String category = "";
|
||||
private long startId = -1L;
|
||||
private long stopId = -1L;
|
||||
public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {
|
||||
@Override
|
||||
public Track createFromParcel(Parcel in) {
|
||||
return new Track(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
@Override
|
||||
public Track[] newArray(int size) {
|
||||
return new Track[size];
|
||||
}
|
||||
};
|
||||
private long id = -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();
|
||||
|
||||
// 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);
|
||||
public Track() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track[] newArray(int size) {
|
||||
return new Track[size];
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
@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);
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
dest.writeParcelable(tripStatistics, 0);
|
||||
for (int i = 0; i < numberOfPoints; ++i) {
|
||||
dest.writeParcelable(locations.get(i), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public long getStartId() {
|
||||
return startId;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setStartId(long startId) {
|
||||
this.startId = startId;
|
||||
}
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public long getStopId() {
|
||||
return stopId;
|
||||
}
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public void setStopId(long stopId) {
|
||||
this.stopId = stopId;
|
||||
}
|
||||
public long getStartId() {
|
||||
return startId;
|
||||
}
|
||||
|
||||
public int getNumberOfPoints() {
|
||||
return numberOfPoints;
|
||||
}
|
||||
public void setStartId(long startId) {
|
||||
this.startId = startId;
|
||||
}
|
||||
|
||||
public void setNumberOfPoints(int numberOfPoints) {
|
||||
this.numberOfPoints = numberOfPoints;
|
||||
}
|
||||
public long getStopId() {
|
||||
return stopId;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
public void setStopId(long stopId) {
|
||||
this.stopId = stopId;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
public int getNumberOfPoints() {
|
||||
return numberOfPoints;
|
||||
}
|
||||
|
||||
public long getModifiedTime() {
|
||||
return modifiedTime;
|
||||
}
|
||||
public void setNumberOfPoints(int numberOfPoints) {
|
||||
this.numberOfPoints = numberOfPoints;
|
||||
}
|
||||
|
||||
public void setModifiedTime(long modifiedTime) {
|
||||
this.modifiedTime = modifiedTime;
|
||||
}
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public TripStatistics getTripStatistics() {
|
||||
return tripStatistics;
|
||||
}
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public void setTripStatistics(TripStatistics tripStatistics) {
|
||||
this.tripStatistics = tripStatistics;
|
||||
}
|
||||
public long getModifiedTime() {
|
||||
return modifiedTime;
|
||||
}
|
||||
|
||||
public void addLocation(Location location) {
|
||||
locations.add(location);
|
||||
}
|
||||
public void setModifiedTime(long modifiedTime) {
|
||||
this.modifiedTime = modifiedTime;
|
||||
}
|
||||
|
||||
public ArrayList<Location> getLocations() {
|
||||
return locations;
|
||||
}
|
||||
public TripStatistics getTripStatistics() {
|
||||
return tripStatistics;
|
||||
}
|
||||
|
||||
public void setLocations(ArrayList<Location> locations) {
|
||||
this.locations = locations;
|
||||
}
|
||||
public void setTripStatistics(TripStatistics tripStatistics) {
|
||||
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
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -20,98 +20,98 @@ import android.location.Location;
|
||||
|
||||
/**
|
||||
* Listener for track data changes.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public interface TrackDataListener {
|
||||
|
||||
/**
|
||||
* Called when the track or its statistics has been updated.
|
||||
*
|
||||
* @param track the track
|
||||
*/
|
||||
void onTrackUpdated(Track track);
|
||||
/**
|
||||
* Called when the track or its statistics has been updated.
|
||||
*
|
||||
* @param track the track
|
||||
*/
|
||||
void onTrackUpdated(Track track);
|
||||
|
||||
/**
|
||||
* Called to clear previously-sent track points.
|
||||
*/
|
||||
void clearTrackPoints();
|
||||
/**
|
||||
* Called to clear previously-sent track points.
|
||||
*/
|
||||
void clearTrackPoints();
|
||||
|
||||
/**
|
||||
* Called when a sampled in track point is read.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void onSampledInTrackPoint(Location location);
|
||||
/**
|
||||
* Called when a sampled in track point is read.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void onSampledInTrackPoint(Location location);
|
||||
|
||||
/**
|
||||
* Called when a sampled out track point is read.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void onSampledOutTrackPoint(Location location);
|
||||
/**
|
||||
* Called when a sampled out track point is read.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void onSampledOutTrackPoint(Location location);
|
||||
|
||||
/**
|
||||
* Called when an invalid track point representing a segment split is read.
|
||||
*/
|
||||
void onSegmentSplit(Location location);
|
||||
/**
|
||||
* Called when an invalid track point representing a segment split is read.
|
||||
*/
|
||||
void onSegmentSplit(Location location);
|
||||
|
||||
/**
|
||||
* Called when finish sending new track points. This gets called after every
|
||||
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
||||
* {@link #onSampledOutTrackPoint(Location)} and
|
||||
* {@link #onSegmentSplit(Location)}.
|
||||
*/
|
||||
void onNewTrackPointsDone();
|
||||
/**
|
||||
* Called when finish sending new track points. This gets called after every
|
||||
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
||||
* {@link #onSampledOutTrackPoint(Location)} and
|
||||
* {@link #onSegmentSplit(Location)}.
|
||||
*/
|
||||
void onNewTrackPointsDone();
|
||||
|
||||
/**
|
||||
* Called to clear previously sent waypoints.
|
||||
*/
|
||||
void clearWaypoints();
|
||||
/**
|
||||
* Called to clear previously sent waypoints.
|
||||
*/
|
||||
void clearWaypoints();
|
||||
|
||||
/**
|
||||
* Called when a new waypoint is read.
|
||||
*
|
||||
* @param waypoint the waypoint
|
||||
*/
|
||||
void onNewWaypoint(Waypoint waypoint);
|
||||
/**
|
||||
* Called when a new waypoint is read.
|
||||
*
|
||||
* @param waypoint the waypoint
|
||||
*/
|
||||
void onNewWaypoint(Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* Called when finish sending new waypoints. This gets called after every
|
||||
* batch of calls to {@link #clearWaypoints()} and
|
||||
* {@link #onNewWaypoint(Waypoint)}.
|
||||
*/
|
||||
void onNewWaypointsDone();
|
||||
/**
|
||||
* Called when finish sending new waypoints. This gets called after every
|
||||
* batch of calls to {@link #clearWaypoints()} and
|
||||
* {@link #onNewWaypoint(Waypoint)}.
|
||||
*/
|
||||
void onNewWaypointsDone();
|
||||
|
||||
/**
|
||||
* Called when the metric units preference value is change.
|
||||
*
|
||||
* @param metricUnits true to use metric units, false to use imperial units
|
||||
* @return true to reload all the data, false otherwise.
|
||||
*/
|
||||
boolean onMetricUnitsChanged(boolean metricUnits);
|
||||
/**
|
||||
* Called when the metric units preference value is change.
|
||||
*
|
||||
* @param metricUnits true to use metric units, false to use imperial units
|
||||
* @return true to reload all the data, false otherwise.
|
||||
*/
|
||||
boolean onMetricUnitsChanged(boolean metricUnits);
|
||||
|
||||
/**
|
||||
* Called when the report speed preference value is changed.
|
||||
*
|
||||
* @param reportSpeed true to report speed, false to report pace
|
||||
* @return true to reload all the data, false otherwise.
|
||||
*/
|
||||
boolean onReportSpeedChanged(boolean reportSpeed);
|
||||
/**
|
||||
* Called when the report speed preference value is changed.
|
||||
*
|
||||
* @param reportSpeed true to report speed, false to report pace
|
||||
* @return true to reload all the data, false otherwise.
|
||||
*/
|
||||
boolean onReportSpeedChanged(boolean reportSpeed);
|
||||
|
||||
/**
|
||||
* Called when the recording gps accuracy preference value is changed.
|
||||
*
|
||||
* @param recordingGpsAccuracy the new value
|
||||
* @return true to reload all the data, false otherwise
|
||||
*/
|
||||
boolean onRecordingGpsAccuracy(int recordingGpsAccuracy);
|
||||
|
||||
/**
|
||||
* Called when the recording distance interval preference value is changed.
|
||||
*
|
||||
* @param recordingDistanceInterval the new value
|
||||
* @return true to reload all the data, false otherwise.
|
||||
*/
|
||||
boolean onRecordingDistanceIntervalChanged(int recordingDistanceInterval);
|
||||
/**
|
||||
* Called when the recording gps accuracy preference value is changed.
|
||||
*
|
||||
* @param recordingGpsAccuracy the new value
|
||||
* @return true to reload all the data, false otherwise
|
||||
*/
|
||||
boolean onRecordingGpsAccuracy(int recordingGpsAccuracy);
|
||||
|
||||
/**
|
||||
* Called when the recording distance interval preference value is changed.
|
||||
*
|
||||
* @param recordingDistanceInterval the new value
|
||||
* @return true to reload all the data, false otherwise.
|
||||
*/
|
||||
boolean onRecordingDistanceIntervalChanged(int recordingDistanceInterval);
|
||||
}
|
||||
|
||||
@@ -28,96 +28,96 @@ import java.util.Set;
|
||||
/**
|
||||
* Manages register/unregister {@link TrackDataListener} and keeping the state
|
||||
* for each registered listener.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TrackDataManager {
|
||||
|
||||
private static final String TAG = TrackDataManager.class.getSimpleName();
|
||||
|
||||
// Map of listener to its track data types
|
||||
private final Map<TrackDataListener, EnumSet<TrackDataType>>
|
||||
listenerToTypesMap = new HashMap<>();
|
||||
private static final String TAG = TrackDataManager.class.getSimpleName();
|
||||
|
||||
// Map of track data type to listeners
|
||||
private final Map<TrackDataType, Set<TrackDataListener>>
|
||||
typeToListenersMap = new EnumMap<>(TrackDataType.class);
|
||||
// Map of listener to its track data types
|
||||
private final Map<TrackDataListener, EnumSet<TrackDataType>>
|
||||
listenerToTypesMap = new HashMap<>();
|
||||
|
||||
public TrackDataManager() {
|
||||
for (TrackDataType trackDataType : TrackDataType.values()) {
|
||||
typeToListenersMap.put(trackDataType, new LinkedHashSet<TrackDataListener>());
|
||||
}
|
||||
}
|
||||
// Map of track data type to listeners
|
||||
private final Map<TrackDataType, Set<TrackDataListener>>
|
||||
typeToListenersMap = new EnumMap<>(TrackDataType.class);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public TrackDataManager() {
|
||||
for (TrackDataType trackDataType : TrackDataType.values()) {
|
||||
typeToListenersMap.put(trackDataType, new LinkedHashSet<TrackDataListener>());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the listener from the typeToListenersMap
|
||||
for (TrackDataType trackDataType : removedTypes) {
|
||||
typeToListenersMap.get(trackDataType).remove(listener);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of {@link TrackDataListener}.
|
||||
*/
|
||||
public int getNumberOfListeners() {
|
||||
return listenerToTypesMap.size();
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track data types for a listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
|
||||
return listenerToTypesMap.get(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the listeners for a {@link TrackDataType}.
|
||||
*
|
||||
* @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);
|
||||
// Remove the listener from the typeToListenersMap
|
||||
for (TrackDataType trackDataType : removedTypes) {
|
||||
typeToListenersMap.get(trackDataType).remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of {@link TrackDataListener}.
|
||||
*/
|
||||
public int getNumberOfListeners() {
|
||||
return listenerToTypesMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track data types for a listener.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
|
||||
return listenerToTypesMap.get(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the listeners for a {@link TrackDataType}.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ package com.google.android.apps.mytracks.content;
|
||||
|
||||
/**
|
||||
* Types of track data.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public enum TrackDataType {
|
||||
TRACKS_TABLE, // tracks table changes
|
||||
WAYPOINTS_TABLE, // waypoints table changes
|
||||
SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
|
||||
SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
|
||||
PREFERENCE // preference changes
|
||||
TRACKS_TABLE, // tracks table changes
|
||||
WAYPOINTS_TABLE, // waypoints table changes
|
||||
SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
|
||||
SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
|
||||
PREFERENCE // preference changes
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -26,36 +26,36 @@ import android.provider.BaseColumns;
|
||||
*/
|
||||
public interface TrackPointsColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "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_ITEMTYPE = "vnd.android.cursor.item/vnd.google.trackpoint";
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
String TABLE_NAME = "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_ITEMTYPE = "vnd.android.cursor.item/vnd.google.trackpoint";
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
// Columns
|
||||
String TRACKID = "trackid";
|
||||
String LONGITUDE = "longitude";
|
||||
String LATITUDE = "latitude";
|
||||
String TIME = "time";
|
||||
String ALTITUDE = "elevation";
|
||||
String ACCURACY = "accuracy";
|
||||
String SPEED = "speed";
|
||||
String BEARING = "bearing";
|
||||
String SENSOR_HEARTRATE = "sensor_heartrate";
|
||||
String SENSOR_CADENCE = "sensor_cadence";
|
||||
String SENSOR_POWER = "sensor_power";
|
||||
// Columns
|
||||
String TRACKID = "trackid";
|
||||
String LONGITUDE = "longitude";
|
||||
String LATITUDE = "latitude";
|
||||
String TIME = "time";
|
||||
String ALTITUDE = "elevation";
|
||||
String ACCURACY = "accuracy";
|
||||
String SPEED = "speed";
|
||||
String BEARING = "bearing";
|
||||
String SENSOR_HEARTRATE = "sensor_heartrate";
|
||||
String SENSOR_CADENCE = "sensor_cadence";
|
||||
String SENSOR_POWER = "sensor_power";
|
||||
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ TRACKID + " INTEGER, "
|
||||
+ LONGITUDE + " INTEGER, "
|
||||
+ LATITUDE + " INTEGER, "
|
||||
+ TIME + " INTEGER, "
|
||||
+ ALTITUDE + " FLOAT, "
|
||||
+ ACCURACY + " FLOAT, "
|
||||
+ SPEED + " FLOAT, "
|
||||
+ BEARING + " FLOAT, "
|
||||
+ SENSOR_HEARTRATE + " FLOAT, "
|
||||
+ SENSOR_CADENCE + " FLOAT, "
|
||||
+ SENSOR_POWER + " FLOAT);";
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ TRACKID + " INTEGER, "
|
||||
+ LONGITUDE + " INTEGER, "
|
||||
+ LATITUDE + " INTEGER, "
|
||||
+ TIME + " INTEGER, "
|
||||
+ ALTITUDE + " FLOAT, "
|
||||
+ ACCURACY + " FLOAT, "
|
||||
+ SPEED + " FLOAT, "
|
||||
+ BEARING + " FLOAT, "
|
||||
+ SENSOR_HEARTRATE + " FLOAT, "
|
||||
+ SENSOR_CADENCE + " FLOAT, "
|
||||
+ SENSOR_POWER + " FLOAT);";
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2008 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -21,85 +21,85 @@ import android.provider.BaseColumns;
|
||||
|
||||
/**
|
||||
* Constants for the tracks table.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public interface TracksColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "tracks";
|
||||
String TABLE_NAME = "tracks";
|
||||
|
||||
/**
|
||||
* Tracks provider uri.
|
||||
*/
|
||||
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/tracks");
|
||||
/**
|
||||
* Tracks provider uri.
|
||||
*/
|
||||
Uri CONTENT_URI = Uri.parse("content://com.google.android.maps.mytracks/tracks");
|
||||
|
||||
/**
|
||||
* Track content type.
|
||||
*/
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.track";
|
||||
/**
|
||||
* Track content type.
|
||||
*/
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.track";
|
||||
|
||||
/**
|
||||
* Track id content type.
|
||||
*/
|
||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.track";
|
||||
/**
|
||||
* Track id content type.
|
||||
*/
|
||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.google.track";
|
||||
|
||||
/**
|
||||
* Tracks table default sort order.
|
||||
*/
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
/**
|
||||
* Tracks table default sort order.
|
||||
*/
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
// Columns
|
||||
String NAME = "name"; // track name
|
||||
String DESCRIPTION = "description"; // track description
|
||||
String CATEGORY = "category"; // track activity type
|
||||
String STARTID = "startid"; // first track point id
|
||||
String STOPID = "stopid"; // last track point id
|
||||
String STARTTIME = "starttime"; // track start time
|
||||
String STOPTIME = "stoptime"; // track stop time
|
||||
String NUMPOINTS = "numpoints"; // number of track points
|
||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||
String TOTALTIME = "totaltime"; // total time
|
||||
String MOVINGTIME = "movingtime"; // moving time
|
||||
String MINLAT = "minlat"; // minimum latitude
|
||||
String MAXLAT = "maxlat"; // maximum latitude
|
||||
String MINLON = "minlon"; // minimum longitude
|
||||
String MAXLON = "maxlon"; // maximum longitude
|
||||
String AVGSPEED = "avgspeed"; // average speed
|
||||
// Columns
|
||||
String NAME = "name"; // track name
|
||||
String DESCRIPTION = "description"; // track description
|
||||
String CATEGORY = "category"; // track activity type
|
||||
String STARTID = "startid"; // first track point id
|
||||
String STOPID = "stopid"; // last track point id
|
||||
String STARTTIME = "starttime"; // track start time
|
||||
String STOPTIME = "stoptime"; // track stop time
|
||||
String NUMPOINTS = "numpoints"; // number of track points
|
||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||
String TOTALTIME = "totaltime"; // total time
|
||||
String MOVINGTIME = "movingtime"; // moving time
|
||||
String MINLAT = "minlat"; // minimum latitude
|
||||
String MAXLAT = "maxlat"; // maximum latitude
|
||||
String MINLON = "minlon"; // minimum longitude
|
||||
String MAXLON = "maxlon"; // maximum longitude
|
||||
String AVGSPEED = "avgspeed"; // average speed
|
||||
|
||||
// average moving speed
|
||||
String AVGMOVINGSPEED = "avgmovingspeed";
|
||||
String MAXSPEED = "maxspeed"; // maximum speed
|
||||
String MINELEVATION = "minelevation"; // minimum elevation
|
||||
String MAXELEVATION = "maxelevation"; // maximum elevation
|
||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
||||
String MINGRADE = "mingrade"; // minimum grade
|
||||
String MAXGRADE = "maxgrade"; // maximum grade
|
||||
String ICON = "icon"; // track activity type icon
|
||||
// average moving speed
|
||||
String AVGMOVINGSPEED = "avgmovingspeed";
|
||||
String MAXSPEED = "maxspeed"; // maximum speed
|
||||
String MINELEVATION = "minelevation"; // minimum elevation
|
||||
String MAXELEVATION = "maxelevation"; // maximum elevation
|
||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
||||
String MINGRADE = "mingrade"; // minimum grade
|
||||
String MAXGRADE = "maxgrade"; // maximum grade
|
||||
String ICON = "icon"; // track activity type icon
|
||||
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" // table
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // id
|
||||
+ NAME + " TEXT, " // name
|
||||
+ DESCRIPTION + " TEXT, " // description
|
||||
+ CATEGORY + " TEXT, " // category
|
||||
+ STARTID + " INTEGER, " // start id
|
||||
+ STOPID + " INTEGER, " // stop id
|
||||
+ STARTTIME + " INTEGER, " // start time
|
||||
+ STOPTIME + " INTEGER, " // stop time
|
||||
+ NUMPOINTS + " INTEGER, " // num points
|
||||
+ TOTALDISTANCE + " FLOAT, " // total distance
|
||||
+ TOTALTIME + " INTEGER, " // total time
|
||||
+ MOVINGTIME + " INTEGER, " // moving time
|
||||
+ MINLAT + " INTEGER, " // min latitude
|
||||
+ MAXLAT + " INTEGER, " // max latitude
|
||||
+ MINLON + " INTEGER, " // min longitude
|
||||
+ MAXLON + " INTEGER, " // max longitude
|
||||
+ AVGSPEED + " FLOAT, " // average speed
|
||||
+ AVGMOVINGSPEED + " FLOAT, " // average moving speed
|
||||
+ MAXSPEED + " FLOAT, " // max speed
|
||||
+ MINELEVATION + " FLOAT, " // min elevation
|
||||
+ MAXELEVATION + " FLOAT, " // max elevation
|
||||
+ ELEVATIONGAIN + " FLOAT, " // elevation gain
|
||||
+ MINGRADE + " FLOAT, " // min grade
|
||||
+ MAXGRADE + " FLOAT, " // max grade
|
||||
+ ICON + " TEXT);"; // icon
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" // table
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // id
|
||||
+ NAME + " TEXT, " // name
|
||||
+ DESCRIPTION + " TEXT, " // description
|
||||
+ CATEGORY + " TEXT, " // category
|
||||
+ STARTID + " INTEGER, " // start id
|
||||
+ STOPID + " INTEGER, " // stop id
|
||||
+ STARTTIME + " INTEGER, " // start time
|
||||
+ STOPTIME + " INTEGER, " // stop time
|
||||
+ NUMPOINTS + " INTEGER, " // num points
|
||||
+ TOTALDISTANCE + " FLOAT, " // total distance
|
||||
+ TOTALTIME + " INTEGER, " // total time
|
||||
+ MOVINGTIME + " INTEGER, " // moving time
|
||||
+ MINLAT + " INTEGER, " // min latitude
|
||||
+ MAXLAT + " INTEGER, " // max latitude
|
||||
+ MINLON + " INTEGER, " // min longitude
|
||||
+ MAXLON + " INTEGER, " // max longitude
|
||||
+ AVGSPEED + " FLOAT, " // average speed
|
||||
+ AVGMOVINGSPEED + " FLOAT, " // average moving speed
|
||||
+ MAXSPEED + " FLOAT, " // max speed
|
||||
+ MINELEVATION + " FLOAT, " // min elevation
|
||||
+ MAXELEVATION + " FLOAT, " // max elevation
|
||||
+ ELEVATIONGAIN + " FLOAT, " // elevation gain
|
||||
+ MINGRADE + " FLOAT, " // min grade
|
||||
+ MAXGRADE + " FLOAT, " // max grade
|
||||
+ ICON + " TEXT);"; // icon
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -25,234 +25,234 @@ import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
/**
|
||||
* A waypoint.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public final class Waypoint implements Parcelable {
|
||||
|
||||
public enum WaypointType {
|
||||
WAYPOINT, STATISTICS
|
||||
}
|
||||
public static final Parcelable.Creator<Waypoint> CREATOR = new Parcelable.Creator<Waypoint>() {
|
||||
@Override
|
||||
public Waypoint createFromParcel(Parcel in) {
|
||||
return new Waypoint(in);
|
||||
}
|
||||
|
||||
private long id = -1L;
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String category = "";
|
||||
private String icon = "";
|
||||
private long trackId = -1L;
|
||||
private WaypointType type = WaypointType.WAYPOINT;
|
||||
private double length = 0.0;
|
||||
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(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;
|
||||
}
|
||||
@Override
|
||||
public Waypoint[] newArray(int size) {
|
||||
return new Waypoint[size];
|
||||
}
|
||||
};
|
||||
private long id = -1L;
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String category = "";
|
||||
private String icon = "";
|
||||
private long trackId = -1L;
|
||||
private WaypointType type = WaypointType.WAYPOINT;
|
||||
private double length = 0.0;
|
||||
private long duration = 0;
|
||||
private long startId = -1L;
|
||||
private long stopId = -1L;
|
||||
private Location location = null;
|
||||
private TripStatistics tripStatistics = null;
|
||||
private String 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);
|
||||
public Waypoint() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint[] newArray(int size) {
|
||||
return new Waypoint[size];
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
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();
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
@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 String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public long getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setTrackId(long trackId) {
|
||||
this.trackId = trackId;
|
||||
}
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public WaypointType getType() {
|
||||
return type;
|
||||
}
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setType(WaypointType type) {
|
||||
this.type = type;
|
||||
}
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public double getLength() {
|
||||
return length;
|
||||
}
|
||||
public long getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
|
||||
public void setLength(double length) {
|
||||
this.length = length;
|
||||
}
|
||||
public void setTrackId(long trackId) {
|
||||
this.trackId = trackId;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
public WaypointType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setDuration(long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
public void setType(WaypointType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getStartId() {
|
||||
return startId;
|
||||
}
|
||||
public double getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setStartId(long startId) {
|
||||
this.startId = startId;
|
||||
}
|
||||
public void setLength(double length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public long getStopId() {
|
||||
return stopId;
|
||||
}
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setStopId(long stopId) {
|
||||
this.stopId = stopId;
|
||||
}
|
||||
public void setDuration(long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
public long getStartId() {
|
||||
return startId;
|
||||
}
|
||||
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
public void setStartId(long startId) {
|
||||
this.startId = startId;
|
||||
}
|
||||
|
||||
public TripStatistics getTripStatistics() {
|
||||
return tripStatistics;
|
||||
}
|
||||
public long getStopId() {
|
||||
return stopId;
|
||||
}
|
||||
|
||||
public void setTripStatistics(TripStatistics tripStatistics) {
|
||||
this.tripStatistics = tripStatistics;
|
||||
}
|
||||
|
||||
public String getPhotoUrl() {
|
||||
return photoUrl;
|
||||
}
|
||||
public void setStopId(long stopId) {
|
||||
this.stopId = stopId;
|
||||
}
|
||||
|
||||
public Uri getPhotoURI() {
|
||||
return Uri.parse(photoUrl);
|
||||
}
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public boolean hasPhoto() {
|
||||
return photoUrl != null && !"".equals(photoUrl);
|
||||
}
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public void setPhotoUrl(String photoUrl) {
|
||||
this.photoUrl = photoUrl;
|
||||
}
|
||||
public TripStatistics getTripStatistics() {
|
||||
return tripStatistics;
|
||||
}
|
||||
|
||||
public void setTripStatistics(TripStatistics tripStatistics) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+83
-85
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -15,111 +15,109 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.content;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
|
||||
import android.os.Parcel;
|
||||
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.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class WaypointCreationRequest implements Parcelable {
|
||||
|
||||
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(
|
||||
WaypointType.WAYPOINT, false);
|
||||
public final static WaypointCreationRequest DEFAULT_STATISTICS = new WaypointCreationRequest(
|
||||
WaypointType.STATISTICS, false);
|
||||
public final static WaypointCreationRequest DEFAULT_START_TRACK = new WaypointCreationRequest(
|
||||
WaypointType.STATISTICS, true);
|
||||
public static final Creator CREATOR = new Creator();
|
||||
private WaypointType type;
|
||||
// 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(
|
||||
WaypointType.WAYPOINT, false);
|
||||
public final static WaypointCreationRequest DEFAULT_STATISTICS = new WaypointCreationRequest(
|
||||
WaypointType.STATISTICS, false);
|
||||
public final static WaypointCreationRequest DEFAULT_START_TRACK = new WaypointCreationRequest(
|
||||
WaypointType.STATISTICS, true);
|
||||
private WaypointCreationRequest(WaypointType type, boolean isTrackStatistics) {
|
||||
this(type, isTrackStatistics, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private WaypointCreationRequest(WaypointType type, boolean isTrackStatistics) {
|
||||
this(type, isTrackStatistics, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public WaypointCreationRequest(WaypointType type, boolean isTrackStatistics, String name,
|
||||
String category, String description, String iconUrl, String photoUrl) {
|
||||
this.type = type;
|
||||
this.isTrackStatistics = isTrackStatistics;
|
||||
this.name = name;
|
||||
this.category = category;
|
||||
this.description = description;
|
||||
this.iconUrl = iconUrl;
|
||||
this.photoUrl = photoUrl;
|
||||
}
|
||||
|
||||
public static class Creator implements Parcelable.Creator<WaypointCreationRequest> {
|
||||
public WaypointCreationRequest(WaypointType type, boolean isTrackStatistics, String name,
|
||||
String category, String description, String iconUrl, String photoUrl) {
|
||||
this.type = type;
|
||||
this.isTrackStatistics = isTrackStatistics;
|
||||
this.name = name;
|
||||
this.category = category;
|
||||
this.description = description;
|
||||
this.iconUrl = iconUrl;
|
||||
this.photoUrl = photoUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WaypointCreationRequest createFromParcel(Parcel source) {
|
||||
return new WaypointCreationRequest(
|
||||
WaypointType.values()[source.readInt()],
|
||||
source.readByte() == 1,
|
||||
source.readString(),
|
||||
source.readString(),
|
||||
source.readString(),
|
||||
source.readString(),
|
||||
source.readString());
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public WaypointCreationRequest[] newArray(int size) {
|
||||
return new WaypointCreationRequest[size];
|
||||
@Override
|
||||
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 int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
public boolean isTrackStatistics() {
|
||||
return isTrackStatistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public WaypointType getType() {
|
||||
return type;
|
||||
}
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public boolean isTrackStatistics() {
|
||||
return isTrackStatistics;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public String getIconUrl() {
|
||||
return iconUrl;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
public String getPhotoUrl() {
|
||||
return photoUrl;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public static class Creator implements Parcelable.Creator<WaypointCreationRequest> {
|
||||
|
||||
public String getIconUrl() {
|
||||
return iconUrl;
|
||||
}
|
||||
|
||||
public String getPhotoUrl() {
|
||||
return photoUrl;
|
||||
}
|
||||
@Override
|
||||
public WaypointCreationRequest createFromParcel(Parcel source) {
|
||||
return new WaypointCreationRequest(
|
||||
WaypointType.values()[source.readInt()],
|
||||
source.readByte() == 1,
|
||||
source.readString(),
|
||||
source.readString(),
|
||||
source.readString(),
|
||||
source.readString(),
|
||||
source.readString());
|
||||
}
|
||||
|
||||
public WaypointCreationRequest[] newArray(int size) {
|
||||
return new WaypointCreationRequest[size];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -21,83 +21,83 @@ import android.provider.BaseColumns;
|
||||
|
||||
/**
|
||||
* Constants for waypoints table.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public interface WaypointsColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "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_ITEMTYPE = "vnd.android.cursor.item/vnd.google.waypoint";
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
String TABLE_NAME = "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_ITEMTYPE = "vnd.android.cursor.item/vnd.google.waypoint";
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
// Columns
|
||||
String NAME = "name"; // waypoint name
|
||||
String DESCRIPTION = "description"; // waypoint description
|
||||
String CATEGORY = "category"; // waypoint category
|
||||
String ICON = "icon"; // waypoint icon
|
||||
String TRACKID = "trackid"; // track id
|
||||
String TYPE = "type"; // type
|
||||
String LENGTH = "length"; // length of the track (without smoothing)
|
||||
String DURATION = "duration"; // total duration of the track (not from last waypoint)
|
||||
String STARTTIME = "starttime"; // start time of the trip statistics
|
||||
String STARTID = "startid"; // start track point id
|
||||
String STOPID = "stopid"; // stop track point id
|
||||
// Columns
|
||||
String NAME = "name"; // waypoint name
|
||||
String DESCRIPTION = "description"; // waypoint description
|
||||
String CATEGORY = "category"; // waypoint category
|
||||
String ICON = "icon"; // waypoint icon
|
||||
String TRACKID = "trackid"; // track id
|
||||
String TYPE = "type"; // type
|
||||
String LENGTH = "length"; // length of the track (without smoothing)
|
||||
String DURATION = "duration"; // total duration of the track (not from last waypoint)
|
||||
String STARTTIME = "starttime"; // start time of the trip statistics
|
||||
String STARTID = "startid"; // start track point id
|
||||
String STOPID = "stopid"; // stop track point id
|
||||
|
||||
String LONGITUDE = "longitude"; // longitude
|
||||
String LATITUDE = "latitude"; // latitude
|
||||
String TIME = "time"; // time
|
||||
String ALTITUDE = "elevation"; // altitude
|
||||
String ACCURACY = "accuracy"; // accuracy
|
||||
String SPEED = "speed"; // speed
|
||||
String BEARING = "bearing"; // bearing
|
||||
String LONGITUDE = "longitude"; // longitude
|
||||
String LATITUDE = "latitude"; // latitude
|
||||
String TIME = "time"; // time
|
||||
String ALTITUDE = "elevation"; // altitude
|
||||
String ACCURACY = "accuracy"; // accuracy
|
||||
String SPEED = "speed"; // speed
|
||||
String BEARING = "bearing"; // bearing
|
||||
|
||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||
String TOTALTIME = "totaltime"; // total time
|
||||
String MOVINGTIME = "movingtime"; // moving time
|
||||
String AVGSPEED = "avgspeed"; // average speed
|
||||
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
||||
String MAXSPEED = "maxspeed"; // max speed
|
||||
String MINELEVATION = "minelevation"; // min elevation
|
||||
String MAXELEVATION = "maxelevation"; // max elevation
|
||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
||||
String MINGRADE = "mingrade"; // min grade
|
||||
String MAXGRADE = "maxgrade"; // max grade
|
||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||
String TOTALTIME = "totaltime"; // total time
|
||||
String MOVINGTIME = "movingtime"; // moving time
|
||||
String AVGSPEED = "avgspeed"; // average speed
|
||||
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
||||
String MAXSPEED = "maxspeed"; // max speed
|
||||
String MINELEVATION = "minelevation"; // min elevation
|
||||
String MAXELEVATION = "maxelevation"; // max elevation
|
||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
||||
String MINGRADE = "mingrade"; // min grade
|
||||
String MAXGRADE = "maxgrade"; // max grade
|
||||
|
||||
String PHOTOURL = "photoUrl"; // url for the photo
|
||||
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ NAME + " TEXT, "
|
||||
+ DESCRIPTION + " TEXT, "
|
||||
+ CATEGORY + " TEXT, "
|
||||
+ ICON + " TEXT, "
|
||||
+ TRACKID + " INTEGER, "
|
||||
+ TYPE + " INTEGER, "
|
||||
+ LENGTH + " FLOAT, "
|
||||
+ DURATION + " INTEGER, "
|
||||
+ STARTTIME + " INTEGER, "
|
||||
+ STARTID + " INTEGER, "
|
||||
+ STOPID + " INTEGER, "
|
||||
+ LONGITUDE + " INTEGER, "
|
||||
+ LATITUDE + " INTEGER, "
|
||||
+ TIME + " INTEGER, "
|
||||
+ ALTITUDE + " FLOAT, "
|
||||
+ ACCURACY + " FLOAT, "
|
||||
+ SPEED + " FLOAT, "
|
||||
+ BEARING + " FLOAT, "
|
||||
+ TOTALDISTANCE + " FLOAT, "
|
||||
+ TOTALTIME + " INTEGER, "
|
||||
+ MOVINGTIME + " INTEGER, "
|
||||
+ AVGSPEED + " FLOAT, "
|
||||
+ AVGMOVINGSPEED + " FLOAT, "
|
||||
+ MAXSPEED + " FLOAT, "
|
||||
+ MINELEVATION + " FLOAT, "
|
||||
+ MAXELEVATION + " FLOAT, "
|
||||
+ ELEVATIONGAIN + " FLOAT, "
|
||||
+ MINGRADE + " FLOAT, "
|
||||
+ MAXGRADE + " FLOAT, "
|
||||
+ PHOTOURL + " TEXT"
|
||||
+ ");";
|
||||
String PHOTOURL = "photoUrl"; // url for the photo
|
||||
|
||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ NAME + " TEXT, "
|
||||
+ DESCRIPTION + " TEXT, "
|
||||
+ CATEGORY + " TEXT, "
|
||||
+ ICON + " TEXT, "
|
||||
+ TRACKID + " INTEGER, "
|
||||
+ TYPE + " INTEGER, "
|
||||
+ LENGTH + " FLOAT, "
|
||||
+ DURATION + " INTEGER, "
|
||||
+ STARTTIME + " INTEGER, "
|
||||
+ STARTID + " INTEGER, "
|
||||
+ STOPID + " INTEGER, "
|
||||
+ LONGITUDE + " INTEGER, "
|
||||
+ LATITUDE + " INTEGER, "
|
||||
+ TIME + " INTEGER, "
|
||||
+ ALTITUDE + " FLOAT, "
|
||||
+ ACCURACY + " FLOAT, "
|
||||
+ SPEED + " FLOAT, "
|
||||
+ BEARING + " FLOAT, "
|
||||
+ TOTALDISTANCE + " FLOAT, "
|
||||
+ TOTALTIME + " INTEGER, "
|
||||
+ MOVINGTIME + " INTEGER, "
|
||||
+ AVGSPEED + " FLOAT, "
|
||||
+ AVGMOVINGSPEED + " FLOAT, "
|
||||
+ MAXSPEED + " FLOAT, "
|
||||
+ MINELEVATION + " FLOAT, "
|
||||
+ MAXELEVATION + " FLOAT, "
|
||||
+ ELEVATIONGAIN + " FLOAT, "
|
||||
+ MINGRADE + " FLOAT, "
|
||||
+ MAXGRADE + " FLOAT, "
|
||||
+ PHOTOURL + " TEXT"
|
||||
+ ");";
|
||||
}
|
||||
|
||||
@@ -5,16 +5,14 @@ public final class SensorDataSet {
|
||||
public static final float DATA_UNAVAILABLE = Integer.MAX_VALUE;
|
||||
|
||||
//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 cadence;
|
||||
private float power;
|
||||
private float batteryLevel;
|
||||
private long creationTimestamp;
|
||||
|
||||
public String sensorName;
|
||||
public String sensorAddress;
|
||||
|
||||
public SensorDataSet(float heartRate, float cadence, float power, float batteryLevel, long creationTimestamp) {
|
||||
this.heartRate = heartRate;
|
||||
this.cadence = cadence;
|
||||
@@ -71,6 +69,7 @@ public final class SensorDataSet {
|
||||
|
||||
/**
|
||||
* Is the data recent considering the current time.
|
||||
*
|
||||
* @param maxAge the maximal age in milliseconds.
|
||||
*/
|
||||
public boolean isRecent(long maxAge) {
|
||||
|
||||
+16
-16
@@ -16,33 +16,33 @@
|
||||
|
||||
package com.google.android.apps.mytracks.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
/**
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
|
||||
/**
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public abstract class AbstractDialogFragment extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Dialog dialog = createDialog();
|
||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final Dialog dialog = createDialog();
|
||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
|
||||
@Override
|
||||
public void onShow(DialogInterface dialogInterface) {
|
||||
DialogUtils.setDialogTitleDivider(getActivity(), dialog);
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
@Override
|
||||
public void onShow(DialogInterface dialogInterface) {
|
||||
DialogUtils.setDialogTitleDivider(getActivity(), dialog);
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
|
||||
protected abstract Dialog createDialog();
|
||||
protected abstract Dialog createDialog();
|
||||
}
|
||||
@@ -50,472 +50,472 @@ import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* A fragment to display track chart to the user.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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
|
||||
private TripStatisticsUpdater tripStatisticsUpdater;
|
||||
private long startTime;
|
||||
// Stats gathered from the received data
|
||||
private TripStatisticsUpdater tripStatisticsUpdater;
|
||||
private long startTime;
|
||||
|
||||
private boolean metricUnits = true;
|
||||
private boolean reportSpeed = true;
|
||||
private int recordingDistanceInterval = PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT;
|
||||
private boolean metricUnits = true;
|
||||
private boolean reportSpeed = true;
|
||||
private int recordingDistanceInterval = PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT;
|
||||
|
||||
// Modes of operation
|
||||
private boolean chartByDistance = true;
|
||||
private boolean[] chartShow = new boolean[] { true, true, true, true, true, true };
|
||||
// Modes of operation
|
||||
private boolean chartByDistance = true;
|
||||
private boolean[] chartShow = new boolean[]{true, true, true, true, true, true};
|
||||
|
||||
// UI elements
|
||||
private ChartView chartView;
|
||||
private ZoomControls zoomControls;
|
||||
// UI elements
|
||||
private ChartView chartView;
|
||||
private ZoomControls zoomControls;
|
||||
|
||||
/**
|
||||
* A runnable that will enable/disable zoom controls and orange pointer as
|
||||
* 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.
|
||||
/**
|
||||
* A runnable that will enable/disable zoom controls and orange pointer as
|
||||
* appropriate and redraw.
|
||||
*/
|
||||
chartView = new ChartView(getActivity());
|
||||
}
|
||||
|
||||
@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() {
|
||||
private final Runnable updateChart = new Runnable() {
|
||||
@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() {
|
||||
if (isResumed()) {
|
||||
chartView.resetScroll();
|
||||
}
|
||||
if (!isResumed() || trackDataHub == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||
chartView.setShowPointer(isSelectedTrackRecording());
|
||||
chartView.invalidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@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 onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@Override
|
||||
public void onSampledOutTrackPoint(Location location) {
|
||||
if (isResumed()) {
|
||||
fillDataPoint(location, null);
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit(Location location) {
|
||||
if (isResumed()) {
|
||||
fillDataPoint(location, null);
|
||||
@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
|
||||
public void onClick(View v) {
|
||||
zoomIn();
|
||||
}
|
||||
});
|
||||
zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
zoomOut();
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPointsDone() {
|
||||
if (isResumed()) {
|
||||
chartView.addDataPoints(pendingPoints);
|
||||
pendingPoints.clear();
|
||||
runOnUiThread(updateChart);
|
||||
@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 clearWaypoints() {
|
||||
if (isResumed()) {
|
||||
chartView.clearWaypoints();
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
checkChartSettings();
|
||||
getActivity().runOnUiThread(updateChart);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypoint(Waypoint waypoint) {
|
||||
if (isResumed() && waypoint != null && LocationUtils.isValidLocation(waypoint.getLocation())) {
|
||||
chartView.addWaypoint(waypoint);
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypointsDone() {
|
||||
if (isResumed()) {
|
||||
runOnUiThread(updateChart);
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
ViewGroup layout = getActivity().findViewById(R.id.chart_view_layout);
|
||||
layout.removeView(chartView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMetricUnitsChanged(boolean metric) {
|
||||
if (isResumed()) {
|
||||
if (metricUnits == metric) {
|
||||
@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() {
|
||||
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;
|
||||
}
|
||||
metricUnits = metric;
|
||||
chartView.setMetricUnits(metricUnits);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
chartView.requestLayout();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReportSpeedChanged(boolean speed) {
|
||||
if (isResumed()) {
|
||||
if (reportSpeed == speed) {
|
||||
@Override
|
||||
public boolean onReportSpeedChanged(boolean speed) {
|
||||
if (isResumed()) {
|
||||
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;
|
||||
}
|
||||
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
|
||||
public boolean onRecordingGpsAccuracy(int minRequiredAccuracy) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRecordingDistanceIntervalChanged(int value) {
|
||||
if (isResumed()) {
|
||||
if (recordingDistanceInterval == value) {
|
||||
@Override
|
||||
public boolean onRecordingGpsAccuracy(int minRequiredAccuracy) {
|
||||
// We don't care.
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@Override
|
||||
public boolean onRecordingDistanceIntervalChanged(int value) {
|
||||
if (isResumed()) {
|
||||
if (recordingDistanceInterval == value) {
|
||||
return false;
|
||||
}
|
||||
recordingDistanceInterval = value;
|
||||
return true;
|
||||
}
|
||||
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();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
data[0] = timeOrDistance;
|
||||
data[1] = elevation;
|
||||
data[2] = speed;
|
||||
data[3] = pace;
|
||||
data[4] = heartRate;
|
||||
data[5] = cadence;
|
||||
data[6] = power;
|
||||
/**
|
||||
* 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);
|
||||
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) {
|
||||
tripStatisticsUpdater = new TripStatisticsUpdater(time);
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setChartView(ChartView view) {
|
||||
chartView = view;
|
||||
}
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setMetricUnits(boolean value) {
|
||||
metricUnits = value;
|
||||
}
|
||||
/**
|
||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void pauseTrackDataHub() {
|
||||
trackDataHub.unregisterTrackDataListener(this);
|
||||
trackDataHub = null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setReportSpeed(boolean value) {
|
||||
reportSpeed = value;
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setChartByDistance(boolean value) {
|
||||
chartByDistance = value;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
+102
-104
@@ -41,117 +41,115 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* A DialogFragment to choose an activity type.
|
||||
*
|
||||
*
|
||||
* @author apoorvn
|
||||
*/
|
||||
public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author apoorvn
|
||||
*/
|
||||
public interface ChooseActivityTypeCaller {
|
||||
public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
|
||||
private static final String KEY_CATEGORY = "category";
|
||||
private ChooseActivityTypeCaller caller;
|
||||
|
||||
public static ChooseActivityTypeDialogFragment newInstance(String category) {
|
||||
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";
|
||||
|
||||
private static final String KEY_CATEGORY = "category";
|
||||
|
||||
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());
|
||||
/**
|
||||
* Called when choose activity type is done.
|
||||
*/
|
||||
void onChooseActivityTypeDone(String iconValue);
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
@@ -25,6 +23,8 @@ import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -34,62 +34,62 @@ import java.util.List;
|
||||
*/
|
||||
public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
|
||||
private final Context context;
|
||||
private final List<Integer> imageIds;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int padding;
|
||||
private int selected = -1;
|
||||
private final Context context;
|
||||
private final List<Integer> imageIds;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int padding;
|
||||
private int selected = -1;
|
||||
|
||||
public ChooseActivityTypeImageAdapter(
|
||||
Context context, List<Integer> imageIds, int width, int height, int padding) {
|
||||
this.context = context;
|
||||
this.imageIds = imageIds;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
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;
|
||||
public ChooseActivityTypeImageAdapter(
|
||||
Context context, List<Integer> imageIds, int width, int height, int padding) {
|
||||
this.context = context;
|
||||
this.imageIds = imageIds;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.padding = padding;
|
||||
}
|
||||
if (position == selected) {
|
||||
imageView.setBackgroundResource(R.drawable.list_selector_background_transition_holo_dark);
|
||||
} else {
|
||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||
|
||||
@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);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
+56
-57
@@ -26,72 +26,71 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* A DialogFragment to confirm delete.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ConfirmDeleteDialogFragment extends AbstractDialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ConfirmDeleteCaller {
|
||||
public static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
||||
private static final String KEY_TRACK_IDS = "trackIds";
|
||||
private ConfirmDeleteCaller caller;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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";
|
||||
private static final String KEY_TRACK_IDS = "trackIds";
|
||||
|
||||
/**
|
||||
* 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());
|
||||
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
|
||||
deleteTrackDialogFragment.setArguments(bundle);
|
||||
return deleteTrackDialogFragment;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog createDialog() {
|
||||
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
@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
|
||||
protected Dialog createDialog() {
|
||||
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
||||
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);
|
||||
}
|
||||
}
|
||||
+67
-68
@@ -23,87 +23,86 @@ import android.os.Bundle;
|
||||
|
||||
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.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* A DialogFragment to delete marker.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class DeleteMarkerDialogFragment extends AbstractDialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface DeleteMarkerCaller {
|
||||
public static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
||||
private static final String KEY_MARKER_IDS = "markerIds";
|
||||
private DeleteMarkerCaller caller;
|
||||
|
||||
public static DeleteMarkerDialogFragment newInstance(long[] markerIds) {
|
||||
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";
|
||||
|
||||
public static DeleteMarkerDialogFragment newInstance(long[] markerIds) {
|
||||
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());
|
||||
/**
|
||||
* Called when delete marker is done.
|
||||
*/
|
||||
void onDeleteMarkerDone();
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+57
-59
@@ -29,72 +29,70 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* A DialogFragment to select a file type, gpx, kml, etc.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class FileTypeDialogFragment extends AbstractDialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface FileTypeCaller {
|
||||
public static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
||||
private static final String KEY_MENU_ID = "menuId";
|
||||
private static final String KEY_TITLE_ID = "titleId";
|
||||
private static final String KEY_OPTION_ID = "optionId";
|
||||
private FileTypeCaller caller;
|
||||
|
||||
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";
|
||||
|
||||
private static final String KEY_MENU_ID = "menuId";
|
||||
private static final String KEY_TITLE_ID = "titleId";
|
||||
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());
|
||||
/**
|
||||
* Called when file type selection is done.
|
||||
*/
|
||||
void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat);
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
+71
-70
@@ -16,91 +16,92 @@
|
||||
|
||||
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.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
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.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
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_DEFAULT_VALUE = "defaultValue";
|
||||
private static final String KEY_TITLE_ID = "titleId";
|
||||
private static final String KEY_PREFERENCE_ID = "preferenceId";
|
||||
private static final String KEY_DEFAULT_VALUE = "defaultValue";
|
||||
private static final String KEY_TITLE_ID = "titleId";
|
||||
|
||||
public static FrequencyDialogFragment newInstance(
|
||||
int preferenceId, int defaultValue, int titleId) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(KEY_PREFERENCE_ID, preferenceId);
|
||||
bundle.putInt(KEY_DEFAULT_VALUE, defaultValue);
|
||||
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||
public static FrequencyDialogFragment newInstance(
|
||||
int preferenceId, int defaultValue, int titleId) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(KEY_PREFERENCE_ID, preferenceId);
|
||||
bundle.putInt(KEY_DEFAULT_VALUE, defaultValue);
|
||||
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||
|
||||
FrequencyDialogFragment frequencyDialogFragment = new FrequencyDialogFragment();
|
||||
frequencyDialogFragment.setArguments(bundle);
|
||||
return frequencyDialogFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
FrequencyDialogFragment frequencyDialogFragment = new FrequencyDialogFragment();
|
||||
frequencyDialogFragment.setArguments(bundle);
|
||||
return frequencyDialogFragment;
|
||||
}
|
||||
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]);
|
||||
}
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
}
|
||||
+210
-210
@@ -52,236 +52,236 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* A fragment to show marker details.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class MarkerDetailFragment extends Fragment {
|
||||
|
||||
public static MarkerDetailFragment newInstance(long markerId, String title) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong(KEY_MARKER_ID, markerId);
|
||||
bundle.putString(KEY_TITLE, title);
|
||||
|
||||
MarkerDetailFragment fragment = new MarkerDetailFragment();
|
||||
fragment.setArguments(bundle);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||
private static final String 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() {
|
||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||
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 onClick(View v) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
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
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Need to update the waypoint in case returning after an edit
|
||||
updateWaypoint(true);
|
||||
updateUi();
|
||||
}
|
||||
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 void onPause() {
|
||||
super.onPause();
|
||||
handler.removeCallbacks(hideText);
|
||||
}
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.marker_detail_fragment, container, false);
|
||||
|
||||
@Override
|
||||
public void setMenuVisibility(boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
/*
|
||||
* View pager caches the neighboring fragments in the resumed state. If
|
||||
* becoming visible from the resumed state, update the UI to display the
|
||||
* text above the image.
|
||||
*/
|
||||
if (isResumed()) {
|
||||
if (menuVisible) {
|
||||
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
|
||||
public void onClick(View v) {
|
||||
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();
|
||||
} else {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
handler.removeCallbacks(hideText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.marker_detail, menu);
|
||||
|
||||
updateWaypoint(false);
|
||||
|
||||
TrackIconUtils.setMenuIconColor(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
||||
FragmentActivity fragmentActivity = getActivity();
|
||||
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
|
||||
public void setMenuVisibility(boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
/*
|
||||
* View pager caches the neighboring fragments in the resumed state. If
|
||||
* becoming visible from the resumed state, update the UI to display the
|
||||
* text above the image.
|
||||
*/
|
||||
if (isResumed()) {
|
||||
if (menuVisible) {
|
||||
updateUi();
|
||||
} else {
|
||||
handler.removeCallbacks(hideText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the waypoint.
|
||||
*
|
||||
* @param refresh true to always update
|
||||
*/
|
||||
private void updateWaypoint(boolean refresh) {
|
||||
if (refresh || waypoint == null) {
|
||||
waypoint = contentProviderUtils.getWaypoint(getArguments().getLong(KEY_MARKER_ID));
|
||||
if (waypoint == null) {
|
||||
Log.d(TAG, "waypoint is null");
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.marker_detail, menu);
|
||||
|
||||
updateWaypoint(false);
|
||||
|
||||
TrackIconUtils.setMenuIconColor(menu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
||||
FragmentActivity fragmentActivity = getActivity();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()) + "]";
|
||||
/**
|
||||
* Updates the waypoint.
|
||||
*
|
||||
* @param refresh true to always update
|
||||
*/
|
||||
private void updateWaypoint(boolean refresh) {
|
||||
if (refresh || waypoint == null) {
|
||||
waypoint = contentProviderUtils.getWaypoint(getArguments().getLong(KEY_MARKER_ID));
|
||||
if (waypoint == null) {
|
||||
Log.d(TAG, "waypoint is null");
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,274 +53,272 @@ import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* A fragment to display track statistics to the user.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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 Handler handlerUpdateUI;
|
||||
private TrackDataHub trackDataHub;
|
||||
private Handler handlerUpdateUI;
|
||||
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
|
||||
private Location lastLocation = null;
|
||||
private TripStatistics lastTripStatistics = null;
|
||||
private SensorDataSet sensorDataSet = null;
|
||||
private String category = "";
|
||||
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
|
||||
private Location lastLocation = null;
|
||||
private TripStatistics lastTripStatistics = null;
|
||||
private SensorDataSet sensorDataSet = null;
|
||||
private final Runnable updateUIeachSecond = new Runnable() {
|
||||
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() {
|
||||
public void run() {
|
||||
if (isResumed() && isSelectedTrackRecording()) {
|
||||
if (!isSelectedTrackPaused() && lastTripStatistics != null) {
|
||||
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis() - lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime());
|
||||
updateSensorDataUI();
|
||||
handlerUpdateUI.postDelayed(this, UnitConversions.ONE_SECOND);
|
||||
}
|
||||
}
|
||||
};
|
||||
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
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.stats, container, false);
|
||||
}
|
||||
Spinner activityTypeIcon = getView().findViewById(R.id.stats_activity_type_icon);
|
||||
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getActivity(), ""));
|
||||
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
((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
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
handlerUpdateUI = new Handler();
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(getContext(), null);
|
||||
TrackRecordingServiceConnectionUtils.startConnection(getContext(), trackRecordingServiceConnection);
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
updateUi(getActivity());
|
||||
if (isSelectedTrackRecording()) {
|
||||
handlerUpdateUI.post(updateUIeachSecond);
|
||||
}
|
||||
}
|
||||
|
||||
Spinner activityTypeIcon = getView().findViewById(R.id.stats_activity_type_icon);
|
||||
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getActivity(), ""));
|
||||
activityTypeIcon.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
((TrackDetailActivity) getActivity()).chooseActivityType(category);
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
handlerUpdateUI.removeCallbacks(updateUIeachSecond);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
});
|
||||
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);
|
||||
}
|
||||
|
||||
@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 void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
updateUi(getActivity());
|
||||
if (isSelectedTrackRecording()) {
|
||||
handlerUpdateUI.post(updateUIeachSecond);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
handlerUpdateUI.removeCallbacks(updateUIeachSecond);
|
||||
}
|
||||
@Override
|
||||
public boolean onRecordingGpsAccuracy(int newValue) {
|
||||
recordingGpsAccuracy = newValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
}
|
||||
@Override
|
||||
public boolean onRecordingDistanceIntervalChanged(int minRecordingDistance) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
StatsUtils.setSensorData(getActivity(), getActivity(), sensorDataSet, isSelectedTrackRecording());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI.
|
||||
*/
|
||||
private void updateUi(FragmentActivity activity) {
|
||||
String trackIconValue = TrackIconUtils.getIconValue(activity, category);
|
||||
StatsUtils.setTripStatisticsValues(activity, activity, null, lastTripStatistics, trackIconValue);
|
||||
StatsUtils.setLocationValues(activity, activity, null, lastLocation, isSelectedTrackRecording());
|
||||
updateSensorDataUI();
|
||||
}
|
||||
/**
|
||||
* Updates the UI.
|
||||
*/
|
||||
private void updateUi(FragmentActivity activity) {
|
||||
String trackIconValue = TrackIconUtils.getIconValue(activity, category);
|
||||
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;
|
||||
|
||||
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.KmlTrackWriter;
|
||||
import com.google.android.apps.mytracks.io.file.exporter.TrackWriter;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Definition of all possible track formats.
|
||||
*/
|
||||
public enum TrackFileFormat implements Parcelable {
|
||||
KML {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
return new KmlTrackWriter(context, multiple, playTrack);
|
||||
}
|
||||
},
|
||||
GPX {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
return new GpxTrackWriter(context.getString(R.string.app_name));
|
||||
}
|
||||
};
|
||||
KML {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
return new KmlTrackWriter(context, multiple, playTrack);
|
||||
}
|
||||
},
|
||||
GPX {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
return new GpxTrackWriter(context.getString(R.string.app_name));
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
|
||||
@Override
|
||||
public TrackFileFormat createFromParcel(final Parcel source) {
|
||||
return TrackFileFormat.values()[source.readInt()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
dest.writeInt(ordinal());
|
||||
}
|
||||
@Override
|
||||
public TrackFileFormat[] newArray(final int size) {
|
||||
return new TrackFileFormat[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
|
||||
@Override
|
||||
public TrackFileFormat createFromParcel(final Parcel source) {
|
||||
return TrackFileFormat.values()[source.readInt()];
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackFileFormat[] newArray(final int size) {
|
||||
return new TrackFileFormat[size];
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
dest.writeInt(ordinal());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new track writer for the format.
|
||||
*
|
||||
* @param context the context
|
||||
* @param multiple true for writing multiple tracks
|
||||
* @param playTrack true to play track
|
||||
*/
|
||||
public abstract TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack);
|
||||
/**
|
||||
* Creates a new track writer for the format.
|
||||
*
|
||||
* @param context the context
|
||||
* @param multiple true for writing multiple tracks
|
||||
* @param playTrack true to play track
|
||||
*/
|
||||
public abstract TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack);
|
||||
|
||||
/**
|
||||
* Returns the mime type for each format.
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return "application/" + getExtension() + "+xml";
|
||||
}
|
||||
/**
|
||||
* Returns the mime type for each format.
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return "application/" + getExtension() + "+xml";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file extension for each format.
|
||||
*/
|
||||
public String getExtension() {
|
||||
return this.name().toLowerCase(Locale.US);
|
||||
}
|
||||
/**
|
||||
* Returns the file extension for each format.
|
||||
*/
|
||||
public String getExtension() {
|
||||
return this.name().toLowerCase(Locale.US);
|
||||
}
|
||||
}
|
||||
+193
-193
@@ -16,219 +16,219 @@
|
||||
|
||||
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.location.Location;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Track Writer for writing tracks to an {@link OutputStream}.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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 Track[] tracks;
|
||||
private final TrackWriter trackWriter;
|
||||
private final TrackExporterListener trackExporterListener;
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final Track[] tracks;
|
||||
private final TrackWriter trackWriter;
|
||||
private final TrackExporterListener trackExporterListener;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param contentProviderUtils the content provider utils
|
||||
* @param tracks the tracks
|
||||
* @param trackWriter the track writer
|
||||
* @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.
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param contentProviderUtils the content provider utils
|
||||
* @param tracks the tracks
|
||||
* @param trackWriter the track writer
|
||||
* @param trackExporterListener the track export listener
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
public FileTrackExporter(ContentProviderUtils contentProviderUtils, Track[] tracks,
|
||||
TrackWriter trackWriter, TrackExporterListener trackExporterListener) {
|
||||
this.contentProviderUtils = contentProviderUtils;
|
||||
this.tracks = tracks;
|
||||
this.trackWriter = trackWriter;
|
||||
this.trackExporterListener = trackExporterListener;
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
public void swapLocations() {
|
||||
Location tempLocation = lastLocation;
|
||||
lastLocation = currentLocation;
|
||||
currentLocation = tempLocation;
|
||||
if (currentLocation != null) {
|
||||
currentLocation.reset();
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+146
-145
@@ -30,172 +30,173 @@ import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Write track as GPX to a file.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
//TODO Can we export Sensor.Data in GPX?
|
||||
public class GpxTrackWriter implements TrackWriter {
|
||||
|
||||
private static final NumberFormat ELEVATION_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);
|
||||
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
||||
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
||||
COORDINATE_FORMAT.setGroupingUsed(false);
|
||||
}
|
||||
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);
|
||||
|
||||
private final String creator;
|
||||
private PrintWriter printWriter;
|
||||
|
||||
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;
|
||||
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
||||
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
||||
COORDINATE_FORMAT.setGroupingUsed(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeHeader(Track[] tracks) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
printWriter.println("<gpx");
|
||||
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>");
|
||||
private final String creator;
|
||||
private PrintWriter printWriter;
|
||||
|
||||
public GpxTrackWriter(String creator) {
|
||||
this.creator = creator;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFooter() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</gpx>");
|
||||
@Override
|
||||
public String getExtension() {
|
||||
return TrackFileFormat.GPX.getExtension();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginWaypoints(Track track) {
|
||||
// Do nothing
|
||||
}
|
||||
@Override
|
||||
public void prepare(OutputStream outputStream) {
|
||||
this.printWriter = new PrintWriter(outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndWaypoints() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@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>");
|
||||
@Override
|
||||
public void close() {
|
||||
if (printWriter != null) {
|
||||
printWriter.flush();
|
||||
printWriter = null;
|
||||
}
|
||||
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 writeHeader(Track[] tracks) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
printWriter.println("<gpx");
|
||||
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
|
||||
public void writeEndTrack(Track track, Location endLocation) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</trk>");
|
||||
@Override
|
||||
public void writeFooter() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</gpx>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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>");
|
||||
@Override
|
||||
public void writeBeginWaypoints(Track track) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) + "\"";
|
||||
}
|
||||
@Override
|
||||
public void writeEndWaypoints() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@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
|
||||
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()) + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
+386
-386
@@ -22,10 +22,10 @@ import android.net.Uri;
|
||||
|
||||
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.DescriptionGeneratorImpl;
|
||||
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.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
@@ -41,420 +41,420 @@ import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Write track as KML to a file.
|
||||
*
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class KmlTrackWriter implements TrackWriter {
|
||||
|
||||
private static final String WAYPOINT_STYLE = "waypoint";
|
||||
private static final String STATISTICS_STYLE = "statistics";
|
||||
private static final String START_STYLE = "start";
|
||||
private static final String END_STYLE = "end";
|
||||
private static final String TRACK_STYLE = "track";
|
||||
private static final String SCHEMA_ID = "schema";
|
||||
private static final String CADENCE = "cadence";
|
||||
private static final String HEART_RATE = "heart_rate";
|
||||
private static final String POWER = "power";
|
||||
private static final String WAYPOINT_STYLE = "waypoint";
|
||||
private static final String STATISTICS_STYLE = "statistics";
|
||||
private static final String START_STYLE = "start";
|
||||
private static final String END_STYLE = "end";
|
||||
private static final String TRACK_STYLE = "track";
|
||||
private static final String SCHEMA_ID = "schema";
|
||||
private static final String CADENCE = "cadence";
|
||||
private static final String HEART_RATE = "heart_rate";
|
||||
private static final String POWER = "power";
|
||||
|
||||
private static final String
|
||||
WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
||||
private static final String
|
||||
STATISTICS_ICON = "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";
|
||||
private static final String
|
||||
START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
|
||||
private static final String
|
||||
END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
|
||||
private static final String
|
||||
TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
||||
private static final String
|
||||
WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
||||
private static final String
|
||||
STATISTICS_ICON = "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";
|
||||
private static final String
|
||||
START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
|
||||
private static final String
|
||||
END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
|
||||
private static final String
|
||||
TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
||||
|
||||
private final Context context;
|
||||
private final boolean multiple;
|
||||
private final boolean playTrack;
|
||||
private final DescriptionGenerator descriptionGenerator;
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final Context context;
|
||||
private final boolean multiple;
|
||||
private final boolean playTrack;
|
||||
private final DescriptionGenerator descriptionGenerator;
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private PrintWriter printWriter;
|
||||
private ArrayList<Float> powerList = new ArrayList<>();
|
||||
private ArrayList<Float> cadenceList = new ArrayList<>();
|
||||
private ArrayList<Float> heartRateList = new ArrayList<>();
|
||||
private boolean hasPower;
|
||||
private boolean hasCadence;
|
||||
private boolean hasHeartRate;
|
||||
private PrintWriter printWriter;
|
||||
private ArrayList<Float> powerList = new ArrayList<>();
|
||||
private ArrayList<Float> cadenceList = new ArrayList<>();
|
||||
private ArrayList<Float> heartRateList = new ArrayList<>();
|
||||
private boolean hasPower;
|
||||
private boolean hasCadence;
|
||||
private boolean hasHeartRate;
|
||||
|
||||
public KmlTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
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;
|
||||
public KmlTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
this(context, multiple, playTrack, new DescriptionGeneratorImpl(context));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeHeader(Track[] tracks) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
|
||||
printWriter.println("xmlns:gx=\"http://www.google.com/kml/ext/2.2\"");
|
||||
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\">");
|
||||
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>");
|
||||
@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 void writeFooter() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</Document>");
|
||||
printWriter.println("</kml>");
|
||||
@Override
|
||||
public String getExtension() {
|
||||
return TrackFileFormat.KML.getExtension();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginWaypoints(Track track) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<Folder><name>"
|
||||
+ StringUtils.formatCData(context.getString(R.string.track_markers, track.getName()))
|
||||
+ "</name>");
|
||||
printWriter.println("<open>1</open>");
|
||||
@Override
|
||||
public void prepare(OutputStream outputStream) {
|
||||
this.printWriter = new PrintWriter(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEndWaypoints() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</Folder>");
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
public void close() {
|
||||
if (printWriter != null) {
|
||||
printWriter.flush();
|
||||
printWriter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>");
|
||||
@Override
|
||||
public void writeHeader(Track[] tracks) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
|
||||
printWriter.println("xmlns:gx=\"http://www.google.com/kml/ext/2.2\"");
|
||||
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\">");
|
||||
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>");
|
||||
}
|
||||
}
|
||||
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>");
|
||||
@Override
|
||||
public void writeFooter() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</Document>");
|
||||
printWriter.println("</kml>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
@Override
|
||||
public void writeBeginWaypoints(Track track) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<Folder><name>"
|
||||
+ StringUtils.formatCData(context.getString(R.string.track_markers, track.getName()))
|
||||
+ "</name>");
|
||||
printWriter.println("<open>1</open>");
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
@Override
|
||||
public void writeEndWaypoints() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("</Folder>");
|
||||
}
|
||||
}
|
||||
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());
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the category.
|
||||
*
|
||||
* @param category the category
|
||||
*/
|
||||
private void writeCategory(String category) {
|
||||
if (category == null || category.equals("")) {
|
||||
return;
|
||||
@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>");
|
||||
}
|
||||
}
|
||||
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>");
|
||||
}
|
||||
@Override
|
||||
public void writeEndTracks() {
|
||||
if (printWriter != null && multiple) {
|
||||
printWriter.println("</Folder>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>");
|
||||
}
|
||||
@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>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>");
|
||||
}
|
||||
@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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>");
|
||||
}
|
||||
}
|
||||
|
||||
+131
-131
@@ -16,12 +16,6 @@
|
||||
|
||||
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.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -29,6 +23,12 @@ import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
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.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -38,148 +38,148 @@ import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* KMZ track exporter.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class KmzTrackExporter implements TrackExporter {
|
||||
|
||||
public static final String KMZ_EXTENSION = "kmz";
|
||||
public static final String KMZ_IMAGES_DIR = "images";
|
||||
public static final String KMZ_KML_FILE = "doc.kml";
|
||||
public static final String KMZ_EXTENSION = "kmz";
|
||||
public static final String KMZ_IMAGES_DIR = "images";
|
||||
public static final String KMZ_KML_FILE = "doc.kml";
|
||||
|
||||
private static final String TAG = KmzTrackExporter.class.getSimpleName();
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
private static final String TAG = KmzTrackExporter.class.getSimpleName();
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final FileTrackExporter fileTrackExporter;
|
||||
private final Track[] tracks;
|
||||
private final long photoSize;
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final FileTrackExporter fileTrackExporter;
|
||||
private final Track[] tracks;
|
||||
private final long photoSize;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param contentProviderUtils the content provider utils
|
||||
* @param fileTrackExporter the file track exporter
|
||||
* @param tracks the tracks to export
|
||||
* @param context the context
|
||||
*/
|
||||
public KmzTrackExporter(ContentProviderUtils contentProviderUtils,
|
||||
FileTrackExporter fileTrackExporter, Track[] tracks, Context context) {
|
||||
this.contentProviderUtils = contentProviderUtils;
|
||||
this.fileTrackExporter = fileTrackExporter;
|
||||
this.tracks = tracks;
|
||||
this.photoSize = PreferencesUtils.getInt(
|
||||
context, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||
}
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param contentProviderUtils the content provider utils
|
||||
* @param fileTrackExporter the file track exporter
|
||||
* @param tracks the tracks to export
|
||||
* @param context the context
|
||||
*/
|
||||
public KmzTrackExporter(ContentProviderUtils contentProviderUtils,
|
||||
FileTrackExporter fileTrackExporter, Track[] tracks, Context context) {
|
||||
this.contentProviderUtils = contentProviderUtils;
|
||||
this.fileTrackExporter = fileTrackExporter;
|
||||
this.tracks = tracks;
|
||||
this.photoSize = PreferencesUtils.getInt(
|
||||
context, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeTrack(OutputStream outputStream) {
|
||||
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) {
|
||||
@Override
|
||||
public boolean writeTrack(OutputStream outputStream) {
|
||||
ZipOutputStream zipOutputStream = null;
|
||||
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) {
|
||||
Log.e(TAG, "Unable to close zip input stream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addImages(ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
|
||||
for (Track track : tracks) {
|
||||
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();
|
||||
Log.e(TAG, "Unable to write track", e);
|
||||
return false;
|
||||
} finally {
|
||||
if (zipOutputStream != null) {
|
||||
try {
|
||||
zipOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close zip input stream", e);
|
||||
}
|
||||
}
|
||||
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(
|
||||
KMZ_IMAGES_DIR + File.separatorChar + uri.getLastPathSegment());
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
|
||||
int sampleSize;
|
||||
if (photoSize == -1) {
|
||||
sampleSize = 1;
|
||||
} else {
|
||||
long size = file.length();
|
||||
// Convert from kilobytes to bytes.
|
||||
long limit = photoSize * 1024;
|
||||
sampleSize = size > limit ? (int) Math.ceil(size / limit) : 1;
|
||||
private void addImages(ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
|
||||
for (Track track : tracks) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath(), options);
|
||||
if (bitmap == null) {
|
||||
return;
|
||||
}
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, zipOutputStream);
|
||||
bitmap.recycle();
|
||||
}
|
||||
ZipEntry zipEntry = new ZipEntry(
|
||||
KMZ_IMAGES_DIR + File.separatorChar + uri.getLastPathSegment());
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
|
||||
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);
|
||||
}
|
||||
int sampleSize;
|
||||
if (photoSize == -1) {
|
||||
sampleSize = 1;
|
||||
} else {
|
||||
long size = file.length();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
-169
@@ -16,12 +16,6 @@
|
||||
|
||||
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.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
@@ -31,197 +25,203 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
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;
|
||||
|
||||
/**
|
||||
* An activity for saving tracks to the external storage. If saving a specific
|
||||
* track, option to save it to a temp directory and play the track afterward.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class SaveActivity extends Activity {
|
||||
|
||||
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_FILE_FORMAT = "track_file_format";
|
||||
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.
|
||||
public static final String EXTRA_PLAY_TRACK = "play_track";
|
||||
@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";
|
||||
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_RESULT_ID = 1;
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_RESULT_ID = 1;
|
||||
|
||||
private TrackFileFormat trackFileFormat;
|
||||
private long[] trackIds;
|
||||
private boolean playTrack;
|
||||
private String directoryDisplayName;
|
||||
private TrackFileFormat trackFileFormat;
|
||||
private long[] trackIds;
|
||||
private boolean playTrack;
|
||||
private String directoryDisplayName;
|
||||
|
||||
private SaveAsyncTask saveAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
private SaveAsyncTask saveAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
// the number of tracks successfully saved
|
||||
private int successCount;
|
||||
// the number of tracks successfully saved
|
||||
private int successCount;
|
||||
|
||||
// the number of tracks to save
|
||||
private int totalCount;
|
||||
// the number of tracks to save
|
||||
private int totalCount;
|
||||
|
||||
// the last successfully saved path
|
||||
private String savedPath;
|
||||
// the last successfully saved path
|
||||
private String savedPath;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
||||
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
|
||||
Intent intent = getIntent();
|
||||
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
||||
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
|
||||
|
||||
if (!FileUtils.isExternalStorageWriteable()) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (!FileUtils.isExternalStorageWriteable()) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
File directory = playTrack ? new File(getCacheDir(), FileUtils.PLAY_TRACKS_DIR)
|
||||
: new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
||||
if (!FileUtils.ensureDirectoryExists(directory)) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
File directory = playTrack ? new File(getCacheDir(), FileUtils.PLAY_TRACKS_DIR)
|
||||
: new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
||||
if (!FileUtils.ensureDirectoryExists(directory)) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (playTrack) {
|
||||
for (File file : directory.listFiles()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
if (playTrack) {
|
||||
for (File file : directory.listFiles()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
directoryDisplayName = playTrack ? directory.getName()
|
||||
: FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||
directoryDisplayName = playTrack ? directory.getName()
|
||||
: FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof SaveAsyncTask) {
|
||||
saveAsyncTask = (SaveAsyncTask) retained;
|
||||
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);
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof SaveAsyncTask) {
|
||||
saveAsyncTask = (SaveAsyncTask) retained;
|
||||
saveAsyncTask.setActivity(this);
|
||||
} 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);
|
||||
saveAsyncTask = new SaveAsyncTask(this, trackIds, trackFileFormat, playTrack, directory);
|
||||
saveAsyncTask.execute();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@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 {
|
||||
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.
|
||||
*
|
||||
* @param aSuccessCount the number of tracks successfully saved
|
||||
* @param aTotalCount the number of tracks to save
|
||||
* @param aSavedPath the last successfully saved path
|
||||
*/
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, String aSavedPath) {
|
||||
successCount = aSuccessCount;
|
||||
totalCount = aTotalCount;
|
||||
savedPath = aSavedPath;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress dialog value.
|
||||
*
|
||||
* @param number the number of points saved
|
||||
* @param max the maximum number of points
|
||||
*/
|
||||
public void setProgressDialogValue(int number, int max) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.setIndeterminate(false);
|
||||
progressDialog.setMax(max);
|
||||
progressDialog.setProgress(Math.min(number, max));
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param aSuccessCount the number of tracks successfully saved
|
||||
* @param aTotalCount the number of tracks to save
|
||||
* @param aSavedPath the last successfully saved path
|
||||
*/
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, String aSavedPath) {
|
||||
successCount = aSuccessCount;
|
||||
totalCount = aTotalCount;
|
||||
savedPath = aSavedPath;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress dialog value.
|
||||
*
|
||||
* @param number the number of points saved
|
||||
* @param max the maximum number of points
|
||||
*/
|
||||
public void setProgressDialogValue(int number, int max) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.setIndeterminate(false);
|
||||
progressDialog.setMax(max);
|
||||
progressDialog.setProgress(Math.min(number, max));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+194
-196
@@ -38,220 +38,218 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Async Task to save tracks to the external storage.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
private WakeLock wakeLock;
|
||||
// the number of tracks successfully saved
|
||||
private int successCount;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
// the number of tracks to save
|
||||
private int totalCount;
|
||||
|
||||
// the number of tracks successfully saved
|
||||
private int successCount;
|
||||
// the last successfully saved path
|
||||
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
|
||||
private String savedPath;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
completed = false;
|
||||
successCount = 0;
|
||||
totalCount = 0;
|
||||
savedPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (saveActivity != null) {
|
||||
saveActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
if (saveTracks(tracks)) {
|
||||
successCount = 1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
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];
|
||||
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) {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
fileOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close file output stream", e);
|
||||
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;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@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];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Track exporting for exporting track to an {@link OutputStream}.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface TrackExporter {
|
||||
|
||||
/**
|
||||
* Write track to an output stream
|
||||
*
|
||||
* @param outputStream the output stream
|
||||
*/
|
||||
boolean writeTrack(OutputStream outputStream);
|
||||
|
||||
/**
|
||||
* Write track to an output stream
|
||||
*
|
||||
* @param outputStream the output stream
|
||||
*/
|
||||
boolean writeTrack(OutputStream outputStream);
|
||||
}
|
||||
|
||||
+9
-9
@@ -17,17 +17,17 @@ package com.google.android.apps.mytracks.io.file.exporter;
|
||||
|
||||
/**
|
||||
* Listener for {@link TrackExporter} progress.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface TrackExporterListener {
|
||||
|
||||
/**
|
||||
* Called to update progress.
|
||||
*
|
||||
* @param number the number of locations written
|
||||
* @param max the maximum number of locations in a track, for calculation of
|
||||
* completion percentage
|
||||
*/
|
||||
void onProgressUpdate(int number, int max);
|
||||
/**
|
||||
* Called to update progress.
|
||||
*
|
||||
* @param number the number of locations written
|
||||
* @param max the maximum number of locations in a track, for calculation of
|
||||
* completion percentage
|
||||
*/
|
||||
void onProgressUpdate(int number, int max);
|
||||
}
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
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.Waypoint;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Interface for writing tracks to a file. The expected sequence of calls is:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* {@link #prepare(OutputStream)}
|
||||
* {@link #writeHeader(Track[])}
|
||||
@@ -46,99 +46,99 @@ import java.io.OutputStream;
|
||||
* {@link #writeFooter()}
|
||||
* {@link #close()}
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public interface TrackWriter {
|
||||
|
||||
/**
|
||||
* Gets the file extension (e.g, gpx, kml, ...).
|
||||
*/
|
||||
String getExtension();
|
||||
/**
|
||||
* Gets the file extension (e.g, gpx, kml, ...).
|
||||
*/
|
||||
String getExtension();
|
||||
|
||||
/**
|
||||
* Prepares the output stream.
|
||||
*
|
||||
* @param outputStream the output stream
|
||||
*/
|
||||
void prepare(OutputStream outputStream);
|
||||
/**
|
||||
* Prepares the output stream.
|
||||
*
|
||||
* @param outputStream the output stream
|
||||
*/
|
||||
void prepare(OutputStream outputStream);
|
||||
|
||||
/**
|
||||
* Closes the output stream.
|
||||
*/
|
||||
void close();
|
||||
/**
|
||||
* Closes the output stream.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Writes the header
|
||||
*
|
||||
* @param tracks the tracks
|
||||
*/
|
||||
void writeHeader(Track[] tracks);
|
||||
/**
|
||||
* Writes the header
|
||||
*
|
||||
* @param tracks the tracks
|
||||
*/
|
||||
void writeHeader(Track[] tracks);
|
||||
|
||||
/**
|
||||
* Writes the footer.
|
||||
*/
|
||||
void writeFooter();
|
||||
/**
|
||||
* Writes the footer.
|
||||
*/
|
||||
void writeFooter();
|
||||
|
||||
/**
|
||||
* Writes the beginning of the waypoints.
|
||||
*
|
||||
* @param track the track
|
||||
*/
|
||||
void writeBeginWaypoints(Track track);
|
||||
/**
|
||||
* Writes the beginning of the waypoints.
|
||||
*
|
||||
* @param track the track
|
||||
*/
|
||||
void writeBeginWaypoints(Track track);
|
||||
|
||||
/**
|
||||
* Writes the end of the waypoints.
|
||||
*/
|
||||
void writeEndWaypoints();
|
||||
/**
|
||||
* Writes the end of the waypoints.
|
||||
*/
|
||||
void writeEndWaypoints();
|
||||
|
||||
/**
|
||||
* Writes a waypoint.
|
||||
*
|
||||
* @param waypoint the waypoint
|
||||
*/
|
||||
void writeWaypoint(Waypoint waypoint);
|
||||
/**
|
||||
* Writes a waypoint.
|
||||
*
|
||||
* @param waypoint the waypoint
|
||||
*/
|
||||
void writeWaypoint(Waypoint waypoint);
|
||||
|
||||
/**
|
||||
* Writes the beginning of the tracks.
|
||||
*/
|
||||
void writeBeginTracks();
|
||||
/**
|
||||
* Writes the beginning of the tracks.
|
||||
*/
|
||||
void writeBeginTracks();
|
||||
|
||||
/**
|
||||
* Writes the end of the tracks,
|
||||
*/
|
||||
void writeEndTracks();
|
||||
/**
|
||||
* Writes the end of the tracks,
|
||||
*/
|
||||
void writeEndTracks();
|
||||
|
||||
/**
|
||||
* Writes the beginning of a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @param startLocation the start location
|
||||
*/
|
||||
void writeBeginTrack(Track track, Location startLocation);
|
||||
/**
|
||||
* Writes the beginning of a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @param startLocation the start location
|
||||
*/
|
||||
void writeBeginTrack(Track track, Location startLocation);
|
||||
|
||||
/**
|
||||
* Writes the end of a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @param endLocation the end location
|
||||
*/
|
||||
void writeEndTrack(Track track, Location endLocation);
|
||||
/**
|
||||
* Writes the end of a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @param endLocation the end location
|
||||
*/
|
||||
void writeEndTrack(Track track, Location endLocation);
|
||||
|
||||
/**
|
||||
* Writes open segment.
|
||||
*/
|
||||
void writeOpenSegment();
|
||||
/**
|
||||
* Writes open segment.
|
||||
*/
|
||||
void writeOpenSegment();
|
||||
|
||||
/**
|
||||
* Writes close segment.
|
||||
*/
|
||||
void writeCloseSegment();
|
||||
/**
|
||||
* Writes close segment.
|
||||
*/
|
||||
void writeCloseSegment();
|
||||
|
||||
/**
|
||||
* Writes a location.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void writeLocation(Location location);
|
||||
/**
|
||||
* Writes a location.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
void writeLocation(Location location);
|
||||
}
|
||||
+508
-515
File diff suppressed because it is too large
Load Diff
+151
-150
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.io.file.importer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.google.android.apps.mytracks.content.ContentProviderUtils;
|
||||
@@ -28,163 +29,163 @@ import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Imports a GPX file.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
|
||||
private static final String TAG_DESCRIPTION = "desc";
|
||||
private static final String TAG_COMMENT = "cmt";
|
||||
private static final String TAG_ELEVATION = "ele";
|
||||
private static final String TAG_GPX = "gpx";
|
||||
private static final String TAG_NAME = "name";
|
||||
private static final String TAG_TIME = "time";
|
||||
private static final String TAG_TRACK = "trk";
|
||||
private static final String TAG_TRACK_POINT = "trkpt";
|
||||
private static final String TAG_TRACK_SEGMENT = "trkseg";
|
||||
private static final String TAG_TYPE = "type";
|
||||
private static final String TAG_WAYPOINT = "wpt";
|
||||
private static final String TAG_DESCRIPTION = "desc";
|
||||
private static final String TAG_COMMENT = "cmt";
|
||||
private static final String TAG_ELEVATION = "ele";
|
||||
private static final String TAG_GPX = "gpx";
|
||||
private static final String TAG_NAME = "name";
|
||||
private static final String TAG_TIME = "time";
|
||||
private static final String TAG_TRACK = "trk";
|
||||
private static final String TAG_TRACK_POINT = "trkpt";
|
||||
private static final String TAG_TRACK_SEGMENT = "trkseg";
|
||||
private static final String TAG_TYPE = "type";
|
||||
private static final String TAG_WAYPOINT = "wpt";
|
||||
|
||||
private static final String ATTRIBUTE_LAT = "lat";
|
||||
private static final String ATTRIBUTE_LON = "lon";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public GpxFileTrackImporter(Context context) {
|
||||
this(context, ContentProviderUtils.Factory.get(context));
|
||||
}
|
||||
private static final String ATTRIBUTE_LAT = "lat";
|
||||
private static final String ATTRIBUTE_LON = "lon";
|
||||
|
||||
@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;
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public GpxFileTrackImporter(Context context) {
|
||||
this(context, ContentProviderUtils.Factory.get(context));
|
||||
}
|
||||
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;
|
||||
}
|
||||
@VisibleForTesting
|
||||
GpxFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) {
|
||||
super(context, -1L, contentProviderUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* On waypoint end.
|
||||
*/
|
||||
private void onWaypointEnd() throws SAXException {
|
||||
addWaypoint(WaypointType.STATISTICS.name().equals(waypointType) ? WaypointType.STATISTICS
|
||||
: WaypointType.WAYPOINT);
|
||||
}
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
+175
-175
@@ -41,199 +41,199 @@ import java.io.File;
|
||||
/**
|
||||
* An activity to import files from the external storage. Optionally to import
|
||||
* one specific file.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ImportActivity extends Activity {
|
||||
|
||||
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_IMPORT_ALL = "import_all";
|
||||
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
||||
|
||||
private static final String TAG = ImportActivity.class.getSimpleName();
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_RESULT_ID = 1;
|
||||
private static final String TAG = ImportActivity.class.getSimpleName();
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_RESULT_ID = 1;
|
||||
|
||||
private ImportAsyncTask importAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
private ImportAsyncTask importAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
private boolean importAll;
|
||||
private boolean importAll;
|
||||
|
||||
// the path on the external storage to import
|
||||
private String directoryDisplayName;
|
||||
// the path on the external storage to import
|
||||
private String directoryDisplayName;
|
||||
|
||||
// the number of files successfully imported
|
||||
private int successCount;
|
||||
// the number of files successfully imported
|
||||
private int successCount;
|
||||
|
||||
// the number of files to import
|
||||
private int totalCount;
|
||||
// the number of files to import
|
||||
private int totalCount;
|
||||
|
||||
// the last successfully imported track id
|
||||
private long trackId;
|
||||
// the last successfully imported track id
|
||||
private long trackId;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
|
||||
TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
if (trackFileFormat == null) {
|
||||
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);
|
||||
Intent intent = getIntent();
|
||||
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
|
||||
TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
if (trackFileFormat == null) {
|
||||
trackFileFormat = TrackFileFormat.GPX;
|
||||
}
|
||||
final Dialog dialog = new AlertDialog.Builder(this).setCancelable(true).setIcon(iconId)
|
||||
.setMessage(message).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialogInterface) {
|
||||
dialogInterface.dismiss();
|
||||
|
||||
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();
|
||||
}
|
||||
}).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();
|
||||
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);
|
||||
}
|
||||
dialogInterface.dismiss();
|
||||
finish();
|
||||
}
|
||||
}).setTitle(titleId).create();
|
||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
final Dialog dialog = new AlertDialog.Builder(this).setCancelable(true).setIcon(iconId)
|
||||
.setMessage(message).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialogInterface) {
|
||||
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
|
||||
public void onShow(DialogInterface dialogInterface) {
|
||||
DialogUtils.setDialogTitleDivider(ImportActivity.this, dialog);
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
default:
|
||||
return null;
|
||||
@Override
|
||||
public void onShow(DialogInterface dialogInterface) {
|
||||
DialogUtils.setDialogTitleDivider(ImportActivity.this, dialog);
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param aSuccessCount the number of files successfully imported
|
||||
* @param aTotalCount the number of files to import
|
||||
* @param aTrackId the last successfully imported track id
|
||||
*/
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, long aTrackId) {
|
||||
successCount = aSuccessCount;
|
||||
totalCount = aTotalCount;
|
||||
trackId = aTrackId;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress dialog value.
|
||||
*
|
||||
* @param number the number of files imported
|
||||
* @param max the maximum number of files
|
||||
*/
|
||||
public void setProgressDialogValue(int number, int max) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.setIndeterminate(false);
|
||||
progressDialog.setMax(max);
|
||||
progressDialog.setProgress(Math.min(number, max));
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param aSuccessCount the number of files successfully imported
|
||||
* @param aTotalCount the number of files to import
|
||||
* @param aTrackId the last successfully imported track id
|
||||
*/
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, long aTrackId) {
|
||||
successCount = aSuccessCount;
|
||||
totalCount = aTotalCount;
|
||||
trackId = aTrackId;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress dialog value.
|
||||
*
|
||||
* @param number the number of files imported
|
||||
* @param max the maximum number of files
|
||||
*/
|
||||
public void setProgressDialogValue(int number, int max) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.setIndeterminate(false);
|
||||
progressDialog.setMax(max);
|
||||
progressDialog.setProgress(Math.min(number, max));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+183
-184
@@ -16,6 +16,12 @@
|
||||
|
||||
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.Track;
|
||||
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.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.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -40,202 +40,201 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* AsyncTask to import files from the external storage.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
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;
|
||||
private final boolean importAll;
|
||||
private final TrackFileFormat trackFileFormat;
|
||||
private final String path;
|
||||
private final Context context;
|
||||
private WakeLock wakeLock;
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
// the number of files successfully imported
|
||||
private int successCount;
|
||||
|
||||
// the number of files successfully imported
|
||||
private int successCount;
|
||||
// the number of files to import
|
||||
private int totalCount;
|
||||
|
||||
// the number of files to import
|
||||
private int totalCount;
|
||||
// the last successfully imported track id
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
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);
|
||||
completed = false;
|
||||
successCount = 0;
|
||||
totalCount = 0;
|
||||
trackId = -1L;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (importActivity != null) {
|
||||
importActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
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;
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
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
|
||||
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) {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
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);
|
||||
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++;
|
||||
}
|
||||
publishProgress(i + 1, totalCount);
|
||||
}
|
||||
return true;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
+245
-245
@@ -22,8 +22,8 @@ import android.net.Uri;
|
||||
|
||||
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.SensorDataSetLocation;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
|
||||
|
||||
@@ -34,278 +34,278 @@ import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Imports a KML file.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
|
||||
private static final String CADENCE = "cadence";
|
||||
private static final String HEART_RATE = "heart_rate";
|
||||
private static final String POWER = "power";
|
||||
private static final String CADENCE = "cadence";
|
||||
private static final String HEART_RATE = "heart_rate";
|
||||
private static final String POWER = "power";
|
||||
|
||||
private static final String STATISTICS_STYLE = "#statistics";
|
||||
private static final String WAYPOINT_STYLE = "#waypoint";
|
||||
private static final String STATISTICS_STYLE = "#statistics";
|
||||
private static final String WAYPOINT_STYLE = "#waypoint";
|
||||
|
||||
private static final String TAG_COORDINATES = "coordinates";
|
||||
private static final String TAG_DESCRIPTION = "description";
|
||||
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_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
||||
private static final String TAG_GX_TRACK = "gx:Track";
|
||||
private static final String TAG_GX_VALUE = "gx:value";
|
||||
private static final String TAG_HREF = "href";
|
||||
private static final String TAG_KML = "kml";
|
||||
private static final String TAG_NAME = "name";
|
||||
private static final String TAG_PHOTO_OVERLAY = "PhotoOverlay";
|
||||
private static final String TAG_PLACEMARK = "Placemark";
|
||||
private static final String TAG_STYLE_URL = "styleUrl";
|
||||
private static final String TAG_VALUE = "value";
|
||||
private static final String TAG_WHEN = "when";
|
||||
private static final String TAG_COORDINATES = "coordinates";
|
||||
private static final String TAG_DESCRIPTION = "description";
|
||||
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_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
||||
private static final String TAG_GX_TRACK = "gx:Track";
|
||||
private static final String TAG_GX_VALUE = "gx:value";
|
||||
private static final String TAG_HREF = "href";
|
||||
private static final String TAG_KML = "kml";
|
||||
private static final String TAG_NAME = "name";
|
||||
private static final String TAG_PHOTO_OVERLAY = "PhotoOverlay";
|
||||
private static final String TAG_PLACEMARK = "Placemark";
|
||||
private static final String TAG_STYLE_URL = "styleUrl";
|
||||
private static final String TAG_VALUE = "value";
|
||||
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 String sensorName;
|
||||
private ArrayList<Location> locationList;
|
||||
private ArrayList<Integer> cadenceList;
|
||||
private ArrayList<Integer> heartRateList;
|
||||
private ArrayList<Integer> powerList;
|
||||
private boolean trackStarted = false;
|
||||
private String sensorName;
|
||||
private ArrayList<Location> locationList;
|
||||
private ArrayList<Integer> cadenceList;
|
||||
private ArrayList<Integer> heartRateList;
|
||||
private ArrayList<Integer> powerList;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
* @param importTrackId track id to import to. -1L to import to a new track.
|
||||
*/
|
||||
public KmlFileTrackImporter(Context context, long importTrackId) {
|
||||
this(context, importTrackId, ContentProviderUtils.Factory.get(context));
|
||||
}
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
* @param importTrackId track id to import to. -1L to import to a new track.
|
||||
*/
|
||||
public KmlFileTrackImporter(Context context, long importTrackId) {
|
||||
this(context, importTrackId, ContentProviderUtils.Factory.get(context));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
KmlFileTrackImporter(
|
||||
Context context, long importTrackId, ContentProviderUtils contentProviderUtils) {
|
||||
super(context, importTrackId, contentProviderUtils);
|
||||
}
|
||||
@VisibleForTesting
|
||||
KmlFileTrackImporter(
|
||||
Context context, long importTrackId, ContentProviderUtils contentProviderUtils) {
|
||||
super(context, importTrackId, contentProviderUtils);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String tag, Attributes attributes)
|
||||
throws SAXException {
|
||||
switch (tag) {
|
||||
case TAG_PLACEMARK:
|
||||
case TAG_PHOTO_OVERLAY:
|
||||
/*
|
||||
* Note that a track is contained in a Placemark, calling onWaypointStart
|
||||
* will clear various track variables like name, category, and
|
||||
* description.
|
||||
*/
|
||||
onWaypointStart();
|
||||
break;
|
||||
case TAG_GX_MULTI_TRACK:
|
||||
trackStarted = true;
|
||||
onTrackStart();
|
||||
break;
|
||||
case TAG_GX_TRACK:
|
||||
if (!trackStarted) {
|
||||
throw new SAXException("No " + TAG_GX_MULTI_TRACK);
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String tag, Attributes attributes)
|
||||
throws SAXException {
|
||||
switch (tag) {
|
||||
case TAG_PLACEMARK:
|
||||
case TAG_PHOTO_OVERLAY:
|
||||
/*
|
||||
* Note that a track is contained in a Placemark, calling onWaypointStart
|
||||
* will clear various track variables like name, category, and
|
||||
* description.
|
||||
*/
|
||||
onWaypointStart();
|
||||
break;
|
||||
case TAG_GX_MULTI_TRACK:
|
||||
trackStarted = true;
|
||||
onTrackStart();
|
||||
break;
|
||||
case TAG_GX_TRACK:
|
||||
if (!trackStarted) {
|
||||
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
|
||||
content = null;
|
||||
}
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On waypoint start.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
// Reset element content
|
||||
content = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* On waypoint end.
|
||||
*/
|
||||
private void onWaypointEnd() throws SAXException {
|
||||
// Add a waypoint if the waypointType matches
|
||||
WaypointType type = null;
|
||||
if (WAYPOINT_STYLE.equals(waypointType)) {
|
||||
type = WaypointType.WAYPOINT;
|
||||
} else if (STATISTICS_STYLE.equals(waypointType)) {
|
||||
type = WaypointType.STATISTICS;
|
||||
/**
|
||||
* On waypoint start.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
if (photoUrl != null) {
|
||||
Uri uri = Uri.parse(photoUrl);
|
||||
photoUrl = getPhotoUrl(uri.getLastPathSegment());
|
||||
}
|
||||
addWaypoint(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* On waypoint location end.
|
||||
*/
|
||||
private void onWaypointLocationEnd() {
|
||||
if (content != null) {
|
||||
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;
|
||||
/**
|
||||
* On waypoint end.
|
||||
*/
|
||||
private void onWaypointEnd() throws SAXException {
|
||||
// Add a waypoint if the waypointType matches
|
||||
WaypointType type = null;
|
||||
if (WAYPOINT_STYLE.equals(waypointType)) {
|
||||
type = WaypointType.WAYPOINT;
|
||||
} else if (STATISTICS_STYLE.equals(waypointType)) {
|
||||
type = WaypointType.STATISTICS;
|
||||
}
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
if (photoUrl != null) {
|
||||
Uri uri = Uri.parse(photoUrl);
|
||||
photoUrl = getPhotoUrl(uri.getLastPathSegment());
|
||||
}
|
||||
addWaypoint(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTrackSegmentStart() {
|
||||
super.onTrackSegmentStart();
|
||||
locationList = new ArrayList<>();
|
||||
heartRateList = new ArrayList<>();
|
||||
cadenceList = new ArrayList<>();
|
||||
powerList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* On track segment end.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* On waypoint location end.
|
||||
*/
|
||||
private void onWaypointLocationEnd() {
|
||||
if (content != null) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On track point end. gx:coord end tag.
|
||||
*/
|
||||
private void onTrackPointEnd() throws SAXException {
|
||||
// Add location to locationList
|
||||
if (content == null) {
|
||||
return;
|
||||
@Override
|
||||
protected void onTrackSegmentStart() {
|
||||
super.onTrackSegmentStart();
|
||||
locationList = new ArrayList<>();
|
||||
heartRateList = new ArrayList<>();
|
||||
cadenceList = new ArrayList<>();
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
locationList.add(location);
|
||||
time = null;
|
||||
}
|
||||
/**
|
||||
* On track segment end.
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* On sensor data start. gx:SimpleArrayData start tag.
|
||||
*/
|
||||
private void onSensorDataStart(Attributes attributes) {
|
||||
sensorName = attributes.getValue(ATTRIBUTE_NAME);
|
||||
}
|
||||
for (int i = 0; i < locationList.size(); i++) {
|
||||
Location location = locationList.get(i);
|
||||
|
||||
/**
|
||||
* On sensor value end. gx:value end tag.
|
||||
*/
|
||||
private void onSensorValueEnd() throws SAXException {
|
||||
if (content == null) {
|
||||
return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
|
||||
|
||||
/**
|
||||
* On sensor data start. gx:SimpleArrayData start tag.
|
||||
*/
|
||||
private void onSensorDataStart(Attributes attributes) {
|
||||
sensorName = attributes.getValue(ATTRIBUTE_NAME);
|
||||
}
|
||||
if (POWER.equals(sensorName)) {
|
||||
powerList.add(value);
|
||||
} else if (HEART_RATE.equals(sensorName)) {
|
||||
heartRateList.add(value);
|
||||
} else if (CADENCE.equals(sensorName)) {
|
||||
cadenceList.add(value);
|
||||
|
||||
/**
|
||||
* On sensor value end. gx:value end tag.
|
||||
*/
|
||||
private void onSensorValueEnd() throws SAXException {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+145
-145
@@ -16,14 +16,14 @@
|
||||
|
||||
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.net.Uri;
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -35,164 +35,164 @@ import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* Imports a KMZ file.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class KmzTrackImporter implements TrackImporter {
|
||||
|
||||
private static final String TAG = KmzTrackImporter.class.getSimpleName();
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
private static final String TAG = KmzTrackImporter.class.getSimpleName();
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private final Context context;
|
||||
private final long importTrackId;
|
||||
private final Context context;
|
||||
private final long importTrackId;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
* @param importTrackId track id to import to. This should not be -1L so that
|
||||
* images in the kmz file can be imported.
|
||||
*/
|
||||
public KmzTrackImporter(Context context, long importTrackId) {
|
||||
this.context = context;
|
||||
this.importTrackId = importTrackId;
|
||||
}
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context the context
|
||||
* @param importTrackId track id to import to. This should not be -1L so that
|
||||
* images in the kmz file can be imported.
|
||||
*/
|
||||
public KmzTrackImporter(Context context, long importTrackId) {
|
||||
this.context = context;
|
||||
this.importTrackId = importTrackId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long importFile(InputStream inputStream) {
|
||||
ZipInputStream zipInputStream = null;
|
||||
long trackId = importTrackId;
|
||||
try {
|
||||
ZipEntry zipEntry;
|
||||
@Override
|
||||
public long importFile(InputStream inputStream) {
|
||||
ZipInputStream zipInputStream = null;
|
||||
long trackId = importTrackId;
|
||||
try {
|
||||
ZipEntry zipEntry;
|
||||
|
||||
zipInputStream = new ZipInputStream(inputStream);
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if (Thread.interrupted()) {
|
||||
Log.d(TAG, "Thread interrupted");
|
||||
cleanImport(trackId);
|
||||
return -1L;
|
||||
}
|
||||
String fileName = zipEntry.getName();
|
||||
if (fileName.equals(KmzTrackExporter.KMZ_KML_FILE)) {
|
||||
trackId = parseKml(zipInputStream);
|
||||
if (trackId == -1L) {
|
||||
Log.d(TAG, "Unable to parse kml in kmz");
|
||||
zipInputStream = new ZipInputStream(inputStream);
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if (Thread.interrupted()) {
|
||||
Log.d(TAG, "Thread interrupted");
|
||||
cleanImport(trackId);
|
||||
return -1L;
|
||||
}
|
||||
String fileName = zipEntry.getName();
|
||||
if (fileName.equals(KmzTrackExporter.KMZ_KML_FILE)) {
|
||||
trackId = parseKml(zipInputStream);
|
||||
if (trackId == -1L) {
|
||||
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);
|
||||
return -1L;
|
||||
}
|
||||
} else {
|
||||
String prefix = KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar;
|
||||
if (fileName.startsWith(prefix)) {
|
||||
readImageFile(zipInputStream, fileName.substring(prefix.length()));
|
||||
}
|
||||
} finally {
|
||||
if (zipInputStream != null) {
|
||||
try {
|
||||
zipInputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close zip input stream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
zipInputStream.closeEntry();
|
||||
}
|
||||
return trackId;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to import file", e);
|
||||
cleanImport(trackId);
|
||||
return -1L;
|
||||
} finally {
|
||||
if (zipInputStream != null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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 {
|
||||
zipInputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close zip input stream", e);
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, importTrackId);
|
||||
byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream));
|
||||
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);
|
||||
if (FileUtils.isDirectory(dir)) {
|
||||
for (File file : dir.listFiles()) {
|
||||
file.delete();
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
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 {
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, importTrackId);
|
||||
byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream));
|
||||
return kmlFileTrackImporter.importFile(byteArrayInputStream);
|
||||
} finally {
|
||||
if (byteArrayInputStream != null) {
|
||||
byteArrayInputStream.close();
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,16 +19,16 @@ import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Interface for a track importer.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface TrackImporter {
|
||||
|
||||
/**
|
||||
* Import a file.
|
||||
*
|
||||
* @param inputStream the file's input stream
|
||||
* @return the imported track id or -1L.
|
||||
*/
|
||||
long importFile(InputStream inputStream);
|
||||
/**
|
||||
* Import a file.
|
||||
*
|
||||
* @param inputStream the file's input stream
|
||||
* @return the imported track id or -1L.
|
||||
*/
|
||||
long importFile(InputStream inputStream);
|
||||
}
|
||||
|
||||
+25
-25
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -19,34 +19,34 @@ package com.google.android.apps.mytracks.services;
|
||||
/**
|
||||
* This is a simple location listener policy that will always dictate the same
|
||||
* polling interval.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class AbsoluteLocationListenerPolicy implements LocationListenerPolicy {
|
||||
|
||||
private final long interval;
|
||||
private final long interval;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param interval the interval to request for gps signal
|
||||
*/
|
||||
public AbsoluteLocationListenerPolicy(long interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param interval the interval to request for gps signal
|
||||
*/
|
||||
public AbsoluteLocationListenerPolicy(long interval) {
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDesiredPollingInterval() {
|
||||
return interval;
|
||||
}
|
||||
@Override
|
||||
public long getDesiredPollingInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinDistance() {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getMinDistance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIdleTime(long idleTime) {
|
||||
// Ignore
|
||||
}
|
||||
@Override
|
||||
public void updateIdleTime(long idleTime) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
+43
-43
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -21,53 +21,53 @@ package com.google.android.apps.mytracks.services;
|
||||
* has been stationary. This policy will dictate a policy based on a min, max
|
||||
* and idle time. The policy will dictate an interval bounded by min and max,
|
||||
* and is half of the idle time.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class AdaptiveLocationListenerPolicy implements LocationListenerPolicy {
|
||||
|
||||
private final long minInterval;
|
||||
private final long maxInterval;
|
||||
private final int minDistance;
|
||||
private final long minInterval;
|
||||
private final long maxInterval;
|
||||
private final int minDistance;
|
||||
|
||||
// The time the user has been idle at the current location, in milliseconds.
|
||||
private long idleTime;
|
||||
// The time the user has been idle at the current location, in milliseconds.
|
||||
private long idleTime;
|
||||
|
||||
/**
|
||||
* Creates a policy that will be bounded by the given minInterval and
|
||||
* maxInterval.
|
||||
*
|
||||
* @param minInterval the smallest interval this policy will dictate, in
|
||||
* milliseconds
|
||||
* @param maxInterval the largest interval this policy will dictate, in
|
||||
* milliseconds
|
||||
* @param minDistance the minimum distance in meters
|
||||
*/
|
||||
public AdaptiveLocationListenerPolicy(long minInterval, long maxInterval, int minDistance) {
|
||||
this.minInterval = minInterval;
|
||||
this.maxInterval = maxInterval;
|
||||
this.minDistance = minDistance;
|
||||
}
|
||||
/**
|
||||
* Creates a policy that will be bounded by the given minInterval and
|
||||
* maxInterval.
|
||||
*
|
||||
* @param minInterval the smallest interval this policy will dictate, in
|
||||
* milliseconds
|
||||
* @param maxInterval the largest interval this policy will dictate, in
|
||||
* milliseconds
|
||||
* @param minDistance the minimum distance in meters
|
||||
*/
|
||||
public AdaptiveLocationListenerPolicy(long minInterval, long maxInterval, int minDistance) {
|
||||
this.minInterval = minInterval;
|
||||
this.maxInterval = maxInterval;
|
||||
this.minDistance = minDistance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an interval half of the idle time, but bounded by minInteval and
|
||||
* maxInterval.
|
||||
*/
|
||||
@Override
|
||||
public long getDesiredPollingInterval() {
|
||||
long desiredInterval = idleTime / 2;
|
||||
// Round to second to avoid setting the interval too often
|
||||
desiredInterval = (desiredInterval / 1000) * 1000;
|
||||
return Math.max(Math.min(maxInterval, desiredInterval), minInterval);
|
||||
}
|
||||
/*
|
||||
* Returns an interval half of the idle time, but bounded by minInteval and
|
||||
* maxInterval.
|
||||
*/
|
||||
@Override
|
||||
public long getDesiredPollingInterval() {
|
||||
long desiredInterval = idleTime / 2;
|
||||
// Round to second to avoid setting the interval too often
|
||||
desiredInterval = (desiredInterval / 1000) * 1000;
|
||||
return Math.max(Math.min(maxInterval, desiredInterval), minInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinDistance() {
|
||||
return minDistance;
|
||||
}
|
||||
@Override
|
||||
public int getMinDistance() {
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIdleTime(long newIdleTime) {
|
||||
idleTime = newIdleTime;
|
||||
}
|
||||
@Override
|
||||
public void updateIdleTime(long newIdleTime) {
|
||||
idleTime = newIdleTime;
|
||||
}
|
||||
}
|
||||
|
||||
+83
-83
@@ -39,95 +39,95 @@ import com.google.android.maps.mytracks.R;
|
||||
*/
|
||||
public class ControlRecordingService extends IntentService implements ServiceConnection {
|
||||
|
||||
private static final String TAG = ControlRecordingService.class.getSimpleName();
|
||||
|
||||
private ITrackRecordingService trackRecordingService;
|
||||
private boolean connected = false;
|
||||
private static final String TAG = ControlRecordingService.class.getSimpleName();
|
||||
|
||||
public ControlRecordingService() {
|
||||
super(ControlRecordingService.class.getSimpleName());
|
||||
}
|
||||
private ITrackRecordingService trackRecordingService;
|
||||
private boolean connected = false;
|
||||
|
||||
@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
|
||||
}
|
||||
public ControlRecordingService() {
|
||||
super(ControlRecordingService.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
waitConnected();
|
||||
onHandleIntent(intent, trackRecordingService);
|
||||
}
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
Intent newIntent = new Intent(this, TrackRecordingService.class);
|
||||
startService(newIntent);
|
||||
bindService(newIntent, this, 0);
|
||||
}
|
||||
unbindService(this);
|
||||
connected = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (connected) {
|
||||
unbindService(this);
|
||||
connected = false;
|
||||
@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
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Starts gps.
|
||||
*/
|
||||
void startGps();
|
||||
/**
|
||||
* Starts gps.
|
||||
*/
|
||||
void startGps();
|
||||
|
||||
/**
|
||||
* Stops gps.
|
||||
*/
|
||||
void stopGps();
|
||||
/**
|
||||
* Stops gps.
|
||||
*/
|
||||
void stopGps();
|
||||
|
||||
/**
|
||||
* Starts recording a new track.
|
||||
*
|
||||
* @return the track ID of the new track.
|
||||
*/
|
||||
long startNewTrack();
|
||||
/**
|
||||
* Starts recording a new track.
|
||||
*
|
||||
* @return the track ID of the new track.
|
||||
*/
|
||||
long startNewTrack();
|
||||
|
||||
/**
|
||||
* Pauses the current recording track.
|
||||
*/
|
||||
void pauseCurrentTrack();
|
||||
/**
|
||||
* Pauses the current recording track.
|
||||
*/
|
||||
void pauseCurrentTrack();
|
||||
|
||||
/**
|
||||
* Resumes the current recording track.
|
||||
*/
|
||||
void resumeCurrentTrack();
|
||||
/**
|
||||
* Resumes the current recording track.
|
||||
*/
|
||||
void resumeCurrentTrack();
|
||||
|
||||
/**
|
||||
* Ends the current recording track.
|
||||
*/
|
||||
void endCurrentTrack();
|
||||
/**
|
||||
* Ends the current recording track.
|
||||
*/
|
||||
void endCurrentTrack();
|
||||
|
||||
/**
|
||||
* Returns true if currently recording a track.
|
||||
*/
|
||||
boolean isRecording();
|
||||
/**
|
||||
* Returns true if currently recording a track.
|
||||
*/
|
||||
boolean isRecording();
|
||||
|
||||
/**
|
||||
* Returns true if the current recording track is paused. Returns true if not recording.
|
||||
*/
|
||||
boolean isPaused();
|
||||
/**
|
||||
* Returns true if the current recording track is paused. Returns true if not recording.
|
||||
*/
|
||||
boolean isPaused();
|
||||
|
||||
/**
|
||||
* Gets the current recording track ID. Returns -1 if not recording.
|
||||
*/
|
||||
long getRecordingTrackId();
|
||||
/**
|
||||
* Gets the current recording track ID. Returns -1 if not recording.
|
||||
*/
|
||||
long getRecordingTrackId();
|
||||
|
||||
/**
|
||||
* Gets the total time for the current recording track. Returns 0 if not recording.
|
||||
*/
|
||||
long getTotalTime();
|
||||
/**
|
||||
* Gets the total time for the current recording track. Returns 0 if not recording.
|
||||
*/
|
||||
long getTotalTime();
|
||||
|
||||
/**
|
||||
* Inserts a waypoint in the current recording track.
|
||||
*
|
||||
* @param request the details of the waypoint to be inserted
|
||||
* @return the ID of the inserted waypoint
|
||||
*/
|
||||
long insertWaypoint(WaypointCreationRequest request);
|
||||
/**
|
||||
* Inserts a waypoint in the current recording track.
|
||||
*
|
||||
* @param request the details of the waypoint to be inserted
|
||||
* @return the ID of the inserted waypoint
|
||||
*/
|
||||
long insertWaypoint(WaypointCreationRequest request);
|
||||
|
||||
/**
|
||||
* Inserts a track point in the current recording track.
|
||||
*
|
||||
* When recording a track, GPS locations are automatically inserted. This is used for
|
||||
* inserting special track points or for testing.
|
||||
*
|
||||
* @param location the track point to be inserted
|
||||
*/
|
||||
void insertTrackPoint(Location location);
|
||||
/**
|
||||
* Inserts a track point in the current recording track.
|
||||
* <p>
|
||||
* When recording a track, GPS locations are automatically inserted. This is used for
|
||||
* inserting special track points or for testing.
|
||||
*
|
||||
* @param location the track point to be inserted
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
SensorDataSet getSensorData();
|
||||
|
||||
/**
|
||||
* Gets the current sensor manager state.
|
||||
*
|
||||
* return a Sensor.SensorState enum value.
|
||||
*/
|
||||
SensorState getSensorState();
|
||||
/**
|
||||
* Gets the current sensor manager state.
|
||||
* <p>
|
||||
* return a Sensor.SensorState enum value.
|
||||
*/
|
||||
SensorState getSensorState();
|
||||
}
|
||||
|
||||
+21
-21
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -20,28 +20,28 @@ package com.google.android.apps.mytracks.services;
|
||||
* This is an interface for classes that will manage the location listener
|
||||
* policy. Different policy options are: {@link AbsoluteLocationListenerPolicy}
|
||||
* and {@link AdaptiveLocationListenerPolicy}.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public interface LocationListenerPolicy {
|
||||
|
||||
/**
|
||||
* Returns the polling interval this policy would like at this moment.
|
||||
*
|
||||
* @return the polling interval
|
||||
*/
|
||||
long getDesiredPollingInterval();
|
||||
/**
|
||||
* Returns the polling interval this policy would like at this moment.
|
||||
*
|
||||
* @return the polling interval
|
||||
*/
|
||||
long getDesiredPollingInterval();
|
||||
|
||||
/**
|
||||
* Returns the minimum distance between updates.
|
||||
*/
|
||||
int getMinDistance();
|
||||
/**
|
||||
* Returns the minimum distance between updates.
|
||||
*/
|
||||
int getMinDistance();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* location
|
||||
*/
|
||||
void updateIdleTime(long idleTime);
|
||||
/**
|
||||
* 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
|
||||
* location
|
||||
*/
|
||||
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.TrackDetailActivity;
|
||||
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.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.Waypoint;
|
||||
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.
|
||||
*/
|
||||
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
|
||||
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:
|
||||
private ExecutorService executorService;
|
||||
private Context context;
|
||||
@@ -124,15 +120,6 @@ public class TrackRecordingService extends Service {
|
||||
|
||||
// The following variables are set when recording:
|
||||
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
|
||||
* 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() {
|
||||
|
||||
@Override
|
||||
|
||||
+113
-116
@@ -16,9 +16,6 @@
|
||||
|
||||
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.Context;
|
||||
import android.content.Intent;
|
||||
@@ -28,136 +25,136 @@ import android.os.IBinder.DeathRecipient;
|
||||
import android.os.RemoteException;
|
||||
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
|
||||
* start/bind/unbind/stop. The service must be started before it can be bound.
|
||||
* Returns the service if it is started and bound.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
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
|
||||
public void binderDied() {
|
||||
Log.d(TAG, "Service died.");
|
||||
setTrackRecordingService(null);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
Log.i(TAG, "Disconnected from the service.");
|
||||
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);
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
Log.i(TAG, "Disconnected from the service.");
|
||||
setTrackRecordingService(null);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
/**
|
||||
* Starts and binds the service.
|
||||
*/
|
||||
public void startAndBind() {
|
||||
bindService(true);
|
||||
}
|
||||
|
||||
if (!startIfNeeded
|
||||
&& !TrackRecordingServiceConnectionUtils.isRecordingServiceRunning(context)) {
|
||||
Log.d(TAG, "Service is not started. Not binding it.");
|
||||
return;
|
||||
/**
|
||||
* Binds the service if it is started.
|
||||
*/
|
||||
public void bindIfStarted() {
|
||||
bindService(false);
|
||||
}
|
||||
|
||||
if (startIfNeeded) {
|
||||
Log.i(TAG, "Starting the service.");
|
||||
context.startService(new Intent(context, TrackRecordingService.class));
|
||||
/**
|
||||
* Unbinds and stops the service.
|
||||
*/
|
||||
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;
|
||||
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();
|
||||
/**
|
||||
* 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)) {
|
||||
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 {
|
||||
|
||||
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
|
||||
public static final int MESSAGE_DEVICE_NAME = 1;
|
||||
public static final int MESSAGE_READ = 2;
|
||||
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 final Context context;
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
public final class BluetoothLEUtils {
|
||||
|
||||
private BluetoothLEUtils() {}
|
||||
private BluetoothLEUtils() {
|
||||
}
|
||||
|
||||
public static int parseHeartRate(BluetoothGattCharacteristic characteristic) {
|
||||
//DOCUMENTATION https://www.bluetooth.com/specifications/gatt/characteristics/
|
||||
|
||||
+106
-109
@@ -23,7 +23,6 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.apps.mytracks.content.sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.content.sensor.SensorState;
|
||||
@@ -35,128 +34,126 @@ import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Bluetooth sensor manager.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Handle a BluetoothGatt.STATE_DISCONNECTED
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class BluetoothSensorManager extends SensorManager {
|
||||
|
||||
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
|
||||
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||
|
||||
private static BluetoothAdapter getDefaultBluetoothAdapter() {
|
||||
// If from the main application thread, return directly
|
||||
if (Thread.currentThread().equals(Looper.getMainLooper().getThread())) {
|
||||
return BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
// Get the default adapter from the main application thread
|
||||
final ArrayList<BluetoothAdapter> adapters = new ArrayList<>(1);
|
||||
final Object mutex = new Object();
|
||||
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.post(new Runnable() {
|
||||
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
|
||||
private final Context context;
|
||||
private final BluetoothConnectionManager bluetoothConnectionManager;
|
||||
private SensorDataSet sensorDataSet = null;
|
||||
// Handler that gets information back from the bluetoothConnectionManager
|
||||
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void run() {
|
||||
adapters.add(BluetoothAdapter.getDefaultAdapter());
|
||||
synchronized (mutex) {
|
||||
mutex.notify();
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
|
||||
String deviceName = message.getData().getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_READ:
|
||||
if (!(message.obj instanceof SensorDataSet)) {
|
||||
Log.e(TAG, "Received message did not contain a SensorDataSet.");
|
||||
sensorDataSet = null;
|
||||
} else {
|
||||
sensorDataSet = (SensorDataSet) message.obj;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
while (adapters.isEmpty()) {
|
||||
synchronized (mutex) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param context the context
|
||||
*/
|
||||
public BluetoothSensorManager(Context context) {
|
||||
this.context = context;
|
||||
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
|
||||
}
|
||||
|
||||
private static BluetoothAdapter getDefaultBluetoothAdapter() {
|
||||
// If from the main application thread, return directly
|
||||
if (Thread.currentThread().equals(Looper.getMainLooper().getThread())) {
|
||||
return BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
// Get the default adapter from the main application thread
|
||||
final ArrayList<BluetoothAdapter> adapters = new ArrayList<>(1);
|
||||
final Object mutex = new Object();
|
||||
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapters.add(BluetoothAdapter.getDefaultAdapter());
|
||||
synchronized (mutex) {
|
||||
mutex.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
while (adapters.isEmpty()) {
|
||||
synchronized (mutex) {
|
||||
try {
|
||||
mutex.wait(UnitConversions.ONE_SECOND);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Interrupted while waiting for default bluetooth adapter", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (adapters.get(0) == null) {
|
||||
Log.w(TAG, "No bluetooth adapter found.");
|
||||
return null;
|
||||
}
|
||||
return adapters.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUpChannel() {
|
||||
if (!isEnabled()) {
|
||||
Log.w(TAG, "Bluetooth not enabled.");
|
||||
return;
|
||||
}
|
||||
String address = PreferencesUtils.getString(context, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
if (PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(address)) {
|
||||
Log.w(TAG, "No bluetooth address.");
|
||||
return;
|
||||
}
|
||||
Log.w(TAG, "Connecting to bluetooth address: " + address);
|
||||
|
||||
BluetoothDevice device;
|
||||
try {
|
||||
mutex.wait(UnitConversions.ONE_SECOND);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Interrupted while waiting for default bluetooth adapter", e);
|
||||
device = bluetoothAdapter.getRemoteDevice(address);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(TAG, "Unable to get remote device for: " + address, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bluetoothConnectionManager.connect(device);
|
||||
}
|
||||
|
||||
if (adapters.get(0) == null) {
|
||||
Log.w(TAG, "No bluetooth adapter found.");
|
||||
return null;
|
||||
|
||||
@Override
|
||||
protected void tearDownChannel() {
|
||||
bluetoothConnectionManager.reset();
|
||||
}
|
||||
return adapters.get(0);
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
private final BluetoothConnectionManager bluetoothConnectionManager;
|
||||
private SensorDataSet sensorDataSet = null;
|
||||
|
||||
// Handler that gets information back from the bluetoothConnectionManager
|
||||
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
|
||||
String deviceName = message.getData().getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
|
||||
break;
|
||||
case BluetoothConnectionManager.MESSAGE_READ:
|
||||
if (!(message.obj instanceof SensorDataSet)) {
|
||||
Log.e(TAG ,"Received message did not contain a SensorDataSet.");
|
||||
sensorDataSet = null;
|
||||
} else {
|
||||
sensorDataSet = (SensorDataSet) message.obj;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@Override
|
||||
public SensorState getSensorState() {
|
||||
return bluetoothConnectionManager.getSensorState();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param context the context
|
||||
*/
|
||||
public BluetoothSensorManager(Context context) {
|
||||
this.context = context;
|
||||
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUpChannel() {
|
||||
if (!isEnabled()) {
|
||||
Log.w(TAG, "Bluetooth not enabled.");
|
||||
return;
|
||||
@Override
|
||||
public SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
String address = PreferencesUtils.getString(context, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
if (PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(address)) {
|
||||
Log.w(TAG, "No bluetooth address.");
|
||||
return;
|
||||
}
|
||||
Log.w(TAG, "Connecting to bluetooth address: " + address);
|
||||
|
||||
BluetoothDevice device;
|
||||
try {
|
||||
device = bluetoothAdapter.getRemoteDevice(address);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(TAG, "Unable to get remote device for: " + address, e);
|
||||
return;
|
||||
}
|
||||
bluetoothConnectionManager.connect(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDownChannel() {
|
||||
bluetoothConnectionManager.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SensorState getSensorState() {
|
||||
return bluetoothConnectionManager.getSensorState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SensorDataSet getSensorDataSet() {
|
||||
return sensorDataSet;
|
||||
}
|
||||
}
|
||||
|
||||
+93
-93
@@ -26,113 +26,113 @@ import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Manage the connection to a sensor.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public abstract class SensorManager {
|
||||
|
||||
public static final long MAX_SENSOR_DATE_SET_AGE = 5000;
|
||||
public static final long MAX_SENSOR_STATE_AGE = 20000;
|
||||
public static final long MAX_SENSOR_DATE_SET_AGE = 5000;
|
||||
public static final long MAX_SENSOR_STATE_AGE = 20000;
|
||||
|
||||
private static final String TAG = SensorManager.class.getSimpleName();
|
||||
private static final int RETRY_PERIOD = 20000;
|
||||
private static final String TAG = SensorManager.class.getSimpleName();
|
||||
private static final int RETRY_PERIOD = 20000;
|
||||
|
||||
private SensorState sensorState = SensorState.NONE;
|
||||
private long sensorStateTimestamp = System.currentTimeMillis();
|
||||
private SensorState sensorState = SensorState.NONE;
|
||||
private long sensorStateTimestamp = System.currentTimeMillis();
|
||||
|
||||
private TimerTask timerTask;
|
||||
private Timer timer;
|
||||
private TimerTask timerTask;
|
||||
private Timer timer;
|
||||
|
||||
/**
|
||||
* Returns true if the sensor is enabled.
|
||||
*/
|
||||
public abstract boolean isEnabled();
|
||||
/**
|
||||
* Returns true if the sensor is enabled.
|
||||
*/
|
||||
public abstract boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Sets up the sensor channel.
|
||||
*/
|
||||
protected abstract void setUpChannel();
|
||||
/**
|
||||
* Sets up the sensor channel.
|
||||
*/
|
||||
protected abstract void setUpChannel();
|
||||
|
||||
/**
|
||||
* Tears down the sensor channel.
|
||||
*/
|
||||
protected abstract void tearDownChannel();
|
||||
/**
|
||||
* Tears down the sensor channel.
|
||||
*/
|
||||
protected abstract void tearDownChannel();
|
||||
|
||||
/**
|
||||
* Gets the sensor data set.
|
||||
*/
|
||||
public abstract SensorDataSet getSensorDataSet();
|
||||
/**
|
||||
* Gets the sensor data set.
|
||||
*/
|
||||
public abstract SensorDataSet getSensorDataSet();
|
||||
|
||||
/**
|
||||
* Starts the sensor.
|
||||
*/
|
||||
public void startSensor() {
|
||||
setUpChannel();
|
||||
timerTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
switch (getSensorState()) {
|
||||
case CONNECTING:
|
||||
if (System.currentTimeMillis() - sensorStateTimestamp > MAX_SENSOR_STATE_AGE) {
|
||||
Log.i(TAG, "Retry setUpChannel");
|
||||
setUpChannel();
|
||||
}
|
||||
break;
|
||||
case NONE:
|
||||
case DISCONNECTED:
|
||||
setUpChannel();
|
||||
break;
|
||||
default:
|
||||
// CONNECTED or SENDING
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
timer = new Timer(SensorManager.class.getSimpleName());
|
||||
timer.schedule(timerTask, RETRY_PERIOD, RETRY_PERIOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the sensor.
|
||||
*/
|
||||
public void stopSensor() {
|
||||
if (timerTask != null) {
|
||||
timerTask.cancel();
|
||||
timerTask = null;
|
||||
/**
|
||||
* Starts the sensor.
|
||||
*/
|
||||
public void startSensor() {
|
||||
setUpChannel();
|
||||
timerTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
switch (getSensorState()) {
|
||||
case CONNECTING:
|
||||
if (System.currentTimeMillis() - sensorStateTimestamp > MAX_SENSOR_STATE_AGE) {
|
||||
Log.i(TAG, "Retry setUpChannel");
|
||||
setUpChannel();
|
||||
}
|
||||
break;
|
||||
case NONE:
|
||||
case DISCONNECTED:
|
||||
setUpChannel();
|
||||
break;
|
||||
default:
|
||||
// CONNECTED or SENDING
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
timer = new Timer(SensorManager.class.getSimpleName());
|
||||
timer.schedule(timerTask, RETRY_PERIOD, RETRY_PERIOD);
|
||||
}
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
|
||||
/**
|
||||
* Stops the sensor.
|
||||
*/
|
||||
public void stopSensor() {
|
||||
if (timerTask != null) {
|
||||
timerTask.cancel();
|
||||
timerTask = null;
|
||||
}
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
}
|
||||
tearDownChannel();
|
||||
}
|
||||
tearDownChannel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sensor state.
|
||||
*
|
||||
* @param sensorState the sensor state
|
||||
*/
|
||||
public void setSensorState(SensorState sensorState) {
|
||||
sensorStateTimestamp = System.currentTimeMillis();
|
||||
this.sensorState = sensorState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sensor state.
|
||||
*/
|
||||
public SensorState getSensorState() {
|
||||
return sensorState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the sensor data set is valid.
|
||||
*/
|
||||
public boolean isSensorDataSetValid() {
|
||||
SensorDataSet sensorDataSet = getSensorDataSet();
|
||||
if (sensorDataSet == null) {
|
||||
return false;
|
||||
/**
|
||||
* Gets the sensor state.
|
||||
*/
|
||||
public SensorState getSensorState() {
|
||||
return sensorState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sensor state.
|
||||
*
|
||||
* @param sensorState the sensor state
|
||||
*/
|
||||
public void setSensorState(SensorState sensorState) {
|
||||
sensorStateTimestamp = System.currentTimeMillis();
|
||||
this.sensorState = sensorState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the sensor data set is valid.
|
||||
*/
|
||||
public boolean isSensorDataSetValid() {
|
||||
SensorDataSet sensorDataSet = getSensorDataSet();
|
||||
if (sensorDataSet == null) {
|
||||
return false;
|
||||
}
|
||||
return sensorDataSet.isRecent(MAX_SENSOR_DATE_SET_AGE);
|
||||
}
|
||||
return sensorDataSet.isRecent(MAX_SENSOR_DATE_SET_AGE);
|
||||
}
|
||||
}
|
||||
|
||||
+55
-54
@@ -25,67 +25,68 @@ import android.content.Context;
|
||||
*/
|
||||
public class SensorManagerFactory {
|
||||
|
||||
private static SensorManager systemSensorManager = null;
|
||||
private static SensorManager sensorManagerTemporary = null;
|
||||
private static SensorManager systemSensorManager = null;
|
||||
private static SensorManager sensorManagerTemporary = null;
|
||||
|
||||
private SensorManagerFactory() {}
|
||||
|
||||
/**
|
||||
* Gets the system sensor manager.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static SensorManager getSystemSensorManager(Context context) {
|
||||
releaseSensorManagerTemporary();
|
||||
releaseSystemSensorManager();
|
||||
systemSensorManager = getSensorManager(context);
|
||||
if (systemSensorManager != null) {
|
||||
systemSensorManager.startSensor();
|
||||
private SensorManagerFactory() {
|
||||
}
|
||||
return systemSensorManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the system sensor manager.
|
||||
*/
|
||||
public static void releaseSystemSensorManager() {
|
||||
if (systemSensorManager != null) {
|
||||
systemSensorManager.stopSensor();
|
||||
/**
|
||||
* Gets the system sensor manager.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static SensorManager getSystemSensorManager(Context context) {
|
||||
releaseSensorManagerTemporary();
|
||||
releaseSystemSensorManager();
|
||||
systemSensorManager = getSensorManager(context);
|
||||
if (systemSensorManager != null) {
|
||||
systemSensorManager.startSensor();
|
||||
}
|
||||
return systemSensorManager;
|
||||
}
|
||||
systemSensorManager = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the temp sensor manager.
|
||||
*/
|
||||
public static SensorManager getSensorManagerTemporary(Context context) {
|
||||
releaseSensorManagerTemporary();
|
||||
if (systemSensorManager != null) {
|
||||
return null;
|
||||
/**
|
||||
* Releases the system sensor manager.
|
||||
*/
|
||||
public static void releaseSystemSensorManager() {
|
||||
if (systemSensorManager != null) {
|
||||
systemSensorManager.stopSensor();
|
||||
}
|
||||
systemSensorManager = null;
|
||||
}
|
||||
sensorManagerTemporary = getSensorManager(context);
|
||||
if (sensorManagerTemporary != null) {
|
||||
sensorManagerTemporary.startSensor();
|
||||
}
|
||||
return sensorManagerTemporary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the temp sensor manager.
|
||||
*/
|
||||
public static void releaseSensorManagerTemporary() {
|
||||
if (sensorManagerTemporary != null) {
|
||||
sensorManagerTemporary.stopSensor();
|
||||
/**
|
||||
* Gets the temp sensor manager.
|
||||
*/
|
||||
public static SensorManager getSensorManagerTemporary(Context context) {
|
||||
releaseSensorManagerTemporary();
|
||||
if (systemSensorManager != null) {
|
||||
return null;
|
||||
}
|
||||
sensorManagerTemporary = getSensorManager(context);
|
||||
if (sensorManagerTemporary != null) {
|
||||
sensorManagerTemporary.startSensor();
|
||||
}
|
||||
return sensorManagerTemporary;
|
||||
}
|
||||
sensorManagerTemporary = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sensor manager.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
private static SensorManager getSensorManager(Context context) {
|
||||
return new BluetoothSensorManager(context);
|
||||
}
|
||||
/**
|
||||
* Releases the temp sensor manager.
|
||||
*/
|
||||
public static void releaseSensorManagerTemporary() {
|
||||
if (sensorManagerTemporary != null) {
|
||||
sensorManagerTemporary.stopSensor();
|
||||
}
|
||||
sensorManagerTemporary = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sensor manager.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
private static SensorManager getSensorManager(Context context) {
|
||||
return new BluetoothSensorManager(context);
|
||||
}
|
||||
}
|
||||
|
||||
+262
-269
@@ -38,294 +38,287 @@ import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class will periodically announce the user's trip statistics.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
|
||||
/**
|
||||
* The rate at which announcements are spoken.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final float TTS_SPEECH_RATE = 0.9f;
|
||||
|
||||
private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName();
|
||||
|
||||
private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() {
|
||||
@Override
|
||||
public void onStart(String utteranceId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDone(String utteranceId) {
|
||||
int result = audioManager.abandonAudioFocus(null);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.w(TAG, "Failed to relinquish audio focus.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String utteranceId) {
|
||||
}
|
||||
};
|
||||
|
||||
private final Context context;
|
||||
private TextToSpeech tts;
|
||||
|
||||
// Response from TTS after its initialization
|
||||
private int initStatus = TextToSpeech.ERROR;
|
||||
|
||||
// True if TTS engine is ready
|
||||
private boolean ready = false;
|
||||
|
||||
// True if speech is allowed
|
||||
private boolean speechAllowed;
|
||||
|
||||
private final AudioManager audioManager;
|
||||
|
||||
/**
|
||||
* Listener which updates {@link #speechAllowed} when the phone state changes.
|
||||
*/
|
||||
private final PhoneStateListener phoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onCallStateChanged(int state, String incomingNumber) {
|
||||
speechAllowed = state == TelephonyManager.CALL_STATE_IDLE;
|
||||
if (!speechAllowed && tts != null && tts.isSpeaking()) {
|
||||
// If we're already speaking, stop it.
|
||||
tts.stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public AnnouncementPeriodicTask(Context context) {
|
||||
this.context = context;
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (tts == null) {
|
||||
tts = newTextToSpeech(context, new OnInitListener() {
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
initStatus = status;
|
||||
}
|
||||
});
|
||||
}
|
||||
speechAllowed = true;
|
||||
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TrackRecordingService trackRecordingService) {
|
||||
if (trackRecordingService == null) {
|
||||
Log.e(TAG, "TrackRecordingService is null.");
|
||||
return;
|
||||
}
|
||||
announce(trackRecordingService.getTripStatistics());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs this task.
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void announce(TripStatistics tripStatistics) {
|
||||
if (tripStatistics == null) {
|
||||
Log.e(TAG, "TripStatistics is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!ready) {
|
||||
ready = initStatus == TextToSpeech.SUCCESS;
|
||||
if (ready) {
|
||||
onTtsReady();
|
||||
}
|
||||
}
|
||||
if (!ready) {
|
||||
Log.i(TAG, "TTS not ready.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!speechAllowed) {
|
||||
Log.i(TAG, "Speech is not allowed at this time.");
|
||||
return;
|
||||
}
|
||||
speakAnnouncement(getAnnouncement(tripStatistics));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_NONE);
|
||||
if (tts != null) {
|
||||
tts.shutdown();
|
||||
tts = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when TTS is ready.
|
||||
*/
|
||||
private void onTtsReady() {
|
||||
Locale locale = Locale.getDefault();
|
||||
int languageAvailability = tts.isLanguageAvailable(locale);
|
||||
if (languageAvailability == TextToSpeech.LANG_MISSING_DATA
|
||||
|| languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||
Log.w(TAG, "Default locale not available, use English.");
|
||||
locale = Locale.ENGLISH;
|
||||
/*
|
||||
* TODO: instead of using english, load the language if missing and show a
|
||||
* toast if not supported. Not able to change the resource strings to
|
||||
* English.
|
||||
*/
|
||||
}
|
||||
tts.setLanguage(locale);
|
||||
|
||||
// Slow down the speed just a bit as it is hard to hear when exercising.
|
||||
tts.setSpeechRate(TTS_SPEECH_RATE);
|
||||
|
||||
tts.setOnUtteranceProgressListener(utteranceListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaks the announcement.
|
||||
*
|
||||
* @param announcement the announcement
|
||||
*/
|
||||
private void speakAnnouncement(String announcement) {
|
||||
int result = audioManager.requestAudioFocus(
|
||||
null, TextToSpeech.Engine.DEFAULT_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.w(TAG, "Failed to request audio focus.");
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't care about the utterance id. It is supplied here to force
|
||||
* onUtteranceCompleted to be called.
|
||||
/**
|
||||
* The rate at which announcements are spoken.
|
||||
*/
|
||||
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
|
||||
}
|
||||
@VisibleForTesting
|
||||
static final float TTS_SPEECH_RATE = 0.9f;
|
||||
|
||||
/**
|
||||
* Create a new {@link TextToSpeech}.
|
||||
*
|
||||
* @param aContext a context
|
||||
* @param onInitListener an on init listener
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected TextToSpeech newTextToSpeech(Context aContext, OnInitListener onInitListener) {
|
||||
return new TextToSpeech(aContext, onInitListener);
|
||||
}
|
||||
private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName();
|
||||
private final Context context;
|
||||
private final AudioManager audioManager;
|
||||
private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() {
|
||||
@Override
|
||||
public void onStart(String utteranceId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the announcement.
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected String getAnnouncement(TripStatistics tripStatistics) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(context);
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
double speed = tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH;
|
||||
@Override
|
||||
public void onDone(String utteranceId) {
|
||||
int result = audioManager.abandonAudioFocus(null);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.w(TAG, "Failed to relinquish audio focus.");
|
||||
}
|
||||
}
|
||||
|
||||
if (distance == 0) {
|
||||
return context.getString(R.string.voice_total_distance_zero);
|
||||
@Override
|
||||
public void onError(String utteranceId) {
|
||||
}
|
||||
};
|
||||
private TextToSpeech tts;
|
||||
// Response from TTS after its initialization
|
||||
private int initStatus = TextToSpeech.ERROR;
|
||||
// True if TTS engine is ready
|
||||
private boolean ready = false;
|
||||
// True if speech is allowed
|
||||
private boolean speechAllowed;
|
||||
/**
|
||||
* Listener which updates {@link #speechAllowed} when the phone state changes.
|
||||
*/
|
||||
private final PhoneStateListener phoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onCallStateChanged(int state, String incomingNumber) {
|
||||
speechAllowed = state == TelephonyManager.CALL_STATE_IDLE;
|
||||
if (!speechAllowed && tts != null && tts.isSpeaking()) {
|
||||
// If we're already speaking, stop it.
|
||||
tts.stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public AnnouncementPeriodicTask(Context context) {
|
||||
this.context = context;
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
@Override
|
||||
public void start() {
|
||||
if (tts == null) {
|
||||
tts = newTextToSpeech(context, new OnInitListener() {
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
initStatus = status;
|
||||
}
|
||||
});
|
||||
}
|
||||
speechAllowed = true;
|
||||
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||
}
|
||||
|
||||
String rate;
|
||||
if (reportSpeed) {
|
||||
int speedId = metricUnits ? R.plurals.voiceSpeedKilometersPerHour
|
||||
: R.plurals.voiceSpeedMilesPerHour;
|
||||
rate = context.getResources().getQuantityString(speedId, getQuantityCount(speed), speed);
|
||||
} else {
|
||||
speed = speed == 0 ? 0.0 : 1 / speed;
|
||||
int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
|
||||
long time = Math.round(
|
||||
speed * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
|
||||
rate = context.getString(paceId, getAnnounceTime(time));
|
||||
@Override
|
||||
public void run(TrackRecordingService trackRecordingService) {
|
||||
if (trackRecordingService == null) {
|
||||
Log.e(TAG, "TrackRecordingService is null.");
|
||||
return;
|
||||
}
|
||||
announce(trackRecordingService.getTripStatistics());
|
||||
}
|
||||
|
||||
int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers
|
||||
: R.plurals.voiceTotalDistanceMiles;
|
||||
String totalDistance = context.getResources()
|
||||
.getQuantityString(totalDistanceId, getQuantityCount(distance), distance);
|
||||
/**
|
||||
* Runs this task.
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void announce(TripStatistics tripStatistics) {
|
||||
if (tripStatistics == null) {
|
||||
Log.e(TAG, "TripStatistics is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
return context.getString(R.string.voice_template, totalDistance,
|
||||
getAnnounceTime(tripStatistics.getMovingTime()), rate);
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!ready) {
|
||||
ready = initStatus == TextToSpeech.SUCCESS;
|
||||
if (ready) {
|
||||
onTtsReady();
|
||||
}
|
||||
}
|
||||
if (!ready) {
|
||||
Log.i(TAG, "TTS not ready.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to phone state.
|
||||
*
|
||||
* @param listener the listener
|
||||
* @param events the interested events
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void listenToPhoneState(PhoneStateListener listener, int events) {
|
||||
TelephonyManager telephony = (TelephonyManager) context.getSystemService(
|
||||
Context.TELEPHONY_SERVICE);
|
||||
if (telephony != null) {
|
||||
telephony.listen(listener, events);
|
||||
if (!speechAllowed) {
|
||||
Log.i(TAG, "Speech is not allowed at this time.");
|
||||
return;
|
||||
}
|
||||
speakAnnouncement(getAnnouncement(tripStatistics));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the announce time.
|
||||
*
|
||||
* @param time the time
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String getAnnounceTime(long time) {
|
||||
int[] parts = StringUtils.getTimeParts(time);
|
||||
String seconds = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceSeconds, parts[0], parts[0]);
|
||||
String minutes = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceMinutes, parts[1], parts[1]);
|
||||
String hours = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceHours, parts[2], parts[2]);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (parts[2] != 0) {
|
||||
sb.append(hours);
|
||||
sb.append(" ");
|
||||
@Override
|
||||
public void shutdown() {
|
||||
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_NONE);
|
||||
if (tts != null) {
|
||||
tts.shutdown();
|
||||
tts = null;
|
||||
}
|
||||
}
|
||||
sb.append(minutes);
|
||||
sb.append(" ");
|
||||
sb.append(seconds);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plural count to be used by getQuantityString. getQuantityString
|
||||
* only supports integer quantities, not a double quantity like "2.2".
|
||||
* <p>
|
||||
* As a temporary workaround, we convert a double quantity to an integer
|
||||
* quantity. If the double quantity is exactly 0, 1, or 2, then we can return
|
||||
* these integer quantities. Otherwise, we cast the double quantity to an
|
||||
* integer quantity. However, we need to make sure that if the casted value is
|
||||
* 0, 1, or 2, we don't return those, instead, return the next biggest integer
|
||||
* 3.
|
||||
*
|
||||
* @param d the double value
|
||||
*/
|
||||
private int getQuantityCount(double d) {
|
||||
if (d == 0) {
|
||||
return 0;
|
||||
} else if (d == 1) {
|
||||
return 1;
|
||||
} else if (d == 2) {
|
||||
return 2;
|
||||
} else {
|
||||
int count = (int) d;
|
||||
return count < 3 ? 3 : count;
|
||||
/**
|
||||
* Called when TTS is ready.
|
||||
*/
|
||||
private void onTtsReady() {
|
||||
Locale locale = Locale.getDefault();
|
||||
int languageAvailability = tts.isLanguageAvailable(locale);
|
||||
if (languageAvailability == TextToSpeech.LANG_MISSING_DATA
|
||||
|| languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||
Log.w(TAG, "Default locale not available, use English.");
|
||||
locale = Locale.ENGLISH;
|
||||
/*
|
||||
* TODO: instead of using english, load the language if missing and show a
|
||||
* toast if not supported. Not able to change the resource strings to
|
||||
* English.
|
||||
*/
|
||||
}
|
||||
tts.setLanguage(locale);
|
||||
|
||||
// Slow down the speed just a bit as it is hard to hear when exercising.
|
||||
tts.setSpeechRate(TTS_SPEECH_RATE);
|
||||
|
||||
tts.setOnUtteranceProgressListener(utteranceListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaks the announcement.
|
||||
*
|
||||
* @param announcement the announcement
|
||||
*/
|
||||
private void speakAnnouncement(String announcement) {
|
||||
int result = audioManager.requestAudioFocus(
|
||||
null, TextToSpeech.Engine.DEFAULT_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.w(TAG, "Failed to request audio focus.");
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't care about the utterance id. It is supplied here to force
|
||||
* onUtteranceCompleted to be called.
|
||||
*/
|
||||
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TextToSpeech}.
|
||||
*
|
||||
* @param aContext a context
|
||||
* @param onInitListener an on init listener
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected TextToSpeech newTextToSpeech(Context aContext, OnInitListener onInitListener) {
|
||||
return new TextToSpeech(aContext, onInitListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the announcement.
|
||||
*
|
||||
* @param tripStatistics the trip statistics
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected String getAnnouncement(TripStatistics tripStatistics) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(context);
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
double speed = tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH;
|
||||
|
||||
if (distance == 0) {
|
||||
return context.getString(R.string.voice_total_distance_zero);
|
||||
}
|
||||
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
|
||||
String rate;
|
||||
if (reportSpeed) {
|
||||
int speedId = metricUnits ? R.plurals.voiceSpeedKilometersPerHour
|
||||
: R.plurals.voiceSpeedMilesPerHour;
|
||||
rate = context.getResources().getQuantityString(speedId, getQuantityCount(speed), speed);
|
||||
} else {
|
||||
speed = speed == 0 ? 0.0 : 1 / speed;
|
||||
int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
|
||||
long time = Math.round(
|
||||
speed * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
|
||||
rate = context.getString(paceId, getAnnounceTime(time));
|
||||
}
|
||||
|
||||
int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers
|
||||
: R.plurals.voiceTotalDistanceMiles;
|
||||
String totalDistance = context.getResources()
|
||||
.getQuantityString(totalDistanceId, getQuantityCount(distance), distance);
|
||||
|
||||
return context.getString(R.string.voice_template, totalDistance,
|
||||
getAnnounceTime(tripStatistics.getMovingTime()), rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to phone state.
|
||||
*
|
||||
* @param listener the listener
|
||||
* @param events the interested events
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void listenToPhoneState(PhoneStateListener listener, int events) {
|
||||
TelephonyManager telephony = (TelephonyManager) context.getSystemService(
|
||||
Context.TELEPHONY_SERVICE);
|
||||
if (telephony != null) {
|
||||
telephony.listen(listener, events);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the announce time.
|
||||
*
|
||||
* @param time the time
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String getAnnounceTime(long time) {
|
||||
int[] parts = StringUtils.getTimeParts(time);
|
||||
String seconds = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceSeconds, parts[0], parts[0]);
|
||||
String minutes = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceMinutes, parts[1], parts[1]);
|
||||
String hours = context.getResources()
|
||||
.getQuantityString(R.plurals.voiceHours, parts[2], parts[2]);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (parts[2] != 0) {
|
||||
sb.append(hours);
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(minutes);
|
||||
sb.append(" ");
|
||||
sb.append(seconds);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plural count to be used by getQuantityString. getQuantityString
|
||||
* only supports integer quantities, not a double quantity like "2.2".
|
||||
* <p>
|
||||
* As a temporary workaround, we convert a double quantity to an integer
|
||||
* quantity. If the double quantity is exactly 0, 1, or 2, then we can return
|
||||
* these integer quantities. Otherwise, we cast the double quantity to an
|
||||
* integer quantity. However, we need to make sure that if the casted value is
|
||||
* 0, 1, or 2, we don't return those, instead, return the next biggest integer
|
||||
* 3.
|
||||
*
|
||||
* @param d the double value
|
||||
*/
|
||||
private int getQuantityCount(double d) {
|
||||
if (d == 0) {
|
||||
return 0;
|
||||
} else if (d == 1) {
|
||||
return 1;
|
||||
} else if (d == 2) {
|
||||
return 2;
|
||||
} else {
|
||||
int count = (int) d;
|
||||
return count < 3 ? 3 : count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -19,13 +19,13 @@ import android.content.Context;
|
||||
|
||||
/**
|
||||
* A {@link PeriodicTaskFactory} for text-to-speech announcement periodic task.
|
||||
*
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class AnnouncementPeriodicTaskFactory implements PeriodicTaskFactory {
|
||||
|
||||
@Override
|
||||
public PeriodicTask create(Context context) {
|
||||
return new AnnouncementPeriodicTask(context);
|
||||
}
|
||||
@Override
|
||||
public PeriodicTask create(Context context) {
|
||||
return new AnnouncementPeriodicTask(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -20,24 +20,24 @@ import com.google.android.apps.mytracks.services.TrackRecordingService;
|
||||
|
||||
/**
|
||||
* This is interface for a task that will be executed on some schedule.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public interface PeriodicTask {
|
||||
/**
|
||||
* Sets up this task for subsequent calls to the run method.
|
||||
*/
|
||||
void start();
|
||||
/**
|
||||
* Sets up this task for subsequent calls to the run method.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* This method will be called periodically.
|
||||
*
|
||||
* @param trackRecordingService the track recording service
|
||||
*/
|
||||
void run(TrackRecordingService trackRecordingService);
|
||||
/**
|
||||
* This method will be called periodically.
|
||||
*
|
||||
* @param trackRecordingService the track recording service
|
||||
*/
|
||||
void run(TrackRecordingService trackRecordingService);
|
||||
|
||||
/**
|
||||
* Shuts down this task and clean up resources.
|
||||
*/
|
||||
void shutdown();
|
||||
/**
|
||||
* Shuts down this task and clean up resources.
|
||||
*/
|
||||
void shutdown();
|
||||
}
|
||||
|
||||
+152
-152
@@ -15,188 +15,188 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services.tasks;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingService;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Execute a periodic task on a time or distance schedule.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class PeriodicTaskExecutor {
|
||||
|
||||
private static final String TAG = PeriodicTaskExecutor.class.getSimpleName();
|
||||
private static final long MINUTE_TO_MILLISECONDS = 60000L;
|
||||
private static final String TAG = PeriodicTaskExecutor.class.getSimpleName();
|
||||
private static final long MINUTE_TO_MILLISECONDS = 60000L;
|
||||
|
||||
private final TrackRecordingService trackRecordingService;
|
||||
private final PeriodicTaskFactory periodicTaskFactory;
|
||||
private final TrackRecordingService trackRecordingService;
|
||||
private final PeriodicTaskFactory periodicTaskFactory;
|
||||
|
||||
/**
|
||||
* The task frequency. A positive value is a time frequency (minutes). A
|
||||
* negative value is a distance frequency (km or mi). A zero value is to turn
|
||||
* off periodic task.
|
||||
*/
|
||||
private int taskFrequency = PreferencesUtils.FREQUENCY_OFF;
|
||||
/**
|
||||
* The task frequency. A positive value is a time frequency (minutes). A
|
||||
* negative value is a distance frequency (km or mi). A zero value is to turn
|
||||
* off periodic task.
|
||||
*/
|
||||
private int taskFrequency = PreferencesUtils.FREQUENCY_OFF;
|
||||
|
||||
private PeriodicTask periodicTask;
|
||||
private PeriodicTask periodicTask;
|
||||
|
||||
// Time periodic task executor
|
||||
private TimerTaskExecutor timerTaskExecutor = null;
|
||||
// Time periodic task executor
|
||||
private TimerTaskExecutor timerTaskExecutor = null;
|
||||
|
||||
private boolean metricUnits;
|
||||
private boolean metricUnits;
|
||||
|
||||
// The next distance for the distance periodic task
|
||||
private double nextTaskDistance = Double.MAX_VALUE;
|
||||
// The next distance for the distance periodic task
|
||||
private double nextTaskDistance = Double.MAX_VALUE;
|
||||
|
||||
public PeriodicTaskExecutor(
|
||||
TrackRecordingService trackRecordingService, PeriodicTaskFactory periodicTaskFactory) {
|
||||
this.trackRecordingService = trackRecordingService;
|
||||
this.periodicTaskFactory = periodicTaskFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the executor.
|
||||
*/
|
||||
public void restore() {
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
|
||||
Log.d(TAG, "Not recording or paused.");
|
||||
return;
|
||||
public PeriodicTaskExecutor(
|
||||
TrackRecordingService trackRecordingService, PeriodicTaskFactory periodicTaskFactory) {
|
||||
this.trackRecordingService = trackRecordingService;
|
||||
this.periodicTaskFactory = periodicTaskFactory;
|
||||
}
|
||||
|
||||
if (!isTimeFrequency()) {
|
||||
if (timerTaskExecutor != null) {
|
||||
timerTaskExecutor.shutdown();
|
||||
timerTaskExecutor = null;
|
||||
}
|
||||
}
|
||||
if (taskFrequency == PreferencesUtils.FREQUENCY_OFF) {
|
||||
Log.d(TAG, "Task frequency is off.");
|
||||
return;
|
||||
/**
|
||||
* Restores the executor.
|
||||
*/
|
||||
public void restore() {
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
|
||||
Log.d(TAG, "Not recording or paused.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isTimeFrequency()) {
|
||||
if (timerTaskExecutor != null) {
|
||||
timerTaskExecutor.shutdown();
|
||||
timerTaskExecutor = null;
|
||||
}
|
||||
}
|
||||
if (taskFrequency == PreferencesUtils.FREQUENCY_OFF) {
|
||||
Log.d(TAG, "Task frequency is off.");
|
||||
return;
|
||||
}
|
||||
|
||||
periodicTask = periodicTaskFactory.create(trackRecordingService);
|
||||
|
||||
// Returning null is ok
|
||||
if (periodicTask == null) {
|
||||
Log.d(TAG, "Periodic task is null.");
|
||||
return;
|
||||
}
|
||||
periodicTask.start();
|
||||
|
||||
if (isTimeFrequency()) {
|
||||
if (timerTaskExecutor == null) {
|
||||
timerTaskExecutor = new TimerTaskExecutor(periodicTask, trackRecordingService);
|
||||
}
|
||||
timerTaskExecutor.scheduleTask(taskFrequency * MINUTE_TO_MILLISECONDS);
|
||||
} else {
|
||||
// For distance periodic task
|
||||
calculateNextTaskDistance();
|
||||
}
|
||||
}
|
||||
|
||||
periodicTask = periodicTaskFactory.create(trackRecordingService);
|
||||
|
||||
// Returning null is ok
|
||||
if (periodicTask == null) {
|
||||
Log.d(TAG, "Periodic task is null.");
|
||||
return;
|
||||
}
|
||||
periodicTask.start();
|
||||
|
||||
if (isTimeFrequency()) {
|
||||
if (timerTaskExecutor == null) {
|
||||
timerTaskExecutor = new TimerTaskExecutor(periodicTask, trackRecordingService);
|
||||
}
|
||||
timerTaskExecutor.scheduleTask(taskFrequency * MINUTE_TO_MILLISECONDS);
|
||||
} else {
|
||||
// For distance periodic task
|
||||
calculateNextTaskDistance();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the executor.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (periodicTask != null) {
|
||||
periodicTask.shutdown();
|
||||
periodicTask = null;
|
||||
}
|
||||
if (timerTaskExecutor != null) {
|
||||
timerTaskExecutor.shutdown();
|
||||
timerTaskExecutor = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the executor.
|
||||
*/
|
||||
public void update() {
|
||||
if (!isDistanceFrequency() || periodicTask == null) {
|
||||
return;
|
||||
}
|
||||
TripStatistics tripStatistics = trackRecordingService.getTripStatistics();
|
||||
if (tripStatistics == null) {
|
||||
return;
|
||||
}
|
||||
double distance = tripStatistics.getTotalDistance()
|
||||
* UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
/**
|
||||
* Shuts down the executor.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (periodicTask != null) {
|
||||
periodicTask.shutdown();
|
||||
periodicTask = null;
|
||||
}
|
||||
if (timerTaskExecutor != null) {
|
||||
timerTaskExecutor.shutdown();
|
||||
timerTaskExecutor = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (distance > nextTaskDistance) {
|
||||
periodicTask.run(trackRecordingService);
|
||||
calculateNextTaskDistance();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Updates the executor.
|
||||
*/
|
||||
public void update() {
|
||||
if (!isDistanceFrequency() || periodicTask == null) {
|
||||
return;
|
||||
}
|
||||
TripStatistics tripStatistics = trackRecordingService.getTripStatistics();
|
||||
if (tripStatistics == null) {
|
||||
return;
|
||||
}
|
||||
double distance = tripStatistics.getTotalDistance()
|
||||
* UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets task frequency.
|
||||
*
|
||||
* @param taskFrequency the task frequency
|
||||
*/
|
||||
public void setTaskFrequency(int taskFrequency) {
|
||||
this.taskFrequency = taskFrequency;
|
||||
restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets metricUnits.
|
||||
*
|
||||
* @param metricUnits true to use metric units
|
||||
*/
|
||||
public void setMetricUnits(boolean metricUnits) {
|
||||
this.metricUnits = metricUnits;
|
||||
calculateNextTaskDistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next distance for the distance periodic task.
|
||||
*/
|
||||
private void calculateNextTaskDistance() {
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()
|
||||
|| periodicTask == null) {
|
||||
return;
|
||||
if (distance > nextTaskDistance) {
|
||||
periodicTask.run(trackRecordingService);
|
||||
calculateNextTaskDistance();
|
||||
}
|
||||
}
|
||||
|
||||
TripStatistics tripStatistics = trackRecordingService.getTripStatistics();
|
||||
if (tripStatistics == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDistanceFrequency()) {
|
||||
nextTaskDistance = Double.MAX_VALUE;
|
||||
Log.d(TAG, "SplitManager: Distance splits disabled.");
|
||||
return;
|
||||
/**
|
||||
* Sets task frequency.
|
||||
*
|
||||
* @param taskFrequency the task frequency
|
||||
*/
|
||||
public void setTaskFrequency(int taskFrequency) {
|
||||
this.taskFrequency = taskFrequency;
|
||||
restore();
|
||||
}
|
||||
|
||||
double distance = tripStatistics.getTotalDistance()
|
||||
* UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
/**
|
||||
* Sets metricUnits.
|
||||
*
|
||||
* @param metricUnits true to use metric units
|
||||
*/
|
||||
public void setMetricUnits(boolean metricUnits) {
|
||||
this.metricUnits = metricUnits;
|
||||
calculateNextTaskDistance();
|
||||
}
|
||||
// The index will be negative since the frequency is negative.
|
||||
int index = (int) (distance / taskFrequency);
|
||||
index -= 1;
|
||||
nextTaskDistance = taskFrequency * index;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if time frequency.
|
||||
*/
|
||||
private boolean isTimeFrequency() {
|
||||
return taskFrequency > 0;
|
||||
}
|
||||
/**
|
||||
* Calculates the next distance for the distance periodic task.
|
||||
*/
|
||||
private void calculateNextTaskDistance() {
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()
|
||||
|| periodicTask == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if distance frequency.
|
||||
*/
|
||||
private boolean isDistanceFrequency() {
|
||||
return taskFrequency < 0;
|
||||
}
|
||||
TripStatistics tripStatistics = trackRecordingService.getTripStatistics();
|
||||
if (tripStatistics == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDistanceFrequency()) {
|
||||
nextTaskDistance = Double.MAX_VALUE;
|
||||
Log.d(TAG, "SplitManager: Distance splits disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
double distance = tripStatistics.getTotalDistance()
|
||||
* UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
// The index will be negative since the frequency is negative.
|
||||
int index = (int) (distance / taskFrequency);
|
||||
index -= 1;
|
||||
nextTaskDistance = taskFrequency * index;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if time frequency.
|
||||
*/
|
||||
private boolean isTimeFrequency() {
|
||||
return taskFrequency > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if distance frequency.
|
||||
*/
|
||||
private boolean isDistanceFrequency() {
|
||||
return taskFrequency < 0;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -20,15 +20,15 @@ import android.content.Context;
|
||||
|
||||
/**
|
||||
* An interface for classes that can create {@link PeriodicTask}.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public interface PeriodicTaskFactory {
|
||||
|
||||
/**
|
||||
* Creates a {@link PeriodicTask}.
|
||||
*
|
||||
* @return the task, or null if the task is not supported
|
||||
*/
|
||||
PeriodicTask create(Context context);
|
||||
/**
|
||||
* Creates a {@link PeriodicTask}.
|
||||
*
|
||||
* @return the task, or null if the task is not supported
|
||||
*/
|
||||
PeriodicTask create(Context context);
|
||||
}
|
||||
+14
-12
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -21,19 +21,21 @@ import com.google.android.apps.mytracks.services.TrackRecordingService;
|
||||
|
||||
/**
|
||||
* A simple task to insert statistics markers periodically.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SplitPeriodicTask implements PeriodicTask {
|
||||
|
||||
@Override
|
||||
public void start() {}
|
||||
@Override
|
||||
public void start() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TrackRecordingService trackRecordingService) {
|
||||
trackRecordingService.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
}
|
||||
@Override
|
||||
public void run(TrackRecordingService trackRecordingService) {
|
||||
trackRecordingService.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {}
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -20,13 +20,13 @@ import android.content.Context;
|
||||
|
||||
/**
|
||||
* A {@link PeriodicTaskFactory} for {@link SplitPeriodicTask}.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SplitPeriodicTaskFactory implements PeriodicTaskFactory {
|
||||
|
||||
@Override
|
||||
public PeriodicTask create(Context context) {
|
||||
return new SplitPeriodicTask();
|
||||
}
|
||||
@Override
|
||||
public PeriodicTask create(Context context) {
|
||||
return new SplitPeriodicTask();
|
||||
}
|
||||
}
|
||||
+55
-55
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -25,67 +25,67 @@ import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* This class will periodically perform a task.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class TimerTaskExecutor {
|
||||
|
||||
private final PeriodicTask periodicTask;
|
||||
private final TrackRecordingService trackRecordingService;
|
||||
private final PeriodicTask periodicTask;
|
||||
private final TrackRecordingService trackRecordingService;
|
||||
|
||||
private TimerTask timerTask;
|
||||
private Timer timer;
|
||||
private TimerTask timerTask;
|
||||
private Timer timer;
|
||||
|
||||
public TimerTaskExecutor(PeriodicTask periodicTask, TrackRecordingService trackRecordingService) {
|
||||
this.periodicTask = periodicTask;
|
||||
this.trackRecordingService = trackRecordingService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the periodic task at an interval.
|
||||
*
|
||||
* @param interval the interval in milliseconds
|
||||
*/
|
||||
public void scheduleTask(long interval) {
|
||||
if (interval <= 0) {
|
||||
return;
|
||||
public TimerTaskExecutor(PeriodicTask periodicTask, TrackRecordingService trackRecordingService) {
|
||||
this.periodicTask = periodicTask;
|
||||
this.trackRecordingService = trackRecordingService;
|
||||
}
|
||||
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
|
||||
return;
|
||||
/**
|
||||
* Schedules the periodic task at an interval.
|
||||
*
|
||||
* @param interval the interval in milliseconds
|
||||
*/
|
||||
public void scheduleTask(long interval) {
|
||||
if (interval <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TripStatistics tripStatistics = trackRecordingService.getTripStatistics();
|
||||
if (tripStatistics == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown();
|
||||
periodicTask.start();
|
||||
timerTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
periodicTask.run(trackRecordingService);
|
||||
}
|
||||
};
|
||||
timer = new Timer(TimerTaskExecutor.class.getSimpleName());
|
||||
long next = System.currentTimeMillis() + interval - (tripStatistics.getTotalTime() % interval);
|
||||
timer.scheduleAtFixedRate(timerTask, new Date(next), interval);
|
||||
}
|
||||
|
||||
TripStatistics tripStatistics = trackRecordingService.getTripStatistics();
|
||||
if (tripStatistics == null) {
|
||||
return;
|
||||
/**
|
||||
* Shuts down.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (timerTask != null) {
|
||||
timerTask.cancel();
|
||||
timerTask = null;
|
||||
}
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
}
|
||||
periodicTask.shutdown();
|
||||
}
|
||||
|
||||
shutdown();
|
||||
periodicTask.start();
|
||||
timerTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
periodicTask.run(trackRecordingService);
|
||||
}
|
||||
};
|
||||
timer = new Timer(TimerTaskExecutor.class.getSimpleName());
|
||||
long next = System.currentTimeMillis() + interval - (tripStatistics.getTotalTime() % interval);
|
||||
timer.scheduleAtFixedRate(timerTask, new Date(next), interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (timerTask != null) {
|
||||
timerTask.cancel();
|
||||
timerTask = null;
|
||||
}
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
}
|
||||
periodicTask.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
+80
-80
@@ -32,96 +32,96 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An abstract activity for all the settings activities.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AbstractSettingsActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
|
||||
ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
PreferenceManager preferenceManager = getPreferenceManager();
|
||||
preferenceManager.setSharedPreferencesName(Constants.SETTINGS_NAME);
|
||||
preferenceManager.setSharedPreferencesMode(Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() != android.R.id.home) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a list preference.
|
||||
*
|
||||
* @param listPreference the list preference
|
||||
* @param summary the summary array
|
||||
* @param options the options array
|
||||
* @param values the values array
|
||||
* @param value the value
|
||||
* @param listener optional listener
|
||||
*/
|
||||
protected void configureListPreference(ListPreference listPreference, final String[] summary,
|
||||
final String[] options, final String[] values, String value,
|
||||
final OnPreferenceChangeListener listener) {
|
||||
listPreference.setEntryValues(values);
|
||||
listPreference.setEntries(options);
|
||||
listPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
updatePreferenceSummary(pref, summary, values, (String) newValue);
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(pref, newValue);
|
||||
ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
PreferenceManager preferenceManager = getPreferenceManager();
|
||||
preferenceManager.setSharedPreferencesName(Constants.SETTINGS_NAME);
|
||||
preferenceManager.setSharedPreferencesMode(Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() != android.R.id.home) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
updatePreferenceSummary(listPreference, summary, values, value);
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(listPreference, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the preference summary.
|
||||
*
|
||||
* @param preference the preference
|
||||
* @param summary the summary array
|
||||
* @param values the values array
|
||||
* @param value the value
|
||||
*/
|
||||
private void updatePreferenceSummary(Preference preference, String[] summary, String[] values, String value) {
|
||||
int index = getIndex(values, value);
|
||||
if (index == -1) {
|
||||
preference.setSummary(R.string.value_unknown);
|
||||
} else {
|
||||
preference.setSummary(summary[index]);
|
||||
/**
|
||||
* Configures a list preference.
|
||||
*
|
||||
* @param listPreference the list preference
|
||||
* @param summary the summary array
|
||||
* @param options the options array
|
||||
* @param values the values array
|
||||
* @param value the value
|
||||
* @param listener optional listener
|
||||
*/
|
||||
protected void configureListPreference(ListPreference listPreference, final String[] summary,
|
||||
final String[] options, final String[] values, String value,
|
||||
final OnPreferenceChangeListener listener) {
|
||||
listPreference.setEntryValues(values);
|
||||
listPreference.setEntries(options);
|
||||
listPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
updatePreferenceSummary(pref, summary, values, (String) newValue);
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(pref, newValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
updatePreferenceSummary(listPreference, summary, values, value);
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(listPreference, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array index for a value.
|
||||
*
|
||||
* @param values the array
|
||||
* @param value the value
|
||||
*/
|
||||
private int getIndex(String[] values, String value) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (value.equals(values[i])) {
|
||||
return i;
|
||||
}
|
||||
/**
|
||||
* Update the preference summary.
|
||||
*
|
||||
* @param preference the preference
|
||||
* @param summary the summary array
|
||||
* @param values the values array
|
||||
* @param value the value
|
||||
*/
|
||||
private void updatePreferenceSummary(Preference preference, String[] summary, String[] values, String value) {
|
||||
int index = getIndex(values, value);
|
||||
if (index == -1) {
|
||||
preference.setSummary(R.string.value_unknown);
|
||||
} else {
|
||||
preference.setSummary(summary[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array index for a value.
|
||||
*
|
||||
* @param values the array
|
||||
* @param value the value
|
||||
*/
|
||||
private int getIndex(String[] values, String value) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (value.equals(values[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
+113
-113
@@ -16,11 +16,6 @@
|
||||
|
||||
package com.google.android.apps.mytracks.settings;
|
||||
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
@@ -34,123 +29,128 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* For entering the default activity type.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ActivityTypePreference extends DialogPreference {
|
||||
|
||||
private RecordingSettingsActivity recordingSettingsActivity;
|
||||
private AutoCompleteTextView textView;
|
||||
private Spinner spinner;
|
||||
private RecordingSettingsActivity recordingSettingsActivity;
|
||||
private AutoCompleteTextView textView;
|
||||
private Spinner spinner;
|
||||
|
||||
public ActivityTypePreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setDialogLayoutResource(R.layout.activity_type_preference);
|
||||
setPositiveButtonText(android.R.string.ok);
|
||||
setNegativeButtonText(android.R.string.cancel);
|
||||
setDialogIcon(null);
|
||||
setPersistent(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateDialogView() {
|
||||
View view = super.onCreateDialogView();
|
||||
textView = view.findViewById(R.id.activity_type_preference_text_view);
|
||||
spinner = view.findViewById(R.id.activity_type_preference_spinner);
|
||||
|
||||
String category = PreferencesUtils.getString(
|
||||
getContext(), R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
|
||||
textView.setText(category);
|
||||
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||
getContext(), R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||
textView.setAdapter(adapter);
|
||||
textView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
String iconValue = TrackIconUtils.getIconValue(
|
||||
getContext(), (String) textView.getAdapter().getItem(position));
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
}
|
||||
});
|
||||
textView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (!hasFocus) {
|
||||
String iconValue = TrackIconUtils.getIconValue(
|
||||
getContext(), textView.getText().toString());
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
String iconValue = TrackIconUtils.getIconValue(getContext(), category);
|
||||
spinner.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getContext(), iconValue));
|
||||
spinner.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
recordingSettingsActivity.showChooseActivityTypeDialog();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
spinner.setOnKeyListener(new View.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
recordingSettingsActivity.showChooseActivityTypeDialog();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showDialog(Bundle state) {
|
||||
super.showDialog(state);
|
||||
DialogUtils.setDialogTitleDivider(getContext(), getDialog());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
if (positiveResult) {
|
||||
String value = textView.getText().toString();
|
||||
if (callChangeListener(value)) {
|
||||
PreferencesUtils.setString(getContext(), R.string.default_activity_key, value);
|
||||
}
|
||||
public ActivityTypePreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setDialogLayoutResource(R.layout.activity_type_preference);
|
||||
setPositiveButtonText(android.R.string.ok);
|
||||
setNegativeButtonText(android.R.string.cancel);
|
||||
setDialogIcon(null);
|
||||
setPersistent(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable state) {
|
||||
super.onRestoreInstanceState(state);
|
||||
String iconValue = TrackIconUtils.getIconValue(getContext(), textView.getText().toString());
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
textView.clearFocus();
|
||||
}
|
||||
@Override
|
||||
protected View onCreateDialogView() {
|
||||
View view = super.onCreateDialogView();
|
||||
textView = view.findViewById(R.id.activity_type_preference_text_view);
|
||||
spinner = view.findViewById(R.id.activity_type_preference_spinner);
|
||||
|
||||
/**
|
||||
* Sets the recording settings activity.
|
||||
*
|
||||
* @param activity the activity
|
||||
*/
|
||||
public void setRecordingSettingsActivity(RecordingSettingsActivity activity) {
|
||||
recordingSettingsActivity = activity;
|
||||
}
|
||||
String category = PreferencesUtils.getString(
|
||||
getContext(), R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
|
||||
textView.setText(category);
|
||||
|
||||
/**
|
||||
* Updates the value of the dialog.
|
||||
*
|
||||
* @param iconValue the icon value
|
||||
*/
|
||||
public void updateValue(String iconValue) {
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
textView.setText(
|
||||
recordingSettingsActivity.getString(TrackIconUtils.getIconActivityType(iconValue)));
|
||||
textView.clearFocus();
|
||||
}
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||
getContext(), R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||
textView.setAdapter(adapter);
|
||||
textView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
String iconValue = TrackIconUtils.getIconValue(
|
||||
getContext(), (String) textView.getAdapter().getItem(position));
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
}
|
||||
});
|
||||
textView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (!hasFocus) {
|
||||
String iconValue = TrackIconUtils.getIconValue(
|
||||
getContext(), textView.getText().toString());
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
String iconValue = TrackIconUtils.getIconValue(getContext(), category);
|
||||
spinner.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getContext(), iconValue));
|
||||
spinner.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
recordingSettingsActivity.showChooseActivityTypeDialog();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
spinner.setOnKeyListener(new View.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
recordingSettingsActivity.showChooseActivityTypeDialog();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showDialog(Bundle state) {
|
||||
super.showDialog(state);
|
||||
DialogUtils.setDialogTitleDivider(getContext(), getDialog());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
if (positiveResult) {
|
||||
String value = textView.getText().toString();
|
||||
if (callChangeListener(value)) {
|
||||
PreferencesUtils.setString(getContext(), R.string.default_activity_key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable state) {
|
||||
super.onRestoreInstanceState(state);
|
||||
String iconValue = TrackIconUtils.getIconValue(getContext(), textView.getText().toString());
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
textView.clearFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recording settings activity.
|
||||
*
|
||||
* @param activity the activity
|
||||
*/
|
||||
public void setRecordingSettingsActivity(RecordingSettingsActivity activity) {
|
||||
recordingSettingsActivity = activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the value of the dialog.
|
||||
*
|
||||
* @param iconValue the icon value
|
||||
*/
|
||||
public void updateValue(String iconValue) {
|
||||
TrackIconUtils.setIconSpinner(spinner, iconValue);
|
||||
textView.setText(
|
||||
recordingSettingsActivity.getString(TrackIconUtils.getIconActivityType(iconValue)));
|
||||
textView.clearFocus();
|
||||
}
|
||||
}
|
||||
|
||||
+135
-137
@@ -16,11 +16,6 @@
|
||||
|
||||
package com.google.android.apps.mytracks.settings;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -28,160 +23,163 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An activity for advanced settings.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AdvancedSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
private static final String TAG = AdvancedSettingsActivity.class.getSimpleName();
|
||||
private static final String TAG = AdvancedSettingsActivity.class.getSimpleName();
|
||||
|
||||
private static final int DIALOG_CONFIRM_RESET_ID = 1;
|
||||
private static final int DIALOG_CONFIRM_RESET_ID = 1;
|
||||
|
||||
private Preference resetPreference;
|
||||
private Preference resetPreference;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
|
||||
/*
|
||||
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
|
||||
* Anonymous inner class will get garbage collected.
|
||||
*/
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
/*
|
||||
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
|
||||
* Anonymous inner class will get garbage collected.
|
||||
*/
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (key != null && key.equals(PreferencesUtils.getKey(
|
||||
AdvancedSettingsActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(
|
||||
AdvancedSettingsActivity.this, R.string.recording_track_id_key);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUi();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (key != null && key.equals(PreferencesUtils.getKey(
|
||||
AdvancedSettingsActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(
|
||||
AdvancedSettingsActivity.this, R.string.recording_track_id_key);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUi();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_advanced);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_advanced);
|
||||
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.photo_size_key));
|
||||
int value = PreferencesUtils.getInt(this, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.photo_size_values);
|
||||
String[] options = new String[values.length];
|
||||
String[] summary = new String[values.length];
|
||||
setPhotoSizeSummaryAndOptions(summary, options, values);
|
||||
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.photo_size_key));
|
||||
int value = PreferencesUtils.getInt(this, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.photo_size_values);
|
||||
String[] options = new String[values.length];
|
||||
String[] summary = new String[values.length];
|
||||
setPhotoSizeSummaryAndOptions(summary, options, values);
|
||||
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
||||
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference arg0) {
|
||||
showDialog(DIALOG_CONFIRM_RESET_ID);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
||||
updateUi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_CONFIRM_RESET_ID:
|
||||
return DialogUtils.createConfirmationDialog(this, R.string.settings_reset_confirm_title,
|
||||
getString(R.string.settings_reset_confirm_message),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int button) {
|
||||
onResetPreferencesConfirmed();
|
||||
}
|
||||
});
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setPhotoSizeSummaryAndOptions(String[] summary, String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
if (value == -1) {
|
||||
options[i] = getString(R.string.settings_advanced_photo_size_original);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_original);
|
||||
} else if (value < 1024) {
|
||||
options[i] = getString(R.string.value_integer_kilobyte, value);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_summary, options[i]);
|
||||
} else {
|
||||
int megabyte = value / 1024;
|
||||
options[i] = getString(R.string.value_integer_megabyte, megabyte);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI based on the recording state.
|
||||
*/
|
||||
private void updateUi() {
|
||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
resetPreference.setEnabled(!isRecording);
|
||||
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the user confirms resetting all settings.
|
||||
*/
|
||||
private void onResetPreferencesConfirmed() {
|
||||
// Change preferences in a separate thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(TAG, "Resetting all settings");
|
||||
|
||||
// Actually wipe preferences and save synchronously
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Give UI feedback in the UI thread
|
||||
runOnUiThread(new Runnable() {
|
||||
resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(
|
||||
AdvancedSettingsActivity.this, R.string.settings_reset_done, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
// Restart the settings activity so all changes are loaded
|
||||
Intent intent = getIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
public boolean onPreferenceClick(Preference arg0) {
|
||||
showDialog(DIALOG_CONFIRM_RESET_ID);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
||||
updateUi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_CONFIRM_RESET_ID:
|
||||
return DialogUtils.createConfirmationDialog(this, R.string.settings_reset_confirm_title,
|
||||
getString(R.string.settings_reset_confirm_message),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int button) {
|
||||
onResetPreferencesConfirmed();
|
||||
}
|
||||
});
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setPhotoSizeSummaryAndOptions(String[] summary, String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
if (value == -1) {
|
||||
options[i] = getString(R.string.settings_advanced_photo_size_original);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_original);
|
||||
} else if (value < 1024) {
|
||||
options[i] = getString(R.string.value_integer_kilobyte, value);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_summary, options[i]);
|
||||
} else {
|
||||
int megabyte = value / 1024;
|
||||
options[i] = getString(R.string.value_integer_megabyte, megabyte);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI based on the recording state.
|
||||
*/
|
||||
private void updateUi() {
|
||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
resetPreference.setEnabled(!isRecording);
|
||||
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the user confirms resetting all settings.
|
||||
*/
|
||||
private void onResetPreferencesConfirmed() {
|
||||
// Change preferences in a separate thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(TAG, "Resetting all settings");
|
||||
|
||||
// Actually wipe preferences and save synchronously
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Give UI feedback in the UI thread
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(
|
||||
AdvancedSettingsActivity.this, R.string.settings_reset_done, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
// Restart the settings activity so all changes are loaded
|
||||
Intent intent = getIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
+33
-33
@@ -16,50 +16,50 @@
|
||||
|
||||
package com.google.android.apps.mytracks.settings;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.ListPreference;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An activity for accessing chart settings.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ChartSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_chart);
|
||||
configXAxisListPreference();
|
||||
}
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_chart);
|
||||
configXAxisListPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateUi();
|
||||
}
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateUi();
|
||||
}
|
||||
|
||||
private void updateUi() {
|
||||
CheckBoxPreference speedCheckBoxPreference = (CheckBoxPreference) findPreference(
|
||||
getString(R.string.chart_show_speed_key));
|
||||
speedCheckBoxPreference.setTitle(
|
||||
PreferencesUtils.isReportSpeed(this) ? R.string.stats_speed : R.string.stats_pace);
|
||||
}
|
||||
private void updateUi() {
|
||||
CheckBoxPreference speedCheckBoxPreference = (CheckBoxPreference) findPreference(
|
||||
getString(R.string.chart_show_speed_key));
|
||||
speedCheckBoxPreference.setTitle(
|
||||
PreferencesUtils.isReportSpeed(this) ? R.string.stats_speed : R.string.stats_pace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the x axis list preference.
|
||||
*/
|
||||
private void configXAxisListPreference() {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.chart_x_axis_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.chart_x_axis_key, PreferencesUtils.CHART_X_AXIS_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.chart_x_axis_values);
|
||||
String[] options = getResources().getStringArray(R.array.chart_x_axis_options);
|
||||
configureListPreference(listPreference, options, options, values, value, null);
|
||||
}
|
||||
/**
|
||||
* Configures the x axis list preference.
|
||||
*/
|
||||
private void configXAxisListPreference() {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.chart_x_axis_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.chart_x_axis_key, PreferencesUtils.CHART_X_AXIS_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.chart_x_axis_values);
|
||||
String[] options = getResources().getStringArray(R.array.chart_x_axis_options);
|
||||
configureListPreference(listPreference, options, options, values, value, null);
|
||||
}
|
||||
}
|
||||
|
||||
+302
-302
@@ -30,329 +30,329 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An activity for accessing recording settings.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
implements ChooseActivityTypeCaller {
|
||||
implements ChooseActivityTypeCaller {
|
||||
|
||||
private static final int DIALOG_CHOOSE_ACTIVITY = 0;
|
||||
private static final int DIALOG_CHOOSE_ACTIVITY = 0;
|
||||
|
||||
private ActivityTypePreference activityTypePreference;
|
||||
private ActivityTypePreference activityTypePreference;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_recording);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_recording);
|
||||
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(this);
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(this);
|
||||
|
||||
configFrequencyPreference(R.string.voice_frequency_key,
|
||||
PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.array.frequency_values, metricUnits);
|
||||
configFrequencyPreference(R.string.split_frequency_key,
|
||||
PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.array.frequency_values, metricUnits);
|
||||
configTrackName();
|
||||
configDefaultActivity();
|
||||
configListPreference(R.string.min_recording_interval_key,
|
||||
PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT, R.array.min_recording_interval_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT,
|
||||
R.array.recording_distance_interval_values, metricUnits);
|
||||
configListPreference(R.string.max_recording_distance_key,
|
||||
PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT, R.array.max_recording_distance_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.recording_gps_accuracy_key,
|
||||
PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT, R.array.recording_gps_accuracy_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.auto_resume_track_timeout_key,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT,
|
||||
R.array.auto_resume_track_timeout_values, metricUnits);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id, Bundle bundle) {
|
||||
if (id != DIALOG_CHOOSE_ACTIVITY) {
|
||||
return null;
|
||||
configFrequencyPreference(R.string.voice_frequency_key,
|
||||
PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.array.frequency_values, metricUnits);
|
||||
configFrequencyPreference(R.string.split_frequency_key,
|
||||
PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.array.frequency_values, metricUnits);
|
||||
configTrackName();
|
||||
configDefaultActivity();
|
||||
configListPreference(R.string.min_recording_interval_key,
|
||||
PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT, R.array.min_recording_interval_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT,
|
||||
R.array.recording_distance_interval_values, metricUnits);
|
||||
configListPreference(R.string.max_recording_distance_key,
|
||||
PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT, R.array.max_recording_distance_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.recording_gps_accuracy_key,
|
||||
PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT, R.array.recording_gps_accuracy_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.auto_resume_track_timeout_key,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT,
|
||||
R.array.auto_resume_track_timeout_values, metricUnits);
|
||||
}
|
||||
|
||||
String category = PreferencesUtils.getString(
|
||||
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
|
||||
return ChooseActivityTypeDialogFragment.getDialog(this, category, this);
|
||||
}
|
||||
|
||||
private void configFrequencyPreference(int key, int defaultValue, int valueArray, boolean metricUnits) {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(key));
|
||||
int value = PreferencesUtils.getInt(this, key, defaultValue);
|
||||
String[] values = getResources().getStringArray(valueArray);
|
||||
String[] options = StringUtils.getFrequencyOptions(this, metricUnits);
|
||||
configureListPreference(preference, options, options, values, String.valueOf(value), null);
|
||||
}
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id, Bundle bundle) {
|
||||
if (id != DIALOG_CHOOSE_ACTIVITY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void configTrackName() {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.track_name_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.track_name_key, PreferencesUtils.TRACK_NAME_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.track_name_values);
|
||||
String[] options = getResources().getStringArray(R.array.track_name_options);
|
||||
configureListPreference(preference, options, options, values, value, null);
|
||||
}
|
||||
String category = PreferencesUtils.getString(
|
||||
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
|
||||
return ChooseActivityTypeDialogFragment.getDialog(this, category, this);
|
||||
}
|
||||
|
||||
private void configDefaultActivity() {
|
||||
activityTypePreference = (ActivityTypePreference) findPreference(
|
||||
getString(R.string.default_activity_key));
|
||||
String defaultActivity = PreferencesUtils.getString(
|
||||
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
|
||||
activityTypePreference.setOnPreferenceChangeListener(
|
||||
new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
String stringValue = (String) newValue;
|
||||
pref.setSummary(stringValue != null
|
||||
&& !stringValue.equals(PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT) ? stringValue
|
||||
private void configFrequencyPreference(int key, int defaultValue, int valueArray, boolean metricUnits) {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(key));
|
||||
int value = PreferencesUtils.getInt(this, key, defaultValue);
|
||||
String[] values = getResources().getStringArray(valueArray);
|
||||
String[] options = StringUtils.getFrequencyOptions(this, metricUnits);
|
||||
configureListPreference(preference, options, options, values, String.valueOf(value), null);
|
||||
}
|
||||
|
||||
private void configTrackName() {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.track_name_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.track_name_key, PreferencesUtils.TRACK_NAME_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.track_name_values);
|
||||
String[] options = getResources().getStringArray(R.array.track_name_options);
|
||||
configureListPreference(preference, options, options, values, value, null);
|
||||
}
|
||||
|
||||
private void configDefaultActivity() {
|
||||
activityTypePreference = (ActivityTypePreference) findPreference(
|
||||
getString(R.string.default_activity_key));
|
||||
String defaultActivity = PreferencesUtils.getString(
|
||||
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT);
|
||||
activityTypePreference.setOnPreferenceChangeListener(
|
||||
new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
String stringValue = (String) newValue;
|
||||
pref.setSummary(stringValue != null
|
||||
&& !stringValue.equals(PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT) ? stringValue
|
||||
: getString(R.string.value_unknown));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
activityTypePreference.setSummary(defaultActivity != null
|
||||
&& !defaultActivity.equals(PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT) ? defaultActivity
|
||||
: getString(R.string.value_unknown));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
activityTypePreference.setSummary(defaultActivity != null
|
||||
&& !defaultActivity.equals(PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT) ? defaultActivity
|
||||
: getString(R.string.value_unknown));
|
||||
activityTypePreference.setRecordingSettingsActivity(this);
|
||||
}
|
||||
|
||||
private void configListPreference(
|
||||
int key, int defaultValue, int valueArray, boolean metricUnits) {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(key));
|
||||
int value = PreferencesUtils.getInt(this, key, defaultValue);
|
||||
String[] values = getResources().getStringArray(valueArray);
|
||||
String[] options = new String[values.length];
|
||||
String[] summary = new String[values.length];
|
||||
switch (key) {
|
||||
case R.string.min_recording_interval_key:
|
||||
setMinRecordingIntervalSummaryAndOptions(summary, options, values);
|
||||
break;
|
||||
case R.string.recording_distance_interval_key:
|
||||
setRecordingDistanceIntervalSummaryAndOptions(summary, options, values, metricUnits);
|
||||
break;
|
||||
case R.string.max_recording_distance_key:
|
||||
setMaxRecordingDistanceSummaryAndOptions(summary, options, values, metricUnits);
|
||||
break;
|
||||
case R.string.recording_gps_accuracy_key:
|
||||
setRecordingGpsAccuracySummaryAndOptions(summary, options, values, metricUnits);
|
||||
break;
|
||||
case R.string.auto_resume_track_timeout_key:
|
||||
setAutoResumeTrackTimeoutSummaryAndOptions(summary, options, values);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
activityTypePreference.setRecordingSettingsActivity(this);
|
||||
}
|
||||
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the min recording interval summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
*/
|
||||
private void setMinRecordingIntervalSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
switch (value) {
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_BATTERY_LIFE:
|
||||
options[i] = getString(R.string.value_adapt_battery_life);
|
||||
summary[i] = options[i];
|
||||
break;
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_ACCURACY:
|
||||
options[i] = getString(R.string.value_adapt_accuracy);
|
||||
summary[i] = options[i];
|
||||
break;
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT:
|
||||
options[i] = getString(R.string.value_smallest_recommended);
|
||||
summary[i] = getString(R.string.value_smallest);
|
||||
break;
|
||||
default:
|
||||
options[i] = value < 60 ? getString(R.string.value_integer_second, value)
|
||||
: getString(R.string.value_integer_minute, value / 60);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_location_frequency_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recording distance interval summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setRecordingDistanceIntervalSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = getString(R.string.value_integer_meter, value);
|
||||
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = getString(R.string.value_integer_feet, feet);
|
||||
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
}
|
||||
summary[i] = getString(R.string.settings_recording_location_frequency_summary, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max recording distance summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setMaxRecordingDistanceSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = getString(R.string.value_integer_meter, value);
|
||||
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
if (feet < 2000) {
|
||||
displayValue = getString(R.string.value_integer_feet, feet);
|
||||
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
double mile = feet * UnitConversions.FT_TO_MI;
|
||||
displayValue = getString(R.string.value_float_mile, mile);
|
||||
options[i] = displayValue;
|
||||
}
|
||||
}
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_max_recording_distance_summary, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recording gps accuracy summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setRecordingGpsAccuracySummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = getString(R.string.value_integer_meter, value);
|
||||
switch (value) {
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
|
||||
options[i] = getString(R.string.value_integer_meter_recommended, value);
|
||||
break;
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
|
||||
options[i] = getString(R.string.value_integer_meter_excellent_gps, value);
|
||||
break;
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_POOR:
|
||||
options[i] = getString(R.string.value_integer_meter_poor_gps, value);
|
||||
break;
|
||||
default:
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
if (feet < 2000) {
|
||||
displayValue = getString(R.string.value_integer_feet, feet);
|
||||
switch (value) {
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
|
||||
options[i] = getString(R.string.value_integer_feet_recommended, feet);
|
||||
break;
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
|
||||
options[i] = getString(R.string.value_integer_feet_excellent_gps, feet);
|
||||
break;
|
||||
private void configListPreference(
|
||||
int key, int defaultValue, int valueArray, boolean metricUnits) {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(key));
|
||||
int value = PreferencesUtils.getInt(this, key, defaultValue);
|
||||
String[] values = getResources().getStringArray(valueArray);
|
||||
String[] options = new String[values.length];
|
||||
String[] summary = new String[values.length];
|
||||
switch (key) {
|
||||
case R.string.min_recording_interval_key:
|
||||
setMinRecordingIntervalSummaryAndOptions(summary, options, values);
|
||||
break;
|
||||
case R.string.recording_distance_interval_key:
|
||||
setRecordingDistanceIntervalSummaryAndOptions(summary, options, values, metricUnits);
|
||||
break;
|
||||
case R.string.max_recording_distance_key:
|
||||
setMaxRecordingDistanceSummaryAndOptions(summary, options, values, metricUnits);
|
||||
break;
|
||||
case R.string.recording_gps_accuracy_key:
|
||||
setRecordingGpsAccuracySummaryAndOptions(summary, options, values, metricUnits);
|
||||
break;
|
||||
case R.string.auto_resume_track_timeout_key:
|
||||
setAutoResumeTrackTimeoutSummaryAndOptions(summary, options, values);
|
||||
break;
|
||||
default:
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
double mile = feet * UnitConversions.FT_TO_MI;
|
||||
displayValue = getString(R.string.value_float_mile, mile);
|
||||
if (value == PreferencesUtils.RECORDING_GPS_ACCURACY_POOR) {
|
||||
options[i] = getString(R.string.value_float_mile_poor_gps, mile);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_min_required_accuracy_summary, displayValue);
|
||||
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the auto resume track timeout summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
*/
|
||||
private void setAutoResumeTrackTimeoutSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
switch (value) {
|
||||
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_NEVER:
|
||||
options[i] = getString(R.string.value_never);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_never_summary);
|
||||
break;
|
||||
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_ALWAYS:
|
||||
options[i] = getString(R.string.value_always);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_always_summary);
|
||||
break;
|
||||
default:
|
||||
options[i] = getString(R.string.value_integer_minute, value);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_summary, options[i]);
|
||||
}
|
||||
/**
|
||||
* Sets the min recording interval summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
*/
|
||||
private void setMinRecordingIntervalSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
switch (value) {
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_BATTERY_LIFE:
|
||||
options[i] = getString(R.string.value_adapt_battery_life);
|
||||
summary[i] = options[i];
|
||||
break;
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_ACCURACY:
|
||||
options[i] = getString(R.string.value_adapt_accuracy);
|
||||
summary[i] = options[i];
|
||||
break;
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT:
|
||||
options[i] = getString(R.string.value_smallest_recommended);
|
||||
summary[i] = getString(R.string.value_smallest);
|
||||
break;
|
||||
default:
|
||||
options[i] = value < 60 ? getString(R.string.value_integer_second, value)
|
||||
: getString(R.string.value_integer_minute, value / 60);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_location_frequency_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void showChooseActivityTypeDialog() {
|
||||
try {
|
||||
removeDialog(DIALOG_CHOOSE_ACTIVITY);
|
||||
} catch (Exception e) {
|
||||
// Can safely ignore.
|
||||
/**
|
||||
* Sets the recording distance interval summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setRecordingDistanceIntervalSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = getString(R.string.value_integer_meter, value);
|
||||
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = getString(R.string.value_integer_feet, feet);
|
||||
if (value == PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
}
|
||||
summary[i] = getString(R.string.settings_recording_location_frequency_summary, displayValue);
|
||||
}
|
||||
}
|
||||
showDialog(DIALOG_CHOOSE_ACTIVITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
activityTypePreference.updateValue(iconValue);
|
||||
}
|
||||
/**
|
||||
* Sets the max recording distance summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setMaxRecordingDistanceSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = getString(R.string.value_integer_meter, value);
|
||||
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
if (feet < 2000) {
|
||||
displayValue = getString(R.string.value_integer_feet, feet);
|
||||
if (value == PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT) {
|
||||
options[i] = getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
double mile = feet * UnitConversions.FT_TO_MI;
|
||||
displayValue = getString(R.string.value_float_mile, mile);
|
||||
options[i] = displayValue;
|
||||
}
|
||||
}
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_max_recording_distance_summary, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recording gps accuracy summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setRecordingGpsAccuracySummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = getString(R.string.value_integer_meter, value);
|
||||
switch (value) {
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
|
||||
options[i] = getString(R.string.value_integer_meter_recommended, value);
|
||||
break;
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
|
||||
options[i] = getString(R.string.value_integer_meter_excellent_gps, value);
|
||||
break;
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_POOR:
|
||||
options[i] = getString(R.string.value_integer_meter_poor_gps, value);
|
||||
break;
|
||||
default:
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
if (feet < 2000) {
|
||||
displayValue = getString(R.string.value_integer_feet, feet);
|
||||
switch (value) {
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT:
|
||||
options[i] = getString(R.string.value_integer_feet_recommended, feet);
|
||||
break;
|
||||
case PreferencesUtils.RECORDING_GPS_ACCURACY_EXCELLENT:
|
||||
options[i] = getString(R.string.value_integer_feet_excellent_gps, feet);
|
||||
break;
|
||||
default:
|
||||
options[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
double mile = feet * UnitConversions.FT_TO_MI;
|
||||
displayValue = getString(R.string.value_float_mile, mile);
|
||||
if (value == PreferencesUtils.RECORDING_GPS_ACCURACY_POOR) {
|
||||
options[i] = getString(R.string.value_float_mile_poor_gps, mile);
|
||||
} else {
|
||||
options[i] = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_min_required_accuracy_summary, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the auto resume track timeout summary and options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
*/
|
||||
private void setAutoResumeTrackTimeoutSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
switch (value) {
|
||||
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_NEVER:
|
||||
options[i] = getString(R.string.value_never);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_never_summary);
|
||||
break;
|
||||
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_ALWAYS:
|
||||
options[i] = getString(R.string.value_always);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_always_summary);
|
||||
break;
|
||||
default:
|
||||
options[i] = getString(R.string.value_integer_minute, value);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void showChooseActivityTypeDialog() {
|
||||
try {
|
||||
removeDialog(DIALOG_CHOOSE_ACTIVITY);
|
||||
} catch (Exception e) {
|
||||
// Can safely ignore.
|
||||
}
|
||||
showDialog(DIALOG_CHOOSE_ACTIVITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
activityTypePreference.updateValue(iconValue);
|
||||
}
|
||||
}
|
||||
|
||||
+44
-44
@@ -33,58 +33,58 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* An activity for accessing sensor settings.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SensorSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_sensors);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_sensors);
|
||||
|
||||
findPreference(getString(R.string.settings_sensor_bluetooth_pairing_key))
|
||||
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
|
||||
startActivity(settingsIntent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Update each time in case the list of bluetooth sensors has changed
|
||||
configBluetoothSensor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bluetooth sensor.
|
||||
*/
|
||||
private void configBluetoothSensor() {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.bluetooth_sensor_key));
|
||||
String value = PreferencesUtils.getString(this, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
List<String> devicesNameList = new ArrayList<>();
|
||||
List<String> devicesAddressList = new ArrayList<>();
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter != null) {
|
||||
BluetoothDeviceUtils.populateDeviceLists(bluetoothAdapter, devicesNameList, devicesAddressList);
|
||||
findPreference(getString(R.string.settings_sensor_bluetooth_pairing_key))
|
||||
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
|
||||
startActivity(settingsIntent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Was the previously configured device unpaired? Then forget it.
|
||||
if (!devicesAddressList.contains(value)) {
|
||||
value = PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT;
|
||||
PreferencesUtils.setString(this, R.string.bluetooth_sensor_key, value);
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Update each time in case the list of bluetooth sensors has changed
|
||||
configBluetoothSensor();
|
||||
}
|
||||
|
||||
devicesNameList.add(0, getString(R.string.value_none));
|
||||
devicesAddressList.add(0, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
/**
|
||||
* Configures the bluetooth sensor.
|
||||
*/
|
||||
private void configBluetoothSensor() {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.bluetooth_sensor_key));
|
||||
String value = PreferencesUtils.getString(this, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
List<String> devicesNameList = new ArrayList<>();
|
||||
List<String> devicesAddressList = new ArrayList<>();
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter != null) {
|
||||
BluetoothDeviceUtils.populateDeviceLists(bluetoothAdapter, devicesNameList, devicesAddressList);
|
||||
}
|
||||
|
||||
String[] options = devicesNameList.toArray(new String[0]);
|
||||
String[] values = devicesAddressList.toArray(new String[0]);
|
||||
configureListPreference(preference, options, options, values, value, null);
|
||||
}
|
||||
// Was the previously configured device unpaired? Then forget it.
|
||||
if (!devicesAddressList.contains(value)) {
|
||||
value = PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT;
|
||||
PreferencesUtils.setString(this, R.string.bluetooth_sensor_key, value);
|
||||
}
|
||||
|
||||
devicesNameList.add(0, getString(R.string.value_none));
|
||||
devicesAddressList.add(0, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
|
||||
String[] options = devicesNameList.toArray(new String[0]);
|
||||
String[] values = devicesAddressList.toArray(new String[0]);
|
||||
configureListPreference(preference, options, options, values, value, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,39 +26,39 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An activity for accessing settings.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
|
||||
configPreference(R.string.settings_chart_key, ChartSettingsActivity.class);
|
||||
configPreference(R.string.settings_stats_key, StatsSettingsActivity.class);
|
||||
configPreference(R.string.settings_recording_key, RecordingSettingsActivity.class);
|
||||
configPreference(R.string.settings_sensor_key, SensorSettingsActivity.class);
|
||||
configPreference(R.string.settings_advanced_key, AdvancedSettingsActivity.class);
|
||||
}
|
||||
configPreference(R.string.settings_chart_key, ChartSettingsActivity.class);
|
||||
configPreference(R.string.settings_stats_key, StatsSettingsActivity.class);
|
||||
configPreference(R.string.settings_recording_key, RecordingSettingsActivity.class);
|
||||
configPreference(R.string.settings_sensor_key, SensorSettingsActivity.class);
|
||||
configPreference(R.string.settings_advanced_key, AdvancedSettingsActivity.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a preference by starting a new activity when it is clicked.
|
||||
*
|
||||
* @param key the preference key
|
||||
* @param cl the class to start the new activity
|
||||
*/
|
||||
private void configPreference(int key, final Class<?> cl) {
|
||||
Preference preference = findPreference(getString(key));
|
||||
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference pref) {
|
||||
Intent intent = IntentUtils.newIntent(SettingsActivity.this, cl);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Configures a preference by starting a new activity when it is clicked.
|
||||
*
|
||||
* @param key the preference key
|
||||
* @param cl the class to start the new activity
|
||||
*/
|
||||
private void configPreference(int key, final Class<?> cl) {
|
||||
Preference preference = findPreference(getString(key));
|
||||
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference pref) {
|
||||
Intent intent = IntentUtils.newIntent(SettingsActivity.this, cl);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+49
-49
@@ -16,68 +16,68 @@
|
||||
|
||||
package com.google.android.apps.mytracks.settings;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
/**
|
||||
* An activity for accessing stats settings.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class StatsSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_statistics);
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_statistics);
|
||||
|
||||
/*
|
||||
* Note configureUnitsListPreference will trigger
|
||||
* configureRateListPreference
|
||||
/*
|
||||
* Note configureUnitsListPreference will trigger
|
||||
* configureRateListPreference
|
||||
*/
|
||||
configUnitsListPreference();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the preferred units list preference.
|
||||
*/
|
||||
configUnitsListPreference();
|
||||
}
|
||||
private void configUnitsListPreference() {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_units_key));
|
||||
OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
|
||||
|
||||
/**
|
||||
* Configures the preferred units list preference.
|
||||
*/
|
||||
private void configUnitsListPreference() {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_units_key));
|
||||
OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
boolean metricUnits = PreferencesUtils.STATS_UNITS_DEFAULT.equals(newValue);
|
||||
configRateListPreference(metricUnits);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_units_values);
|
||||
String[] options = getResources().getStringArray(R.array.stats_units_options);
|
||||
configureListPreference(listPreference, options, options, values, value, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
boolean metricUnits = PreferencesUtils.STATS_UNITS_DEFAULT.equals(newValue);
|
||||
configRateListPreference(metricUnits);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_units_values);
|
||||
String[] options = getResources().getStringArray(R.array.stats_units_options);
|
||||
configureListPreference(listPreference, options, options, values, value, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the preferred rate list preference.
|
||||
*
|
||||
* @param metricUnits true if metric units
|
||||
*/
|
||||
private void configRateListPreference(boolean metricUnits) {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_rate_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_rate_key, PreferencesUtils.STATS_RATE_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_rate_values);
|
||||
String[] options = getResources().getStringArray(
|
||||
metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
|
||||
configureListPreference(listPreference, options, options, values, value, null);
|
||||
}
|
||||
/**
|
||||
* Configures the preferred rate list preference.
|
||||
*
|
||||
* @param metricUnits true if metric units
|
||||
*/
|
||||
private void configRateListPreference(boolean metricUnits) {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_rate_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_rate_key, PreferencesUtils.STATS_RATE_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_rate_values);
|
||||
String[] options = getResources().getStringArray(
|
||||
metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
|
||||
configureListPreference(listPreference, options, options, values, value, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -22,115 +22,113 @@ import androidx.annotation.NonNull;
|
||||
* This class maintains a buffer of doubles. This buffer is a convenient class
|
||||
* for storing a series of doubles and calculating information about them. This
|
||||
* is a FIFO buffer.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class DoubleBuffer {
|
||||
|
||||
// The location that the next write will occur at.
|
||||
private int index;
|
||||
// The sliding buffer of doubles.
|
||||
private final double[] buffer;
|
||||
// The location that the next write will occur at.
|
||||
private int index;
|
||||
// True if the buffer is full
|
||||
private boolean isFull;
|
||||
|
||||
// The sliding buffer of doubles.
|
||||
private final double[] buffer;
|
||||
|
||||
// True if the buffer is full
|
||||
private boolean isFull;
|
||||
|
||||
/**
|
||||
* Creates a buffer with a certain size.
|
||||
*
|
||||
* @param size the size
|
||||
*/
|
||||
public DoubleBuffer(int size) {
|
||||
if (size < 1) {
|
||||
throw new IllegalArgumentException("The buffer size must be greater than 1.");
|
||||
}
|
||||
buffer = new double[size];
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the buffer.
|
||||
*/
|
||||
public void reset() {
|
||||
index = 0;
|
||||
isFull = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the buffer is full.
|
||||
*/
|
||||
public boolean isFull() {
|
||||
return isFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the average of the buffer.
|
||||
*/
|
||||
public double getAverage() {
|
||||
int numberOfEntries = isFull ? buffer.length : index;
|
||||
if (numberOfEntries == 0) {
|
||||
return 0;
|
||||
}
|
||||
double sum = 0;
|
||||
for (int i = 0; i < numberOfEntries; i++) {
|
||||
sum += buffer[i];
|
||||
}
|
||||
return sum / numberOfEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the average and the variance of the buffer.
|
||||
*
|
||||
* @return an array of two elements - the first is the average, the second is
|
||||
* the variance
|
||||
*/
|
||||
public double[] getAverageAndVariance() {
|
||||
int numberOfEntries = isFull ? buffer.length : index;
|
||||
if (numberOfEntries == 0) {
|
||||
return new double[] { 0, 0 };
|
||||
/**
|
||||
* Creates a buffer with a certain size.
|
||||
*
|
||||
* @param size the size
|
||||
*/
|
||||
public DoubleBuffer(int size) {
|
||||
if (size < 1) {
|
||||
throw new IllegalArgumentException("The buffer size must be greater than 1.");
|
||||
}
|
||||
buffer = new double[size];
|
||||
reset();
|
||||
}
|
||||
|
||||
double sum = 0;
|
||||
double sumSquares = 0;
|
||||
for (int i = 0; i < numberOfEntries; i++) {
|
||||
sum += buffer[i];
|
||||
sumSquares += Math.pow(buffer[i], 2);
|
||||
/**
|
||||
* Resets the buffer.
|
||||
*/
|
||||
public void reset() {
|
||||
index = 0;
|
||||
isFull = false;
|
||||
}
|
||||
|
||||
double average = sum / numberOfEntries;
|
||||
double variance = sumSquares / numberOfEntries - Math.pow(average, 2);
|
||||
return new double[] { average, variance };
|
||||
}
|
||||
/**
|
||||
* Returns true if the buffer is full.
|
||||
*/
|
||||
public boolean isFull() {
|
||||
return isFull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a double to the buffer. If the buffer is full the oldest element is
|
||||
* overwritten.
|
||||
*
|
||||
* @param value the double to add
|
||||
*/
|
||||
public void setNext(double value) {
|
||||
if (index == buffer.length) {
|
||||
index = 0;
|
||||
/**
|
||||
* Gets the average of the buffer.
|
||||
*/
|
||||
public double getAverage() {
|
||||
int numberOfEntries = isFull ? buffer.length : index;
|
||||
if (numberOfEntries == 0) {
|
||||
return 0;
|
||||
}
|
||||
double sum = 0;
|
||||
for (int i = 0; i < numberOfEntries; i++) {
|
||||
sum += buffer[i];
|
||||
}
|
||||
return sum / numberOfEntries;
|
||||
}
|
||||
buffer[index] = value;
|
||||
index++;
|
||||
if (index == buffer.length) {
|
||||
isFull = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("Full: ");
|
||||
builder.append(isFull);
|
||||
builder.append("\n");
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
builder.append((i == index) ? "<<" : "[");
|
||||
builder.append(buffer[i]);
|
||||
builder.append((i == index) ? ">> " : "] ");
|
||||
/**
|
||||
* Gets the average and the variance of the buffer.
|
||||
*
|
||||
* @return an array of two elements - the first is the average, the second is
|
||||
* the variance
|
||||
*/
|
||||
public double[] getAverageAndVariance() {
|
||||
int numberOfEntries = isFull ? buffer.length : index;
|
||||
if (numberOfEntries == 0) {
|
||||
return new double[]{0, 0};
|
||||
}
|
||||
|
||||
double sum = 0;
|
||||
double sumSquares = 0;
|
||||
for (int i = 0; i < numberOfEntries; i++) {
|
||||
sum += buffer[i];
|
||||
sumSquares += Math.pow(buffer[i], 2);
|
||||
}
|
||||
|
||||
double average = sum / numberOfEntries;
|
||||
double variance = sumSquares / numberOfEntries - Math.pow(average, 2);
|
||||
return new double[]{average, variance};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a double to the buffer. If the buffer is full the oldest element is
|
||||
* overwritten.
|
||||
*
|
||||
* @param value the double to add
|
||||
*/
|
||||
public void setNext(double value) {
|
||||
if (index == buffer.length) {
|
||||
index = 0;
|
||||
}
|
||||
buffer[index] = value;
|
||||
index++;
|
||||
if (index == buffer.length) {
|
||||
isFull = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("Full: ");
|
||||
builder.append(isFull);
|
||||
builder.append("\n");
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
builder.append((i == index) ? "<<" : "[");
|
||||
builder.append(buffer[i]);
|
||||
builder.append((i == index) ? ">> " : "] ");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -20,101 +20,101 @@ import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* A helper class that tracks a minimum and a maximum of a variable.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ExtremityMonitor {
|
||||
|
||||
// The smallest value seen so far.
|
||||
private double min;
|
||||
// The smallest value seen so far.
|
||||
private double min;
|
||||
|
||||
// The largest value seen so far.
|
||||
private double max;
|
||||
// The largest value seen so far.
|
||||
private double max;
|
||||
|
||||
public ExtremityMonitor() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this object to it's initial state where the min and max are unknown.
|
||||
*/
|
||||
public void reset() {
|
||||
min = Double.POSITIVE_INFINITY;
|
||||
max = Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum value seen.
|
||||
*/
|
||||
public double getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum value seen.
|
||||
*/
|
||||
public double getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the min and the max with a new value.
|
||||
*
|
||||
* @param value the new value
|
||||
* @return true if an extremity was found
|
||||
*/
|
||||
public boolean update(double value) {
|
||||
boolean changed = false;
|
||||
if (value < min) {
|
||||
min = value;
|
||||
changed = true;
|
||||
public ExtremityMonitor() {
|
||||
reset();
|
||||
}
|
||||
if (value > max) {
|
||||
max = value;
|
||||
changed = true;
|
||||
|
||||
/**
|
||||
* Resets this object to it's initial state where the min and max are unknown.
|
||||
*/
|
||||
public void reset() {
|
||||
min = Double.POSITIVE_INFINITY;
|
||||
max = Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum and maximum values.
|
||||
*
|
||||
* @param min the minimum value
|
||||
* @param max the maximum value
|
||||
*/
|
||||
public void set(double min, double max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
/**
|
||||
* Gets the minimum value seen.
|
||||
*/
|
||||
public double getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum value.
|
||||
*
|
||||
* @param min the minimum value
|
||||
*/
|
||||
public void setMin(double min) {
|
||||
this.min = min;
|
||||
}
|
||||
/**
|
||||
* Sets the minimum value.
|
||||
*
|
||||
* @param min the minimum value
|
||||
*/
|
||||
public void setMin(double min) {
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum value.
|
||||
*
|
||||
* @param max the maximum value
|
||||
*/
|
||||
public void setMax(double max) {
|
||||
this.max = max;
|
||||
}
|
||||
/**
|
||||
* Gets the maximum value seen.
|
||||
*/
|
||||
public double getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if has data.
|
||||
*/
|
||||
public boolean hasData() {
|
||||
return min != Double.POSITIVE_INFINITY && max != Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
/**
|
||||
* Sets the maximum value.
|
||||
*
|
||||
* @param max the maximum value
|
||||
*/
|
||||
public void setMax(double max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Min: " + min + " Max: " + max;
|
||||
}
|
||||
/**
|
||||
* Updates the min and the max with a new value.
|
||||
*
|
||||
* @param value the new value
|
||||
* @return true if an extremity was found
|
||||
*/
|
||||
public boolean update(double value) {
|
||||
boolean changed = false;
|
||||
if (value < min) {
|
||||
min = value;
|
||||
changed = true;
|
||||
}
|
||||
if (value > max) {
|
||||
max = value;
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum and maximum values.
|
||||
*
|
||||
* @param min the minimum value
|
||||
* @param max the maximum value
|
||||
*/
|
||||
public void set(double min, double max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if has data.
|
||||
*/
|
||||
public boolean hasData() {
|
||||
return min != Double.POSITIVE_INFINITY && max != Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Min: " + min + " Max: " + max;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user