merge heads

This commit is contained in:
Jimmy Shih
2012-03-20 10:23:19 -07:00
parent 1893c3557d
commit 08a17a58ef
21 changed files with 640 additions and 377 deletions
@@ -3,6 +3,7 @@ package com.google.android.apps.mytracks;
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.ApiAdapterFactory;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
@@ -12,7 +13,6 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Window;
import android.widget.ScrollView;
import android.widget.TextView;
@@ -60,8 +60,9 @@ public class AggregatedStatsActivity extends Activity implements
this.tracksProvider = MyTracksProviderUtils.Factory.get(this);
// We don't need a window title bar:
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Show the action bar (or nothing at all).
ApiAdapterFactory.getApiAdapter().showActionBar(this);
setContentView(R.layout.stats);
ScrollView sv = ((ScrollView) findViewById(R.id.scrolly));
@@ -28,6 +28,7 @@ import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.stats.DoubleBuffer;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
@@ -41,12 +42,9 @@ import android.location.Location;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.widget.LinearLayout;
import android.widget.ZoomControls;
@@ -61,7 +59,8 @@ import java.util.EnumSet;
*/
public class ChartActivity extends Activity implements TrackDataListener {
private static final int CHART_SETTINGS_DIALOG = 1;
public static final int CHART_SETTINGS_DIALOG = 1;
private final DoubleBuffer elevationBuffer =
new DoubleBuffer(Constants.ELEVATION_SMOOTHING_FACTOR);
private final DoubleBuffer speedBuffer =
@@ -84,7 +83,6 @@ public class ChartActivity extends Activity implements TrackDataListener {
* UI elements:
*/
private ChartView chartView;
private MenuItem chartSettingsMenuItem;
private LinearLayout busyPane;
private ZoomControls zoomControls;
@@ -118,7 +116,9 @@ public class ChartActivity extends Activity implements TrackDataListener {
// The volume we want to control is the Text-To-Speech volume
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Show the action bar (or nothing at all).
ApiAdapterFactory.getApiAdapter().showActionBar(this);
setContentView(R.layout.mytracks_charts);
ViewGroup layout = (ViewGroup) findViewById(R.id.elevation_chart);
chartView = new ChartView(this);
@@ -183,25 +183,6 @@ public class ChartActivity extends Activity implements TrackDataListener {
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
chartSettingsMenuItem = menu.add(Menu.NONE, Constants.MENU_CHART_SETTINGS, Menu.NONE,
R.string.menu_chart_view_chart_settings);
chartSettingsMenuItem.setIcon(R.drawable.chart_settings);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Constants.MENU_CHART_SETTINGS:
showDialog(CHART_SETTINGS_DIALOG);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == CHART_SETTINGS_DIALOG) {
@@ -486,11 +467,6 @@ public class ChartActivity extends Activity implements TrackDataListener {
return chartView;
}
@VisibleForTesting
MenuItem getChartSettingsMenuItem() {
return chartSettingsMenuItem;
}
@VisibleForTesting
void setTrackMaxSpeed(double maxSpeed) {
trackMaxSpeed = maxSpeed;
@@ -40,14 +40,6 @@ public abstract class Constants {
public static final int SHOW_WAYPOINT = 1;
public static final int WELCOME = 2;
/*
* Menu ids:
*/
public static final int MENU_MY_LOCATION = 1;
public static final int MENU_TOGGLE_LAYERS = 2;
public static final int MENU_CHART_SETTINGS = 3;
/*
* Context menu ids. Sorted alphabetically.
*/
@@ -69,6 +61,13 @@ public abstract class Constants {
public static final int MENU_SHARE_TCX_FILE = 115;
public static final int MENU_SHOW = 116;
public static final int MENU_WRITE_TO_SD_CARD = 117;
/*
* Main screen tab tags:
*/
public static final String MAP_TAB_TAG = "map";
public static final String STATS_TAB_TAG = "stats";
public static final String CHART_TAB_TAG = "chart";
/**
* The number of distance readings to smooth to get a stable signal.
@@ -29,6 +29,7 @@ import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileForm
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
import com.google.android.apps.mytracks.io.sendtogoogle.UploadServiceChooserActivity;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.GeoRect;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.PlayTrackUtils;
@@ -55,7 +56,6 @@ import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.view.Window;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -90,6 +90,12 @@ public class MapActivity extends com.google.android.maps.MapActivity
*/
private boolean keepMyLocationVisible;
/**
* True if we've already zoomed into the current location.
* Only relevant when {@link #keepMyLocationVisible} is true.
*/
private boolean myLocationWasZoomedIn;
/**
* The ID of a track on which we want to show a waypoint.
* The waypoint will be shown as soon as the track is loaded.
@@ -126,9 +132,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
private LinearLayout busyPane;
private ImageButton optionsBtn;
private MenuItem myLocation;
private MenuItem toggleLayers;
/**
* We are not displaying driving directions. Just an arbitrary track that is
* not associated to any licensed mapping data. Therefore it should be okay to
@@ -159,8 +162,8 @@ public class MapActivity extends com.google.android.maps.MapActivity
// The volume we want to control is the Text-To-Speech volume
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
// We don't need a window title bar:
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Show the action bar (or nothing at all).
ApiAdapterFactory.getApiAdapter().showActionBar(this);
// Inflate the layout:
setContentView(R.layout.mytracks_layout);
@@ -288,6 +291,18 @@ public class MapActivity extends com.google.android.maps.MapActivity
return r.contains(geoPoint);
}
/**
* Centers (and keeps centered) the map on the current location.
*/
public void showMyLocation() {
dataHub.forceUpdateLocation();
keepMyLocationVisible = true;
myLocationWasZoomedIn = false;
if (currentLocation != null) {
showCurrentLocation();
}
}
/**
* Moves the location pointer to the current location and center the map if
* the current location is outside the visible area.
@@ -304,6 +319,11 @@ public class MapActivity extends com.google.android.maps.MapActivity
GeoPoint geoPoint = LocationUtils.getGeoPoint(currentLocation);
MapController controller = mapView.getController();
controller.animateTo(geoPoint);
if (!myLocationWasZoomedIn && mapView.getZoomLevel() < 18) {
// Only zoom in the first time we show the location.
myLocationWasZoomedIn = true;
controller.setZoom(18);
}
}
}
@@ -561,45 +581,18 @@ public class MapActivity extends com.google.android.maps.MapActivity
startActivity(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
myLocation = menu.add(
Menu.NONE, Constants.MENU_MY_LOCATION, Menu.NONE, R.string.menu_map_view_my_location);
myLocation.setIcon(android.R.drawable.ic_menu_mylocation);
toggleLayers = menu.add(
Menu.NONE, Constants.MENU_TOGGLE_LAYERS, Menu.NONE, R.string.menu_map_view_satellite_mode);
toggleLayers.setIcon(android.R.drawable.ic_menu_mapmode);
return true;
/**
* Returns whether the map is currently in satellite view mode.
*/
public boolean isSatelliteView() {
return mapView.isSatellite();
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
toggleLayers.setTitle(mapView.isSatellite() ?
R.string.menu_map_view_map_mode : R.string.menu_map_view_satellite_mode);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Constants.MENU_MY_LOCATION: {
dataHub.forceUpdateLocation();
keepMyLocationVisible = true;
if (mapView.getZoomLevel() < 18) {
mapView.getController().setZoom(18);
}
if (currentLocation != null) {
showCurrentLocation();
}
return true;
}
case Constants.MENU_TOGGLE_LAYERS: {
mapView.setSatellite(!mapView.isSatellite());
return true;
}
}
return super.onOptionsItemSelected(item);
/**
* Changes whether the map should be in satellite view mode.
*/
public void setSatelliteView(boolean sat) {
mapView.setSatellite(sat);
}
@Override
@@ -15,6 +15,9 @@
*/
package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.CHART_TAB_TAG;
import static com.google.android.apps.mytracks.Constants.MAP_TAB_TAG;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
@@ -30,11 +33,19 @@ import android.view.MenuItem;
class MenuManager {
private final MyTracks activity;
private MenuItem recordTrack;
private MenuItem stopRecording;
public MenuManager(MyTracks activity) {
this.activity = activity;
}
/**
* Fills the menu with its initial options.
*
* @param menu the menu to fill
* @return true on success, false otherwise
*/
public boolean onCreateOptionsMenu(Menu menu) {
activity.getMenuInflater().inflate(R.menu.main, menu);
@@ -42,26 +53,70 @@ class MenuManager {
return true;
}
/**
* Prepares the menu for display.
*
* @param menu the menu to update for display
* @param hasRecorded whether any track has been recorded
* @param isRecording whether we're recording a track now
* @param hasSelectedTrack whether there's a track currently selected for display
* @param isSatelliteMode whether the map is currently in satellite mode
* @param currentTabTag the tag for the currently-displayed tab
*/
public void onPrepareOptionsMenu(Menu menu, boolean hasRecorded,
boolean isRecording, boolean hasSelectedTrack) {
boolean isRecording, boolean hasSelectedTrack,
boolean isSatelliteMode, String currentTabTag) {
menu.findItem(R.id.menu_markers)
.setEnabled(hasRecorded && hasSelectedTrack);
menu.findItem(R.id.menu_record_track)
.setEnabled(!isRecording)
.setVisible(!isRecording);
menu.findItem(R.id.menu_stop_recording)
.setEnabled(isRecording)
.setVisible(isRecording);
recordTrack = menu.findItem(R.id.menu_record_track);
stopRecording = menu.findItem(R.id.menu_stop_recording);
updateActionItems(isRecording);
menu.findItem(R.id.menu_chart_settings)
.setVisible(CHART_TAB_TAG.equals(currentTabTag));
boolean isMapTab = MAP_TAB_TAG.equals(currentTabTag);
menu.findItem(R.id.menu_my_location)
.setVisible(isMapTab);
menu.findItem(R.id.menu_satellite_mode)
.setVisible(isMapTab)
.setTitle(isSatelliteMode
? R.string.menu_map_view_map_mode
: R.string.menu_map_view_satellite_mode);
}
/**
* Updates the action items.<br>
* TODO: also update the action items when
* <ul>
* <li>start/stop a recording from the home screen widget.<il>
* <li>receive a direct service call to start/stop a recording.<il>
* </ul>
*
* @param isRecording true if recording a track
*/
private void updateActionItems(boolean isRecording) {
recordTrack.setEnabled(!isRecording).setVisible(!isRecording);
stopRecording.setEnabled(isRecording).setVisible(isRecording);
}
/**
* Called when an option from the menu is selected.
*
* @param item the selected item
* @return true if the action was handled, false otherwise
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_record_track: {
activity.startRecording();
updateActionItems(true);
return true;
}
case R.id.menu_stop_recording: {
activity.stopRecording();
updateActionItems(false);
return true;
}
case R.id.menu_tracks: {
@@ -90,11 +145,28 @@ class MenuManager {
case R.id.menu_search: {
// TODO: Pass the current track ID and current location to do some fancier ranking.
activity.onSearchRequested();
return true;
}
case R.id.menu_chart_settings: {
activity.showChartSettings();
return true;
}
case R.id.menu_my_location: {
activity.showMyLocation();
return true;
}
case R.id.menu_satellite_mode: {
activity.toggleSatelliteView();
return true;
}
}
return false;
}
/**
* Starts an activity with the given class.
*/
private boolean startActivity(Class<? extends Activity> activityClass) {
activity.startActivity(new Intent(activity, activityClass));
return true;
@@ -15,6 +15,9 @@
*/
package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.CHART_TAB_TAG;
import static com.google.android.apps.mytracks.Constants.MAP_TAB_TAG;
import static com.google.android.apps.mytracks.Constants.STATS_TAB_TAG;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
@@ -54,7 +57,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.widget.RelativeLayout;
import android.widget.TabHost;
import android.widget.Toast;
@@ -148,23 +150,23 @@ public class MyTracks extends TabActivity implements OnTouchListener {
serviceConnection = new TrackRecordingServiceConnection(this, serviceBindCallback);
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
// We don't need a window title bar:
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Show the action bar (or nothing at all).
ApiAdapterFactory.getApiAdapter().showActionBar(this);
// If the user just starts typing (on a device with a keyboard), we start a search.
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
final Resources res = getResources();
final TabHost tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec("tab1")
tabHost.addTab(tabHost.newTabSpec(MAP_TAB_TAG)
.setIndicator("Map", res.getDrawable(
android.R.drawable.ic_menu_mapmode))
.setContent(new Intent(this, MapActivity.class)));
tabHost.addTab(tabHost.newTabSpec("tab2")
tabHost.addTab(tabHost.newTabSpec(STATS_TAB_TAG)
.setIndicator("Stats", res.getDrawable(R.drawable.menu_stats))
.setContent(new Intent(this, StatsActivity.class)));
tabHost.addTab(tabHost.newTabSpec("tab3")
tabHost.addTab(tabHost.newTabSpec(CHART_TAB_TAG)
.setIndicator("Chart", res.getDrawable(R.drawable.menu_elevation))
.setContent(new Intent(this, ChartActivity.class)));
@@ -298,9 +300,15 @@ public class MyTracks extends TabActivity implements OnTouchListener {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MapActivity map = getMapTab();
boolean isSatelliteView = map != null ? map.isSatelliteView() : false;
menuManager.onPrepareOptionsMenu(menu, providerUtils.getLastTrack() != null,
ServiceUtils.isRecording(this, serviceConnection.getServiceIfBound(), preferences),
dataHub.isATrackSelected());
dataHub.isATrackSelected(),
isSatelliteView,
getTabHost().getCurrentTabTag());
return super.onPrepareOptionsMenu(menu);
}
@@ -362,7 +370,11 @@ public class MyTracks extends TabActivity implements OnTouchListener {
if (results != null) {
final long waypointId = results.getLongExtra(WaypointDetails.WAYPOINT_ID_EXTRA, -1);
if (waypointId >= 0) {
showWaypoint(trackId, waypointId);
MapActivity map = getMapTab();
if (map != null) {
getTabHost().setCurrentTab(0);
map.showWaypoint(waypointId);
}
}
}
break;
@@ -486,4 +498,48 @@ public class MyTracks extends TabActivity implements OnTouchListener {
long getSelectedTrackId() {
return dataHub.getSelectedTrackId();
}
/**
* Asks the chart tab to show its settings.
*/
public void showChartSettings() {
ChartActivity chart = getChartTab();
if (chart != null) {
chart.showDialog(ChartActivity.CHART_SETTINGS_DIALOG);
}
}
/**
* Asks the map tab to show the map in satellite mode.
*/
public void toggleSatelliteView() {
MapActivity mapTab = getMapTab();
if (mapTab != null) {
mapTab.setSatelliteView(!mapTab.isSatelliteView());
}
}
/**
* Asks the map tab to jump to the current location.
*/
public void showMyLocation() {
MapActivity mapTab = getMapTab();
if (mapTab != null) {
mapTab.showMyLocation();
}
}
/**
* Returns the map tab instance if available, or null otherwise.
*/
private MapActivity getMapTab() {
return (MapActivity) getLocalActivityManager().getActivity(MAP_TAB_TAG);
}
/**
* Returns the chart tab instance if available, or null otherwise.
*/
private ChartActivity getChartTab() {
return (ChartActivity) getLocalActivityManager().getActivity(CHART_TAB_TAG);
}
}
@@ -23,6 +23,7 @@ import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType;
import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.services.ServiceUtils;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
@@ -34,7 +35,6 @@ import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Window;
import android.widget.ScrollView;
import android.widget.TextView;
@@ -107,8 +107,8 @@ public class StatsActivity extends Activity implements TrackDataListener {
// The volume we want to control is the Text-To-Speech volume
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
// We don't need a window title bar:
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Show the action bar (or nothing at all).
ApiAdapterFactory.getApiAdapter().showActionBar(this);
setContentView(R.layout.stats);
@@ -117,7 +117,7 @@ public class StatsActivity extends Activity implements TrackDataListener {
showUnknownLocation();
updateLabels();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
if (metrics.heightPixels > 600) {
@@ -0,0 +1,33 @@
/*
* 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.util;
import android.app.Activity;
import android.view.Window;
/**
* API level 11 specific implementation of the {@link ApiAdapter}.
*
* @author Jimmy Shih
*/
public class Api11Adapter extends Api9Adapter {
@Override
public void showActionBar(Activity activity) {
activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
}
}
@@ -23,12 +23,14 @@ import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerTask;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
import android.view.Window;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -50,7 +52,7 @@ public class Api7Adapter implements ApiAdapter {
public BackupPreferencesListener getBackupPreferencesListener(Context context) {
return new BackupPreferencesListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// Do nothing
}
};
@@ -65,7 +67,7 @@ public class Api7Adapter implements ApiAdapter {
public void enableStrictMode() {
// Not supported
}
@Override
public byte[] copyByteArray(byte[] input, int start, int end) {
int length = end - start;
@@ -73,7 +75,7 @@ public class Api7Adapter implements ApiAdapter {
System.arraycopy(input, start, output, 0, length);
return output;
}
@Override
public HttpTransport getHttpTransport() {
return new ApacheHttpTransport();
@@ -99,4 +101,11 @@ public class Api7Adapter implements ApiAdapter {
}
return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.SPP_UUID);
}
@Override
public void showActionBar(Activity activity) {
// Action bar not available, just hide the title and let actions be shown
// via the regular menu.
activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
}
@@ -19,6 +19,7 @@ import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
import com.google.api.client.http.HttpTransport;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
@@ -98,4 +99,15 @@ public interface ApiAdapter {
* @param bluetoothDevice
*/
public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException;
/**
* Shows the action bar, if the current system supports it.
* If it's not supported, the title bar will be kept hidden.
*
* Due to changes in API level 11.
*
* @param activity the current activity
*/
public void showActionBar(Activity activity);
}
@@ -31,7 +31,10 @@ public class ApiAdapterFactory {
*/
public static ApiAdapter getApiAdapter() {
if (apiAdapter == null) {
if (Build.VERSION.SDK_INT >= 10) {
if (Build.VERSION.SDK_INT >= 11) {
apiAdapter = new Api11Adapter();
return apiAdapter;
} else if (Build.VERSION.SDK_INT >= 10) {
apiAdapter = new Api10Adapter();
return apiAdapter;
} else if (Build.VERSION.SDK_INT >= 9) {