Add test cases about TripStatisticsUpdater.addLocation().

This commit is contained in:
youtaol
2013-02-27 18:29:30 +08:00
commit dbfbfb1b1f
910 changed files with 72953 additions and 0 deletions
@@ -0,0 +1,158 @@
/*
* 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
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 com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.util.Log;
/**
* A dynamic speed path descriptor.
*
* @author Vangelis S.
*/
public class DynamicSpeedTrackPathDescriptor implements TrackPathDescriptor {
private final OnSharedPreferenceChangeListener
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.track_color_mode_percentage_key))) {
speedMargin = PreferencesUtils.getInt(context, R.string.track_color_mode_percentage_key,
PreferencesUtils.TRACK_COLOR_MODE_PERCENTAGE_DEFAULT);
}
}
};
private final Context context;
private int speedMargin;
private int slowSpeed;
private int normalSpeed;
private double averageMovingSpeed;
@VisibleForTesting
static final int CRITICAL_DIFFERENCE_PERCENTAGE = 20;
public DynamicSpeedTrackPathDescriptor(Context context) {
this.context = context;
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
}
@Override
public int getSlowSpeed() {
slowSpeed = (int) (averageMovingSpeed - (averageMovingSpeed * speedMargin / 100.0));
return slowSpeed;
}
@Override
public int getNormalSpeed() {
normalSpeed = (int) (averageMovingSpeed + (averageMovingSpeed * speedMargin / 100.0));
return normalSpeed;
}
@Override
public boolean updateState() {
long selectedTrackId = PreferencesUtils.getLong(context, R.string.selected_track_id_key);
if (selectedTrackId == PreferencesUtils.SELECTED_TRACK_ID_DEFAULT) {
Log.d(TAG, "No selected track id.");
return false;
}
Track track = MyTracksProviderUtils.Factory.get(context).getTrack(selectedTrackId);
if (track == null) {
Log.d(TAG, "No track for " + selectedTrackId);
return false;
}
TripStatistics tripStatistics = track.getTripStatistics();
double newAverageMovingSpeed = (int) Math.floor(
tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH);
if (isDifferenceSignificant(averageMovingSpeed, newAverageMovingSpeed)) {
averageMovingSpeed = newAverageMovingSpeed;
return true;
} else {
return false;
}
}
/**
* Returns true if the average moving speed and the new average moving speed
* are significantly different.
*
* @param oldAverageMovingSpeed
* @param newAverageMovingSpeed
*/
@VisibleForTesting
boolean isDifferenceSignificant(double oldAverageMovingSpeed, double newAverageMovingSpeed) {
if (oldAverageMovingSpeed == 0) {
return newAverageMovingSpeed != 0;
}
// Here, both oldAverageMovingSpeed and newAverageMovingSpeed are not zero.
double maxValue = Math.max(oldAverageMovingSpeed, newAverageMovingSpeed);
double differencePercentage = Math.abs(oldAverageMovingSpeed - newAverageMovingSpeed) / maxValue
* 100.0;
return differencePercentage >= CRITICAL_DIFFERENCE_PERCENTAGE;
}
/**
* Gets the speed margin.
*/
@VisibleForTesting
int getSpeedMargin() {
return speedMargin;
}
/**
* Sets the speed margin.
*/
@VisibleForTesting
void setSpeedMargin(int value) {
speedMargin = value;
}
/**
* Gets the average moving speed.
*/
@VisibleForTesting
double getAverageMovingSpeed() {
return averageMovingSpeed;
}
/**
* Sets the average moving speed.
*
* @param value the value
*/
@VisibleForTesting
void setAverageMovingSpeed(double value) {
averageMovingSpeed = value;
}
}
@@ -0,0 +1,77 @@
/*
* 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
/**
* A fixed speed path descriptor.
*
* @author Vangelis S.
*/
public class FixedSpeedTrackPathDescriptor implements TrackPathDescriptor {
private final OnSharedPreferenceChangeListener
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.track_color_mode_slow_key))) {
slowSpeed = PreferencesUtils.getInt(context, R.string.track_color_mode_slow_key,
PreferencesUtils.TRACK_COLOR_MODE_SLOW_DEFAULT);
}
if (key == null || key.equals(
PreferencesUtils.getKey(context, R.string.track_color_mode_medium_key))) {
normalSpeed = PreferencesUtils.getInt(context, R.string.track_color_mode_medium_key,
PreferencesUtils.TRACK_COLOR_MODE_MEDIUM_DEFAULT);
}
}
};
private final Context context;
private int slowSpeed;
private int normalSpeed;
public FixedSpeedTrackPathDescriptor(Context context) {
this.context = context;
context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
}
@Override
public int getSlowSpeed() {
return slowSpeed;
}
@Override
public int getNormalSpeed() {
return normalSpeed;
}
@Override
public boolean updateState() {
return false;
}
}
@@ -0,0 +1,131 @@
/*
* 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import java.util.ArrayList;
import java.util.List;
/**
* A path painter that varies the path colors based on fixed speeds or average
* speed margin depending of the TrackPathDescriptor passed to its constructor.
*
* @author Vangelis S.
*/
public class MultiColorTrackPath implements TrackPath {
private final TrackPathDescriptor trackPathDescriptor;
private final int slowColor;
private final int normalColor;
private final int fastColor;
public MultiColorTrackPath(Context context, TrackPathDescriptor trackPathDescriptor) {
this.trackPathDescriptor = trackPathDescriptor;
slowColor = context.getResources().getColor(R.color.slow_path);
normalColor = context.getResources().getColor(R.color.normal_path);
fastColor = context.getResources().getColor(R.color.fast_path);
}
@Override
public boolean updateState() {
return trackPathDescriptor.updateState();
}
@Override
public void updatePath(GoogleMap googleMap, ArrayList<Polyline> paths, int startIndex,
List<CachedLocation> locations) {
if (googleMap == null) {
return;
}
if (startIndex >= locations.size()) {
return;
}
boolean newSegment = startIndex == 0 || !locations.get(startIndex - 1).isValid();
LatLng lastLatLng = startIndex != 0 ? locations.get(startIndex -1).getLatLng() : null;
ArrayList<LatLng> lastSegmentPoints = new ArrayList<LatLng>();
int lastSegmentColor = paths.size() != 0 ? paths.get(paths.size() - 1).getColor() : slowColor;
boolean useLastPolyline = true;
for (int i = startIndex; i < locations.size(); ++i) {
CachedLocation cachedLocation = locations.get(i);
// If not valid, start a new segment
if (!cachedLocation.isValid()) {
newSegment = true;
lastLatLng = null;
continue;
}
LatLng latLng = cachedLocation.getLatLng();
int color = getColor(cachedLocation.getSpeed());
// Either update point or draw a line from the last point
if (newSegment) {
TrackPathUtils.addPath(googleMap, paths, lastSegmentPoints, lastSegmentColor, useLastPolyline);
useLastPolyline = false;
lastSegmentColor = color;
newSegment = false;
}
if (lastSegmentColor == color) {
lastSegmentPoints.add(latLng);
} else {
TrackPathUtils.addPath(googleMap, paths, lastSegmentPoints, lastSegmentColor, useLastPolyline);
useLastPolyline = false;
if (lastLatLng != null) {
lastSegmentPoints.add(lastLatLng);
}
lastSegmentPoints.add(latLng);
lastSegmentColor = color;
}
lastLatLng = latLng;
}
TrackPathUtils.addPath(googleMap, paths, lastSegmentPoints, lastSegmentColor, useLastPolyline);
}
@VisibleForTesting
protected int getColor(int speed) {
if (speed <= trackPathDescriptor.getSlowSpeed()) {
return slowColor;
} else if (speed <= trackPathDescriptor.getNormalSpeed()) {
return normalColor;
} else {
return fastColor;
}
}
@VisibleForTesting
protected int getSlowColor() {
return slowColor;
}
@VisibleForTesting
protected int getNormalColor() {
return normalColor;
}
@VisibleForTesting
protected int getFastColor() {
return fastColor;
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import java.util.ArrayList;
import java.util.List;
/**
* A single color track path.
*
* @author Jimmy Shih
*/
public class SingleColorTrackPath implements TrackPath {
final int color;
public SingleColorTrackPath(Context context) {
color = context.getResources().getColor(R.color.fast_path);
}
@Override
public boolean updateState() {
return false;
}
@Override
public void updatePath(GoogleMap googleMap, ArrayList<Polyline> paths, int startIndex,
List<CachedLocation> locations) {
if (googleMap == null) {
return;
}
if (startIndex >= locations.size()) {
return;
}
boolean newSegment = startIndex == 0 || !locations.get(startIndex - 1).isValid();
ArrayList<LatLng> lastSegmentPoints = new ArrayList<LatLng>();
boolean useLastPolyline = true;
for (int i = startIndex; i < locations.size(); i++) {
CachedLocation cachedLocation = locations.get(i);
// If not valid, start a new segment
if (!cachedLocation.isValid()) {
newSegment = true;
continue;
}
LatLng latLng = cachedLocation.getLatLng();
if (newSegment) {
TrackPathUtils.addPath(googleMap, paths, lastSegmentPoints, color, useLastPolyline);
useLastPolyline = false;
newSegment = false;
}
lastSegmentPoints.add(latLng);
}
TrackPathUtils.addPath(googleMap, paths, lastSegmentPoints, color, useLastPolyline);
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.Polyline;
import java.util.ArrayList;
import java.util.List;
/**
* A track path.
*
* @author Jimmy Shih
*/
public interface TrackPath {
public boolean updateState();
/**
* Updates the path.
*
* @param startIndex the start index
* @param points the points
*/
public void updatePath(GoogleMap googleMap, ArrayList<Polyline> paths, int startIndex,
List<CachedLocation> points);
}
@@ -0,0 +1,40 @@
/*
* 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
/**
* An interface for classes which describe how to draw a track path.
*
* @author Vangelis S.
*/
public interface TrackPathDescriptor {
/**
* Gets the maximum speed which is considered slow.
*/
public int getSlowSpeed();
/**
* Gets the maximum speed which is considered normal.
*/
public int getNormalSpeed();
/**
* Updates state. Returns true if the state is updated.
*/
public boolean updateState();
}
@@ -0,0 +1,48 @@
/*
* 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
import com.google.android.maps.mytracks.R;
import android.content.Context;
/**
* A factory for {@link TrackPath}.
*
* @author Vangelis S.
*/
public class TrackPathFactory {
private TrackPathFactory() {}
/**
* Get a new {@link TrackPath}.
*
* @param context the context
*/
public static TrackPath getTrackPath(Context context, String trackColorMode) {
if (context.getString(R.string.settings_map_track_color_mode_dynamic_value)
.equals(trackColorMode)) {
return new MultiColorTrackPath(context, new DynamicSpeedTrackPathDescriptor(context));
} else if (context.getString(R.string.settings_map_track_color_mode_fixed_value)
.equals(trackColorMode)) {
return new MultiColorTrackPath(context, new FixedSpeedTrackPathDescriptor(context));
} else {
return new SingleColorTrackPath(context);
}
}
}
@@ -0,0 +1,62 @@
/*
* 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
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.maps;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import java.util.ArrayList;
/**
* Various utility functions for track path painting.
*
* @author Vangelis S.
*/
public class TrackPathUtils {
private TrackPathUtils() {}
/**
* Add a path.
*
* @param googleMap the google map
* @param paths the existing paths
* @param points the path points
* @param color the path color
* @param append true to append to the last path
*/
public static void addPath(GoogleMap googleMap, ArrayList<Polyline> paths,
ArrayList<LatLng> points, int color, boolean append) {
if (points.size() == 0) {
return;
}
if (append && paths.size() != 0) {
Polyline lastPolyline = paths.get(paths.size() - 1);
ArrayList<LatLng> pathPoints = new ArrayList<LatLng>();
pathPoints.addAll(lastPolyline.getPoints());
pathPoints.addAll(points);
lastPolyline.setPoints(pathPoints);
} else {
PolylineOptions polylineOptions = new PolylineOptions().addAll(points).width(5).color(color);
Polyline polyline = googleMap.addPolyline(polylineOptions);
paths.add(polyline);
}
points.clear();
}
}