forked from upstream-mirrors/OpenTracks
443 lines
14 KiB
Java
443 lines
14 KiB
Java
/*
|
|
* Copyright 2008 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
* use this file except in compliance with the License. You may obtain a copy of
|
|
* the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations under
|
|
* the License.
|
|
*/
|
|
package com.google.android.apps.mytracks;
|
|
|
|
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
|
import com.google.android.apps.mytracks.content.MyTracksProviderUtilsImpl;
|
|
import com.google.android.apps.mytracks.content.Track;
|
|
import com.google.android.apps.mytracks.content.TracksColumns;
|
|
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
|
import com.google.android.apps.mytracks.services.StatusAnnouncerFactory;
|
|
import com.google.android.apps.mytracks.util.ApiFeatures;
|
|
import com.google.android.maps.mytracks.R;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.content.SharedPreferences;
|
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|
import android.database.ContentObserver;
|
|
import android.location.Location;
|
|
import android.location.LocationListener;
|
|
import android.location.LocationManager;
|
|
import android.location.LocationProvider;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.util.DisplayMetrics;
|
|
import android.util.Log;
|
|
import android.view.Menu;
|
|
import android.view.MenuItem;
|
|
import android.view.Window;
|
|
import android.widget.ScrollView;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
/**
|
|
* An activity that displays track statistics to the user.
|
|
*
|
|
* @author Sandor Dornbush
|
|
*/
|
|
public class StatsActivity extends Activity
|
|
implements OnSharedPreferenceChangeListener {
|
|
|
|
private StatsUtilities utils;
|
|
private UIUpdateThread thread;
|
|
|
|
private ContentObserver observer;
|
|
|
|
/**
|
|
* The id of the currently selected track.
|
|
*/
|
|
private long selectedTrackId = -1;
|
|
|
|
/**
|
|
* The id of the currently recording track.
|
|
*/
|
|
private long recordingTrackId = -1;
|
|
|
|
/**
|
|
* The start time of the selected track.
|
|
*/
|
|
private long startTime = -1;
|
|
|
|
/**
|
|
* True if distances should be displayed in metric units (from shared
|
|
* preferences).
|
|
*/
|
|
private boolean metricUnits = true;
|
|
|
|
/**
|
|
* True if pace should be displayed as dist/time (from shared preferences).
|
|
*/
|
|
private boolean displaySpeed = true;
|
|
|
|
/**
|
|
* true if activity has resumed and is on top
|
|
*/
|
|
private boolean activityOnTop = false;
|
|
|
|
/**
|
|
* If true, the statistics for the current segment are shown, otherwise
|
|
* for the full track.
|
|
*/
|
|
private boolean showCurrentSegment = false;
|
|
|
|
private MyTracksProviderUtils providerUtils;
|
|
|
|
/**
|
|
* A runnable for posting to the UI thread. Will update the total time field.
|
|
*/
|
|
private final Runnable updateResults = new Runnable() {
|
|
public void run() {
|
|
updateTotalTime();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A thread that updates the total time field every second.
|
|
*/
|
|
private class UIUpdateThread extends Thread {
|
|
|
|
public UIUpdateThread() {
|
|
super();
|
|
Log.i(MyTracksConstants.TAG, "Created UI update thread");
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
Log.i(MyTracksConstants.TAG, "Started UI update thread");
|
|
while (MyTracks.getInstance().isRecording()) {
|
|
long sleeptime = 1000;
|
|
runOnUiThread(updateResults);
|
|
try {
|
|
Thread.sleep(sleeptime);
|
|
} catch (InterruptedException e) {
|
|
Log.w(MyTracksConstants.TAG,
|
|
"StatsActivity: Caught exception on sleep.", e);
|
|
break;
|
|
}
|
|
}
|
|
Log.w(MyTracksConstants.TAG, "UIUpdateThread finished.");
|
|
}
|
|
}
|
|
|
|
/** Called when the activity is first created. */
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
utils = new StatsUtilities(this);
|
|
providerUtils = new MyTracksProviderUtilsImpl(getContentResolver());
|
|
|
|
// The volume we want to control is the Text-To-Speech volume
|
|
int volumeStream =
|
|
new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream();
|
|
setVolumeControlStream(volumeStream);
|
|
|
|
// We don't need a window title bar:
|
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
|
|
setContentView(R.layout.stats);
|
|
|
|
Handler contentHandler = new Handler();
|
|
observer = new ContentObserver(contentHandler) {
|
|
@Override
|
|
public void onChange(boolean selfChange) {
|
|
Log.d(MyTracksConstants.TAG, "StatsActivity: ContentObserver.onChange");
|
|
restoreStats();
|
|
super.onChange(selfChange);
|
|
}
|
|
};
|
|
|
|
ScrollView sv = ((ScrollView) findViewById(R.id.scrolly));
|
|
sv.setScrollBarStyle(ScrollView.SCROLLBARS_OUTSIDE_INSET);
|
|
|
|
SharedPreferences preferences =
|
|
getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0);
|
|
if (preferences != null) {
|
|
selectedTrackId = preferences.getLong(
|
|
getString(R.string.selected_track_key), -1);
|
|
recordingTrackId = preferences.getLong(
|
|
getString(R.string.recording_track_key), -1);
|
|
metricUnits = preferences.getBoolean(
|
|
getString(R.string.metric_units_key), true);
|
|
displaySpeed =
|
|
preferences.getBoolean(getString(R.string.report_speed_key), true);
|
|
checkLiveTrack();
|
|
restoreStats();
|
|
showUnknownLocation();
|
|
preferences.registerOnSharedPreferenceChangeListener(this);
|
|
}
|
|
utils.setMetricUnits(metricUnits);
|
|
utils.setReportSpeed(displaySpeed);
|
|
utils.updateUnits();
|
|
utils.setSpeedLabel(R.id.speed_label, R.string.speed, R.string.pace_label);
|
|
utils.setSpeedLabels();
|
|
|
|
DisplayMetrics metrics = new DisplayMetrics();
|
|
getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
|
if (metrics.heightPixels > 600) {
|
|
((TextView) findViewById(R.id.speed_register)).setTextSize(80.0f);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
unregisterLocationListener();
|
|
if (thread != null) {
|
|
thread.interrupt();
|
|
thread = null;
|
|
}
|
|
getContentResolver().unregisterContentObserver(observer);
|
|
activityOnTop = false;
|
|
super.onPause();
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
activityOnTop = true;
|
|
checkLiveTrack();
|
|
restoreStats();
|
|
showUnknownLocation();
|
|
super.onResume();
|
|
}
|
|
|
|
@Override
|
|
public void onSharedPreferenceChanged(
|
|
final SharedPreferences sharedPreferences, final String key) {
|
|
Log.d(MyTracksConstants.TAG,
|
|
"StatsActivity: onSharedPreferences changed " + key);
|
|
if (key != null) {
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (key.equals(getString(R.string.selected_track_key))) {
|
|
selectedTrackId =
|
|
sharedPreferences.getLong(
|
|
getString(R.string.selected_track_key),
|
|
-1);
|
|
checkLiveTrack();
|
|
restoreStats();
|
|
showUnknownLocation();
|
|
} else if (key.equals(getString(R.string.recording_track_key))) {
|
|
recordingTrackId =
|
|
sharedPreferences.getLong(
|
|
getString(R.string.recording_track_key),
|
|
-1);
|
|
checkLiveTrack();
|
|
restoreStats();
|
|
showUnknownLocation();
|
|
} else if (key.equals(getString(R.string.metric_units_key))) {
|
|
metricUnits =
|
|
sharedPreferences.getBoolean(
|
|
getString(R.string.metric_units_key), true);
|
|
utils.setMetricUnits(metricUnits);
|
|
utils.updateUnits();
|
|
restoreStats();
|
|
} else if (key.equals(getString(R.string.report_speed_key))) {
|
|
displaySpeed =
|
|
sharedPreferences.getBoolean(
|
|
getString(R.string.report_speed_key),
|
|
true);
|
|
utils.setReportSpeed(displaySpeed);
|
|
utils.updateUnits();
|
|
utils.setSpeedLabel(
|
|
R.id.speed_label, R.string.speed, R.string.pace_label);
|
|
Log.w(MyTracksConstants.TAG, "Setting speed labels");
|
|
utils.setSpeedLabels();
|
|
restoreStats();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
super.onCreateOptionsMenu(menu);
|
|
MenuItem currentSegment = menu.add(0,
|
|
MyTracksConstants.MENU_CURRENT_SEGMENT, 0, R.string.current_segment);
|
|
currentSegment.setIcon(R.drawable.menu_by_distance);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
MenuItem item = menu.findItem(MyTracksConstants.MENU_CURRENT_SEGMENT);
|
|
if (item != null) {
|
|
item.setTitle(showCurrentSegment
|
|
? getString(R.string.current_track)
|
|
: getString(R.string.current_segment));
|
|
}
|
|
return super.onPrepareOptionsMenu(menu);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case MyTracksConstants.MENU_CURRENT_SEGMENT:
|
|
showCurrentSegment = !showCurrentSegment;
|
|
restoreStats();
|
|
return true;
|
|
}
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
private final LocationListener locationListener = new LocationListener() {
|
|
@Override
|
|
public void onLocationChanged(Location l) {
|
|
if (selectedTrackIsRecording()) {
|
|
showLocation(l);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onProviderDisabled(String provider) {
|
|
// Do nothing
|
|
}
|
|
|
|
@Override
|
|
public void onProviderEnabled(String provider) {
|
|
// Do nothing
|
|
}
|
|
|
|
@Override
|
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
// Do nothing
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Registers to receive location updates from the GPS location provider.
|
|
*/
|
|
private void registerLocationListener() {
|
|
LocationManager locationManager =
|
|
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
|
if (locationManager != null) {
|
|
LocationProvider gpsProvider =
|
|
locationManager.getProvider(MyTracksConstants.GPS_PROVIDER);
|
|
if (gpsProvider == null) {
|
|
Toast.makeText(this, getString(R.string.error_no_gps_location_provider),
|
|
Toast.LENGTH_LONG).show();
|
|
return;
|
|
} else {
|
|
Log.d(MyTracksConstants.TAG, "StatsActivity: Using location provider "
|
|
+ gpsProvider.getName());
|
|
}
|
|
locationManager.requestLocationUpdates(gpsProvider.getName(),
|
|
0/*minTime*/, 0/*minDist*/, locationListener);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unregisters all location listener.
|
|
*/
|
|
private void unregisterLocationListener() {
|
|
LocationManager locationManager =
|
|
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
|
if (locationManager != null) {
|
|
locationManager.removeUpdates(locationListener);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return true if the selected track is the currently recording track
|
|
*/
|
|
private boolean selectedTrackIsRecording() {
|
|
return MyTracks.getInstance().isRecording()
|
|
&& selectedTrackId == recordingTrackId;
|
|
}
|
|
|
|
/**
|
|
* Reads values for selected tracks from provider and update the UI.
|
|
*/
|
|
private void restoreStats() {
|
|
if (selectedTrackId < 0) {
|
|
utils.setAllToUnknown();
|
|
return;
|
|
}
|
|
|
|
Track track = providerUtils.getTrack(selectedTrackId);
|
|
if (track == null || track.getStatistics() == null) {
|
|
utils.setAllToUnknown();
|
|
return;
|
|
}
|
|
|
|
startTime = track.getStatistics().getStartTime();
|
|
if (!selectedTrackIsRecording()) {
|
|
utils.setTime(R.id.total_time_register,
|
|
track.getStatistics().getTotalTime());
|
|
}
|
|
utils.setAllStats(track.getStatistics());
|
|
}
|
|
|
|
/**
|
|
* Checks if this activity needs to update live track data or not.
|
|
* If so, make sure that:
|
|
* a) a thread keeps updating the total time
|
|
* b) a location listener is registered
|
|
* c) a content observer is registered
|
|
* Otherwise unregister listeners, observers, and kill update thread.
|
|
*/
|
|
private void checkLiveTrack() {
|
|
final boolean isRecording = selectedTrackIsRecording();
|
|
final boolean startThread =
|
|
(thread == null) && isRecording && activityOnTop;
|
|
final boolean killThread =
|
|
(thread != null) && (!isRecording || !activityOnTop);
|
|
if (startThread) {
|
|
thread = new UIUpdateThread();
|
|
thread.start();
|
|
getContentResolver().registerContentObserver(
|
|
TracksColumns.CONTENT_URI, false, observer);
|
|
getContentResolver().registerContentObserver(
|
|
WaypointsColumns.CONTENT_URI, false, observer);
|
|
registerLocationListener();
|
|
} else if (killThread) {
|
|
thread.interrupt();
|
|
thread = null;
|
|
getContentResolver().unregisterContentObserver(observer);
|
|
unregisterLocationListener();
|
|
}
|
|
}
|
|
|
|
public void updateTotalTime() {
|
|
if (selectedTrackIsRecording()) {
|
|
utils.setTime(R.id.total_time_register,
|
|
System.currentTimeMillis() - startTime);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the given location fields (latitude, longitude, altitude) and all
|
|
* other fields.
|
|
*
|
|
* @param l may be null (will set location fields to unknown)
|
|
*/
|
|
private void showLocation(Location l) {
|
|
utils.setAltitude(R.id.elevation_register, l.getAltitude());
|
|
utils.setLatLong(R.id.latitude_register, l.getLatitude());
|
|
utils.setLatLong(R.id.longitude_register, l.getLongitude());
|
|
utils.setSpeed(R.id.speed_register, l.getSpeed() * 3.6);
|
|
}
|
|
|
|
private void showUnknownLocation() {
|
|
utils.setUnknown(R.id.elevation_register);
|
|
utils.setUnknown(R.id.latitude_register);
|
|
utils.setUnknown(R.id.longitude_register);
|
|
utils.setUnknown(R.id.speed_register);
|
|
}
|
|
}
|