forked from upstream-mirrors/OpenTracks
merge heads
This commit is contained in:
@@ -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,26 +28,26 @@ 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;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
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.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.ZoomControls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -61,7 +61,8 @@ import java.util.EnumSet;
|
||||
*/
|
||||
public class ChartActivity extends Activity implements TrackDataListener {
|
||||
|
||||
private static final int CHART_SETTINGS_DIALOG = 1;
|
||||
private static final int DIALOG_CHART_SETTINGS_ID = 0;
|
||||
|
||||
private final DoubleBuffer elevationBuffer =
|
||||
new DoubleBuffer(Constants.ELEVATION_SMOOTHING_FACTOR);
|
||||
private final DoubleBuffer speedBuffer =
|
||||
@@ -84,7 +85,6 @@ public class ChartActivity extends Activity implements TrackDataListener {
|
||||
* UI elements:
|
||||
*/
|
||||
private ChartView chartView;
|
||||
private MenuItem chartSettingsMenuItem;
|
||||
private LinearLayout busyPane;
|
||||
private ZoomControls zoomControls;
|
||||
|
||||
@@ -118,7 +118,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,64 +185,59 @@ 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) {
|
||||
final ChartSettingsDialog settingsDialog = new ChartSettingsDialog(this);
|
||||
settingsDialog.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface arg0, int which) {
|
||||
if (which != DialogInterface.BUTTON_POSITIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||
chartView.setChartValueSeriesEnabled(i, settingsDialog.isSeriesEnabled(i));
|
||||
}
|
||||
setMode(settingsDialog.getMode());
|
||||
chartView.postInvalidate();
|
||||
}
|
||||
});
|
||||
return settingsDialog;
|
||||
if (id != DIALOG_CHART_SETTINGS_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.onCreateDialog(id);
|
||||
}
|
||||
View view = getLayoutInflater().inflate(R.layout.chart_settings, null);
|
||||
final RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.chart_settings_x);
|
||||
radioGroup.check(chartView.getMode() == Mode.BY_DISTANCE
|
||||
? R.id.chart_settings_by_distance : R.id.chart_settings_by_time);
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, Dialog dialog) {
|
||||
super.onPrepareDialog(id, dialog);
|
||||
final CheckBox[] checkBoxes = new CheckBox[ChartView.NUM_SERIES];
|
||||
checkBoxes[ChartView.ELEVATION_SERIES] = (CheckBox) view.findViewById(
|
||||
R.id.chart_settings_elevation);
|
||||
checkBoxes[ChartView.SPEED_SERIES] = (CheckBox) view.findViewById(R.id.chart_settings_speed);
|
||||
checkBoxes[ChartView.POWER_SERIES] = (CheckBox) view.findViewById(R.id.chart_settings_power);
|
||||
checkBoxes[ChartView.CADENCE_SERIES] = (CheckBox) view.findViewById(
|
||||
R.id.chart_settings_cadence);
|
||||
checkBoxes[ChartView.HEART_RATE_SERIES] = (CheckBox) view.findViewById(
|
||||
R.id.chart_settings_heart_rate);
|
||||
|
||||
if (id == CHART_SETTINGS_DIALOG) {
|
||||
prepareSettingsDialog((ChartSettingsDialog) dialog);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareSettingsDialog(ChartSettingsDialog settingsDialog) {
|
||||
settingsDialog.setMode(chartView.getMode());
|
||||
settingsDialog.setDisplaySpeed(reportSpeed);
|
||||
// set checkboxes values
|
||||
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||
settingsDialog.setSeriesEnabled(i, chartView.isChartValueSeriesEnabled(i));
|
||||
checkBoxes[i].setChecked(chartView.isChartValueSeriesEnabled(i));
|
||||
}
|
||||
checkBoxes[ChartView.SPEED_SERIES].setText(reportSpeed
|
||||
? R.string.stat_speed : R.string.stat_pace);
|
||||
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setMode(radioGroup.getCheckedRadioButtonId() == R.id.chart_settings_by_distance
|
||||
? Mode.BY_DISTANCE
|
||||
: Mode.BY_TIME);
|
||||
for (int i = 0; i < ChartView.NUM_SERIES; i++) {
|
||||
chartView.setChartValueSeriesEnabled(i, checkBoxes[i].isChecked());
|
||||
}
|
||||
chartView.postInvalidate();
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.menu_chart_settings)
|
||||
.setView(view)
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the chart settings dialog
|
||||
*/
|
||||
public void showChartSettingsDialog() {
|
||||
showDialog(DIALOG_CHART_SETTINGS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,11 +483,6 @@ public class ChartActivity extends Activity implements TrackDataListener {
|
||||
return chartView;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
MenuItem getChartSettingsMenuItem() {
|
||||
return chartSettingsMenuItem;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setTrackMaxSpeed(double maxSpeed) {
|
||||
trackMaxSpeed = maxSpeed;
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.ChartView.Mode;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
/**
|
||||
* An activity that allows the user to set the chart settings.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ChartSettingsDialog extends Dialog {
|
||||
private RadioButton distance;
|
||||
private CheckBox[] series;
|
||||
private OnClickListener clickListener;
|
||||
|
||||
public ChartSettingsDialog(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.chart_settings);
|
||||
Button cancel = (Button) findViewById(R.id.chart_settings_cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (clickListener != null) {
|
||||
clickListener.onClick(ChartSettingsDialog.this, BUTTON_NEGATIVE);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Button okButton = (Button) findViewById(R.id.chart_settings_ok);
|
||||
okButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (clickListener != null) {
|
||||
clickListener.onClick(ChartSettingsDialog.this, BUTTON_POSITIVE);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
distance = (RadioButton) findViewById(R.id.chart_settings_by_distance);
|
||||
|
||||
series = new CheckBox[ChartView.NUM_SERIES];
|
||||
series[ChartView.ELEVATION_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_elevation);
|
||||
series[ChartView.SPEED_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_speed);
|
||||
series[ChartView.POWER_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_power);
|
||||
series[ChartView.CADENCE_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_cadence);
|
||||
series[ChartView.HEART_RATE_SERIES] =
|
||||
(CheckBox) findViewById(R.id.chart_settings_heart_rate);
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
RadioGroup rd = (RadioGroup) findViewById(R.id.chart_settings_x);
|
||||
rd.check(mode == Mode.BY_DISTANCE
|
||||
? R.id.chart_settings_by_distance
|
||||
: R.id.chart_settings_by_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to display speed or pace.
|
||||
*
|
||||
* @param displaySpeed true to display speed
|
||||
*/
|
||||
public void setDisplaySpeed(boolean displaySpeed) {
|
||||
series[ChartView.SPEED_SERIES].setText(displaySpeed ? R.string.stat_speed : R.string.stat_pace);
|
||||
}
|
||||
|
||||
public void setSeriesEnabled(int seriesIdx, boolean enabled) {
|
||||
series[seriesIdx].setChecked(enabled);
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
if (distance == null) return Mode.BY_DISTANCE;
|
||||
|
||||
return distance.isChecked() ? Mode.BY_DISTANCE : Mode.BY_TIME;
|
||||
}
|
||||
|
||||
public boolean isSeriesEnabled(int seriesIdx) {
|
||||
if (series == null) return true;
|
||||
|
||||
return series[seriesIdx].isChecked();
|
||||
}
|
||||
|
||||
public void setOnClickListener(OnClickListener clickListener) {
|
||||
this.clickListener = clickListener;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
CheckBox[] getSeries() {
|
||||
return series;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
/**
|
||||
* Checks with the user if he prefers the metric units or the imperial units.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
class CheckUnits {
|
||||
private static final String CHECK_UNITS_PREFERENCE_FILE = "checkunits";
|
||||
private static final String CHECK_UNITS_PREFERENCE_KEY = "checkunits.checked";
|
||||
private static boolean metric = true;
|
||||
|
||||
public static void check(final Context context) {
|
||||
final SharedPreferences checkUnitsSharedPreferences = context.getSharedPreferences(
|
||||
CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
|
||||
if (checkUnitsSharedPreferences.getBoolean(CHECK_UNITS_PREFERENCE_KEY, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(context.getString(R.string.preferred_units_title));
|
||||
CharSequence[] items = { context.getString(R.string.preferred_units_metric),
|
||||
context.getString(R.string.preferred_units_imperial) };
|
||||
builder.setSingleChoiceItems(items, 0, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == 0) {
|
||||
metric = true;
|
||||
} else {
|
||||
metric = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setCancelable(true);
|
||||
builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
recordCheckPerformed(checkUnitsSharedPreferences);
|
||||
SharedPreferences useMetricPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = useMetricPreferences.edit();
|
||||
String key = context.getString(R.string.metric_units_key);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor.putBoolean(key, metric));
|
||||
}
|
||||
});
|
||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
recordCheckPerformed(checkUnitsSharedPreferences);
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private static void recordCheckPerformed(SharedPreferences preferences) {
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(
|
||||
preferences.edit().putBoolean(CHECK_UNITS_PREFERENCE_KEY, true));
|
||||
}
|
||||
|
||||
private CheckUnits() {}
|
||||
}
|
||||
@@ -36,47 +36,22 @@ public abstract class Constants {
|
||||
/*
|
||||
* onActivityResult request codes:
|
||||
*/
|
||||
public static final int SHOW_TRACK = 0;
|
||||
public static final int SHARE_GPX_FILE = 1;
|
||||
public static final int SHARE_KML_FILE = 2;
|
||||
public static final int SHARE_CSV_FILE = 3;
|
||||
public static final int SHARE_TCX_FILE = 4;
|
||||
public static final int SAVE_GPX_FILE = 5;
|
||||
public static final int SAVE_KML_FILE = 6;
|
||||
public static final int SAVE_CSV_FILE = 7;
|
||||
public static final int SAVE_TCX_FILE = 8;
|
||||
public static final int SHOW_WAYPOINT = 9;
|
||||
public static final int WELCOME = 10;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
public static final int SHOW_WAYPOINT = 0;
|
||||
|
||||
/*
|
||||
* Context menu ids. Sorted alphabetically.
|
||||
*/
|
||||
public static final int MENU_CLEAR_MAP = 100;
|
||||
public static final int MENU_DELETE = 101;
|
||||
public static final int MENU_EDIT = 102;
|
||||
public static final int MENU_PLAY = 103;
|
||||
public static final int MENU_SAVE_CSV_FILE = 104;
|
||||
public static final int MENU_SAVE_GPX_FILE = 105;
|
||||
public static final int MENU_SAVE_KML_FILE = 106;
|
||||
public static final int MENU_SAVE_TCX_FILE = 107;
|
||||
public static final int MENU_SEND_TO_GOOGLE = 108;
|
||||
public static final int MENU_SHARE = 109;
|
||||
public static final int MENU_SHARE_CSV_FILE = 110;
|
||||
public static final int MENU_SHARE_FUSION_TABLE = 111;
|
||||
public static final int MENU_SHARE_GPX_FILE = 112;
|
||||
public static final int MENU_SHARE_KML_FILE = 113;
|
||||
public static final int MENU_SHARE_MAP = 114;
|
||||
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;
|
||||
public static final int MENU_DELETE = 100;
|
||||
public static final int MENU_EDIT = 101;
|
||||
public static final int MENU_SHOW = 102;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -169,29 +144,6 @@ public abstract class Constants {
|
||||
public static final String RESUME_TRACK_EXTRA_NAME =
|
||||
"com.google.android.apps.mytracks.RESUME_TRACK";
|
||||
|
||||
public static int getActionFromMenuId(int menuId) {
|
||||
switch (menuId) {
|
||||
case Constants.MENU_SHARE_KML_FILE:
|
||||
return Constants.SHARE_KML_FILE;
|
||||
case Constants.MENU_SHARE_GPX_FILE:
|
||||
return Constants.SHARE_GPX_FILE;
|
||||
case Constants.MENU_SHARE_CSV_FILE:
|
||||
return Constants.SHARE_CSV_FILE;
|
||||
case Constants.MENU_SHARE_TCX_FILE:
|
||||
return Constants.SHARE_TCX_FILE;
|
||||
case Constants.MENU_SAVE_GPX_FILE:
|
||||
return Constants.SAVE_GPX_FILE;
|
||||
case Constants.MENU_SAVE_KML_FILE:
|
||||
return Constants.SAVE_KML_FILE;
|
||||
case Constants.MENU_SAVE_CSV_FILE:
|
||||
return Constants.SAVE_CSV_FILE;
|
||||
case Constants.MENU_SAVE_TCX_FILE:
|
||||
return Constants.SAVE_TCX_FILE;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String MAPSHOP_BASE_URL =
|
||||
"https://maps.google.com/maps/ms";
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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.util.ApiAdapterFactory;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A utility class that can be used to delete all tracks and track points
|
||||
* from the provider, including asking for confirmation from the user via
|
||||
* a dialog.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class DeleteAllTracks extends Handler {
|
||||
|
||||
private final Context context;
|
||||
private final Runnable done;
|
||||
|
||||
public DeleteAllTracks(Context context, Runnable done) {
|
||||
this.context = context;
|
||||
this.done = done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
|
||||
AlertDialog dialog = null;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setMessage(
|
||||
context.getString(R.string.track_list_delete_all_confirm_message));
|
||||
builder.setTitle(context.getString(R.string.generic_confirm_title));
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
builder.setPositiveButton(context.getString(R.string.generic_yes),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
dialogInterface.dismiss();
|
||||
Log.w(Constants.TAG, "deleting all!");
|
||||
MyTracksProviderUtils.Factory.get(context).deleteAllTracks();
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
Editor editor = prefs.edit();
|
||||
// TODO: Go through data manager
|
||||
editor.putLong(context.getString(R.string.selected_track_key), -1);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
if (done != null) {
|
||||
Handler h = new Handler();
|
||||
h.post(done);
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(context.getString(R.string.generic_no),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
dialogInterface.dismiss();
|
||||
}
|
||||
});
|
||||
dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Activity used to delete a track.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class DeleteTrack extends Activity
|
||||
implements DialogInterface.OnClickListener, OnCancelListener {
|
||||
private static final int CONFIRM_DIALOG = 1;
|
||||
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
|
||||
private long deleteTrackId;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Uri data = intent.getData();
|
||||
if (!Intent.ACTION_DELETE.equals(action) ||
|
||||
!UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
|
||||
Log.e(TAG, "Got bad delete intent: " + intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
deleteTrackId = ContentUris.parseId(data);
|
||||
|
||||
showDialog(CONFIRM_DIALOG);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != CONFIRM_DIALOG) {
|
||||
Log.e(TAG, "Unknown dialog " + id);
|
||||
return null;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(getString(R.string.track_list_delete_track_confirm_message));
|
||||
builder.setTitle(getString(R.string.generic_confirm_title));
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
builder.setPositiveButton(getString(R.string.generic_yes), this);
|
||||
builder.setNegativeButton(getString(R.string.generic_no), this);
|
||||
builder.setOnCancelListener(this);
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int which) {
|
||||
dialogInterface.dismiss();
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
deleteTrack();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
|
||||
}
|
||||
|
||||
private void deleteTrack() {
|
||||
providerUtils.deleteTrack(deleteTrackId);
|
||||
|
||||
// If the track we just deleted was selected, unselect it.
|
||||
String selectedKey = getString(R.string.selected_track_key);
|
||||
SharedPreferences preferences = getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
if (preferences.getLong(selectedKey, -1) == deleteTrackId) {
|
||||
Editor editor = preferences.edit().putLong(selectedKey, -1);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
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.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* An activity to export all the tracks to the SD card.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ExportActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
||||
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
|
||||
private ExportAsyncTask exportAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof ExportAsyncTask) {
|
||||
exportAsyncTask = (ExportAsyncTask) retained;
|
||||
exportAsyncTask.setActivity(this);
|
||||
} else {
|
||||
Intent intent = getIntent();
|
||||
TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
|
||||
exportAsyncTask = new ExportAsyncTask(this, trackFileFormat);
|
||||
exportAsyncTask.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
exportAsyncTask.setActivity(null);
|
||||
return exportAsyncTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != DIALOG_PROGRESS_ID) {
|
||||
return null;
|
||||
}
|
||||
progressDialog = DialogUtils.createHorizontalProgressDialog(
|
||||
this, R.string.export_progress_message, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
exportAsyncTask.cancel(true);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
return progressDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param success true if the AsyncTask is successful
|
||||
* @param messageId message id to display to user
|
||||
*/
|
||||
public void onAsyncTaskCompleted(boolean success, int messageId) {
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the progress dialog value.
|
||||
*
|
||||
* @param number the number of tracks exported
|
||||
* @param max the maximum number of tracks
|
||||
*/
|
||||
public void setProgressDialogValue(int number, int max) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.setIndeterminate(false);
|
||||
progressDialog.setMax(max);
|
||||
progressDialog.setProgress(Math.min(number, max));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* 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.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriter;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* A class that will export all tracks to the sd card.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ExportAllTracks {
|
||||
// These must line up with the index in the array.
|
||||
public static final int GPX_OPTION_INDEX = 0;
|
||||
public static final int KML_OPTION_INDEX = 1;
|
||||
public static final int CSV_OPTION_INDEX = 2;
|
||||
public static final int TCX_OPTION_INDEX = 3;
|
||||
|
||||
private final Activity activity;
|
||||
private WakeLock wakeLock;
|
||||
private ProgressDialog progress;
|
||||
|
||||
private TrackFileFormat format = TrackFileFormat.GPX;
|
||||
|
||||
private final DialogInterface.OnClickListener itemClick =
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case GPX_OPTION_INDEX:
|
||||
format = TrackFileFormat.GPX;
|
||||
break;
|
||||
case KML_OPTION_INDEX:
|
||||
format = TrackFileFormat.KML;
|
||||
break;
|
||||
case CSV_OPTION_INDEX:
|
||||
format = TrackFileFormat.CSV;
|
||||
break;
|
||||
case TCX_OPTION_INDEX:
|
||||
format = TrackFileFormat.TCX;
|
||||
break;
|
||||
default:
|
||||
Log.w(Constants.TAG, "Unknown export format: " + which);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public ExportAllTracks(Activity activity) {
|
||||
this.activity = activity;
|
||||
Log.i(Constants.TAG, "ExportAllTracks: Starting");
|
||||
|
||||
String exportFileFormat = activity.getString(R.string.track_list_export_file);
|
||||
String fileTypes[] = activity.getResources().getStringArray(R.array.file_types);
|
||||
|
||||
String[] choices = new String[fileTypes.length];
|
||||
for (int i = 0; i < fileTypes.length; i++) {
|
||||
choices[i] = String.format(exportFileFormat, fileTypes[i]);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(R.string.track_list_export_all);
|
||||
builder.setSingleChoiceItems(choices, 0, itemClick);
|
||||
builder.setPositiveButton(R.string.generic_ok, positiveClick);
|
||||
builder.setNegativeButton(R.string.generic_cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private final DialogInterface.OnClickListener positiveClick =
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
new Thread(runner, "ExportAllTracks").start();
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable runner = new Runnable() {
|
||||
public void run() {
|
||||
aquireLocksAndExport();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes sure that we keep the phone from sleeping.
|
||||
* See if there is a current track. Aquire a wake lock if there is no
|
||||
* current track.
|
||||
*/
|
||||
private void aquireLocksAndExport() {
|
||||
SharedPreferences prefs = activity.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
long recordingTrackId = -1;
|
||||
if (prefs != null) {
|
||||
recordingTrackId =
|
||||
prefs.getLong(activity.getString(R.string.recording_track_key), -1);
|
||||
}
|
||||
if (recordingTrackId != -1) {
|
||||
wakeLock = SystemUtils.acquireWakeLock(activity, wakeLock);
|
||||
}
|
||||
|
||||
// Now we can safely export everything.
|
||||
exportAll();
|
||||
|
||||
// Release the wake lock if we recorded one.
|
||||
// TODO check what happens if we started recording after getting this lock.
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
Log.i(Constants.TAG, "ExportAllTracks: Releasing wake lock.");
|
||||
}
|
||||
Log.i(Constants.TAG, "ExportAllTracks: Done");
|
||||
showToast(R.string.export_success, Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
private void makeProgressDialog(final int trackCount) {
|
||||
String exportMsg = activity.getString(R.string.track_list_export_all);
|
||||
progress = new ProgressDialog(activity);
|
||||
progress.setIcon(android.R.drawable.ic_dialog_info);
|
||||
progress.setTitle(exportMsg);
|
||||
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progress.setMax(trackCount);
|
||||
progress.setProgress(0);
|
||||
progress.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually export the tracks.
|
||||
* This should be called after the wake locks have been aquired.
|
||||
*/
|
||||
private void exportAll() {
|
||||
// Get a cursor over all tracks.
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
MyTracksProviderUtils providerUtils =
|
||||
MyTracksProviderUtils.Factory.get(activity);
|
||||
cursor = providerUtils.getTracksCursor(null, null, TracksColumns._ID);
|
||||
if (cursor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int trackCount = cursor.getCount();
|
||||
Log.i(Constants.TAG,
|
||||
"ExportAllTracks: Exporting: " + cursor.getCount() + " tracks.");
|
||||
int idxTrackId = cursor.getColumnIndexOrThrow(TracksColumns._ID);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
makeProgressDialog(trackCount);
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; cursor.moveToNext(); i++) {
|
||||
final int status = i;
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
synchronized (this) {
|
||||
if (progress == null) {
|
||||
return;
|
||||
}
|
||||
progress.setProgress(status);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
long id = cursor.getLong(idxTrackId);
|
||||
Log.i(Constants.TAG, "ExportAllTracks: exporting: " + id);
|
||||
TrackWriter writer =
|
||||
TrackWriterFactory.newWriter(activity, providerUtils, id, format);
|
||||
if (writer == null) {
|
||||
showToast(R.string.export_error, Toast.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
|
||||
writer.writeTrack();
|
||||
|
||||
if (!writer.wasSuccess()) {
|
||||
// Abort the whole export on the first error.
|
||||
showToast(writer.getErrorMessage(), Toast.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
if (progress != null) {
|
||||
synchronized (this) {
|
||||
progress.dismiss();
|
||||
progress = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showToast(final int messageId, final int length) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(activity, messageId, length).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriter;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
|
||||
/**
|
||||
* AsyncTask to export all the tracks to the SD card.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private ExportActivity exportActivity;
|
||||
private final TrackFileFormat trackFileFormat;
|
||||
private final Context context;
|
||||
private final MyTracksProviderUtils myTracksProviderUtils;
|
||||
private WakeLock wakeLock;
|
||||
private TrackWriter trackWriter;
|
||||
|
||||
// true if the AsyncTask result is success
|
||||
private boolean success;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
// message id to return to the activity
|
||||
private int messageId;
|
||||
|
||||
/**
|
||||
* Creates an AsyncTask.
|
||||
*
|
||||
* @param exportActivity the activity currently associated with this AsyncTask
|
||||
* @param trackFileFormat the track file format
|
||||
*/
|
||||
public ExportAsyncTask(ExportActivity exportActivity, TrackFileFormat trackFileFormat) {
|
||||
this.exportActivity = exportActivity;
|
||||
this.trackFileFormat = trackFileFormat;
|
||||
context = exportActivity.getApplicationContext();
|
||||
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(exportActivity);
|
||||
|
||||
SharedPreferences sharedPreferences = exportActivity.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
long recordingTrackId = sharedPreferences.getLong(
|
||||
exportActivity.getString(R.string.recording_track_key), -1L);
|
||||
|
||||
// Get the wake lock if not recording
|
||||
if (recordingTrackId == -1L) {
|
||||
wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
|
||||
}
|
||||
success = false;
|
||||
completed = false;
|
||||
messageId = R.string.export_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current {@link ExportActivity} associated with this AyncTask.
|
||||
*
|
||||
* @param exportActivity the current {@link ExportActivity}, can be null
|
||||
*/
|
||||
public void setActivity(ExportActivity exportActivity) {
|
||||
this.exportActivity = exportActivity;
|
||||
if (completed && exportActivity != null) {
|
||||
exportActivity.onAsyncTaskCompleted(success, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (exportActivity != null) {
|
||||
exportActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getTracksCursor(null, null, TracksColumns._ID);
|
||||
if (cursor == null) {
|
||||
messageId = R.string.export_success;
|
||||
return true;
|
||||
}
|
||||
int count = cursor.getCount();
|
||||
int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
cursor.moveToPosition(i);
|
||||
long id = cursor.getLong(idIndex);
|
||||
trackWriter = TrackWriterFactory.newWriter(
|
||||
context, myTracksProviderUtils, id, trackFileFormat);
|
||||
if (trackWriter == null) {
|
||||
return false;
|
||||
}
|
||||
trackWriter.writeTrack();
|
||||
|
||||
if (!trackWriter.wasSuccess()) {
|
||||
messageId = trackWriter.getErrorMessage();
|
||||
return false;
|
||||
}
|
||||
publishProgress(i + 1, count);
|
||||
}
|
||||
messageId = R.string.export_success;
|
||||
return true;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
// Release the wake lock if obtained
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
if (exportActivity != null) {
|
||||
exportActivity.setProgressDialogValue(values[0], values[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
success = result;
|
||||
completed = true;
|
||||
if (exportActivity != null) {
|
||||
exportActivity.onAsyncTaskCompleted(success, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
if (trackWriter != null) {
|
||||
trackWriter.stopWriteTrack();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* An activity to import GPX files from the SD card. Optionally to import one
|
||||
* GPX file and display it in My Tracks.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ImportActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_IMPORT_ALL = "import_all";
|
||||
|
||||
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 boolean importAll;
|
||||
|
||||
// path on the SD card to import
|
||||
private String path;
|
||||
|
||||
// number of succesfully imported files
|
||||
private int successCount;
|
||||
|
||||
// number of files to import
|
||||
private int totalCount;
|
||||
|
||||
// last successfully imported track id
|
||||
private long trackId;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
|
||||
if (importAll) {
|
||||
path = FileUtils.buildExternalDirectoryPath("gpx");
|
||||
} 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 (!UriUtils.isFileUri(data)) {
|
||||
Log.d(TAG, "Invalid data: " + intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
path = data.getPath();
|
||||
}
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof ImportAsyncTask) {
|
||||
importAsyncTask = (ImportAsyncTask) retained;
|
||||
importAsyncTask.setActivity(this);
|
||||
} else {
|
||||
importAsyncTask = new ImportAsyncTask(this, importAll, path);
|
||||
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);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
return progressDialog;
|
||||
case DIALOG_RESULT_ID:
|
||||
String message;
|
||||
if (successCount == 0) {
|
||||
message = getString(R.string.import_no_file, path);
|
||||
} else {
|
||||
String totalFiles = getResources()
|
||||
.getQuantityString(R.plurals.importGpxFiles, totalCount, totalCount);
|
||||
message = getString(R.string.import_success, successCount, totalFiles, path);
|
||||
}
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setMessage(message)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (!importAll && trackId != -1L) {
|
||||
Intent intent = new Intent(ImportActivity.this, TrackDetailActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(TrackDetailActivity.TRACK_ID, trackId);
|
||||
startActivity(intent);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param success true if the AsyncTask is successful
|
||||
* @param imported the number of files successfully imported
|
||||
* @param total the total number of files to import
|
||||
* @param id the last successfully imported track id
|
||||
*/
|
||||
public void onAsyncTaskCompleted(boolean success, int imported, int total, long id) {
|
||||
successCount = imported;
|
||||
totalCount = total;
|
||||
trackId = id;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
if (success) {
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.import_error, path), Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.GpxImporter;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* A class that will import all GPX tracks in /sdcard/MyTracks/gpx/
|
||||
*
|
||||
* @author David Piggott
|
||||
*/
|
||||
public class ImportAllTracks {
|
||||
|
||||
private final Activity activity;
|
||||
private FileUtils fileUtils;
|
||||
private boolean singleTrackSelected;
|
||||
private String gpxPath;
|
||||
private WakeLock wakeLock;
|
||||
private ProgressDialog progress;
|
||||
private int gpxFileCount;
|
||||
private int importSuccessCount;
|
||||
private long importedTrackIds[];
|
||||
|
||||
public ImportAllTracks(Activity activity) {
|
||||
this(activity, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to import tracks.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param path path of the gpx file to import and display. If null, then just
|
||||
* import all the gpx files under MyTracks/gpx and do not display any
|
||||
* track.
|
||||
*/
|
||||
public ImportAllTracks(Activity activity, String path) {
|
||||
Log.i(Constants.TAG, "ImportAllTracks: Starting");
|
||||
this.activity = activity;
|
||||
fileUtils = new FileUtils();
|
||||
singleTrackSelected = path != null;
|
||||
gpxPath = path == null ? fileUtils.buildExternalDirectoryPath("gpx") : path;
|
||||
new Thread(runner).start();
|
||||
}
|
||||
|
||||
private final Runnable runner = new Runnable() {
|
||||
public void run() {
|
||||
aquireLocksAndImport();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes sure that we keep the phone from sleeping. See if there is a current
|
||||
* track. Acquire a wake lock if there is no current track.
|
||||
*/
|
||||
private void aquireLocksAndImport() {
|
||||
SharedPreferences prefs = activity.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
long recordingTrackId = -1;
|
||||
if (prefs != null) {
|
||||
recordingTrackId = prefs.getLong(activity.getString(R.string.recording_track_key), -1);
|
||||
}
|
||||
if (recordingTrackId == -1) {
|
||||
wakeLock = SystemUtils.acquireWakeLock(activity, wakeLock);
|
||||
}
|
||||
|
||||
// Now we can safely import everything.
|
||||
importAll();
|
||||
|
||||
// Release the wake lock if we acquired one.
|
||||
// TODO check what happens if we started recording after getting this lock.
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
Log.i(Constants.TAG, "ImportAllTracks: Releasing wake lock.");
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showDoneDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showDoneDialog() {
|
||||
Log.i(Constants.TAG, "ImportAllTracks: Done");
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
if (gpxFileCount == 0) {
|
||||
builder.setMessage(activity.getString(R.string.import_no_file, gpxPath));
|
||||
} else {
|
||||
String totalFiles = activity.getResources().getQuantityString(
|
||||
R.plurals.importGpxFiles, gpxFileCount, gpxFileCount);
|
||||
builder.setMessage(
|
||||
activity.getString(R.string.import_success, importSuccessCount, totalFiles, gpxPath));
|
||||
}
|
||||
builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (singleTrackSelected) {
|
||||
long lastTrackId = importedTrackIds[importedTrackIds.length - 1];
|
||||
Uri trackUri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, lastTrackId);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(trackUri, TracksColumns.CONTENT_ITEMTYPE);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
activity.startActivity(intent);
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void makeProgressDialog(final int trackCount) {
|
||||
String importMsg = activity.getString(R.string.track_list_import_all);
|
||||
progress = new ProgressDialog(activity);
|
||||
progress.setIcon(android.R.drawable.ic_dialog_info);
|
||||
progress.setTitle(importMsg);
|
||||
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progress.setMax(trackCount);
|
||||
progress.setProgress(0);
|
||||
progress.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually import the tracks. This should be called after the wake locks have
|
||||
* been acquired.
|
||||
*/
|
||||
private void importAll() {
|
||||
MyTracksProviderUtils providerUtils = MyTracksProviderUtils.Factory.get(activity);
|
||||
|
||||
if (!fileUtils.isSdCardAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<File> gpxFiles = getGpxFiles();
|
||||
gpxFileCount = gpxFiles.size();
|
||||
if (gpxFileCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(Constants.TAG, "ImportAllTracks: Importing: " + gpxFileCount + " tracks.");
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
makeProgressDialog(gpxFileCount);
|
||||
}
|
||||
});
|
||||
|
||||
Iterator<File> gpxFilesIterator = gpxFiles.iterator();
|
||||
for (int currentFileNumber = 0; gpxFilesIterator.hasNext(); currentFileNumber++) {
|
||||
File currentFile = gpxFilesIterator.next();
|
||||
final int status = currentFileNumber;
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
synchronized (this) {
|
||||
if (progress == null) {
|
||||
return;
|
||||
}
|
||||
progress.setProgress(status);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (importFile(currentFile, providerUtils)) {
|
||||
importSuccessCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (progress != null) {
|
||||
synchronized (this) {
|
||||
progress.dismiss();
|
||||
progress = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to import a GPX file. Returns true on success, issues error
|
||||
* notifications and returns false on failure.
|
||||
*/
|
||||
private boolean importFile(final File gpxFile, MyTracksProviderUtils providerUtils) {
|
||||
Log.i(Constants.TAG, "ImportAllTracks: importing: " + gpxFile.getName());
|
||||
try {
|
||||
importedTrackIds = GpxImporter.importGPXFile(new FileInputStream(gpxFile), providerUtils);
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(Constants.TAG, "GPX file wasn't found/went missing: "
|
||||
+ gpxFile.getAbsolutePath(), e);
|
||||
} catch (ParserConfigurationException e) {
|
||||
Log.w(Constants.TAG, "Error parsing file: " + gpxFile.getAbsolutePath(), e);
|
||||
} catch (SAXException e) {
|
||||
Log.w(Constants.TAG, "Error parsing file: " + gpxFile.getAbsolutePath(), e);
|
||||
} catch (IOException e) {
|
||||
Log.w(Constants.TAG, "Error reading file: " + gpxFile.getAbsolutePath(), e);
|
||||
}
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(activity, activity.getString(R.string.import_error, gpxFile.getName()),
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the GPX files. If singleTrackSelected is true, returns a
|
||||
* list containing just the gpxPath file. If singleTrackSelected is false,
|
||||
* returns a list of GPX files under the gpxPath directory.
|
||||
*/
|
||||
private List<File> getGpxFiles() {
|
||||
List<File> gpxFiles = new LinkedList<File>();
|
||||
File file = new File(gpxPath);
|
||||
if (singleTrackSelected) {
|
||||
gpxFiles.add(file);
|
||||
} else {
|
||||
File[] gpxFileCandidates = file.listFiles();
|
||||
if (gpxFileCandidates != null) {
|
||||
for (File candidate : gpxFileCandidates) {
|
||||
if (!candidate.isDirectory() && candidate.getName().endsWith(".gpx")) {
|
||||
gpxFiles.add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return gpxFiles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.io.file.GpxImporter;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
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;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* AsyncTask to import GPX files from the SD card.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private static final String TAG = ImportAsyncTask.class.getSimpleName();
|
||||
|
||||
private ImportActivity importActivity;
|
||||
private final boolean importAll;
|
||||
private final String path;
|
||||
private final MyTracksProviderUtils myTracksProviderUtils;
|
||||
private WakeLock wakeLock;
|
||||
|
||||
// true if the AsyncTask result is success
|
||||
private boolean success;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
// number of files successfully imported
|
||||
private int successCount;
|
||||
|
||||
// number of files to import
|
||||
private int totalCount;
|
||||
|
||||
// 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 path path to import GPX files
|
||||
*/
|
||||
public ImportAsyncTask(ImportActivity importActivity, boolean importAll, String path) {
|
||||
this.importActivity = importActivity;
|
||||
this.importAll = importAll;
|
||||
this.path = path;
|
||||
|
||||
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(importActivity);
|
||||
SharedPreferences sharedPreferences = importActivity.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
long recordingTrackId = sharedPreferences.getLong(
|
||||
importActivity.getString(R.string.recording_track_key), -1L);
|
||||
|
||||
// Get the wake lock if not recording
|
||||
if (recordingTrackId == -1L) {
|
||||
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
|
||||
}
|
||||
|
||||
success = false;
|
||||
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(success, successCount, totalCount, trackId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (importActivity != null) {
|
||||
importActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
if (!FileUtils.isSdCardAvailable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
File file = files.get(i);
|
||||
if (importFile(file)) {
|
||||
successCount++;
|
||||
}
|
||||
publishProgress(i + 1, totalCount);
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
// Release the wake lock if obtained
|
||||
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) {
|
||||
success = result;
|
||||
completed = true;
|
||||
if (importActivity != null) {
|
||||
importActivity.onAsyncTaskCompleted(success, successCount, totalCount, trackId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a GPX file.
|
||||
*
|
||||
* @param file the file
|
||||
*/
|
||||
private boolean importFile(final File file) {
|
||||
try {
|
||||
long trackIds[] = GpxImporter.importGPXFile(new FileInputStream(file), myTracksProviderUtils);
|
||||
int length = trackIds.length;
|
||||
if (length > 0) {
|
||||
trackId = trackIds[length - 1];
|
||||
}
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.d(TAG, "file: " + file.getAbsolutePath(), e);
|
||||
return false;
|
||||
} catch (ParserConfigurationException e) {
|
||||
Log.d(TAG, "file: " + file.getAbsolutePath(), e);
|
||||
return false;
|
||||
} catch (SAXException e) {
|
||||
Log.d(TAG, "file: " + file.getAbsolutePath(), e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "file: " + file.getAbsolutePath(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of GPX files. If importAll is true, returns a list of GPX 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 file = new File(path);
|
||||
if (importAll) {
|
||||
File[] candidates = file.listFiles();
|
||||
if (candidates != null) {
|
||||
for (File candidate : candidates) {
|
||||
if (!candidate.isDirectory() && candidate.getName().endsWith(".gpx")) {
|
||||
files.add(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
files.add(file);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
||||
@@ -22,39 +22,24 @@ import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType;
|
||||
import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
||||
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;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.android.maps.MapController;
|
||||
import com.google.android.maps.MapView;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
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;
|
||||
import android.widget.TextView;
|
||||
@@ -72,8 +57,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
|
||||
implements View.OnTouchListener, View.OnClickListener,
|
||||
TrackDataListener {
|
||||
|
||||
private static final int DIALOG_INSTALL_EARTH = 0;
|
||||
|
||||
// Saved instance state keys:
|
||||
// ---------------------------
|
||||
|
||||
@@ -88,6 +71,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.
|
||||
@@ -122,10 +111,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
|
||||
private LinearLayout messagePane;
|
||||
private TextView messageText;
|
||||
private LinearLayout busyPane;
|
||||
private ImageButton optionsBtn;
|
||||
|
||||
private MenuItem myLocation;
|
||||
private MenuItem toggleLayers;
|
||||
|
||||
/**
|
||||
* We are not displaying driving directions. Just an arbitrary track that is
|
||||
@@ -157,8 +142,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);
|
||||
@@ -177,10 +162,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
|
||||
messagePane = (LinearLayout) findViewById(R.id.messagepane);
|
||||
messageText = (TextView) findViewById(R.id.messagetext);
|
||||
busyPane = (LinearLayout) findViewById(R.id.busypane);
|
||||
optionsBtn = (ImageButton) findViewById(R.id.showOptions);
|
||||
|
||||
optionsBtn.setOnCreateContextMenuListener(contextMenuListener);
|
||||
optionsBtn.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -235,27 +216,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_INSTALL_EARTH:
|
||||
return PlayTrackUtils.createInstallEarthDialog(this);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility functions:
|
||||
// -------------------
|
||||
|
||||
/**
|
||||
* Shows the options button if a track is selected, or hide it if not.
|
||||
*/
|
||||
private void updateOptionsButton(boolean trackSelected) {
|
||||
optionsBtn.setVisibility(
|
||||
trackSelected ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a location is visible.
|
||||
*
|
||||
@@ -286,6 +246,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.
|
||||
@@ -302,6 +274,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,8 +385,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
|
||||
@Override
|
||||
public void run() {
|
||||
boolean trackSelected = track != null;
|
||||
updateOptionsButton(trackSelected);
|
||||
|
||||
mapOverlay.setTrackDrawingEnabled(trackSelected);
|
||||
|
||||
if (trackSelected) {
|
||||
@@ -430,153 +405,24 @@ public class MapActivity extends com.google.android.maps.MapActivity
|
||||
});
|
||||
}
|
||||
|
||||
private final OnCreateContextMenuListener contextMenuListener =
|
||||
new OnCreateContextMenuListener() {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
menu.setHeaderTitle(R.string.track_list_context_menu_title);
|
||||
menu.add(Menu.NONE, Constants.MENU_EDIT, Menu.NONE, R.string.track_list_edit_track);
|
||||
if (!dataHub.isRecordingSelected()) {
|
||||
String saveFileFormat = getString(R.string.track_list_save_file);
|
||||
String shareFileFormat = getString(R.string.track_list_share_file);
|
||||
String fileTypes[] = getResources().getStringArray(R.array.file_types);
|
||||
|
||||
menu.add(Menu.NONE, Constants.MENU_PLAY, Menu.NONE, R.string.track_list_play);
|
||||
menu.add(Menu.NONE, Constants.MENU_SEND_TO_GOOGLE, Menu.NONE,
|
||||
R.string.track_list_send_google);
|
||||
SubMenu share = menu.addSubMenu(
|
||||
Menu.NONE, Constants.MENU_SHARE, Menu.NONE, R.string.track_list_share_track);
|
||||
share.add(
|
||||
Menu.NONE, Constants.MENU_SHARE_MAP, Menu.NONE, R.string.track_list_share_map);
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_FUSION_TABLE, Menu.NONE,
|
||||
R.string.track_list_share_fusion_table);
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_GPX_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[0]));
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_KML_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[1]));
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_CSV_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[2]));
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_TCX_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[3]));
|
||||
SubMenu save = menu.addSubMenu(
|
||||
Menu.NONE, Constants.MENU_WRITE_TO_SD_CARD, Menu.NONE, R.string.track_list_save_sd);
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_GPX_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[0]));
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_KML_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[1]));
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_CSV_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[2]));
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_TCX_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[3]));
|
||||
menu.add(Menu.NONE, Constants.MENU_CLEAR_MAP, Menu.NONE, R.string.track_list_clear_map);
|
||||
menu.add(Menu.NONE, Constants.MENU_DELETE, Menu.NONE, R.string.track_list_delete_track);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
Intent intent;
|
||||
long trackId = dataHub.getSelectedTrackId();
|
||||
switch (item.getItemId()) {
|
||||
case Constants.MENU_EDIT:
|
||||
intent = new Intent(this, TrackDetail.class).putExtra(TrackDetail.TRACK_ID, trackId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case Constants.MENU_PLAY:
|
||||
if (PlayTrackUtils.isEarthInstalled(this)) {
|
||||
PlayTrackUtils.playTrack(this, trackId);
|
||||
return true;
|
||||
} else {
|
||||
showDialog(DIALOG_INSTALL_EARTH);
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_SEND_TO_GOOGLE:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, true, true));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case Constants.MENU_SHARE_MAP:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, false, false));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case Constants.MENU_SHARE_FUSION_TABLE:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, false, true, false));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case Constants.MENU_SHARE_GPX_FILE:
|
||||
case Constants.MENU_SHARE_KML_FILE:
|
||||
case Constants.MENU_SHARE_CSV_FILE:
|
||||
case Constants.MENU_SHARE_TCX_FILE:
|
||||
case Constants.MENU_SAVE_GPX_FILE:
|
||||
case Constants.MENU_SAVE_KML_FILE:
|
||||
case Constants.MENU_SAVE_CSV_FILE:
|
||||
case Constants.MENU_SAVE_TCX_FILE:
|
||||
SaveActivity.handleExportTrackAction(
|
||||
this, trackId, Constants.getActionFromMenuId(item.getItemId()));
|
||||
return true;
|
||||
case Constants.MENU_CLEAR_MAP:
|
||||
dataHub.unloadCurrentTrack();
|
||||
return true;
|
||||
case Constants.MENU_DELETE:
|
||||
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
|
||||
intent = new Intent(Intent.ACTION_DELETE, uri);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
default:
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
/**
|
||||
* Returns whether the map is currently in satellite view mode.
|
||||
*/
|
||||
public boolean isSatelliteView() {
|
||||
return mapView.isSatellite();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@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
|
||||
public void onClick(View v) {
|
||||
if (v == messagePane) {
|
||||
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||
} else if (v == optionsBtn) {
|
||||
optionsBtn.performLongClick();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
/**
|
||||
* Manage the application menus.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
class MenuManager {
|
||||
|
||||
private final MyTracks activity;
|
||||
|
||||
public MenuManager(MyTracks activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
activity.getMenuInflater().inflate(R.menu.main, menu);
|
||||
|
||||
// TODO: Replace search button with search widget if API level >= 11
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onPrepareOptionsMenu(Menu menu, boolean hasRecorded,
|
||||
boolean isRecording, boolean hasSelectedTrack) {
|
||||
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);
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_record_track: {
|
||||
activity.startRecording();
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_stop_recording: {
|
||||
activity.stopRecording();
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_tracks: {
|
||||
activity.startActivityForResult(new Intent(activity, TrackList.class),
|
||||
Constants.SHOW_TRACK);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_markers: {
|
||||
Intent startIntent = new Intent(activity, WaypointsList.class);
|
||||
startIntent.putExtra("trackid", activity.getSelectedTrackId());
|
||||
activity.startActivityForResult(startIntent, Constants.SHOW_WAYPOINT);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_sensor_state: {
|
||||
return startActivity(SensorStateActivity.class);
|
||||
}
|
||||
case R.id.menu_settings: {
|
||||
return startActivity(SettingsActivity.class);
|
||||
}
|
||||
case R.id.menu_aggregated_statistics: {
|
||||
return startActivity(AggregatedStatsActivity.class);
|
||||
}
|
||||
case R.id.menu_help: {
|
||||
return startActivity(WelcomeActivity.class);
|
||||
}
|
||||
case R.id.menu_search: {
|
||||
// TODO: Pass the current track ID and current location to do some fancier ranking.
|
||||
activity.onSearchRequested();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean startActivity(Class<? extends Activity> activityClass) {
|
||||
activity.startActivity(new Intent(activity, activityClass));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,489 +0,0 @@
|
||||
/*
|
||||
* 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 static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.AnalyticsUtils;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.EulaUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.TabActivity;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The super activity that embeds our sub activities.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MyTracks extends TabActivity implements OnTouchListener {
|
||||
private static final int DIALOG_EULA_ID = 0;
|
||||
|
||||
private TrackDataHub dataHub;
|
||||
|
||||
/**
|
||||
* Menu manager.
|
||||
*/
|
||||
private MenuManager menuManager;
|
||||
|
||||
/**
|
||||
* Preferences.
|
||||
*/
|
||||
private SharedPreferences preferences;
|
||||
|
||||
/**
|
||||
* True if a new track should be created after the track recording service
|
||||
* binds.
|
||||
*/
|
||||
private boolean startNewTrackRequested = false;
|
||||
|
||||
/**
|
||||
* Utilities to deal with the database.
|
||||
*/
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
|
||||
/*
|
||||
* Tabs/View navigation:
|
||||
*/
|
||||
|
||||
private NavControls navControls;
|
||||
|
||||
private final Runnable changeTab = new Runnable() {
|
||||
public void run() {
|
||||
getTabHost().setCurrentTab(navControls.getCurrentIcons());
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Recording service interaction:
|
||||
*/
|
||||
|
||||
private final Runnable serviceBindCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (serviceConnection) {
|
||||
ITrackRecordingService service = serviceConnection.getServiceIfBound();
|
||||
if (startNewTrackRequested && service != null) {
|
||||
Log.i(TAG, "Starting recording");
|
||||
startNewTrackRequested = false;
|
||||
startRecordingNewTrack(service);
|
||||
} else if (startNewTrackRequested) {
|
||||
Log.w(TAG, "Not yet starting recording");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private TrackRecordingServiceConnection serviceConnection;
|
||||
|
||||
/*
|
||||
* Application lifetime events:
|
||||
* ============================
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.d(TAG, "MyTracks.onCreate");
|
||||
super.onCreate(savedInstanceState);
|
||||
if (!SystemUtils.isRelease(this)) {
|
||||
ApiAdapterFactory.getApiAdapter().enableStrictMode();
|
||||
}
|
||||
|
||||
AnalyticsUtils.sendPageViews(this, "/appstart");
|
||||
|
||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||
preferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
dataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
|
||||
menuManager = new MenuManager(this);
|
||||
serviceConnection = new TrackRecordingServiceConnection(this, serviceBindCallback);
|
||||
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
|
||||
// We don't need a window title bar:
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
// 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")
|
||||
.setIndicator("Map", res.getDrawable(
|
||||
android.R.drawable.ic_menu_mapmode))
|
||||
.setContent(new Intent(this, MapActivity.class)));
|
||||
tabHost.addTab(tabHost.newTabSpec("tab2")
|
||||
.setIndicator("Stats", res.getDrawable(R.drawable.menu_stats))
|
||||
.setContent(new Intent(this, StatsActivity.class)));
|
||||
tabHost.addTab(tabHost.newTabSpec("tab3")
|
||||
.setIndicator("Chart", res.getDrawable(R.drawable.menu_elevation))
|
||||
.setContent(new Intent(this, ChartActivity.class)));
|
||||
|
||||
// Hide the tab widget itself. We'll use overlayed prev/next buttons to
|
||||
// switch between the tabs:
|
||||
tabHost.getTabWidget().setVisibility(View.GONE);
|
||||
|
||||
RelativeLayout layout = new RelativeLayout(this);
|
||||
LayoutParams params =
|
||||
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
|
||||
layout.setLayoutParams(params);
|
||||
navControls =
|
||||
new NavControls(this, layout,
|
||||
getResources().obtainTypedArray(R.array.left_icons),
|
||||
getResources().obtainTypedArray(R.array.right_icons),
|
||||
changeTab);
|
||||
navControls.show();
|
||||
tabHost.addView(layout);
|
||||
layout.setOnTouchListener(this);
|
||||
|
||||
if (!EulaUtils.getEulaValue(this)) {
|
||||
showDialog(DIALOG_EULA_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
Log.d(TAG, "MyTracks.onStart");
|
||||
super.onStart();
|
||||
dataHub.start();
|
||||
|
||||
// Ensure that service is running and bound if we're supposed to be recording
|
||||
if (ServiceUtils.isRecording(this, null, preferences)) {
|
||||
serviceConnection.startAndBind();
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Uri data = intent.getData();
|
||||
if ((Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action))
|
||||
&& TracksColumns.CONTENT_ITEMTYPE.equals(intent.getType())
|
||||
&& UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
|
||||
long trackId = ContentUris.parseId(data);
|
||||
dataHub.loadTrack(trackId);
|
||||
} else if (Intent.ACTION_VIEW.equals(action)
|
||||
&& WaypointsColumns.CONTENT_ITEMTYPE.equals(intent.getType())
|
||||
&& UriUtils.matchesContentUri(data, WaypointsColumns.CONTENT_URI)) {
|
||||
// TODO(rdamazio): Waypoint URIs should be base/trackid/waypointid
|
||||
long waypointId = ContentUris.parseId(data);
|
||||
Waypoint waypoint = providerUtils.getWaypoint(waypointId);
|
||||
long trackId = waypoint.getTrackId();
|
||||
|
||||
// Request that the waypoint is shown (now or when the right track is loaded).
|
||||
showWaypoint(trackId, waypointId);
|
||||
|
||||
// Load the right track, if not loaded already.
|
||||
dataHub.loadTrack(trackId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
// Called when the current activity is being displayed or re-displayed
|
||||
// to the user.
|
||||
Log.d(TAG, "MyTracks.onResume");
|
||||
serviceConnection.bindIfRunning();
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
// Called when activity is going into the background, but has not (yet) been
|
||||
// killed. Shouldn't block longer than approx. 2 seconds.
|
||||
Log.d(TAG, "MyTracks.onPause");
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Log.d(TAG, "MyTracks.onStop");
|
||||
dataHub.stop();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Log.d(TAG, "MyTracks.onDestroy");
|
||||
serviceConnection.unbind();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_EULA_ID:
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.eula_title);
|
||||
builder.setMessage(EulaUtils.getEulaMessage(this));
|
||||
builder.setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
EulaUtils.setEulaValue(MyTracks.this);
|
||||
Intent startIntent = new Intent(MyTracks.this, WelcomeActivity.class);
|
||||
startActivityForResult(startIntent, Constants.WELCOME);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.eula_decline, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setCancelable(true);
|
||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
return menuManager.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
menuManager.onPrepareOptionsMenu(menu, providerUtils.getLastTrack() != null,
|
||||
ServiceUtils.isRecording(this, serviceConnection.getServiceIfBound(), preferences),
|
||||
dataHub.isATrackSelected());
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return menuManager.onOptionsItemSelected(item)
|
||||
? true
|
||||
: super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/*
|
||||
* Key events:
|
||||
* ===========
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean onTrackballEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if (ServiceUtils.isRecording(this, serviceConnection.getServiceIfBound(), preferences)) {
|
||||
try {
|
||||
insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Cannot insert statistics marker.", e);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Cannot insert statistics marker.", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTrackballEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode,
|
||||
final Intent results) {
|
||||
Log.d(TAG, "MyTracks.onActivityResult");
|
||||
long trackId = dataHub.getSelectedTrackId();
|
||||
if (results != null) {
|
||||
trackId = results.getLongExtra("trackid", trackId);
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case Constants.SHOW_TRACK: {
|
||||
if (results != null) {
|
||||
if (trackId >= 0) {
|
||||
dataHub.loadTrack(trackId);
|
||||
|
||||
// The track list passed the requested action as result code. Hand
|
||||
// it off to the onActivityResult for further processing:
|
||||
if (resultCode != Constants.SHOW_TRACK) {
|
||||
onActivityResult(resultCode, Activity.RESULT_OK, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Constants.SHOW_WAYPOINT: {
|
||||
if (results != null) {
|
||||
final long waypointId = results.getLongExtra(WaypointDetails.WAYPOINT_ID_EXTRA, -1);
|
||||
if (waypointId >= 0) {
|
||||
showWaypoint(trackId, waypointId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Constants.WELCOME: {
|
||||
CheckUnits.check(this);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
Log.w(TAG, "Warning unhandled request code: " + requestCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showWaypoint(long trackId, long waypointId) {
|
||||
MapActivity map =
|
||||
(MapActivity) getLocalActivityManager().getActivity("tab1");
|
||||
if (map != null) {
|
||||
getTabHost().setCurrentTab(0);
|
||||
map.showWaypoint(trackId, waypointId);
|
||||
} else {
|
||||
Log.e(TAG, "Couldnt' get map tab");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
navControls.show();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a waypoint marker.
|
||||
*
|
||||
* TODO: Merge with WaypointsList#insertWaypoint.
|
||||
*
|
||||
* @return Id of the inserted statistics marker.
|
||||
* @throws RemoteException If the call on the service failed.
|
||||
*/
|
||||
private long insertWaypoint(WaypointCreationRequest request) throws RemoteException {
|
||||
ITrackRecordingService trackRecordingService = serviceConnection.getServiceIfBound();
|
||||
if (trackRecordingService == null) {
|
||||
throw new IllegalStateException("The recording service is not bound.");
|
||||
}
|
||||
try {
|
||||
long waypointId = trackRecordingService.insertWaypoint(request);
|
||||
if (waypointId >= 0) {
|
||||
Toast.makeText(this, R.string.marker_insert_success, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return waypointId;
|
||||
} catch (RemoteException e) {
|
||||
Toast.makeText(this, R.string.marker_insert_error, Toast.LENGTH_LONG).show();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void startRecordingNewTrack(
|
||||
ITrackRecordingService trackRecordingService) {
|
||||
try {
|
||||
long recordingTrackId = trackRecordingService.startNewTrack();
|
||||
// Select the recording track.
|
||||
dataHub.loadTrack(recordingTrackId);
|
||||
Toast.makeText(this, getString(R.string.track_record_success), Toast.LENGTH_SHORT).show();
|
||||
// TODO: We catch Exception, because after eliminating the service process
|
||||
// all exceptions it may throw are no longer wrapped in a RemoteException.
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, getString(R.string.track_record_error), Toast.LENGTH_SHORT).show();
|
||||
Log.w(TAG, "Unable to start recording.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the track recording service (if not already running) and binds to
|
||||
* it. Starts recording a new track.
|
||||
*/
|
||||
void startRecording() {
|
||||
synchronized (serviceConnection) {
|
||||
startNewTrackRequested = true;
|
||||
serviceConnection.startAndBind();
|
||||
|
||||
// Binding was already requested before, it either already happened
|
||||
// (in which case running the callback manually triggers the actual recording start)
|
||||
// or it will happen in the future
|
||||
// (in which case running the callback now will have no effect).
|
||||
serviceBindCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the track recording service and unbinds from it. Will display a toast
|
||||
* "Stopped recording" and pop up the Track Details activity.
|
||||
*/
|
||||
void stopRecording() {
|
||||
// Save the track id as the shared preference will overwrite the recording track id.
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
long currentTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1);
|
||||
|
||||
ITrackRecordingService trackRecordingService = serviceConnection.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
try {
|
||||
trackRecordingService.endCurrentTrack();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to stop recording.", e);
|
||||
}
|
||||
}
|
||||
|
||||
serviceConnection.stop();
|
||||
|
||||
if (currentTrackId > 0) {
|
||||
Intent intent = new Intent(MyTracks.this, TrackDetail.class);
|
||||
intent.putExtra(TrackDetail.TRACK_ID, currentTrackId);
|
||||
intent.putExtra(TrackDetail.SHOW_CANCEL, false);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
long getSelectedTrackId() {
|
||||
return dataHub.getSelectedTrackId();
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@ package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.services.RemoveTempFilesService;
|
||||
import com.google.android.apps.mytracks.util.AnalyticsUtils;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.maps.mytracks.BuildConfig;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
@@ -32,6 +35,10 @@ public class MyTracksApplication extends Application {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
if (BuildConfig.DEBUG) {
|
||||
ApiAdapterFactory.getApiAdapter().enableStrictMode();
|
||||
}
|
||||
AnalyticsUtils.sendPageViews(this, "/appstart");
|
||||
startService(new Intent(this, RemoveTempFilesService.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,22 +23,18 @@ import com.google.android.apps.mytracks.content.SearchEngine.ScoredResult;
|
||||
import com.google.android.apps.mytracks.content.SearchEngine.SearchQuery;
|
||||
import com.google.android.apps.mytracks.content.SearchEngineProvider;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.SearchRecentSuggestions;
|
||||
import android.util.Log;
|
||||
@@ -193,7 +189,7 @@ public class SearchActivity extends ListActivity {
|
||||
resultMap.put(DESCRIPTION_FIELD, waypoint.getDescription());
|
||||
resultMap.put(CATEGORY_FIELD, waypoint.getCategory());
|
||||
// In the same place as we show time for tracks, show the track name for waypoints.
|
||||
resultMap.put(TIME_FIELD, getString(R.string.track_list_track_name, trackName));
|
||||
resultMap.put(TIME_FIELD, getString(R.string.search_track_location, trackName));
|
||||
resultMap.put(STATS_FIELD, StringUtils.formatDateTime(this, waypoint.getLocation().getTime()));
|
||||
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId());
|
||||
resultMap.put(WAYPOINT_ID_FIELD, waypoint.getId());
|
||||
@@ -251,15 +247,12 @@ public class SearchActivity extends ListActivity {
|
||||
}
|
||||
|
||||
private Intent createViewDataIntent(Map<String, Object> clickedData) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
Intent intent = new Intent(this, TrackDetailActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
if (clickedData.containsKey(WAYPOINT_ID_FIELD)) {
|
||||
long waypointId = (Long) clickedData.get(WAYPOINT_ID_FIELD);
|
||||
Uri uri = ContentUris.withAppendedId(WaypointsColumns.CONTENT_URI, waypointId);
|
||||
intent.setDataAndType(uri, WaypointsColumns.CONTENT_ITEMTYPE);
|
||||
intent.putExtra(TrackDetailActivity.WAYPOINT_ID, (Long) clickedData.get(WAYPOINT_ID_FIELD));
|
||||
} else {
|
||||
long trackId = (Long) clickedData.get(TRACK_ID_FIELD);
|
||||
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
|
||||
intent.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
|
||||
intent.putExtra(TrackDetailActivity.TRACK_ID, (Long) clickedData.get(TRACK_ID_FIELD));
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ package com.google.android.apps.mytracks;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.io.backup.BackupActivityHelper;
|
||||
import com.google.android.apps.mytracks.io.backup.BackupActivity;
|
||||
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
|
||||
import com.google.android.apps.mytracks.io.backup.RestoreChooserActivity;
|
||||
import com.google.android.apps.mytracks.services.sensors.ant.AntUtils;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
|
||||
@@ -29,7 +30,6 @@ import com.google.android.maps.mytracks.R;
|
||||
import android.app.Dialog;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
@@ -62,8 +62,9 @@ import java.util.Set;
|
||||
*/
|
||||
public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
private static final int DIALOG_CONFIRM_RESET = 0;
|
||||
private static final int DIALOG_CONFIRM_ACCESS = 1;
|
||||
private static final int DIALOG_CONFIRM_RESET_ID = 0;
|
||||
private static final int DIALOG_CONFIRM_ACCESS_ID = 1;
|
||||
private static final int DIALOG_CONFIRM_RESTORE_ID = 2;
|
||||
|
||||
// Value when the task frequency is off.
|
||||
private static final String TASK_FREQUENCY_OFF = "0";
|
||||
@@ -150,7 +151,7 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference arg0) {
|
||||
showDialog(DIALOG_CONFIRM_RESET);
|
||||
showDialog(DIALOG_CONFIRM_RESET_ID);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -162,7 +163,7 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if ((Boolean) newValue) {
|
||||
showDialog(DIALOG_CONFIRM_ACCESS);
|
||||
showDialog(DIALOG_CONFIRM_ACCESS_ID);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@@ -174,17 +175,18 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_CONFIRM_RESET:
|
||||
case DIALOG_CONFIRM_RESET_ID:
|
||||
return DialogUtils.createConfirmationDialog(
|
||||
this, R.string.settings_reset_confirm_message, new OnClickListener() {
|
||||
this, R.string.settings_reset_confirm_message, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int button) {
|
||||
onResetPreferencesConfirmed();
|
||||
}
|
||||
});
|
||||
case DIALOG_CONFIRM_ACCESS:
|
||||
return DialogUtils.createConfirmationDialog(
|
||||
this, R.string.settings_sharing_allow_access_confirm_message, new OnClickListener() {
|
||||
case DIALOG_CONFIRM_ACCESS_ID:
|
||||
return DialogUtils.createConfirmationDialog(this,
|
||||
R.string.settings_sharing_allow_access_confirm_message,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int button) {
|
||||
CheckBoxPreference pref = (CheckBoxPreference) findPreference(
|
||||
@@ -192,6 +194,15 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
pref.setChecked(true);
|
||||
}
|
||||
});
|
||||
case DIALOG_CONFIRM_RESTORE_ID:
|
||||
return DialogUtils.createConfirmationDialog(this,
|
||||
R.string.settings_backup_restore_confirm_message,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startActivity(new Intent(SettingsActivity.this, RestoreChooserActivity.class));
|
||||
}
|
||||
});
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -354,9 +365,7 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
BackupActivityHelper backupHelper =
|
||||
new BackupActivityHelper(SettingsActivity.this);
|
||||
backupHelper.writeBackup();
|
||||
startActivity(new Intent(SettingsActivity.this, BackupActivity.class));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -364,9 +373,7 @@ public class SettingsActivity extends PreferenceActivity {
|
||||
new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
BackupActivityHelper backupHelper =
|
||||
new BackupActivityHelper(SettingsActivity.this);
|
||||
backupHelper.restoreBackup();
|
||||
showDialog(DIALOG_CONFIRM_RESTORE_ID);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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,550 @@
|
||||
/*
|
||||
* 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 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 com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.UploadServiceChooserActivity;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.PlayTrackUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.TabActivity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* An activity to show the track detail.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class TrackDetailActivity extends TabActivity implements OnTouchListener {
|
||||
|
||||
public static final String TRACK_ID = "track_id";
|
||||
public static final String WAYPOINT_ID = "waypoint_id";
|
||||
|
||||
private static final String TAG = TrackDetailActivity.class.getSimpleName();
|
||||
private static final int DIALOG_INSTALL_EARTH_ID = 0;
|
||||
private static final int DIALOG_DELETE_CURRENT_ID = 1;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private TrackDataHub trackDataHub;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private NavControls navControls;
|
||||
|
||||
private MenuItem stopRecordingMenuItem;
|
||||
private MenuItem insertMarkerMenuItem;
|
||||
private MenuItem playMenuItem;
|
||||
private MenuItem shareMenuItem;
|
||||
private MenuItem sendGoogleMenuItem;
|
||||
private MenuItem saveMenuItem;
|
||||
private MenuItem editMenuItem;
|
||||
private MenuItem deleteMenuItem;
|
||||
|
||||
private final Runnable changeTab = new Runnable() {
|
||||
public void run() {
|
||||
getTabHost().setCurrentTab(navControls.getCurrentIcons());
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 key can be null
|
||||
if (getString(R.string.recording_track_key).equals(key)) {
|
||||
if (isRecording()) {
|
||||
trackRecordingServiceConnection.startAndBind();
|
||||
}
|
||||
updateMenu();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
trackDataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
|
||||
// Show the action bar (or nothing at all).
|
||||
ApiAdapterFactory.getApiAdapter().showActionBar(this);
|
||||
|
||||
final Resources res = getResources();
|
||||
final TabHost tabHost = getTabHost();
|
||||
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(STATS_TAB_TAG)
|
||||
.setIndicator("Stats", res.getDrawable(R.drawable.ic_menu_statistics))
|
||||
.setContent(new Intent(this, StatsActivity.class)));
|
||||
tabHost.addTab(tabHost.newTabSpec(CHART_TAB_TAG)
|
||||
.setIndicator("Chart", res.getDrawable(R.drawable.menu_elevation))
|
||||
.setContent(new Intent(this, ChartActivity.class)));
|
||||
|
||||
// Hide the tab widget itself. We'll use overlayed prev/next buttons to
|
||||
// switch between the tabs:
|
||||
tabHost.getTabWidget().setVisibility(View.GONE);
|
||||
|
||||
RelativeLayout layout = new RelativeLayout(this);
|
||||
LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
|
||||
layout.setLayoutParams(params);
|
||||
navControls = new NavControls(this, layout, getResources().obtainTypedArray(R.array.left_icons),
|
||||
getResources().obtainTypedArray(R.array.right_icons), changeTab);
|
||||
navControls.show();
|
||||
tabHost.addView(layout);
|
||||
layout.setOnTouchListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
trackDataHub.start();
|
||||
|
||||
if (isRecording()) {
|
||||
trackRecordingServiceConnection.startAndBind();
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
long trackId = intent.getLongExtra(TRACK_ID, -1L);
|
||||
if (trackId != -1L) {
|
||||
trackDataHub.loadTrack(trackId);
|
||||
return;
|
||||
}
|
||||
|
||||
long waypointId = intent.getLongExtra(WAYPOINT_ID, -1L);
|
||||
if (waypointId != -1L) {
|
||||
Waypoint waypoint = MyTracksProviderUtils.Factory.get(this).getWaypoint(waypointId);
|
||||
if (waypoint != null) {
|
||||
trackId = waypoint.getTrackId();
|
||||
trackDataHub.loadTrack(trackId);
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
getTabHost().setCurrentTab(0);
|
||||
mapActivity.showWaypoint(trackId, waypointId);
|
||||
} else {
|
||||
Log.e(TAG, "MapActivity is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No track id or waypoint id, return to the track list activity.
|
||||
startTrackListActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
trackRecordingServiceConnection.bindIfRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
trackDataHub.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_INSTALL_EARTH_ID:
|
||||
return PlayTrackUtils.createInstallEarthDialog(this);
|
||||
case DIALOG_DELETE_CURRENT_ID:
|
||||
return DialogUtils.createConfirmationDialog(this,
|
||||
R.string.track_detail_delete_confirm_message, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
deleteCurrentTrack();
|
||||
startTrackListActivity();
|
||||
}
|
||||
});
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.track_detail, menu);
|
||||
String fileTypes[] = getResources().getStringArray(R.array.file_types);
|
||||
menu.findItem(R.id.menu_save_gpx).setTitle(getString(R.string.menu_save_format, fileTypes[0]));
|
||||
menu.findItem(R.id.menu_save_kml).setTitle(getString(R.string.menu_save_format, fileTypes[1]));
|
||||
menu.findItem(R.id.menu_save_csv).setTitle(getString(R.string.menu_save_format, fileTypes[2]));
|
||||
menu.findItem(R.id.menu_save_tcx).setTitle(getString(R.string.menu_save_format, fileTypes[3]));
|
||||
|
||||
menu.findItem(R.id.menu_share_gpx).setTitle(getString(R.string.menu_share_file, fileTypes[0]));
|
||||
menu.findItem(R.id.menu_share_kml).setTitle(getString(R.string.menu_share_file, fileTypes[1]));
|
||||
menu.findItem(R.id.menu_share_csv).setTitle(getString(R.string.menu_share_file, fileTypes[2]));
|
||||
menu.findItem(R.id.menu_share_tcx).setTitle(getString(R.string.menu_share_file, fileTypes[3]));
|
||||
|
||||
stopRecordingMenuItem = menu.findItem(R.id.menu_stop_recording);
|
||||
insertMarkerMenuItem = menu.findItem(R.id.menu_insert_marker);
|
||||
playMenuItem = menu.findItem(R.id.menu_play);
|
||||
shareMenuItem = menu.findItem(R.id.menu_share);
|
||||
sendGoogleMenuItem = menu.findItem(R.id.menu_send_google);
|
||||
saveMenuItem = menu.findItem(R.id.menu_save);
|
||||
editMenuItem = menu.findItem(R.id.menu_edit);
|
||||
deleteMenuItem = menu.findItem(R.id.menu_delete);
|
||||
|
||||
updateMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
String currentTabTag = getTabHost().getCurrentTabTag();
|
||||
menu.findItem(R.id.menu_chart_settings).setVisible(CHART_TAB_TAG.equals(currentTabTag));
|
||||
menu.findItem(R.id.menu_my_location).setVisible(MAP_TAB_TAG.equals(currentTabTag));
|
||||
|
||||
// Set map or satellite mode
|
||||
MapActivity mapActivity = getMapActivity();
|
||||
boolean isSatelliteMode = mapActivity != null ? mapActivity.isSatelliteView() : false;
|
||||
menu.findItem(R.id.menu_satellite_mode).setVisible(MAP_TAB_TAG.equals(currentTabTag))
|
||||
.setTitle(isSatelliteMode ? R.string.menu_map_mode : R.string.menu_satellite_mode);
|
||||
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
MapActivity mapActivity;
|
||||
Intent intent;
|
||||
long trackId = trackDataHub.getSelectedTrackId();
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_stop_recording:
|
||||
updateMenuItems(false);
|
||||
stopRecording();
|
||||
return true;
|
||||
case R.id.menu_insert_marker:
|
||||
// TODO: Add insert marker when updating WaypointList to ICS
|
||||
return true;
|
||||
case R.id.menu_play:
|
||||
if (PlayTrackUtils.isEarthInstalled(this)) {
|
||||
PlayTrackUtils.playTrack(this, trackId);
|
||||
} else {
|
||||
showDialog(DIALOG_INSTALL_EARTH_ID);
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_share_map:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, false, false));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.menu_share_fusion_table:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, false, true, false));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.menu_share_gpx:
|
||||
startSaveActivity(trackId, TrackFileFormat.GPX, true);
|
||||
return true;
|
||||
case R.id.menu_share_kml:
|
||||
startSaveActivity(trackId, TrackFileFormat.KML, true);
|
||||
return true;
|
||||
case R.id.menu_share_csv:
|
||||
startSaveActivity(trackId, TrackFileFormat.CSV, true);
|
||||
return true;
|
||||
case R.id.menu_share_tcx:
|
||||
startSaveActivity(trackId, TrackFileFormat.TCX, true);
|
||||
return true;
|
||||
case R.id.menu_markers:
|
||||
intent = new Intent(this, WaypointsList.class)
|
||||
.putExtra("trackid", trackDataHub.getSelectedTrackId());
|
||||
startActivityForResult(intent, Constants.SHOW_WAYPOINT);
|
||||
return true;
|
||||
case R.id.menu_send_google:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, true, true));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.menu_save_gpx:
|
||||
startSaveActivity(trackId, TrackFileFormat.GPX, false);
|
||||
return true;
|
||||
case R.id.menu_save_kml:
|
||||
startSaveActivity(trackId, TrackFileFormat.KML, false);
|
||||
return true;
|
||||
case R.id.menu_save_csv:
|
||||
startSaveActivity(trackId, TrackFileFormat.CSV, false);
|
||||
return true;
|
||||
case R.id.menu_save_tcx:
|
||||
startSaveActivity(trackId, TrackFileFormat.TCX, false);
|
||||
return true;
|
||||
case R.id.menu_edit:
|
||||
startActivity(new Intent(this, TrackEditActivity.class)
|
||||
.putExtra(TrackEditActivity.TRACK_ID, trackId));
|
||||
return true;
|
||||
case R.id.menu_delete:
|
||||
showDialog(DIALOG_DELETE_CURRENT_ID);
|
||||
return true;
|
||||
case R.id.menu_my_location:
|
||||
mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
mapActivity.showMyLocation();
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_satellite_mode:
|
||||
mapActivity = getMapActivity();
|
||||
if (mapActivity != null) {
|
||||
mapActivity.setSatelliteView(!mapActivity.isSatelliteView());
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_chart_settings:
|
||||
ChartActivity chartActivity = getChartActivity();
|
||||
if (chartActivity != null) {
|
||||
chartActivity.showChartSettingsDialog();
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_sensor_state:
|
||||
startActivity(new Intent(this, SensorStateActivity.class));
|
||||
return true;
|
||||
case R.id.menu_settings:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
case R.id.menu_help:
|
||||
startActivity(new Intent(this, WelcomeActivity.class));
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, final Intent results) {
|
||||
if (requestCode != Constants.SHOW_WAYPOINT) {
|
||||
Log.d(TAG, "Invalid request code: " + requestCode);
|
||||
return;
|
||||
}
|
||||
if (results != null) {
|
||||
long waypointId = results.getLongExtra(WaypointDetails.WAYPOINT_ID_EXTRA, -1L);
|
||||
if (waypointId != -1L) {
|
||||
MapActivity mapActivity = (MapActivity) getLocalActivityManager().getActivity(MAP_TAB_TAG);
|
||||
if (mapActivity != null) {
|
||||
getTabHost().setCurrentTab(0);
|
||||
mapActivity.showWaypoint(waypointId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTrackballEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if (isRecording()) {
|
||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection
|
||||
.getServiceIfBound();
|
||||
if (trackRecordingService == null) {
|
||||
Log.e(TAG, "The track recording service is null");
|
||||
return true;
|
||||
}
|
||||
boolean success = false;
|
||||
try {
|
||||
long waypointId = trackRecordingService.insertWaypoint(
|
||||
WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
if (waypointId != -1L) {
|
||||
success = true;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to insert waypoint", e);
|
||||
}
|
||||
Toast.makeText(this,
|
||||
success ? R.string.marker_insert_success : R.string.marker_insert_error,
|
||||
success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onTrackballEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
navControls.show();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the menu.
|
||||
*/
|
||||
private void updateMenu() {
|
||||
updateMenuItems(isRecording());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the menu items.
|
||||
*
|
||||
* @param isRecording true if recording
|
||||
*/
|
||||
private void updateMenuItems(boolean isRecording) {
|
||||
if (stopRecordingMenuItem != null) {
|
||||
stopRecordingMenuItem.setVisible(isRecording);
|
||||
}
|
||||
if (insertMarkerMenuItem != null) {
|
||||
insertMarkerMenuItem.setVisible(isRecording);
|
||||
}
|
||||
if (playMenuItem != null) {
|
||||
playMenuItem.setVisible(!isRecording);
|
||||
}
|
||||
if (shareMenuItem != null) {
|
||||
shareMenuItem.setVisible(!isRecording);
|
||||
}
|
||||
if (sendGoogleMenuItem != null) {
|
||||
sendGoogleMenuItem.setVisible(!isRecording);
|
||||
}
|
||||
if (saveMenuItem != null) {
|
||||
saveMenuItem.setVisible(!isRecording);
|
||||
}
|
||||
if (editMenuItem != null) {
|
||||
editMenuItem.setVisible(!isRecording);
|
||||
}
|
||||
if (deleteMenuItem != null) {
|
||||
deleteMenuItem.setVisible(!isRecording);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the current track.
|
||||
*/
|
||||
private void deleteCurrentTrack() {
|
||||
long trackId = trackDataHub.getSelectedTrackId();
|
||||
MyTracksProviderUtils.Factory.get(TrackDetailActivity.this).deleteTrack(trackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the {@link TrackListActivity} and ends this activity.
|
||||
*/
|
||||
private void startTrackListActivity() {
|
||||
Intent intent = new Intent(this, TrackListActivity.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the {@link SaveActivity} to save a track.
|
||||
*
|
||||
* @param trackId the track id
|
||||
* @param trackFileFormat the track file format
|
||||
* @param shareTrack true to share the track after saving
|
||||
*/
|
||||
private void startSaveActivity(
|
||||
long trackId, TrackFileFormat trackFileFormat, boolean shareTrack) {
|
||||
Intent intent = new Intent(this, SaveActivity.class)
|
||||
.putExtra(SaveActivity.EXTRA_TRACK_ID, trackId)
|
||||
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat)
|
||||
.putExtra(SaveActivity.EXTRA_SHARE_TRACK, shareTrack);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if recording.
|
||||
*/
|
||||
private boolean isRecording() {
|
||||
return ServiceUtils.isRecording(
|
||||
this, trackRecordingServiceConnection.getServiceIfBound(), sharedPreferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the recording and the track recording service connection and shows
|
||||
* {@link TrackEditActivity}.
|
||||
*/
|
||||
private void stopRecording() {
|
||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection
|
||||
.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
try {
|
||||
trackRecordingService.endCurrentTrack();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to stop recording.", e);
|
||||
}
|
||||
}
|
||||
trackRecordingServiceConnection.stop();
|
||||
|
||||
long recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
|
||||
if (recordingTrackId != -1L) {
|
||||
Intent intent = new Intent(this, TrackEditActivity.class)
|
||||
.putExtra(TrackEditActivity.SHOW_CANCEL, false)
|
||||
.putExtra(TrackEditActivity.TRACK_ID, recordingTrackId);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map activity, can be null.
|
||||
*/
|
||||
private MapActivity getMapActivity() {
|
||||
return (MapActivity) getLocalActivityManager().getActivity(MAP_TAB_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chart activity, can be null.
|
||||
*/
|
||||
private ChartActivity getChartActivity() {
|
||||
return (ChartActivity) getLocalActivityManager().getActivity(CHART_TAB_TAG);
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -35,12 +35,12 @@ import android.widget.EditText;
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class TrackDetail extends Activity implements OnClickListener {
|
||||
public class TrackEditActivity extends Activity implements OnClickListener {
|
||||
|
||||
public static final String TRACK_ID = "trackId";
|
||||
public static final String SHOW_CANCEL = "showCancel";
|
||||
|
||||
private static final String TAG = TrackDetail.class.getSimpleName();
|
||||
private static final String TAG = TrackEditActivity.class.getSimpleName();
|
||||
|
||||
private Long trackId;
|
||||
private MyTracksProviderUtils myTracksProviderUtils;
|
||||
@@ -53,7 +53,7 @@ public class TrackDetail extends Activity implements OnClickListener {
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setContentView(R.layout.track_detail);
|
||||
setContentView(R.layout.track_edit);
|
||||
|
||||
trackId = getIntent().getLongExtra(TRACK_ID, -1);
|
||||
if (trackId < 0) {
|
||||
@@ -71,22 +71,22 @@ public class TrackDetail extends Activity implements OnClickListener {
|
||||
return;
|
||||
}
|
||||
|
||||
trackName = (EditText) findViewById(R.id.track_detail_track_name);
|
||||
trackName = (EditText) findViewById(R.id.track_edit_track_name);
|
||||
trackName.setText(track.getName());
|
||||
|
||||
activityType = (AutoCompleteTextView) findViewById(R.id.track_detail_activity_type);
|
||||
activityType = (AutoCompleteTextView) 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);
|
||||
trackDescription = (EditText) findViewById(R.id.track_detail_track_description);
|
||||
trackDescription = (EditText) findViewById(R.id.track_edit_track_description);
|
||||
trackDescription.setText(track.getDescription());
|
||||
|
||||
Button save = (Button) findViewById(R.id.track_detail_save);
|
||||
Button save = (Button) findViewById(R.id.track_edit_save);
|
||||
save.setOnClickListener(this);
|
||||
|
||||
Button cancel = (Button) findViewById(R.id.track_detail_cancel);
|
||||
Button cancel = (Button) findViewById(R.id.track_edit_cancel);
|
||||
if (getIntent().getBooleanExtra(SHOW_CANCEL, true)) {
|
||||
cancel.setOnClickListener(this);
|
||||
cancel.setVisibility(View.VISIBLE);
|
||||
@@ -98,11 +98,11 @@ public class TrackDetail extends Activity implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.track_detail_save:
|
||||
case R.id.track_edit_save:
|
||||
save();
|
||||
finish();
|
||||
break;
|
||||
case R.id.track_detail_cancel:
|
||||
case R.id.track_edit_cancel:
|
||||
finish();
|
||||
break;
|
||||
default:
|
||||
@@ -1,373 +0,0 @@
|
||||
/*
|
||||
* 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 static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.UploadServiceChooserActivity;
|
||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.PlayTrackUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ListActivity;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.view.Window;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SimpleCursorAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A list activity displaying all the recorded tracks. There's a context
|
||||
* menu (via long press) displaying various options such as showing, editing,
|
||||
* deleting, sending to Google, or writing to SD card.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class TrackList extends ListActivity
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener,
|
||||
View.OnClickListener {
|
||||
|
||||
private static final int DIALOG_INSTALL_EARTH = 0;
|
||||
|
||||
private int contextPosition = -1;
|
||||
private long trackId = -1;
|
||||
private ListView listView = null;
|
||||
private boolean metricUnits = true;
|
||||
|
||||
private Cursor tracksCursor = null;
|
||||
|
||||
/**
|
||||
* The id of the currently recording track.
|
||||
*/
|
||||
private long recordingTrackId = -1;
|
||||
|
||||
private final OnCreateContextMenuListener contextMenuListener =
|
||||
new OnCreateContextMenuListener() {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
menu.setHeaderTitle(R.string.track_list_context_menu_title);
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
contextPosition = info.position;
|
||||
trackId = TrackList.this.listView.getAdapter().getItemId(contextPosition);
|
||||
menu.add(Menu.NONE, Constants.MENU_SHOW, Menu.NONE, R.string.track_list_show_on_map);
|
||||
menu.add(Menu.NONE, Constants.MENU_EDIT, Menu.NONE, R.string.track_list_edit_track);
|
||||
if (!isRecording() || trackId != recordingTrackId) {
|
||||
String saveFileFormat = getString(R.string.track_list_save_file);
|
||||
String shareFileFormat = getString(R.string.track_list_share_file);
|
||||
String fileTypes[] = getResources().getStringArray(R.array.file_types);
|
||||
|
||||
menu.add(Menu.NONE, Constants.MENU_PLAY, Menu.NONE, R.string.track_list_play);
|
||||
menu.add(Menu.NONE, Constants.MENU_SEND_TO_GOOGLE, Menu.NONE,
|
||||
R.string.track_list_send_google);
|
||||
SubMenu share = menu.addSubMenu(
|
||||
Menu.NONE, Constants.MENU_SHARE, Menu.NONE, R.string.track_list_share_track);
|
||||
share.add(
|
||||
Menu.NONE, Constants.MENU_SHARE_MAP, Menu.NONE, R.string.track_list_share_map);
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_FUSION_TABLE, Menu.NONE,
|
||||
R.string.track_list_share_fusion_table);
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_GPX_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[0]));
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_KML_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[1]));
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_CSV_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[2]));
|
||||
share.add(Menu.NONE, Constants.MENU_SHARE_TCX_FILE, Menu.NONE,
|
||||
String.format(shareFileFormat, fileTypes[3]));
|
||||
SubMenu save = menu.addSubMenu(
|
||||
Menu.NONE, Constants.MENU_WRITE_TO_SD_CARD, Menu.NONE, R.string.track_list_save_sd);
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_GPX_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[0]));
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_KML_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[1]));
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_CSV_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[2]));
|
||||
save.add(Menu.NONE, Constants.MENU_SAVE_TCX_FILE, Menu.NONE,
|
||||
String.format(saveFileFormat, fileTypes[3]));
|
||||
menu.add(Menu.NONE, Constants.MENU_DELETE, Menu.NONE, R.string.track_list_delete_track);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable serviceBindingChanged = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateButtonsEnabled();
|
||||
}
|
||||
};
|
||||
|
||||
private TrackRecordingServiceConnection serviceConnection;
|
||||
private SharedPreferences preferences;
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(
|
||||
SharedPreferences sharedPreferences, String key) {
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
if (key.equals(getString(R.string.metric_units_key))) {
|
||||
metricUnits = sharedPreferences.getBoolean(
|
||||
getString(R.string.metric_units_key), true);
|
||||
if (tracksCursor != null && !tracksCursor.isClosed()) {
|
||||
tracksCursor.requery();
|
||||
}
|
||||
}
|
||||
if (key.equals(getString(R.string.recording_track_key))) {
|
||||
recordingTrackId = sharedPreferences.getLong(
|
||||
getString(R.string.recording_track_key), -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
Intent result = new Intent();
|
||||
result.putExtra("trackid", id);
|
||||
setResult(Constants.SHOW_TRACK, result);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
Intent intent;
|
||||
switch (item.getItemId()) {
|
||||
case Constants.MENU_SHOW: {
|
||||
onListItemClick(null, null, 0, trackId);
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_EDIT: {
|
||||
intent = new Intent(this, TrackDetail.class).putExtra(TrackDetail.TRACK_ID, trackId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_PLAY:
|
||||
if (PlayTrackUtils.isEarthInstalled(this)) {
|
||||
PlayTrackUtils.playTrack(this, trackId);
|
||||
return true;
|
||||
} else {
|
||||
showDialog(DIALOG_INSTALL_EARTH);
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_SEND_TO_GOOGLE:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, true, true));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case Constants.MENU_SHARE_MAP:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, false, false));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case Constants.MENU_SHARE_FUSION_TABLE:
|
||||
intent = new Intent(this, UploadServiceChooserActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, false, true, false));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case Constants.MENU_SHARE_GPX_FILE:
|
||||
case Constants.MENU_SHARE_KML_FILE:
|
||||
case Constants.MENU_SHARE_CSV_FILE:
|
||||
case Constants.MENU_SHARE_TCX_FILE:
|
||||
case Constants.MENU_SAVE_GPX_FILE:
|
||||
case Constants.MENU_SAVE_KML_FILE:
|
||||
case Constants.MENU_SAVE_CSV_FILE:
|
||||
case Constants.MENU_SAVE_TCX_FILE:
|
||||
SaveActivity.handleExportTrackAction(
|
||||
this, trackId, Constants.getActionFromMenuId(item.getItemId()));
|
||||
return true;
|
||||
case Constants.MENU_DELETE:
|
||||
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
|
||||
intent = new Intent(Intent.ACTION_DELETE)
|
||||
.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
default:
|
||||
Log.w(TAG, "Unknown menu item: " + item.getItemId() + "(" + item.getTitle() + ")");
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.tracklist_btn_delete_all: {
|
||||
Handler h = new DeleteAllTracks(this, null);
|
||||
h.handleMessage(null);
|
||||
break;
|
||||
}
|
||||
case R.id.tracklist_btn_export_all: {
|
||||
new ExportAllTracks(this);
|
||||
break;
|
||||
}
|
||||
case R.id.tracklist_btn_import_all: {
|
||||
new ImportAllTracks(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// We don't need a window title bar:
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
setContentView(R.layout.mytracks_list);
|
||||
|
||||
listView = getListView();
|
||||
listView.setOnCreateContextMenuListener(contextMenuListener);
|
||||
|
||||
preferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
serviceConnection = new TrackRecordingServiceConnection(this, serviceBindingChanged);
|
||||
|
||||
View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
|
||||
deleteAll.setOnClickListener(this);
|
||||
|
||||
View exportAll = findViewById(R.id.tracklist_btn_export_all);
|
||||
exportAll.setOnClickListener(this);
|
||||
|
||||
updateButtonsEnabled();
|
||||
|
||||
findViewById(R.id.tracklist_btn_import_all).setOnClickListener(this);
|
||||
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
metricUnits =
|
||||
preferences.getBoolean(getString(R.string.metric_units_key), true);
|
||||
recordingTrackId =
|
||||
preferences.getLong(getString(R.string.recording_track_key), -1);
|
||||
|
||||
tracksCursor = getContentResolver().query(
|
||||
TracksColumns.CONTENT_URI, null, null, null, "_id DESC");
|
||||
startManagingCursor(tracksCursor);
|
||||
setListAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
serviceConnection.bindIfRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
serviceConnection.unbind();
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.search_only, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_INSTALL_EARTH:
|
||||
return PlayTrackUtils.createInstallEarthDialog(this);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback from menu/search_only.xml */
|
||||
public void onSearch(@SuppressWarnings("unused") MenuItem i) {
|
||||
onSearchRequested();
|
||||
}
|
||||
|
||||
private void updateButtonsEnabled() {
|
||||
View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
|
||||
View exportAll = findViewById(R.id.tracklist_btn_export_all);
|
||||
|
||||
boolean notRecording = !isRecording();
|
||||
deleteAll.setEnabled(notRecording);
|
||||
exportAll.setEnabled(notRecording);
|
||||
}
|
||||
|
||||
private void setListAdapter() {
|
||||
// Get a cursor with all tracks
|
||||
SimpleCursorAdapter adapter = new SimpleCursorAdapter(
|
||||
this,
|
||||
R.layout.mytracks_list_item,
|
||||
tracksCursor,
|
||||
new String[] { TracksColumns.NAME, TracksColumns.STARTTIME,
|
||||
TracksColumns.TOTALDISTANCE, TracksColumns.DESCRIPTION,
|
||||
TracksColumns.CATEGORY },
|
||||
new int[] { R.id.track_list_item_name, R.id.track_list_item_time,
|
||||
R.id.track_list_item_stats, R.id.track_list_item_description,
|
||||
R.id.track_list_item_category });
|
||||
|
||||
final int startTimeIdx =
|
||||
tracksCursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
final int totalTimeIdx =
|
||||
tracksCursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
||||
final int totalDistanceIdx =
|
||||
tracksCursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||
|
||||
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
|
||||
@Override
|
||||
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
|
||||
TextView textView = (TextView) view;
|
||||
if (columnIndex == startTimeIdx) {
|
||||
long time = cursor.getLong(startTimeIdx);
|
||||
textView.setText(StringUtils.formatDateTime(TrackList.this, time));
|
||||
} else if (columnIndex == totalDistanceIdx) {
|
||||
double totalDistance = cursor.getDouble(totalDistanceIdx);
|
||||
long totalTime = cursor.getLong(totalTimeIdx);
|
||||
String totalDistanceStr = StringUtils.formatTimeDistance(
|
||||
TrackList.this, totalTime, totalDistance, metricUnits);
|
||||
textView.setText(totalDistanceStr);
|
||||
} else {
|
||||
textView.setText(cursor.getString(columnIndex));
|
||||
if (textView.getText().length() < 1) {
|
||||
textView.setVisibility(View.GONE);
|
||||
} else {
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
setListAdapter(adapter);
|
||||
}
|
||||
|
||||
private boolean isRecording() {
|
||||
return ServiceUtils.isRecording(TrackList.this, serviceConnection.getServiceIfBound(), preferences);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* 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.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.ServiceUtils;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.CheckUnitsUtils;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.EulaUtils;
|
||||
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.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.ResourceCursorAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* An activity displaying a list of tracks.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class TrackListActivity extends FragmentActivity {
|
||||
|
||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||
private static final String CHECK_UNITS_DIALOG_TAG = "checkUnitsDialog";
|
||||
private static final String DELETE_ALL_DIALOG_TAG = "deleteAllDialog";
|
||||
private static final String EULA_DIALOG_TAG = "eulaDialog";
|
||||
private static final String EXPORT_ALL_DIALOG_TAG = "exportAllDialog";
|
||||
|
||||
private static final int WELCOME_ACTIVITY_REQUEST_CODE = 0;
|
||||
|
||||
private static final String[] PROJECTION = new String[] {
|
||||
TracksColumns._ID,
|
||||
TracksColumns.NAME,
|
||||
TracksColumns.CATEGORY,
|
||||
TracksColumns.TOTALTIME,
|
||||
TracksColumns.TOTALDISTANCE,
|
||||
TracksColumns.STARTTIME,
|
||||
TracksColumns.DESCRIPTION };
|
||||
|
||||
// Callback when the trackRecordingServiceConnection binding changes.
|
||||
private final Runnable bindChangedCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!startNewRecording) {
|
||||
return;
|
||||
}
|
||||
|
||||
ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service == null) {
|
||||
Log.d(TAG, "service not available to start a new recording");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
recordingTrackId = service.startNewTrack();
|
||||
startNewRecording = false;
|
||||
Toast.makeText(
|
||||
TrackListActivity.this, R.string.track_list_record_success, Toast.LENGTH_SHORT).show();
|
||||
Log.d(TAG, "Started a new recording");
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(TrackListActivity.this, R.string.track_list_record_error, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
Log.e(TAG, "Unable to start a new recording.", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
// Note that key can be null
|
||||
if (getString(R.string.metric_units_key).equals(key)) {
|
||||
metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), true);
|
||||
}
|
||||
if (getString(R.string.recording_track_key).equals(key)) {
|
||||
recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
|
||||
if (isRecording()) {
|
||||
trackRecordingServiceConnection.startAndBind();
|
||||
}
|
||||
updateMenu();
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private boolean metricUnits;
|
||||
private long recordingTrackId;
|
||||
private ListView listView;
|
||||
private ResourceCursorAdapter adapter;
|
||||
|
||||
// True to start a new recording.
|
||||
private boolean startNewRecording = false;
|
||||
|
||||
private MenuItem recordTrack;
|
||||
private MenuItem stopRecording;
|
||||
private MenuItem search;
|
||||
private MenuItem importAll;
|
||||
private MenuItem exportAll;
|
||||
private MenuItem deleteAll;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
setContentView(R.layout.track_list);
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(
|
||||
this, bindChangedCallback);
|
||||
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
metricUnits = sharedPreferences.getBoolean(getString(R.string.metric_units_key), true);
|
||||
recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
|
||||
|
||||
listView = (ListView) findViewById(R.id.track_list);
|
||||
listView.setOnItemClickListener(new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Intent intent = new Intent(TrackListActivity.this, TrackDetailActivity.class)
|
||||
.putExtra(TrackDetailActivity.TRACK_ID, id);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
adapter = new ResourceCursorAdapter(this, R.layout.track_list_item, null, 0) {
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
int idIndex = cursor.getColumnIndex(TracksColumns._ID);
|
||||
int nameIndex = cursor.getColumnIndex(TracksColumns.NAME);
|
||||
int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY);
|
||||
int timeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
||||
int distanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||
int startIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION);
|
||||
|
||||
boolean isRecording = cursor.getLong(idIndex) == recordingTrackId;
|
||||
|
||||
TextView name = (TextView) view.findViewById(R.id.track_list_item_name);
|
||||
name.setText(cursor.getString(nameIndex));
|
||||
name.setCompoundDrawablesWithIntrinsicBounds(
|
||||
isRecording ? R.drawable.menu_record_track : R.drawable.track, 0, 0, 0);
|
||||
|
||||
TextView category = (TextView) view.findViewById(R.id.track_list_item_category);
|
||||
category.setText(cursor.getString(categoryIndex));
|
||||
|
||||
TextView time = (TextView) view.findViewById(R.id.track_list_item_time);
|
||||
time.setText(StringUtils.formatElapsedTime(cursor.getLong(timeIndex)));
|
||||
time.setVisibility(isRecording ? View.GONE : View.VISIBLE);
|
||||
|
||||
TextView distance = (TextView) view.findViewById(R.id.track_list_item_distance);
|
||||
distance.setText(StringUtils.formatDistance(
|
||||
TrackListActivity.this, cursor.getDouble(distanceIndex), metricUnits));
|
||||
distance.setVisibility(isRecording ? View.GONE : View.VISIBLE);
|
||||
|
||||
TextView start = (TextView) view.findViewById(R.id.track_list_item_start);
|
||||
start.setText(
|
||||
StringUtils.formatDateTime(TrackListActivity.this, cursor.getLong(startIndex)));
|
||||
start.setVisibility(start.getText().equals(name.getText()) ? View.GONE : View.VISIBLE);
|
||||
|
||||
TextView description = (TextView) view.findViewById(R.id.track_list_item_description);
|
||||
description.setText(cursor.getString(descriptionIndex));
|
||||
description.setVisibility(description.getText().length() == 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
};
|
||||
listView.setAdapter(adapter);
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return new CursorLoader(TrackListActivity.this,
|
||||
TracksColumns.CONTENT_URI,
|
||||
PROJECTION,
|
||||
null,
|
||||
null,
|
||||
TracksColumns._ID + " DESC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
adapter.swapCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
adapter.swapCursor(null);
|
||||
}
|
||||
});
|
||||
|
||||
if (!EulaUtils.getEulaValue(this)) {
|
||||
new EulaDialogFragment().show(getSupportFragmentManager(), EULA_DIALOG_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (isRecording()) {
|
||||
trackRecordingServiceConnection.startAndBind();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
trackRecordingServiceConnection.bindIfRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, final Intent results) {
|
||||
if (requestCode == WELCOME_ACTIVITY_REQUEST_CODE) {
|
||||
if (!CheckUnitsUtils.getCheckUnitsValue(this)) {
|
||||
/*
|
||||
* See bug http://code.google.com/p/android/issues/detail?id=23761.
|
||||
* Cannot use
|
||||
* CheckUnitsDialogFragment().show(getSupportFragmentManager(),
|
||||
* CHECK_UNITS_DIALOG_TAG). Need to use commitAllowingStateLoss with the
|
||||
* support package.
|
||||
*/
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
fragmentTransaction.add(new CheckUnitsDialogFragment(), CHECK_UNITS_DIALOG_TAG);
|
||||
fragmentTransaction.commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.track_list, menu);
|
||||
|
||||
recordTrack = menu.findItem(R.id.menu_record_track);
|
||||
stopRecording = menu.findItem(R.id.menu_stop_recording);
|
||||
search = menu.findItem(R.id.menu_search);
|
||||
importAll = menu.findItem(R.id.menu_import_all);
|
||||
exportAll = menu.findItem(R.id.menu_export_all);
|
||||
deleteAll = menu.findItem(R.id.menu_delete_all);
|
||||
|
||||
ApiAdapterFactory.getApiAdapter().configureSearchWidget(this, search);
|
||||
updateMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the menu based on whether My Tracks is recording or not.
|
||||
*/
|
||||
private void updateMenu() {
|
||||
boolean isRecording = isRecording();
|
||||
updateMenuItems(isRecording);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the menu items.
|
||||
*
|
||||
* @param isRecording true if recording
|
||||
*/
|
||||
private void updateMenuItems(boolean isRecording) {
|
||||
if (recordTrack != null) {
|
||||
recordTrack.setVisible(!isRecording);
|
||||
}
|
||||
if (stopRecording != null) {
|
||||
stopRecording.setVisible(isRecording);
|
||||
}
|
||||
if (importAll != null) {
|
||||
importAll.setVisible(!isRecording);
|
||||
}
|
||||
if (exportAll != null) {
|
||||
exportAll.setVisible(!isRecording);
|
||||
}
|
||||
if (deleteAll != null) {
|
||||
deleteAll.setVisible(!isRecording);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_record_track:
|
||||
updateMenuItems(true);
|
||||
startRecording();
|
||||
return true;
|
||||
case R.id.menu_stop_recording:
|
||||
updateMenuItems(false);
|
||||
stopRecording();
|
||||
return true;
|
||||
case R.id.menu_search:
|
||||
return ApiAdapterFactory.getApiAdapter().handleSearchMenuSelection(this);
|
||||
case R.id.menu_import_all:
|
||||
startActivity(
|
||||
new Intent(this, ImportActivity.class).putExtra(ImportActivity.EXTRA_IMPORT_ALL, true));
|
||||
return true;
|
||||
case R.id.menu_export_all:
|
||||
new ExportAllDialogFragment().show(getSupportFragmentManager(), EXPORT_ALL_DIALOG_TAG);
|
||||
return true;
|
||||
case R.id.menu_delete_all:
|
||||
new DeleteAllDialogFragment().show(getSupportFragmentManager(), DELETE_ALL_DIALOG_TAG);
|
||||
return true;
|
||||
case R.id.menu_aggregated_statistics:
|
||||
startActivity(new Intent(this, AggregatedStatsActivity.class));
|
||||
return true;
|
||||
case R.id.menu_settings:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
case R.id.menu_help:
|
||||
startActivity(new Intent(this, WelcomeActivity.class));
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if recording.
|
||||
*/
|
||||
private boolean isRecording() {
|
||||
return ServiceUtils.isRecording(
|
||||
this, trackRecordingServiceConnection.getServiceIfBound(), sharedPreferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new recording.
|
||||
*/
|
||||
private void startRecording() {
|
||||
startNewRecording = true;
|
||||
trackRecordingServiceConnection.startAndBind();
|
||||
|
||||
/*
|
||||
* If the binding has happened, then invoke the callback to start a new
|
||||
* recording. If the binding hasn't happened, then invoking the callback
|
||||
* will have no effect. But when the binding occurs, the callback will get
|
||||
* invoked.
|
||||
*/
|
||||
bindChangedCallback.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current recording.
|
||||
*/
|
||||
private void stopRecording() {
|
||||
ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service != null) {
|
||||
try {
|
||||
service.endCurrentTrack();
|
||||
if (recordingTrackId != -1L) {
|
||||
Intent intent = new Intent(this, TrackEditActivity.class)
|
||||
.putExtra(TrackEditActivity.SHOW_CANCEL, false)
|
||||
.putExtra(TrackEditActivity.TRACK_ID, recordingTrackId);
|
||||
startActivity(intent);
|
||||
}
|
||||
recordingTrackId = -1L;
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Unable to stop recording.", e);
|
||||
}
|
||||
}
|
||||
trackRecordingServiceConnection.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
if (ApiAdapterFactory.getApiAdapter().handleSearchKey(search)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Units DialogFragment.
|
||||
*/
|
||||
public static class CheckUnitsDialogFragment extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
return new AlertDialog.Builder(getActivity()).setCancelable(true)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
CheckUnitsUtils.setCheckUnitsValue(getActivity());
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
CheckUnitsUtils.setCheckUnitsValue(getActivity());
|
||||
|
||||
int position = ((AlertDialog) dialog).getListView().getSelectedItemPosition();
|
||||
SharedPreferences sharedPreferences = getActivity()
|
||||
.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(sharedPreferences.edit()
|
||||
.putBoolean(getString(R.string.metric_units_key), position == 0));
|
||||
}
|
||||
})
|
||||
.setSingleChoiceItems(new CharSequence[] { getString(R.string.preferred_units_metric),
|
||||
getString(R.string.preferred_units_imperial) }, 0, null)
|
||||
.setTitle(R.string.preferred_units_title)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete All DialogFragment.
|
||||
*/
|
||||
public static class DeleteAllDialogFragment extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return DialogUtils.createConfirmationDialog(getActivity(),
|
||||
R.string.track_list_delete_all_confirm_message, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
MyTracksProviderUtils.Factory.get(getActivity()).deleteAllTracks();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Eula DialogFragment.
|
||||
*/
|
||||
public static class EulaDialogFragment extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setCancelable(true)
|
||||
.setMessage(EulaUtils.getEulaMessage(getActivity()))
|
||||
.setNegativeButton(R.string.eula_decline, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
getActivity().finish();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
getActivity().finish();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
EulaUtils.setEulaValue(getActivity());
|
||||
getActivity().startActivityForResult(
|
||||
new Intent(getActivity(), WelcomeActivity.class), WELCOME_ACTIVITY_REQUEST_CODE);
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.eula_title)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export All DialogFragment.
|
||||
*/
|
||||
public static class ExportAllDialogFragment extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
String fileTypes[] = getResources().getStringArray(R.array.file_types);
|
||||
String[] choices = new String[fileTypes.length];
|
||||
for (int i = 0; i < fileTypes.length; i++) {
|
||||
choices[i] = getString(R.string.menu_export_all_format, fileTypes[i]);
|
||||
}
|
||||
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
int index = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
Intent intent = new Intent(getActivity(), ExportActivity.class).putExtra(
|
||||
ExportActivity.EXTRA_TRACK_FILE_FORMAT,
|
||||
(Parcelable) TrackFileFormat.values()[index]);
|
||||
getActivity().startActivity(intent);
|
||||
}
|
||||
})
|
||||
.setSingleChoiceItems(choices, 0, null)
|
||||
.setTitle(R.string.menu_export_all)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,11 @@ import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
import com.google.android.apps.mytracks.services.ITrackRecordingService;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
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.app.ListActivity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -57,9 +58,10 @@ import android.widget.Toast;
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class WaypointsList extends ListActivity
|
||||
implements View.OnClickListener {
|
||||
public class WaypointsList extends ListActivity implements View.OnClickListener {
|
||||
|
||||
private static final int DIALOG_DELETE_CURRENT_ID = 0;
|
||||
|
||||
private int contextPosition = -1;
|
||||
private long trackId = -1;
|
||||
private long selectedWaypointId = -1;
|
||||
@@ -119,7 +121,7 @@ public class WaypointsList extends ListActivity
|
||||
return true;
|
||||
}
|
||||
case Constants.MENU_DELETE: {
|
||||
deleteWaypoint(selectedWaypointId);
|
||||
showDialog(DIALOG_DELETE_CURRENT_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,15 +201,36 @@ public class WaypointsList extends ListActivity
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != DIALOG_DELETE_CURRENT_ID) {
|
||||
return null;
|
||||
}
|
||||
return DialogUtils.createConfirmationDialog(this,
|
||||
R.string.marker_list_delete_marker_confirm_message, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
providerUtils.deleteWaypoint(
|
||||
selectedWaypointId, new DescriptionGeneratorImpl(WaypointsList.this));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.search_only, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Callback from menu/search_only.xml */
|
||||
public void onSearch(@SuppressWarnings("unused") MenuItem i) {
|
||||
onSearchRequested();
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_search:
|
||||
onSearchRequested();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -303,34 +326,4 @@ public class WaypointsList extends ListActivity
|
||||
});
|
||||
setListAdapter(adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the way point with the given id.
|
||||
* Prompts the user if he want to really delete it.
|
||||
*/
|
||||
public void deleteWaypoint(final long waypointId) {
|
||||
AlertDialog dialog = null;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(getString(R.string.marker_list_delete_marker_confirm_message));
|
||||
builder.setTitle(getString(R.string.generic_confirm_title));
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
builder.setPositiveButton(getString(R.string.generic_yes),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
dialogInterface.dismiss();
|
||||
providerUtils.deleteWaypoint(waypointId,
|
||||
new DescriptionGeneratorImpl(WaypointsList.this));
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(getString(R.string.generic_no),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
dialogInterface.dismiss();
|
||||
}
|
||||
});
|
||||
dialog = builder.create();
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.TextView;
|
||||
@@ -57,31 +56,30 @@ public class WelcomeActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
AlertDialog.Builder builder;
|
||||
switch (id) {
|
||||
case DIALOG_ABOUT_ID:
|
||||
LayoutInflater layoutInflator = LayoutInflater.from(this);
|
||||
View view = layoutInflator.inflate(R.layout.about, null);
|
||||
TextView aboutVersionTextView = (TextView) view.findViewById(R.id.about_version);
|
||||
aboutVersionTextView.setText(SystemUtils.getMyTracksVersion(this));
|
||||
|
||||
builder = new AlertDialog.Builder(this);
|
||||
builder.setView(view);
|
||||
builder.setPositiveButton(R.string.generic_ok, null);
|
||||
builder.setNegativeButton(R.string.about_license, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
showDialog(DIALOG_EULA_ID);
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
View view = getLayoutInflater().inflate(R.layout.about, null);
|
||||
TextView aboutVersion = (TextView) view.findViewById(R.id.about_version);
|
||||
aboutVersion.setText(SystemUtils.getMyTracksVersion(this));
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.about_license, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
showDialog(DIALOG_EULA_ID);
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, null)
|
||||
.setTitle(R.string.my_tracks_app_name)
|
||||
.setView(view)
|
||||
.create();
|
||||
case DIALOG_EULA_ID:
|
||||
builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.eula_title);
|
||||
builder.setMessage(EulaUtils.getEulaMessage(this));
|
||||
builder.setPositiveButton(R.string.generic_ok, null);
|
||||
builder.setCancelable(true);
|
||||
return builder.create();
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setMessage(EulaUtils.getEulaMessage(this))
|
||||
.setPositiveButton(R.string.generic_ok, null)
|
||||
.setTitle(R.string.eula_title)
|
||||
.create();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
+6
-3
@@ -15,21 +15,24 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.backup;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.backup.BackupManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
/**
|
||||
* Implementation of {@link BackupPreferencesListener} that calls the
|
||||
* {@link BackupManager}.
|
||||
* {@link BackupManager}. <br>
|
||||
* For API Level 8 or higher.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class BackupPreferencesListenerImpl implements BackupPreferencesListener {
|
||||
@TargetApi(8)
|
||||
public class Api8BackupPreferencesListener implements BackupPreferencesListener {
|
||||
|
||||
private final BackupManager backupManager;
|
||||
|
||||
public BackupPreferencesListenerImpl(Context context) {
|
||||
public Api8BackupPreferencesListener(Context context) {
|
||||
this.backupManager = new BackupManager(context);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.io.backup;
|
||||
|
||||
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.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* Activity to backup data to the SD card.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class BackupActivity extends Activity {
|
||||
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
|
||||
private BackupAsyncTask backupAsyncTask;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof BackupAsyncTask) {
|
||||
backupAsyncTask = (BackupAsyncTask) retained;
|
||||
backupAsyncTask.setActivity(this);
|
||||
} else {
|
||||
backupAsyncTask = new BackupAsyncTask(this);
|
||||
backupAsyncTask.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
backupAsyncTask.setActivity(null);
|
||||
return backupAsyncTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != DIALOG_PROGRESS_ID) {
|
||||
return null;
|
||||
}
|
||||
return DialogUtils.createSpinnerProgressDialog(
|
||||
this, R.string.settings_backup_now_progress_message, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param success true if the AsyncTask is successful
|
||||
* @param messageId message id to display to user
|
||||
*/
|
||||
public void onAsyncTaskCompleted(boolean success, int messageId) {
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.io.backup;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Helper which shows a UI for writing or restoring a backup,
|
||||
* and calls the appropriate handler for actually executing those
|
||||
* operations.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class BackupActivityHelper {
|
||||
|
||||
private static final Comparator<Date> REVERSE_DATE_ORDER =
|
||||
new Comparator<Date>() {
|
||||
@Override
|
||||
public int compare(Date s1, Date s2) {
|
||||
return s2.compareTo(s1);
|
||||
}
|
||||
};
|
||||
|
||||
private final FileUtils fileUtils;
|
||||
private final ExternalFileBackup backup;
|
||||
private final Activity activity;
|
||||
|
||||
public BackupActivityHelper(Activity activity) {
|
||||
this.activity = activity;
|
||||
this.fileUtils = new FileUtils();
|
||||
this.backup = new ExternalFileBackup(activity, fileUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a full backup to the default file.
|
||||
* This shows the results to the user.
|
||||
*/
|
||||
public void writeBackup() {
|
||||
if (!fileUtils.isSdCardAvailable()) {
|
||||
showToast(R.string.sd_card_error_no_storage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!backup.isBackupsDirectoryAvailable(true)) {
|
||||
showToast(R.string.sd_card_error_create_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
final ProgressDialog progressDialog = ProgressDialog.show(
|
||||
activity,
|
||||
activity.getString(R.string.generic_progress_title),
|
||||
activity.getString(R.string.settings_backup_now_progress_message),
|
||||
true);
|
||||
|
||||
// Do the writing in another thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
backup.writeToDefaultFile();
|
||||
showToast(R.string.sd_card_success_write_file);
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Failed to write backup", e);
|
||||
showToast(R.string.sd_card_error_write_file);
|
||||
} finally {
|
||||
dismissDialog(progressDialog);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a full backup from the SD card.
|
||||
* The user will be given a choice of which backup to restore as well as a
|
||||
* confirmation dialog.
|
||||
*/
|
||||
public void restoreBackup() {
|
||||
// Get the list of existing backups
|
||||
if (!fileUtils.isSdCardAvailable()) {
|
||||
showToast(R.string.sd_card_error_no_storage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!backup.isBackupsDirectoryAvailable(false)) {
|
||||
showToast(R.string.settings_backup_restore_no_backup);
|
||||
return;
|
||||
}
|
||||
|
||||
final Date[] backupDates = backup.getAvailableBackups();
|
||||
if (backupDates == null || backupDates.length == 0) {
|
||||
showToast(R.string.settings_backup_restore_no_backup);
|
||||
return;
|
||||
}
|
||||
Arrays.sort(backupDates, REVERSE_DATE_ORDER);
|
||||
|
||||
// Show a confirmation dialog
|
||||
Builder confirmationDialogBuilder = new AlertDialog.Builder(activity);
|
||||
confirmationDialogBuilder.setMessage(R.string.settings_backup_restore_confirm_message);
|
||||
confirmationDialogBuilder.setCancelable(true);
|
||||
confirmationDialogBuilder.setPositiveButton(android.R.string.yes,
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
pickBackupForRestore(backupDates);
|
||||
}
|
||||
});
|
||||
confirmationDialogBuilder.setNegativeButton(android.R.string.no, null);
|
||||
confirmationDialogBuilder.create().show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a backup list for the user to pick, then restores it.
|
||||
*
|
||||
* @param backupDates the list of available backup files
|
||||
*/
|
||||
private void pickBackupForRestore(final Date[] backupDates) {
|
||||
if (backupDates.length == 1) {
|
||||
// Only one choice, don't bother showing the list
|
||||
restoreFromDateAsync(backupDates[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a user-visible version of the backup filenames
|
||||
final String backupDateStrs[] = new String[backupDates.length];
|
||||
for (int i = 0; i < backupDates.length; i++) {
|
||||
backupDateStrs[i] = StringUtils.formatDateTime(activity, backupDates[i].getTime());
|
||||
}
|
||||
|
||||
// Show a dialog for the user to pick which backup to restore
|
||||
Builder dialogBuilder = new AlertDialog.Builder(activity);
|
||||
dialogBuilder.setCancelable(true);
|
||||
dialogBuilder.setTitle(R.string.settings_backup_restore_select_title);
|
||||
dialogBuilder.setItems(backupDateStrs, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// User picked to restore this one
|
||||
restoreFromDateAsync(backupDates[which]);
|
||||
}
|
||||
});
|
||||
dialogBuilder.create().show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a progress dialog, then starts restoring the backup asynchronously.
|
||||
*
|
||||
* @param date the date
|
||||
*/
|
||||
private void restoreFromDateAsync(final Date date) {
|
||||
// Show a progress dialog
|
||||
ProgressDialog.show(
|
||||
activity,
|
||||
activity.getString(R.string.generic_progress_title),
|
||||
activity.getString(R.string.settings_backup_restore_progress_message),
|
||||
true);
|
||||
|
||||
// Do the actual importing in another thread (don't block the UI)
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
backup.restoreFromDate(date);
|
||||
showToast(R.string.sd_card_success_read_file);
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Failed to restore backup", e);
|
||||
showToast(R.string.sd_card_error_read_file);
|
||||
} finally {
|
||||
// Data may have been restored, "reboot" the app to catch it
|
||||
restartApplication();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts My Tracks completely.
|
||||
* This forces any modified data to be re-read.
|
||||
*/
|
||||
private void restartApplication() {
|
||||
Intent intent = new Intent(activity, MyTracks.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a toast with the given contents.
|
||||
*/
|
||||
private void showToast(final int resId) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(activity, resId, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely dismisses the given dialog.
|
||||
*/
|
||||
private void dismissDialog(final Dialog dialog) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.io.backup;
|
||||
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* AsyncTask to backup data to the SD card.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class BackupAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private static final String TAG = BackupAsyncTask.class.getSimpleName();
|
||||
|
||||
private BackupActivity backupActivity;
|
||||
private final ExternalFileBackup externalFileBackup;
|
||||
|
||||
// true if the AsyncTask result is success
|
||||
private boolean success;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
// message id to return to the activity
|
||||
private int messageId;
|
||||
|
||||
/**
|
||||
* Creates an AsyncTask.
|
||||
*
|
||||
* @param backupActivity the activity currently associated with this
|
||||
* AsyncTask
|
||||
*/
|
||||
public BackupAsyncTask(BackupActivity backupActivity) {
|
||||
this.backupActivity = backupActivity;
|
||||
this.externalFileBackup = new ExternalFileBackup(backupActivity);
|
||||
success = false;
|
||||
completed = false;
|
||||
messageId = R.string.sd_card_error_write_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current {@link BackupActivity} associated with this AyncTask.
|
||||
*
|
||||
* @param activity the current {@link BackupActivity}, can be null
|
||||
*/
|
||||
public void setActivity(BackupActivity activity) {
|
||||
this.backupActivity = activity;
|
||||
if (completed && backupActivity != null) {
|
||||
backupActivity.onAsyncTaskCompleted(success, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (backupActivity != null) {
|
||||
backupActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
if (!FileUtils.isSdCardAvailable()) {
|
||||
messageId = R.string.sd_card_error_no_storage;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!externalFileBackup.isBackupsDirectoryAvailable(true)) {
|
||||
messageId = R.string.sd_card_error_create_dir;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
externalFileBackup.writeToDefaultFile();
|
||||
messageId = R.string.sd_card_success_write_file;
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "IO exception", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
success = result;
|
||||
completed = true;
|
||||
if (backupActivity != null) {
|
||||
backupActivity.onAsyncTaskCompleted(success, messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,11 +65,9 @@ class ExternalFileBackup {
|
||||
private static final int COMPRESSION_LEVEL = 8;
|
||||
|
||||
private final Context context;
|
||||
private final FileUtils fileUtils;
|
||||
|
||||
public ExternalFileBackup(Context context, FileUtils fileUtils) {
|
||||
public ExternalFileBackup(Context context) {
|
||||
this.context = context;
|
||||
this.fileUtils = fileUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,12 +85,12 @@ class ExternalFileBackup {
|
||||
* @param create whether to try creating the directory if it doesn't exist
|
||||
*/
|
||||
private File getBackupsDirectory(boolean create) {
|
||||
String dirName = fileUtils.buildExternalDirectoryPath(BACKUPS_SUBDIR);
|
||||
String dirName = FileUtils.buildExternalDirectoryPath(BACKUPS_SUBDIR);
|
||||
final File dir = new File(dirName);
|
||||
Log.d(Constants.TAG, "Dir: " + dir.getAbsolutePath());
|
||||
if (create) {
|
||||
// Try to create - if that fails, return null
|
||||
return fileUtils.ensureDirectoryExists(dir) ? dir : null;
|
||||
return FileUtils.ensureDirectoryExists(dir) ? dir : null;
|
||||
} else {
|
||||
// Return it if it already exists, otherwise return null
|
||||
return dir.isDirectory() ? dir : null;
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.google.android.apps.mytracks.io.backup;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.backup.BackupAgent;
|
||||
import android.app.backup.BackupDataInput;
|
||||
import android.app.backup.BackupDataOutput;
|
||||
@@ -34,6 +35,7 @@ import java.io.IOException;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@TargetApi(8)
|
||||
public class MyTracksBackupAgent extends BackupAgent {
|
||||
private static final String PREFERENCES_ENTITY = "prefs";
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.io.backup;
|
||||
|
||||
import com.google.android.apps.mytracks.TrackListActivity;
|
||||
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 android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* An activity to restore data from the SD card.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class RestoreActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_DATE = "date";
|
||||
|
||||
private static final String TAG = RestoreActivity.class.getSimpleName();
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
|
||||
private RestoreAsyncTask restoreAsyncTask;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof RestoreAsyncTask) {
|
||||
restoreAsyncTask = (RestoreAsyncTask) retained;
|
||||
restoreAsyncTask.setActivity(this);
|
||||
} else {
|
||||
long date = getIntent().getLongExtra(EXTRA_DATE, -1L);
|
||||
if (date == -1L) {
|
||||
Log.d(TAG, "Invalid date");
|
||||
finish();
|
||||
}
|
||||
restoreAsyncTask = new RestoreAsyncTask(this, new Date(date));
|
||||
restoreAsyncTask.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
restoreAsyncTask.setActivity(null);
|
||||
return restoreAsyncTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != DIALOG_PROGRESS_ID) {
|
||||
return null;
|
||||
}
|
||||
return DialogUtils.createSpinnerProgressDialog(this,
|
||||
R.string.settings_backup_restore_progress_message, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param success true if the AsyncTask is successful
|
||||
* @param messageId message id to display to user
|
||||
*/
|
||||
public void onAsyncTaskCompleted(boolean success, int messageId) {
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
|
||||
startActivity(
|
||||
new Intent(this, TrackListActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.io.backup;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* AsyncTask to restore data from the SD card.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class RestoreAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private static final String TAG = RestoreAsyncTask.class.getSimpleName();
|
||||
|
||||
private RestoreActivity restoreActivity;
|
||||
private final Date date;
|
||||
private final ExternalFileBackup externalFileBackup;
|
||||
|
||||
// true if the AsyncTask result is success
|
||||
private boolean success;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
// message id to return to the activity
|
||||
private int messageId;
|
||||
|
||||
/**
|
||||
* Creates an AsyncTask.
|
||||
*
|
||||
* @param restoreActivity the activity currently associated with this
|
||||
* AsyncTask
|
||||
* @param date the date to retore from
|
||||
*/
|
||||
public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) {
|
||||
this.restoreActivity = restoreActivity;
|
||||
this.date = date;
|
||||
this.externalFileBackup = new ExternalFileBackup(restoreActivity);
|
||||
success = false;
|
||||
completed = false;
|
||||
messageId = R.string.sd_card_error_read_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current {@link RestoreActivity} associated with this AyncTask.
|
||||
*
|
||||
* @param activity the current {@link RestoreActivity}, can be null
|
||||
*/
|
||||
public void setActivity(RestoreActivity activity) {
|
||||
this.restoreActivity = activity;
|
||||
if (completed && restoreActivity != null) {
|
||||
restoreActivity.onAsyncTaskCompleted(success, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (restoreActivity != null) {
|
||||
restoreActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
externalFileBackup.restoreFromDate(date);
|
||||
messageId = R.string.sd_card_success_read_file;
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "IO exception", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
success = result;
|
||||
completed = true;
|
||||
if (restoreActivity != null) {
|
||||
restoreActivity.onAsyncTaskCompleted(success, messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.io.backup;
|
||||
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* An activity to choose a date to restore from.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class RestoreChooserActivity extends Activity {
|
||||
|
||||
private static final Comparator<Date> REVERSE_DATE_ORDER = new Comparator<Date>() {
|
||||
@Override
|
||||
public int compare(Date s1, Date s2) {
|
||||
return s2.compareTo(s1);
|
||||
}
|
||||
};
|
||||
private static final int DIALOG_CHOOSER_ID = 0;
|
||||
|
||||
private Date[] backupDates;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
ExternalFileBackup externalFileBackup = new ExternalFileBackup(this);
|
||||
|
||||
// Get the list of existing backups
|
||||
if (!FileUtils.isSdCardAvailable()) {
|
||||
Toast.makeText(this, R.string.sd_card_error_no_storage, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!externalFileBackup.isBackupsDirectoryAvailable(false)) {
|
||||
Toast.makeText(this, R.string.settings_backup_restore_no_backup, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
backupDates = externalFileBackup.getAvailableBackups();
|
||||
if (backupDates == null || backupDates.length == 0) {
|
||||
Toast.makeText(this, R.string.settings_backup_restore_no_backup, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (backupDates.length == 1) {
|
||||
startActivity(new Intent(this, RestoreActivity.class)
|
||||
.putExtra(RestoreActivity.EXTRA_DATE, backupDates[0].getTime()));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Arrays.sort(backupDates, REVERSE_DATE_ORDER);
|
||||
showDialog(DIALOG_CHOOSER_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
if (id != DIALOG_CHOOSER_ID) {
|
||||
return null;
|
||||
}
|
||||
String items[] = new String[backupDates.length];
|
||||
for (int i = 0; i < backupDates.length; i++) {
|
||||
items[i] = StringUtils.formatDateTime(this, backupDates[i].getTime());
|
||||
}
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setItems(items, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startActivity(new Intent(RestoreChooserActivity.this, RestoreActivity.class).putExtra(
|
||||
RestoreActivity.EXTRA_DATE, backupDates[which].getTime()));
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.settings_backup_restore_select_title)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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.io.file;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.ImportAllTracks;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* An activity that imports a track from a file and displays the track in My Tracks.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ImportActivity extends Activity {
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
Uri data = intent.getData();
|
||||
if (!(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_ATTACH_DATA.equals(action))) {
|
||||
Log.e(TAG, "Received an intent with unsupported action: " + intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UriUtils.isFileUri(data)) {
|
||||
Log.e(TAG, "Received an intent with unsupported data: " + intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
String path = data.getPath();
|
||||
Log.i(TAG, "Importing GPX file at " + path);
|
||||
new ImportAllTracks(this, path);
|
||||
}
|
||||
}
|
||||
@@ -13,27 +13,21 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.io.file;
|
||||
|
||||
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.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.PlayTrackUtils;
|
||||
import com.google.android.apps.mytracks.util.UriUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -42,219 +36,184 @@ import android.util.Log;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Activity for saving a track to a file (and optionally sending that file).
|
||||
* Activity for saving a track to the SD card, and optionally share or play the
|
||||
* track.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class SaveActivity extends Activity {
|
||||
public static final String EXTRA_FILE_FORMAT = "file_format";
|
||||
public static final String EXTRA_SHARE_FILE = "share_file";
|
||||
public static final String EXTRA_PLAY_FILE = "play_file";
|
||||
|
||||
private static final int RESULT_DIALOG = 1;
|
||||
/* VisibleForTesting */
|
||||
static final int PROGRESS_DIALOG = 2;
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
||||
public static final String EXTRA_SHARE_TRACK = "share_track";
|
||||
public static final String EXTRA_PLAY_TRACK = "play_track";
|
||||
|
||||
private static final String TAG = SaveActivity.class.getSimpleName();
|
||||
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_RESULT_ID = 1;
|
||||
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
private long trackId;
|
||||
private TrackWriter writer;
|
||||
private boolean shareFile;
|
||||
private boolean playFile;
|
||||
private TrackFileFormat format;
|
||||
private WriteProgressController controller;
|
||||
private TrackFileFormat trackFileFormat;
|
||||
private boolean shareTrack;
|
||||
private boolean playTrack;
|
||||
|
||||
private SaveAsyncTask saveAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
// result from the AsyncTask
|
||||
private boolean success;
|
||||
|
||||
// message id from the AsyncTask
|
||||
private int messageId;
|
||||
|
||||
// path of the saved file
|
||||
private String filePath;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
providerUtils = MyTracksProviderUtils.Factory.get(this);
|
||||
Intent intent = getIntent();
|
||||
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
if (trackId < 0) {
|
||||
Log.d(TAG, "Invalid track id");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
shareTrack = intent.getBooleanExtra(EXTRA_SHARE_TRACK, false);
|
||||
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof SaveAsyncTask) {
|
||||
saveAsyncTask = (SaveAsyncTask) retained;
|
||||
saveAsyncTask.setActivity(this);
|
||||
} else {
|
||||
TrackWriter trackWriter = TrackWriterFactory.newWriter(
|
||||
this, MyTracksProviderUtils.Factory.get(this), trackId, trackFileFormat);
|
||||
if (trackWriter == null) {
|
||||
Log.e(TAG, "Track writer is null");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (shareTrack || playTrack) {
|
||||
// Save to the temp directory
|
||||
String dirName = FileUtils.buildExternalDirectoryPath(
|
||||
trackFileFormat.getExtension(), "tmp");
|
||||
trackWriter.setDirectory(new File(dirName));
|
||||
}
|
||||
saveAsyncTask = new SaveAsyncTask(this, trackWriter);
|
||||
saveAsyncTask.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
String type = intent.getType();
|
||||
Uri data = intent.getData();
|
||||
if (!getString(R.string.track_action_save).equals(action)
|
||||
|| !TracksColumns.CONTENT_ITEMTYPE.equals(type)
|
||||
|| !UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
|
||||
Log.e(TAG, "Got bad save intent: " + intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
trackId = ContentUris.parseId(data);
|
||||
|
||||
int formatIdx = intent.getIntExtra(EXTRA_FILE_FORMAT, -1);
|
||||
format = TrackFileFormat.values()[formatIdx];
|
||||
shareFile = intent.getBooleanExtra(EXTRA_SHARE_FILE, false);
|
||||
playFile = intent.getBooleanExtra(EXTRA_PLAY_FILE, false);
|
||||
|
||||
writer = TrackWriterFactory.newWriter(this, providerUtils, trackId, format);
|
||||
if (writer == null) {
|
||||
Log.e(TAG, "Unable to build writer");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (shareFile || playFile) {
|
||||
// If the file is for sending, save it to a temporary location instead.
|
||||
FileUtils fileUtils = new FileUtils();
|
||||
String extension = format.getExtension();
|
||||
String dirName = fileUtils.buildExternalDirectoryPath(extension, "tmp");
|
||||
|
||||
File dir = new File(dirName);
|
||||
writer.setDirectory(dir);
|
||||
}
|
||||
|
||||
controller = new WriteProgressController(this, writer, PROGRESS_DIALOG);
|
||||
controller.setOnCompletionListener(new WriteProgressController.OnCompletionListener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
onWriteComplete();
|
||||
}
|
||||
});
|
||||
controller.startWrite();
|
||||
}
|
||||
|
||||
private void onWriteComplete() {
|
||||
if (shareFile) {
|
||||
shareWrittenFile();
|
||||
} if (playFile) {
|
||||
playWrittenFile();
|
||||
} else {
|
||||
showResultDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void shareWrittenFile() {
|
||||
if (!writer.wasSuccess()) {
|
||||
showResultDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
// Share the file.
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
|
||||
getResources().getText(R.string.share_track_subject).toString());
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT,
|
||||
getResources().getText(R.string.share_track_file_body_format)
|
||||
.toString());
|
||||
shareIntent.setType(format.getMimeType());
|
||||
Uri u = Uri.fromFile(new File(writer.getAbsolutePath()));
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, u);
|
||||
shareIntent.putExtra(getString(R.string.track_id_broadcast_extra), trackId);
|
||||
startActivity(Intent.createChooser(shareIntent,
|
||||
getResources().getText(R.string.share_track_picker_title).toString()));
|
||||
}
|
||||
|
||||
private void playWrittenFile() {
|
||||
if (!writer.wasSuccess()) {
|
||||
showResultDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = Uri.fromFile(new File(writer.getAbsolutePath()));
|
||||
Intent intent = new Intent()
|
||||
.setClassName(PlayTrackUtils.GOOGLE_EARTH_PACKAGE, PlayTrackUtils.GOOGLE_EARTH_CLASS)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.setDataAndType(uri, PlayTrackUtils.KML_MIME_TYPE)
|
||||
.putExtra(PlayTrackUtils.TOUR_FEATURE_ID, KmlTrackWriter.TOUR_FEATURE_ID);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void showResultDialog() {
|
||||
removeDialog(RESULT_DIALOG);
|
||||
showDialog(RESULT_DIALOG);
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
saveAsyncTask.setActivity(null);
|
||||
return saveAsyncTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case RESULT_DIALOG:
|
||||
return createResultDialog();
|
||||
case PROGRESS_DIALOG:
|
||||
if (controller != null) {
|
||||
return controller.createProgressDialog();
|
||||
}
|
||||
//$FALL-THROUGH$
|
||||
case DIALOG_PROGRESS_ID:
|
||||
progressDialog = DialogUtils.createHorizontalProgressDialog(
|
||||
this, R.string.sd_card_progress_message, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
saveAsyncTask.cancel(true);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
return progressDialog;
|
||||
case DIALOG_RESULT_ID:
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setIcon(success
|
||||
? android.R.drawable.ic_dialog_info : android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(messageId)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
dialog.dismiss();
|
||||
onPostResultDialog();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int arg1) {
|
||||
dialog.dismiss();
|
||||
onPostResultDialog();
|
||||
}
|
||||
})
|
||||
.setTitle(success ? R.string.generic_success_title : R.string.generic_error_title)
|
||||
.create();
|
||||
default:
|
||||
return super.onCreateDialog(id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Dialog createResultDialog() {
|
||||
boolean success = writer.wasSuccess();
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(writer.getErrorMessage());
|
||||
builder.setPositiveButton(R.string.generic_ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int arg1) {
|
||||
dialog.dismiss();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setOnCancelListener(new OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
dialog.dismiss();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setIcon(success ? android.R.drawable.ic_dialog_info :
|
||||
android.R.drawable.ic_dialog_alert);
|
||||
builder.setTitle(success ? R.string.generic_success_title : R.string.generic_error_title);
|
||||
return builder.create();
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param isSuccess true if the AsyncTask is successful
|
||||
* @param aMessageId the id of the AsyncTask message
|
||||
* @param aPath the path of the saved file
|
||||
*/
|
||||
public void onAsyncTaskCompleted(boolean isSuccess, int aMessageId, String aPath) {
|
||||
this.success = isSuccess;
|
||||
this.messageId = aMessageId;
|
||||
this.filePath = aPath;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
|
||||
public static void handleExportTrackAction(Context ctx, long trackId, int actionCode) {
|
||||
if (trackId < 0) {
|
||||
return;
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
|
||||
TrackFileFormat exportFormat = null;
|
||||
switch (actionCode) {
|
||||
case Constants.SAVE_GPX_FILE:
|
||||
case Constants.SHARE_GPX_FILE:
|
||||
exportFormat = TrackFileFormat.GPX;
|
||||
break;
|
||||
case Constants.SAVE_KML_FILE:
|
||||
case Constants.SHARE_KML_FILE:
|
||||
exportFormat = TrackFileFormat.KML;
|
||||
break;
|
||||
case Constants.SAVE_CSV_FILE:
|
||||
case Constants.SHARE_CSV_FILE:
|
||||
exportFormat = TrackFileFormat.CSV;
|
||||
break;
|
||||
case Constants.SAVE_TCX_FILE:
|
||||
case Constants.SHARE_TCX_FILE:
|
||||
exportFormat = TrackFileFormat.TCX;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Warning unhandled action code: " + actionCode);
|
||||
/**
|
||||
* To be invoked after showing the result dialog.
|
||||
*/
|
||||
private void onPostResultDialog() {
|
||||
if (success) {
|
||||
if (shareTrack) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND)
|
||||
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filePath)))
|
||||
.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_track_subject))
|
||||
.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_track_file_body_format))
|
||||
.putExtra(getString(R.string.track_id_broadcast_extra), trackId)
|
||||
.setType(trackFileFormat.getMimeType());
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_track_picker_title)));
|
||||
} else if (playTrack) {
|
||||
Intent intent = new Intent()
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.putExtra(PlayTrackUtils.TOUR_FEATURE_ID, KmlTrackWriter.TOUR_FEATURE_ID)
|
||||
.setClassName(PlayTrackUtils.GOOGLE_EARTH_PACKAGE, PlayTrackUtils.GOOGLE_EARTH_CLASS)
|
||||
.setDataAndType(Uri.fromFile(new File(filePath)), PlayTrackUtils.KML_MIME_TYPE);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
boolean shareFile = false;
|
||||
switch (actionCode) {
|
||||
case Constants.SHARE_GPX_FILE:
|
||||
case Constants.SHARE_KML_FILE:
|
||||
case Constants.SHARE_CSV_FILE:
|
||||
case Constants.SHARE_TCX_FILE:
|
||||
shareFile = true;
|
||||
}
|
||||
|
||||
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
|
||||
|
||||
Intent intent = new Intent(ctx, SaveActivity.class);
|
||||
intent.setAction(ctx.getString(R.string.track_action_save));
|
||||
intent.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
|
||||
intent.putExtra(EXTRA_FILE_FORMAT, exportFormat.ordinal());
|
||||
intent.putExtra(EXTRA_SHARE_FILE, shareFile);
|
||||
ctx.startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.io.file;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
/**
|
||||
* Async Task to save a track to the SD card.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private SaveActivity saveActivity;
|
||||
private final TrackWriter trackWriter;
|
||||
|
||||
// true if the AsyncTask result is success
|
||||
private boolean success;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
/**
|
||||
* Creates an AsyncTask.
|
||||
*
|
||||
* @param saveActivity the {@link SaveActivity} currently associated with this
|
||||
* AsyncTask
|
||||
* @param trackWriter the track writer
|
||||
*/
|
||||
public SaveAsyncTask(SaveActivity saveActivity, TrackWriter trackWriter) {
|
||||
this.saveActivity = saveActivity;
|
||||
this.trackWriter = trackWriter;
|
||||
success = false;
|
||||
completed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current {@link SaveActivity} associated with this AyncTask.
|
||||
*
|
||||
* @param saveActivity the current {@link SaveActivity}, can be null
|
||||
*/
|
||||
public void setActivity(SaveActivity saveActivity) {
|
||||
this.saveActivity = saveActivity;
|
||||
if (completed && saveActivity != null) {
|
||||
saveActivity.onAsyncTaskCompleted(
|
||||
success, trackWriter.getErrorMessage(), trackWriter.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (saveActivity != null) {
|
||||
saveActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
trackWriter.setOnWriteListener(new TrackWriter.OnWriteListener() {
|
||||
@Override
|
||||
public void onWrite(int number, int max) {
|
||||
// Update the progress dialog once every 500 points
|
||||
if (number % 500 == 0) {
|
||||
publishProgress(number, max);
|
||||
}
|
||||
}
|
||||
});
|
||||
trackWriter.writeTrack();
|
||||
return trackWriter.wasSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
if (saveActivity != null) {
|
||||
saveActivity.setProgressDialogValue(values[0], values[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
success = result;
|
||||
completed = true;
|
||||
if (saveActivity != null) {
|
||||
saveActivity.onAsyncTaskCompleted(
|
||||
success, trackWriter.getErrorMessage(), trackWriter.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
trackWriter.stopWriteTrack();
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,6 @@ import java.io.File;
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public interface TrackWriter {
|
||||
/** This listener is used to signal completion of track write */
|
||||
public interface OnCompletionListener {
|
||||
public void onComplete();
|
||||
}
|
||||
|
||||
/** This listener is used to signal track writes. */
|
||||
public interface OnWriteListener {
|
||||
@@ -43,11 +39,6 @@ public interface TrackWriter {
|
||||
public void onWrite(int number, int max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets listener to be invoked when the writer has finished.
|
||||
*/
|
||||
void setOnCompletionListener(OnCompletionListener onCompletionListener);
|
||||
|
||||
/**
|
||||
* Sets a listener to be invoked for each location writer.
|
||||
*/
|
||||
@@ -63,12 +54,6 @@ public interface TrackWriter {
|
||||
*/
|
||||
String getAbsolutePath();
|
||||
|
||||
/**
|
||||
* Writes the given track id to the SD card.
|
||||
* This is non-blocking.
|
||||
*/
|
||||
void writeTrackAsync();
|
||||
|
||||
/**
|
||||
* Writes the given track id to the SD card.
|
||||
* This is blocking.
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
@@ -34,7 +36,7 @@ public class TrackWriterFactory {
|
||||
/**
|
||||
* Definition of all possible track formats.
|
||||
*/
|
||||
public enum TrackFileFormat {
|
||||
public enum TrackFileFormat implements Parcelable {
|
||||
GPX {
|
||||
@Override
|
||||
TrackFormatWriter newFormatWriter(Context context) {
|
||||
@@ -60,6 +62,28 @@ public class TrackWriterFactory {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
dest.writeInt(ordinal());
|
||||
}
|
||||
|
||||
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
|
||||
@Override
|
||||
public TrackFileFormat createFromParcel(final Parcel source) {
|
||||
return TrackFileFormat.values()[source.readInt()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackFileFormat[] newArray(final int size) {
|
||||
return new TrackFileFormat[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and returns a new format writer for each format.
|
||||
*/
|
||||
|
||||
@@ -52,12 +52,10 @@ class TrackWriterImpl implements TrackWriter {
|
||||
private final MyTracksProviderUtils providerUtils;
|
||||
private final Track track;
|
||||
private final TrackFormatWriter writer;
|
||||
private final FileUtils fileUtils;
|
||||
private boolean success = false;
|
||||
private int errorMessage = -1;
|
||||
private File directory = null;
|
||||
private File file = null;
|
||||
private OnCompletionListener onCompletionListener;
|
||||
private OnWriteListener onWriteListener;
|
||||
private Thread writeThread;
|
||||
|
||||
@@ -67,12 +65,6 @@ class TrackWriterImpl implements TrackWriter {
|
||||
this.providerUtils = providerUtils;
|
||||
this.track = track;
|
||||
this.writer = writer;
|
||||
this.fileUtils = new FileUtils();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnCompletionListener(OnCompletionListener onCompletionListener) {
|
||||
this.onCompletionListener = onCompletionListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,8 +82,7 @@ class TrackWriterImpl implements TrackWriter {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTrackAsync() {
|
||||
private void writeTrackAsync() {
|
||||
writeThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -131,7 +122,6 @@ class TrackWriterImpl implements TrackWriter {
|
||||
}
|
||||
}
|
||||
}
|
||||
finished();
|
||||
}
|
||||
|
||||
public void stopWriteTrack() {
|
||||
@@ -163,18 +153,6 @@ class TrackWriterImpl implements TrackWriter {
|
||||
* ===============
|
||||
*/
|
||||
|
||||
private void finished() {
|
||||
if (onCompletionListener != null) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onCompletionListener.onComplete();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given runnable in the UI thread.
|
||||
*/
|
||||
@@ -195,7 +173,7 @@ class TrackWriterImpl implements TrackWriter {
|
||||
}
|
||||
|
||||
// Make sure the file doesn't exist yet (possibly by changing the filename)
|
||||
String fileName = fileUtils.buildUniqueFileName(
|
||||
String fileName = FileUtils.buildUniqueFileName(
|
||||
directory, track.getName(), writer.getExtension());
|
||||
if (fileName == null) {
|
||||
Log.e(Constants.TAG,
|
||||
@@ -220,16 +198,16 @@ class TrackWriterImpl implements TrackWriter {
|
||||
protected boolean canWriteFile() {
|
||||
if (directory == null) {
|
||||
String dirName =
|
||||
fileUtils.buildExternalDirectoryPath(writer.getExtension());
|
||||
FileUtils.buildExternalDirectoryPath(writer.getExtension());
|
||||
directory = newFile(dirName);
|
||||
}
|
||||
|
||||
if (!fileUtils.isSdCardAvailable()) {
|
||||
if (!FileUtils.isSdCardAvailable()) {
|
||||
Log.i(Constants.TAG, "Could not find SD card.");
|
||||
errorMessage = R.string.sd_card_error_no_storage;
|
||||
return false;
|
||||
}
|
||||
if (!fileUtils.ensureDirectoryExists(directory)) {
|
||||
if (!FileUtils.ensureDirectoryExists(directory)) {
|
||||
Log.i(Constants.TAG, "Could not create export directory.");
|
||||
errorMessage = R.string.sd_card_error_create_dir;
|
||||
return false;
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* 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.io.file;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
/**
|
||||
* Given a {@link TrackWriter}, this class manages the process of writing the
|
||||
* data in the track represented by the writer. This includes the display of
|
||||
* a progress dialog, updating the progress bar in said dialog, and notifying
|
||||
* interested parties when the write completes.
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
class WriteProgressController {
|
||||
/**
|
||||
* This listener is used to notify interested parties when the write has
|
||||
* completed.
|
||||
*/
|
||||
public interface OnCompletionListener {
|
||||
/**
|
||||
* When this method is invoked, the write has completed, and the progress
|
||||
* dialog has been dismissed. Whether the write succeeded can be
|
||||
* determined by examining the {@link TrackWriter}.
|
||||
*/
|
||||
public void onComplete();
|
||||
}
|
||||
|
||||
private final Activity activity;
|
||||
private final TrackWriter writer;
|
||||
private ProgressDialog dialog;
|
||||
|
||||
private OnCompletionListener onCompletionListener;
|
||||
private final int progressDialogId;
|
||||
|
||||
/**
|
||||
* @param activity the activity associated with this write
|
||||
* @param writer the writer which writes the track to disk. Note that this
|
||||
* class will use the writer's completion listener. If callers are
|
||||
* interested in notification upon completion of the write, they should
|
||||
* use {@link #setOnCompletionListener}.
|
||||
*/
|
||||
public WriteProgressController(Activity activity, TrackWriter writer, int progressDialogId) {
|
||||
this.activity = activity;
|
||||
this.writer = writer;
|
||||
this.progressDialogId = progressDialogId;
|
||||
|
||||
writer.setOnCompletionListener(writerCompleteListener);
|
||||
writer.setOnWriteListener(writerWriteListener);
|
||||
}
|
||||
|
||||
/** Set a listener to be invoked when the write completes. */
|
||||
public void setOnCompletionListener(OnCompletionListener onCompletionListener) {
|
||||
this.onCompletionListener = onCompletionListener;
|
||||
}
|
||||
|
||||
// For testing purpose
|
||||
OnCompletionListener getOnCompletionListener() {
|
||||
return onCompletionListener;
|
||||
}
|
||||
|
||||
public ProgressDialog createProgressDialog() {
|
||||
dialog = new ProgressDialog(activity);
|
||||
dialog.setIcon(android.R.drawable.ic_dialog_info);
|
||||
dialog.setTitle(activity.getString(R.string.generic_progress_title));
|
||||
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
dialog.setMessage(activity.getString(R.string.sd_card_progress_write_file));
|
||||
dialog.setIndeterminate(true);
|
||||
dialog.setOnCancelListener(dialogCancelListener);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/** Initiate an asynchronous write. */
|
||||
public void startWrite() {
|
||||
activity.showDialog(progressDialogId);
|
||||
writer.writeTrackAsync();
|
||||
}
|
||||
|
||||
/** VisibleForTesting */
|
||||
ProgressDialog getDialog() {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private final DialogInterface.OnCancelListener dialogCancelListener =
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialogInterface) {
|
||||
writer.stopWriteTrack();
|
||||
}
|
||||
};
|
||||
|
||||
private final TrackWriter.OnCompletionListener writerCompleteListener =
|
||||
new TrackWriter.OnCompletionListener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
activity.dismissDialog(progressDialogId);
|
||||
|
||||
if (onCompletionListener != null) {
|
||||
onCompletionListener.onComplete();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final TrackWriter.OnWriteListener writerWriteListener =
|
||||
new TrackWriter.OnWriteListener() {
|
||||
@Override
|
||||
public void onWrite(int number, int max) {
|
||||
if (number % 500 == 0) {
|
||||
dialog.setIndeterminate(false);
|
||||
dialog.setMax(max);
|
||||
dialog.setProgress(Math.min(number, max));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+15
-7
@@ -425,18 +425,26 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
boolean isSuccess = response.isSuccessStatusCode();
|
||||
if (isSuccess) {
|
||||
InputStream content;
|
||||
InputStream content = null;
|
||||
try {
|
||||
content = response.getContent();
|
||||
if (setTableId) {
|
||||
tableId = SendFusionTablesUtils.getTableId(content);
|
||||
if (tableId == null) {
|
||||
Log.d(TAG, "tableId is null");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to get response", e);
|
||||
return false;
|
||||
}
|
||||
if (setTableId) {
|
||||
tableId = SendFusionTablesUtils.getTableId(content);
|
||||
if (tableId == null) {
|
||||
Log.d(TAG, "tableId is null");
|
||||
return false;
|
||||
} finally {
|
||||
if (content != null) {
|
||||
try {
|
||||
content.close();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to close content", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -153,7 +153,7 @@ public class SpreadsheetsClient extends GDataServiceClient {
|
||||
* @return a parser for the indicated feed
|
||||
* @throws ParseException if the response from the server could not be parsed
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public GDataParser getParserForFeed(
|
||||
Class feedEntryClass, String feedUri, String authToken)
|
||||
@@ -227,7 +227,7 @@ public class SpreadsheetsClient extends GDataServiceClient {
|
||||
* Converts an HTTP exception which happened while writing to the equivalent
|
||||
* local exception.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void convertHttpExceptionForWrites(
|
||||
Class entryClass, String message, HttpException cause)
|
||||
throws ParseException, IOException {
|
||||
@@ -253,7 +253,7 @@ public class SpreadsheetsClient extends GDataServiceClient {
|
||||
/**
|
||||
* Parses one entry from the input stream.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Entry parseEntry(Class entryClass, InputStream is)
|
||||
throws ParseException, IOException {
|
||||
GDataParser parser = null;
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class XmlDocsGDataParserFactory implements GDataParserFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public GDataParser createParser(Class cls, InputStream is)
|
||||
throws ParseException {
|
||||
|
||||
@@ -14,8 +14,6 @@ import java.util.Vector;
|
||||
*/
|
||||
public class MapsFeature {
|
||||
|
||||
private static final long serialVersionUID = 8439035544430497236L;
|
||||
|
||||
/** A marker feature displays an icon at a single point on the map. */
|
||||
public static final int MARKER = 0;
|
||||
|
||||
|
||||
@@ -169,7 +169,6 @@ public class MapsGDataConverter {
|
||||
xmlSerializer.endTag(null, "Placemark");
|
||||
xmlSerializer.flush();
|
||||
entry.setContent(writer.toString());
|
||||
Log.d("My Google Maps", "Generated kml:\n" + entry.getContent());
|
||||
Log.d("My Google Maps", "Edit URI: " + entry.getEditUri());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public class XmlMapsGDataParserFactory implements GDataParserFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public GDataParser createParser(Class cls, InputStream is)
|
||||
throws ParseException {
|
||||
|
||||
@@ -17,16 +17,14 @@ package com.google.android.apps.mytracks.io.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsMapMetadata;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
@@ -46,13 +44,11 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class ChooseMapActivity extends Activity {
|
||||
|
||||
private static final int PROGRESS_DIALOG = 1;
|
||||
@VisibleForTesting
|
||||
static final int ERROR_DIALOG = 2;
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_ERROR_ID = 1;
|
||||
|
||||
private SendRequest sendRequest;
|
||||
private ChooseMapAsyncTask asyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
@VisibleForTesting
|
||||
ArrayAdapter<ListItem> arrayAdapter;
|
||||
|
||||
@@ -122,39 +118,34 @@ public class ChooseMapActivity extends Activity {
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case PROGRESS_DIALOG:
|
||||
progressDialog = new ProgressDialog(this);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
progressDialog.setMessage(getString(R.string.maps_list_loading));
|
||||
progressDialog.setCancelable(true);
|
||||
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
asyncTask.cancel(true);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
|
||||
progressDialog.setTitle(R.string.generic_progress_title);
|
||||
return progressDialog;
|
||||
case ERROR_DIALOG:
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
builder.setTitle(R.string.generic_error_title);
|
||||
builder.setMessage(R.string.maps_list_error);
|
||||
builder.setPositiveButton(R.string.generic_ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int arg1) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setOnCancelListener(new OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
case DIALOG_PROGRESS_ID:
|
||||
return DialogUtils.createSpinnerProgressDialog(
|
||||
this, R.string.maps_list_progress_message, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
asyncTask.cancel(true);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
case DIALOG_ERROR_ID:
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(R.string.maps_list_error)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int arg1) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.generic_error_title)
|
||||
.create();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -192,7 +183,7 @@ public class ChooseMapActivity extends Activity {
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(PROGRESS_DIALOG);
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +191,7 @@ public class ChooseMapActivity extends Activity {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void showErrorDialog() {
|
||||
showDialog(ERROR_DIALOG);
|
||||
showDialog(DIALOG_ERROR_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +199,7 @@ public class ChooseMapActivity extends Activity {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void removeProgressDialog() {
|
||||
removeDialog(PROGRESS_DIALOG);
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.android.apps.mytracks.io.gdata.maps.MapsGDataConverter;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsMapMetadata;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.XmlMapsGDataParserFactory;
|
||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.wireless.gdata.client.GDataClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.parser.GDataParser;
|
||||
@@ -75,19 +76,29 @@ public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
private ArrayList<MapsMapMetadata> mapData;
|
||||
|
||||
public ChooseMapAsyncTask(ChooseMapActivity activity, Account account) {
|
||||
this.activity = activity;
|
||||
this.account = account;
|
||||
|
||||
context = activity.getApplicationContext();
|
||||
gDataClient = GDataClientFactory.getGDataClient(context);
|
||||
mapsClient = new MapsClient(
|
||||
gDataClient, new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||
|
||||
canRetry = true;
|
||||
completed = false;
|
||||
success = false;
|
||||
this(activity, account, activity.getApplicationContext(), GDataClientFactory
|
||||
.getGDataClient(activity.getApplicationContext()), new MapsClient(
|
||||
GDataClientFactory.getGDataClient(activity.getApplicationContext()),
|
||||
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates this constructor for test.
|
||||
*/
|
||||
public ChooseMapAsyncTask(ChooseMapActivity activity, Account account,
|
||||
Context context, GDataClient gDataClient, MapsClient mapsClient) {
|
||||
this.activity = activity;
|
||||
this.account = account;
|
||||
|
||||
this.context = context;
|
||||
this.gDataClient = gDataClient;
|
||||
this.mapsClient = mapsClient;
|
||||
|
||||
canRetry = true;
|
||||
completed = false;
|
||||
success = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the activity associated with this AyncTask.
|
||||
*
|
||||
@@ -107,17 +118,17 @@ public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return getMaps();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
closeClient();
|
||||
try {
|
||||
return getMaps();
|
||||
} finally {
|
||||
if (gDataClient != null) {
|
||||
gDataClient.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
closeClient();
|
||||
success = result;
|
||||
completed = true;
|
||||
if (activity != null) {
|
||||
@@ -125,21 +136,13 @@ public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the gdata client.
|
||||
*/
|
||||
private void closeClient() {
|
||||
if (gDataClient != null) {
|
||||
gDataClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the maps from Google Maps.
|
||||
*
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean getMaps() {
|
||||
@VisibleForTesting
|
||||
boolean getMaps() {
|
||||
// Reset the per request states
|
||||
authToken = null;
|
||||
mapIds = new ArrayList<String>();
|
||||
@@ -194,7 +197,8 @@ public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
* Retries upload. Invalidates the authToken. If can retry, invokes
|
||||
* {@link ChooseMapAsyncTask#getMaps()}. Returns false if cannot retry.
|
||||
*/
|
||||
private boolean retryUpload() {
|
||||
@VisibleForTesting
|
||||
boolean retryUpload() {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
@@ -206,4 +210,38 @@ public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the complete status of task.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean getCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the complete status of task.
|
||||
* @param completed
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setCompleted(boolean completed) {
|
||||
this.completed = completed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status of canRetry.
|
||||
* @param canRetry status of canRetry
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setCanRetry(boolean canRetry) {
|
||||
this.canRetry = canRetry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of canRetry.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean getCanRetry() {
|
||||
return canRetry;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.wireless.gdata.client.GDataClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
@@ -73,8 +74,10 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
private static final int MAX_POINTS_PER_UPLOAD = 500;
|
||||
|
||||
private static final int PROGRESS_FETCH_MAP_ID = 5;
|
||||
private static final int PROGRESS_UPLOAD_DATA_MIN = 10;
|
||||
private static final int PROGRESS_UPLOAD_DATA_MAX = 90;
|
||||
@VisibleForTesting
|
||||
static final int PROGRESS_UPLOAD_DATA_MIN = 10;
|
||||
@VisibleForTesting
|
||||
static final int PROGRESS_UPLOAD_DATA_MAX = 90;
|
||||
private static final int PROGRESS_UPLOAD_WAYPOINTS = 95;
|
||||
private static final int PROGRESS_COMPLETE = 100;
|
||||
|
||||
@@ -83,8 +86,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
private final long trackId;
|
||||
private final Account account;
|
||||
private final String chooseMapId;
|
||||
private final Context context;
|
||||
private final MyTracksProviderUtils myTracksProviderUtils;
|
||||
private final Context context;
|
||||
private final GDataClient gDataClient;
|
||||
private final MapsClient mapsClient;
|
||||
|
||||
@@ -94,15 +97,25 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
private String mapId;
|
||||
int currentSegment;
|
||||
|
||||
public SendMapsAsyncTask(SendMapsActivity activity, long trackId, Account account,
|
||||
String chooseMapId) {
|
||||
this(activity, trackId, account, chooseMapId, MyTracksProviderUtils.Factory.get(activity
|
||||
.getApplicationContext()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is created for test.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public SendMapsAsyncTask (
|
||||
SendMapsActivity activity, long trackId, Account account, String chooseMapId) {
|
||||
SendMapsActivity activity, long trackId, Account account, String chooseMapId, MyTracksProviderUtils myTracksProviderUtils) {
|
||||
super(activity);
|
||||
this.trackId = trackId;
|
||||
this.account = account;
|
||||
this.chooseMapId = chooseMapId;
|
||||
|
||||
this.myTracksProviderUtils = myTracksProviderUtils;
|
||||
context = activity.getApplicationContext();
|
||||
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
|
||||
gDataClient = GDataClientFactory.getGDataClient(context);
|
||||
mapsClient = new MapsClient(
|
||||
gDataClient, new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||
@@ -201,7 +214,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param track the Track
|
||||
* @return true if able to fetch the mapId variable.
|
||||
*/
|
||||
private boolean fetchSendMapId(Track track) {
|
||||
@VisibleForTesting
|
||||
boolean fetchSendMapId(Track track) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
@@ -239,7 +253,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param track the track
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean uploadAllTrackPoints(Track track) {
|
||||
@VisibleForTesting
|
||||
boolean uploadAllTrackPoints(Track track) {
|
||||
Cursor locationsCursor = null;
|
||||
try {
|
||||
SharedPreferences prefs = context.getSharedPreferences(
|
||||
@@ -313,9 +328,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
if (lastLocation != null) {
|
||||
distances.add(tripStatisticsBuilder.getStatistics().getTotalDistance());
|
||||
elevations.add(elevationBuffer.getAverage());
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
track.setDescription("<p>" + track.getDescription() + "</p><p>"
|
||||
+ descriptionGenerator.generateTrackDescription(track, distances, elevations) + "</p>");
|
||||
|
||||
track.setDescription(getTrackDescription(track, distances, elevations));
|
||||
if (!uploadMarker(context.getString(R.string.marker_label_end, track.getName()),
|
||||
track.getDescription(), END_ICON_URL, lastLocation)) {
|
||||
Log.d(TAG, "Unable to create an end marker");
|
||||
@@ -330,6 +344,21 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description of a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @param distances distance vectors
|
||||
* @param elevations elevation vectors
|
||||
* @return the description of a track.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String getTrackDescription(Track track, Vector<Double> distances, Vector<Double> elevations) {
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
return "<p>" + track.getDescription() + "</p><p>"
|
||||
+ descriptionGenerator.generateTrackDescription(track, distances, elevations) + "</p>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares and uploads a list of locations from a track.
|
||||
*
|
||||
@@ -338,7 +367,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param lastBatch true if it is the last batch of locations
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean prepareAndUploadPoints(Track track, List<Location> locations, boolean lastBatch) {
|
||||
@VisibleForTesting
|
||||
boolean prepareAndUploadPoints(Track track, List<Location> locations, boolean lastBatch) {
|
||||
// Prepare locations
|
||||
ArrayList<Track> splitTracks = SendToGoogleUtils.prepareLocations(track, locations);
|
||||
|
||||
@@ -367,7 +397,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param location marker location
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean uploadMarker(
|
||||
@VisibleForTesting
|
||||
boolean uploadMarker(
|
||||
String title, String description, String iconUrl, Location location) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
@@ -426,7 +457,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
*
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean uploadWaypoints() {
|
||||
@VisibleForTesting
|
||||
boolean uploadWaypoints() {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getWaypointsCursor(
|
||||
@@ -471,10 +503,43 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param uploaded the number of uploaded locations
|
||||
* @param total the number of total locations
|
||||
*/
|
||||
private void updateProgress(int uploaded, int total) {
|
||||
double totalPercentage = (double) uploaded / total;
|
||||
double scaledPercentage = totalPercentage
|
||||
* (PROGRESS_UPLOAD_DATA_MAX - PROGRESS_UPLOAD_DATA_MIN) + PROGRESS_UPLOAD_DATA_MIN;
|
||||
publishProgress((int) scaledPercentage);
|
||||
@VisibleForTesting
|
||||
void updateProgress(int uploaded, int total) {
|
||||
publishProgress(getPercentage(uploaded, total));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count the percentage of the number of locations uploaded.
|
||||
*
|
||||
* @param uploaded the number of uploaded locations
|
||||
* @param total the number of total locations
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static int getPercentage(int uploaded, int total) {
|
||||
double totalPercentage = (double) uploaded / total;
|
||||
double scaledPercentage = totalPercentage
|
||||
* (PROGRESS_UPLOAD_DATA_MAX - PROGRESS_UPLOAD_DATA_MIN) + PROGRESS_UPLOAD_DATA_MIN;
|
||||
return (int) scaledPercentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mapID.
|
||||
*
|
||||
* @return mapId
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String getMapId() {
|
||||
return mapId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of mapsGDataConverter.
|
||||
*
|
||||
* @param mapsGDataConverter new value of mapsGDataConverter
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setMapsGDataConverter(MapsGDataConverter mapsGDataConverter) {
|
||||
this.mapsGDataConverter = mapsGDataConverter;
|
||||
}
|
||||
}
|
||||
+12
-17
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.google.android.apps.mytracks.io.sendtogoogle;
|
||||
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -41,7 +42,7 @@ import android.os.Bundle;
|
||||
*/
|
||||
public abstract class AbstractSendActivity extends Activity {
|
||||
|
||||
private static final int PROGRESS_DIALOG = 1;
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
|
||||
protected SendRequest sendRequest;
|
||||
private AbstractSendAsyncTask asyncTask;
|
||||
@@ -70,26 +71,18 @@ public abstract class AbstractSendActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case PROGRESS_DIALOG:
|
||||
progressDialog = new ProgressDialog(this);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
|
||||
progressDialog.setTitle(getString(R.string.send_google_progress_title, getServiceName()));
|
||||
progressDialog.setMax(100);
|
||||
progressDialog.setProgress(0);
|
||||
progressDialog.setCancelable(true);
|
||||
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
if (id != DIALOG_PROGRESS_ID) {
|
||||
return null;
|
||||
}
|
||||
progressDialog = DialogUtils.createHorizontalProgressDialog(
|
||||
this, R.string.send_google_progress_message, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
asyncTask.cancel(true);
|
||||
startNextActivity(false, true);
|
||||
}
|
||||
});
|
||||
return progressDialog;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}, getServiceName());
|
||||
return progressDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +98,7 @@ public abstract class AbstractSendActivity extends Activity {
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(PROGRESS_DIALOG);
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +108,9 @@ public abstract class AbstractSendActivity extends Activity {
|
||||
*/
|
||||
public void setProgressDialogValue(int value) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.setIndeterminate(false);
|
||||
progressDialog.setProgress(value);
|
||||
progressDialog.setMax(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-10
@@ -76,7 +76,14 @@ public abstract class AbstractSendAsyncTask extends AsyncTask<Void, Integer, Boo
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return performTask();
|
||||
try {
|
||||
return performTask();
|
||||
} finally {
|
||||
closeConnection();
|
||||
if (success) {
|
||||
saveResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,21 +96,12 @@ public abstract class AbstractSendAsyncTask extends AsyncTask<Void, Integer, Boo
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
success = result;
|
||||
if (success) {
|
||||
saveResult();
|
||||
}
|
||||
completed = true;
|
||||
closeConnection();
|
||||
if (activity != null) {
|
||||
activity.onAsyncTaskCompleted(success);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retries the task. First, invalidates the auth token. If can retry, invokes
|
||||
* {@link #performTask()}. Returns false if cannot retry.
|
||||
|
||||
+58
-53
@@ -55,8 +55,8 @@ public class AccountChooserActivity extends Activity {
|
||||
|
||||
private static final String TAG = AccountChooserActivity.class.getSimpleName();
|
||||
|
||||
private static final int NO_ACCOUNT_DIALOG = 1;
|
||||
private static final int CHOOSE_ACCOUNT_DIALOG = 2;
|
||||
private static final int DIALOG_NO_ACCOUNT_ID = 0;
|
||||
private static final int DIALOG_CHOOSER_ID = 1;
|
||||
|
||||
/**
|
||||
* A callback after getting the permission to access a Google service.
|
||||
@@ -108,77 +108,82 @@ public class AccountChooserActivity extends Activity {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (accounts.length == 0) {
|
||||
showDialog(NO_ACCOUNT_DIALOG);
|
||||
showDialog(DIALOG_NO_ACCOUNT_ID);
|
||||
} else if (accounts.length > 1 ) {
|
||||
showDialog(CHOOSE_ACCOUNT_DIALOG);
|
||||
showDialog(DIALOG_CHOOSER_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
AlertDialog.Builder builder;
|
||||
switch (id) {
|
||||
case NO_ACCOUNT_DIALOG:
|
||||
builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.send_google_no_account_title);
|
||||
builder.setMessage(R.string.send_google_no_account_message);
|
||||
builder.setCancelable(true);
|
||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
case DIALOG_NO_ACCOUNT_ID:
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setMessage(R.string.send_google_no_account_message)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.send_google_no_account_title)
|
||||
.create();
|
||||
case DIALOG_CHOOSER_ID:
|
||||
return createChooserDialog();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a chooser dialog.
|
||||
*/
|
||||
private Dialog createChooserDialog() {
|
||||
String[] choices = new String[accounts.length];
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
choices[i] = accounts[i].name;
|
||||
}
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.generic_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
case CHOOSE_ACCOUNT_DIALOG:
|
||||
builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.send_google_choose_account_title);
|
||||
|
||||
String[] choices = new String[accounts.length];
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
choices[i] = accounts[i].name;
|
||||
}
|
||||
builder.setSingleChoiceItems(choices, selectedAccountIndex, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
selectedAccountIndex = which;
|
||||
}
|
||||
});
|
||||
|
||||
builder.setCancelable(true);
|
||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.generic_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Account account = accounts[selectedAccountIndex];
|
||||
SharedPreferences prefs = getSharedPreferences(
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
Editor editor = prefs.edit();
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putString(getString(R.string.preferred_account_key), account.name);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
|
||||
sendRequest.setAccount(account);
|
||||
getPermission(MapsConstants.SERVICE_NAME, sendRequest.isSendMaps(), mapsCallback);
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.setSingleChoiceItems(
|
||||
choices, selectedAccountIndex, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
selectedAccountIndex = which;
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.send_google_choose_account_title)
|
||||
.create();
|
||||
}
|
||||
|
||||
private PermissionCallback spreadsheetsCallback = new PermissionCallback() {
|
||||
|
||||
+5
-2
@@ -19,6 +19,7 @@ package com.google.android.apps.mytracks.io.sendtogoogle;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
@@ -109,11 +110,12 @@ public class SendToGoogleUtils {
|
||||
* @param segment the track segment
|
||||
* @param splitTracks an array of track segments
|
||||
*/
|
||||
private static void prepareTrackSegment(Track segment, ArrayList<Track> splitTracks) {
|
||||
@VisibleForTesting
|
||||
static boolean prepareTrackSegment(Track segment, ArrayList<Track> splitTracks) {
|
||||
// Make sure the segment has at least 2 points
|
||||
if (segment.getLocations().size() < 2) {
|
||||
Log.d(TAG, "segment has less than 2 points");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// For a new segment, sets it stop time
|
||||
@@ -128,5 +130,6 @@ public class SendToGoogleUtils {
|
||||
LocationUtils.decimate(segment, 2.0);
|
||||
|
||||
splitTracks.add(segment);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+76
-81
@@ -43,7 +43,7 @@ import android.widget.TextView;
|
||||
public class UploadResultActivity extends Activity {
|
||||
|
||||
private static final String TEXT_PLAIN_TYPE = "text/plain";
|
||||
private static final int RESULT_DIALOG = 1;
|
||||
private static final int DIALOG_RESULT_ID = 0;
|
||||
|
||||
private SendRequest sendRequest;
|
||||
private Track track;
|
||||
@@ -75,104 +75,99 @@ public class UploadResultActivity extends Activity {
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
showDialog(RESULT_DIALOG);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case RESULT_DIALOG:
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
View view = getLayoutInflater().inflate(R.layout.upload_result, null);
|
||||
builder.setView(view);
|
||||
if (id != DIALOG_RESULT_ID) {
|
||||
return null;
|
||||
}
|
||||
View view = getLayoutInflater().inflate(R.layout.upload_result, null);
|
||||
|
||||
LinearLayout mapsResult = (LinearLayout) view.findViewById(R.id.upload_result_maps_result);
|
||||
LinearLayout fusionTablesResult = (LinearLayout) view.findViewById(
|
||||
R.id.upload_result_fusion_tables_result);
|
||||
LinearLayout docsResult = (LinearLayout) view.findViewById(R.id.upload_result_docs_result);
|
||||
LinearLayout mapsResult = (LinearLayout) view.findViewById(R.id.upload_result_maps_result);
|
||||
LinearLayout fusionTablesResult = (LinearLayout) view.findViewById(
|
||||
R.id.upload_result_fusion_tables_result);
|
||||
LinearLayout docsResult = (LinearLayout) view.findViewById(R.id.upload_result_docs_result);
|
||||
|
||||
ImageView mapsResultIcon = (ImageView) view.findViewById(
|
||||
R.id.upload_result_maps_result_icon);
|
||||
ImageView fusionTablesResultIcon = (ImageView) view.findViewById(
|
||||
R.id.upload_result_fusion_tables_result_icon);
|
||||
ImageView docsResultIcon = (ImageView) view.findViewById(
|
||||
R.id.upload_result_docs_result_icon);
|
||||
ImageView mapsResultIcon = (ImageView) view.findViewById(R.id.upload_result_maps_result_icon);
|
||||
ImageView fusionTablesResultIcon = (ImageView) view.findViewById(
|
||||
R.id.upload_result_fusion_tables_result_icon);
|
||||
ImageView docsResultIcon = (ImageView) view.findViewById(R.id.upload_result_docs_result_icon);
|
||||
|
||||
TextView successFooter = (TextView) view.findViewById(R.id.upload_result_success_footer);
|
||||
TextView errorFooter = (TextView) view.findViewById(R.id.upload_result_error_footer);
|
||||
TextView successFooter = (TextView) view.findViewById(R.id.upload_result_success_footer);
|
||||
TextView errorFooter = (TextView) view.findViewById(R.id.upload_result_error_footer);
|
||||
|
||||
boolean hasError = false;
|
||||
if (!sendRequest.isSendMaps()) {
|
||||
mapsResult.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (!sendRequest.isMapsSuccess()) {
|
||||
mapsResultIcon.setImageResource(R.drawable.failure);
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
boolean hasError = false;
|
||||
if (!sendRequest.isSendMaps()) {
|
||||
mapsResult.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (!sendRequest.isMapsSuccess()) {
|
||||
mapsResultIcon.setImageResource(R.drawable.failure);
|
||||
mapsResultIcon.setContentDescription(getString(R.string.generic_error_title));
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sendRequest.isSendFusionTables()) {
|
||||
fusionTablesResult.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (!sendRequest.isFusionTablesSuccess()) {
|
||||
fusionTablesResultIcon.setImageResource(R.drawable.failure);
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
if (!sendRequest.isSendFusionTables()) {
|
||||
fusionTablesResult.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (!sendRequest.isFusionTablesSuccess()) {
|
||||
fusionTablesResultIcon.setImageResource(R.drawable.failure);
|
||||
fusionTablesResultIcon.setContentDescription(getString(R.string.generic_error_title));
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sendRequest.isSendDocs()) {
|
||||
docsResult.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (!sendRequest.isDocsSuccess()) {
|
||||
docsResultIcon.setImageResource(R.drawable.failure);
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
if (!sendRequest.isSendDocs()) {
|
||||
docsResult.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (!sendRequest.isDocsSuccess()) {
|
||||
docsResultIcon.setImageResource(R.drawable.failure);
|
||||
docsResultIcon.setContentDescription(getString(R.string.generic_error_title));
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
builder.setTitle(R.string.generic_error_title);
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
successFooter.setVisibility(View.GONE);
|
||||
} else {
|
||||
builder.setTitle(R.string.generic_success_title);
|
||||
builder.setIcon(android.R.drawable.ic_dialog_info);
|
||||
errorFooter.setVisibility(View.GONE);
|
||||
}
|
||||
if (hasError) {
|
||||
successFooter.setVisibility(View.GONE);
|
||||
} else {
|
||||
errorFooter.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
builder.setCancelable(true);
|
||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setIcon(hasError ? android.R.drawable.ic_dialog_alert : android.R.drawable.ic_dialog_info)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(
|
||||
getString(R.string.generic_ok), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (!sendRequest.isShowAll() && shareUrl != null) {
|
||||
startShareUrlActivity(shareUrl);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
});
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (!sendRequest.isShowAll() && shareUrl != null) {
|
||||
startShareUrlActivity(shareUrl);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setTitle(hasError ? R.string.generic_error_title : R.string.generic_success_title)
|
||||
.setView(view);
|
||||
|
||||
// Add a Share URL button if showing all the options and a shareUrl
|
||||
// exists
|
||||
if (sendRequest.isShowAll() && shareUrl != null) {
|
||||
builder.setNegativeButton(getString(R.string.send_google_result_share_url),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startShareUrlActivity(shareUrl);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
return builder.create();
|
||||
default:
|
||||
return null;
|
||||
// Add a Share URL button if showing all the options and a shareUrl exists
|
||||
if (sendRequest.isShowAll() && shareUrl != null) {
|
||||
builder.setNegativeButton(
|
||||
R.string.send_google_result_share_url, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startShareUrlActivity(shareUrl);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+72
-72
@@ -22,6 +22,7 @@ import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -30,27 +31,26 @@ import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A chooser to select the Google services to upload a track to.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class UploadServiceChooserActivity extends Activity {
|
||||
|
||||
private static final int SERVICE_PICKER_DIALOG = 1;
|
||||
private static final int DIALOG_CHOOSER_ID = 0;
|
||||
|
||||
private SendRequest sendRequest;
|
||||
private Dialog dialog;
|
||||
private AlertDialog alertDialog;
|
||||
|
||||
private TableRow mapsTableRow;
|
||||
private TableRow fusionTablesTableRow;
|
||||
@@ -64,9 +64,6 @@ public class UploadServiceChooserActivity extends Activity {
|
||||
private RadioButton newMapRadioButton;
|
||||
private RadioButton existingMapRadioButton;
|
||||
|
||||
private Button cancel;
|
||||
private Button send;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -76,74 +73,78 @@ public class UploadServiceChooserActivity extends Activity {
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
showDialog(SERVICE_PICKER_DIALOG);
|
||||
showDialog(DIALOG_CHOOSER_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case SERVICE_PICKER_DIALOG:
|
||||
dialog = new Dialog(this);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setContentView(R.layout.upload_service_chooser);
|
||||
dialog.setCancelable(true);
|
||||
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
if (id != DIALOG_CHOOSER_ID) {
|
||||
return null;
|
||||
}
|
||||
View view = getLayoutInflater().inflate(R.layout.upload_service_chooser, null);
|
||||
|
||||
mapsTableRow = (TableRow) view.findViewById(R.id.send_google_maps_row);
|
||||
fusionTablesTableRow = (TableRow) view.findViewById(R.id.send_google_fusion_tables_row);
|
||||
docsTableRow = (TableRow) view.findViewById(R.id.send_google_docs_row);
|
||||
|
||||
mapsCheckBox = (CheckBox) view.findViewById(R.id.send_google_maps);
|
||||
fusionTablesCheckBox = (CheckBox) view.findViewById(R.id.send_google_fusion_tables);
|
||||
docsCheckBox = (CheckBox) view.findViewById(R.id.send_google_docs);
|
||||
|
||||
mapsOptionTableRow = (TableRow) view.findViewById(R.id.send_google_maps_option_row);
|
||||
newMapRadioButton = (RadioButton) view.findViewById(R.id.send_google_new_map);
|
||||
existingMapRadioButton = (RadioButton) view.findViewById(R.id.send_google_existing_map);
|
||||
|
||||
// Setup checkboxes
|
||||
OnCheckedChangeListener checkBoxListener = new OnCheckedChangeListener() {
|
||||
public void onCheckedChanged(CompoundButton button, boolean checked) {
|
||||
updateStateBySelection();
|
||||
}
|
||||
};
|
||||
mapsCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
fusionTablesCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
docsCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
|
||||
// Setup initial state
|
||||
initState();
|
||||
|
||||
// Update state based on sendRequest
|
||||
updateStateBySendRequest();
|
||||
|
||||
// Update state based on current selection
|
||||
updateStateBySelection();
|
||||
|
||||
alertDialog = new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.generic_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface d) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
mapsTableRow = (TableRow) dialog.findViewById(R.id.send_google_maps_row);
|
||||
fusionTablesTableRow = (TableRow) dialog.findViewById(R.id.send_google_fusion_tables_row);
|
||||
docsTableRow = (TableRow) dialog.findViewById(R.id.send_google_docs_row);
|
||||
|
||||
mapsCheckBox = (CheckBox) dialog.findViewById(R.id.send_google_maps);
|
||||
fusionTablesCheckBox = (CheckBox) dialog.findViewById(R.id.send_google_fusion_tables);
|
||||
docsCheckBox = (CheckBox) dialog.findViewById(R.id.send_google_docs);
|
||||
|
||||
mapsOptionTableRow = (TableRow) dialog.findViewById(R.id.send_google_maps_option_row);
|
||||
newMapRadioButton = (RadioButton) dialog.findViewById(R.id.send_google_new_map);
|
||||
existingMapRadioButton = (RadioButton) dialog.findViewById(R.id.send_google_existing_map);
|
||||
|
||||
// Setup checkboxes
|
||||
OnCheckedChangeListener checkBoxListener = new OnCheckedChangeListener() {
|
||||
public void onCheckedChanged(CompoundButton button, boolean checked) {
|
||||
updateStateBySelection();
|
||||
}
|
||||
};
|
||||
mapsCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
fusionTablesCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
docsCheckBox.setOnCheckedChangeListener(checkBoxListener);
|
||||
|
||||
// Setup buttons
|
||||
cancel = (Button) dialog.findViewById(R.id.send_google_cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
send = (Button) dialog.findViewById(R.id.send_google_send_now);
|
||||
send.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
})
|
||||
.setPositiveButton(R.string.send_google_send_now, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
saveState();
|
||||
startNextActivity();
|
||||
if (sendMaps() || sendFusionTables() || sendDocs()) {
|
||||
startNextActivity();
|
||||
} else {
|
||||
Toast.makeText(UploadServiceChooserActivity.this,
|
||||
R.string.send_google_no_service_selected, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Setup initial state
|
||||
initState();
|
||||
|
||||
// Update state based on sendRequest
|
||||
updateStateBySendRequest();
|
||||
|
||||
// Update state based on current user selection
|
||||
updateStateBySelection();
|
||||
|
||||
return dialog;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.send_google_title)
|
||||
.setView(view)
|
||||
.create();
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +187,6 @@ public class UploadServiceChooserActivity extends Activity {
|
||||
*/
|
||||
private void updateStateBySelection() {
|
||||
mapsOptionTableRow.setVisibility(sendMaps() ? View.VISIBLE : View.GONE);
|
||||
send.setEnabled(sendMaps() || sendFusionTables() || sendDocs());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +242,7 @@ public class UploadServiceChooserActivity extends Activity {
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends stats to Google Analytics.
|
||||
*/
|
||||
@@ -259,12 +259,12 @@ public class UploadServiceChooserActivity extends Activity {
|
||||
}
|
||||
AnalyticsUtils.sendPageViews(this, pages.toArray(new String[pages.size()]));
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
Dialog getDialog() {
|
||||
return dialog;
|
||||
AlertDialog getAlertDialog() {
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
SendRequest getSendRequest() {
|
||||
return sendRequest;
|
||||
|
||||
@@ -91,10 +91,7 @@ public class RemoveTempFilesService extends Service {
|
||||
}
|
||||
|
||||
private void cleanTempDirectory(String name) {
|
||||
FileUtils fileUtils = new FileUtils();
|
||||
String dirName = fileUtils.buildExternalDirectoryPath(name, "tmp");
|
||||
File dir = new File(dirName);
|
||||
cleanTempDirectory(dir);
|
||||
cleanTempDirectory(new File(FileUtils.buildExternalDirectoryPath(name, "tmp")));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.android.apps.mytracks.Constants.RESUME_TRACK_EXTRA_NAME
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGenerator;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
@@ -484,9 +484,10 @@ public class TrackRecordingService extends Service {
|
||||
Notification notification = new Notification(
|
||||
R.drawable.arrow_320, null /* tickerText */,
|
||||
System.currentTimeMillis());
|
||||
Intent intent = new Intent(this, TrackDetailActivity.class)
|
||||
.putExtra(TrackDetailActivity.TRACK_ID, recordingTrackId);
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(
|
||||
this, 0 /* requestCode */, new Intent(this, MyTracks.class),
|
||||
0 /* flags */);
|
||||
this, 0 /* requestCode */, intent, 0 /* flags */);
|
||||
notification.setLatestEventInfo(this, getString(R.string.my_tracks_app_name),
|
||||
getString(R.string.track_record_notification), contentIntent);
|
||||
notification.flags += Notification.FLAG_NO_CLEAR;
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.services;
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.maps.mytracks.BuildConfig;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -157,7 +158,7 @@ public class TrackRecordingServiceConnection {
|
||||
|
||||
Log.i(TAG, "Binding to the service");
|
||||
Intent intent = new Intent(context, TrackRecordingService.class);
|
||||
int flags = SystemUtils.isRelease(context) ? 0 : Context.BIND_DEBUG_UNBIND;
|
||||
int flags = BuildConfig.DEBUG ? Context.BIND_DEBUG_UNBIND : 0;
|
||||
context.bindService(intent, serviceConnection, flags);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ import android.util.Log;
|
||||
* Initialization begins in {@link #setupChannel}, which is invoked by the
|
||||
* {@link SensorManager} when track recording begins. {@link #setupChannel}
|
||||
* asks the ANT Radio Service (via {@link AntInterface}) to start, using a
|
||||
* {@link AntInterface.ServiceListener} to indicate when the service has
|
||||
* AntInterface.ServiceListener to indicate when the service has
|
||||
* connected. {@link #serviceConnected} claims and enables the Radio Service,
|
||||
* and then resets it to a known state for our use. Completion of reset is
|
||||
* indicated by receipt of a startup message (see {@link AntStartupMessage}).
|
||||
|
||||
+22
-22
@@ -23,7 +23,7 @@ import com.dsi.ant.exception.AntInterfaceException;
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.maps.mytracks.BuildConfig;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -32,7 +32,7 @@ import android.util.Log;
|
||||
|
||||
/**
|
||||
* A sensor manager to the PC7 SRM ANT+ bridge.
|
||||
*
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Umran Abdulla
|
||||
*/
|
||||
@@ -54,15 +54,15 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
private static final int INDEX_MESSAGE_SPEED = 5;
|
||||
private static final int INDEX_MESSAGE_CADENCE = 7;
|
||||
private static final int INDEX_MESSAGE_BPM = 8;
|
||||
|
||||
|
||||
private static final int MSG_INITIAL = 5;
|
||||
private static final int MSG_DATA = 6;
|
||||
|
||||
|
||||
private short deviceNumber;
|
||||
|
||||
public AntSrmBridgeSensorManager(Context context) {
|
||||
super(context);
|
||||
|
||||
|
||||
Log.i(TAG, "new ANT SRM Bridge Sensor Manager created");
|
||||
|
||||
deviceNumber = WILDCARD;
|
||||
@@ -77,15 +77,15 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
}
|
||||
Log.i(TAG, "Will pair with device: " + deviceNumber);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean handleMessage(byte messageId, byte[] messageData) {
|
||||
if (super.handleMessage(messageId, messageData)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SystemUtils.isRelease(context)) {
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "Received ANT msg: " + AntUtils.antMessageToString(messageId) + "(" + messageId + ")");
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode an ant device message.
|
||||
* @param messageData The byte array received from the device.
|
||||
@@ -132,10 +132,10 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
}
|
||||
|
||||
setSensorState(Sensor.SensorState.CONNECTED);
|
||||
|
||||
|
||||
int messageType = antMessage[INDEX_MESSAGE_TYPE] & 0xFF;
|
||||
Log.d(TAG, "Received message-type=" + messageType);
|
||||
|
||||
|
||||
switch (messageType) {
|
||||
case MSG_INITIAL:
|
||||
break;
|
||||
@@ -149,7 +149,7 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
{
|
||||
int messageId = msg[INDEX_MESSAGE_ID] & 0xFF;
|
||||
Log.d(TAG, "Received message-id=" + messageId);
|
||||
|
||||
|
||||
int powerVal = (((msg[INDEX_MESSAGE_POWER] & 0xFF) << 8) |
|
||||
(msg[INDEX_MESSAGE_POWER+1] & 0xFF));
|
||||
@SuppressWarnings("unused")
|
||||
@@ -158,12 +158,12 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
int cadenceVal = (msg[INDEX_MESSAGE_CADENCE] & 0xFF);
|
||||
int bpmVal = (msg[INDEX_MESSAGE_BPM] & 0xFF);
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
Sensor.SensorData.Builder power =
|
||||
|
||||
Sensor.SensorData.Builder power =
|
||||
Sensor.SensorData.newBuilder()
|
||||
.setValue(powerVal)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
|
||||
|
||||
/*
|
||||
* Although speed is available from the SRM Bridge, MyTracks doesn't use the value, and
|
||||
* computes speed from the GPS location data.
|
||||
@@ -171,12 +171,12 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
// Sensor.SensorData.Builder speed = Sensor.SensorData.newBuilder().setValue(speedVal).setState(
|
||||
// Sensor.SensorState.SENDING);
|
||||
|
||||
Sensor.SensorData.Builder cadence =
|
||||
Sensor.SensorData.Builder cadence =
|
||||
Sensor.SensorData.newBuilder()
|
||||
.setValue(cadenceVal)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
|
||||
Sensor.SensorData.Builder bpm =
|
||||
Sensor.SensorData.Builder bpm =
|
||||
Sensor.SensorData.newBuilder()
|
||||
.setValue(bpmVal)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
@@ -189,7 +189,7 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
.setHeartRate(bpm)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
void handleChannelId(byte[] rawMessage) {
|
||||
AntChannelIdMessage message = new AntChannelIdMessage(rawMessage);
|
||||
deviceNumber = message.getDeviceNumber();
|
||||
@@ -203,12 +203,12 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
|
||||
}
|
||||
|
||||
private void handleMessageResponse(byte[] rawMessage) {
|
||||
AntChannelResponseMessage message =
|
||||
AntChannelResponseMessage message =
|
||||
new AntChannelResponseMessage(rawMessage);
|
||||
if (!SystemUtils.isRelease(context)) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "Received ANT Response: " + AntUtils.antMessageToString(message.getMessageId()) +
|
||||
"(" + message.getMessageId() + ")" +
|
||||
", Code: " + AntUtils.antEventToStr(message.getMessageCode()) +
|
||||
", Code: " + AntUtils.antEventToStr(message.getMessageCode()) +
|
||||
"(" + message.getMessageCode() + ")");
|
||||
}
|
||||
switch (message.getMessageId()) {
|
||||
|
||||
+7
-4
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.services.tasks;
|
||||
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
@@ -28,12 +29,14 @@ import java.util.HashMap;
|
||||
|
||||
|
||||
/**
|
||||
* This class will periodically announce the user's trip statistics for Froyo and future handsets.
|
||||
* This class will request and release audio focus.
|
||||
* This class will periodically announce the user's trip statistics. This class
|
||||
* will request and release audio focus. <br>
|
||||
* For API Level 8 or higher.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class FroyoStatusAnnouncerTask extends StatusAnnouncerTask {
|
||||
@TargetApi(8)
|
||||
public class Api8StatusAnnouncerTask extends StatusAnnouncerTask {
|
||||
private final static HashMap<String, String> SPEECH_PARAMS = new HashMap<String, String>();
|
||||
static {
|
||||
SPEECH_PARAMS.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "not_used");
|
||||
@@ -51,7 +54,7 @@ public class FroyoStatusAnnouncerTask extends StatusAnnouncerTask {
|
||||
}
|
||||
};
|
||||
|
||||
public FroyoStatusAnnouncerTask(Context context) {
|
||||
public Api8StatusAnnouncerTask(Context context) {
|
||||
super(context);
|
||||
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.util;
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.services.sensors.BluetoothConnectionManager;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.util.Log;
|
||||
@@ -29,6 +30,7 @@ import java.io.IOException;
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@TargetApi(10)
|
||||
public class Api10Adapter extends Api9Adapter {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Window;
|
||||
import android.widget.SearchView;
|
||||
|
||||
/**
|
||||
* API level 11 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@TargetApi(11)
|
||||
public class Api11Adapter extends Api10Adapter {
|
||||
|
||||
@Override
|
||||
public void showActionBar(Activity activity) {
|
||||
activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureSearchWidget(Activity activity, MenuItem menuItem) {
|
||||
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
||||
SearchView searchView = (SearchView) menuItem.getActionView();
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.getComponentName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleSearchMenuSelection(Activity activity) {
|
||||
// Returns false to allow the platform to expand the search widget.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.annotation.TargetApi;
|
||||
import android.view.MenuItem;
|
||||
|
||||
/**
|
||||
* API level 14 specific implementation of the {@link ApiAdapter}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@TargetApi(14)
|
||||
public class Api14Adapter extends Api11Adapter {
|
||||
|
||||
@Override
|
||||
public boolean handleSearchKey(MenuItem menuItem) {
|
||||
menuItem.expandActionView();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,15 @@ 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.MenuItem;
|
||||
import android.view.Window;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -50,7 +53,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 +68,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 +76,7 @@ public class Api7Adapter implements ApiAdapter {
|
||||
System.arraycopy(input, start, output, 0, length);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HttpTransport getHttpTransport() {
|
||||
return new ApacheHttpTransport();
|
||||
@@ -99,4 +102,28 @@ 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureSearchWidget(Activity activity, MenuItem menuItem) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleSearchMenuSelection(Activity activity) {
|
||||
activity.onSearchRequested();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleSearchKey(MenuItem menuItem) {
|
||||
// Return false and allow the framework to handle the search key.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
|
||||
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListenerImpl;
|
||||
import com.google.android.apps.mytracks.services.tasks.FroyoStatusAnnouncerTask;
|
||||
import com.google.android.apps.mytracks.io.backup.Api8BackupPreferencesListener;
|
||||
import com.google.android.apps.mytracks.services.tasks.Api8StatusAnnouncerTask;
|
||||
import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -31,11 +31,11 @@ public class Api8Adapter extends Api7Adapter {
|
||||
|
||||
@Override
|
||||
public PeriodicTask getStatusAnnouncerTask(Context context) {
|
||||
return new FroyoStatusAnnouncerTask(context);
|
||||
return new Api8StatusAnnouncerTask(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackupPreferencesListener getBackupPreferencesListener(Context context) {
|
||||
return new BackupPreferencesListenerImpl(context);
|
||||
return new Api8BackupPreferencesListener(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.StrictMode;
|
||||
import android.util.Log;
|
||||
@@ -30,6 +31,7 @@ import java.util.Arrays;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@TargetApi(9)
|
||||
public class Api9Adapter extends Api8Adapter {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,10 +19,12 @@ 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;
|
||||
import android.content.SharedPreferences;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -98,4 +100,42 @@ 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);
|
||||
|
||||
/**
|
||||
* Configures the search widget.
|
||||
*
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param menuItem the search menu item
|
||||
*/
|
||||
public void configureSearchWidget(Activity activity, MenuItem menuItem);
|
||||
|
||||
/**
|
||||
* Handles the search menu selection. Returns true if handled.
|
||||
*
|
||||
* Due to changes in API level 11.
|
||||
*
|
||||
* @param activity the activity
|
||||
*/
|
||||
public boolean handleSearchMenuSelection(Activity activity);
|
||||
|
||||
/**
|
||||
* Handles the search key press. Returns true if handled.
|
||||
*
|
||||
* Due to changes in API level 14.
|
||||
*
|
||||
* @param menu the search menu
|
||||
*/
|
||||
public boolean handleSearchKey(MenuItem menu);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,13 @@ public class ApiAdapterFactory {
|
||||
*/
|
||||
public static ApiAdapter getApiAdapter() {
|
||||
if (apiAdapter == null) {
|
||||
if (Build.VERSION.SDK_INT >= 10) {
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
apiAdapter = new Api14Adapter();
|
||||
return apiAdapter;
|
||||
} else 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) {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
/**
|
||||
* Utilities for checking units.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class CheckUnitsUtils {
|
||||
|
||||
private static final String CHECK_UNITS_PREFERENCE_FILE = "checkunits";
|
||||
private static final String CHECK_UNITS_PREFERENCE_KEY = "checkunits.checked";
|
||||
|
||||
private CheckUnitsUtils() {}
|
||||
|
||||
public static boolean getCheckUnitsValue(Context context) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
|
||||
return sharedPreferences.getBoolean(CHECK_UNITS_PREFERENCE_KEY, false);
|
||||
}
|
||||
|
||||
public static void setCheckUnitsValue(Context context) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPreferences.edit().putBoolean(CHECK_UNITS_PREFERENCE_KEY, true);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
}
|
||||
@@ -20,34 +20,83 @@ import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
/**
|
||||
* Utilities for creating dialogs.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class DialogUtils {
|
||||
|
||||
private DialogUtils() {}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a confirmation dialog.
|
||||
*
|
||||
*
|
||||
* @param context the context
|
||||
* @param messageId the id of the confirmation message
|
||||
* @param onClickListener the listener to invoke when the users clicks OK
|
||||
* @param messageId the confirmation message id
|
||||
* @param onClickListener the listener to invoke when the user clicks OK
|
||||
*/
|
||||
public static Dialog createConfirmationDialog(
|
||||
Context context, int messageId, OnClickListener onClickListener) {
|
||||
Context context, int messageId, DialogInterface.OnClickListener onClickListener) {
|
||||
return new AlertDialog.Builder(context)
|
||||
.setCancelable(true)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(messageId)
|
||||
.setMessage(context.getString(messageId))
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(android.R.string.ok, onClickListener)
|
||||
.setTitle(R.string.generic_confirm_title)
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a spinner progress dialog.
|
||||
*
|
||||
* @param context the context
|
||||
* @param messageId the progress message id
|
||||
* @param onCancelListener the cancel listener
|
||||
*/
|
||||
public static ProgressDialog createSpinnerProgressDialog(
|
||||
Context context, int messageId, DialogInterface.OnCancelListener onCancelListener) {
|
||||
return createProgressDialog(true, context, messageId, onCancelListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a horizontal progress dialog.
|
||||
*
|
||||
* @param context the context
|
||||
* @param messageId the progress message id
|
||||
* @param onCancelListener the cancel listener
|
||||
* @param formatArgs the format arguments for the messageId
|
||||
*/
|
||||
public static ProgressDialog createHorizontalProgressDialog(Context context, int messageId,
|
||||
DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
|
||||
return createProgressDialog(false, context, messageId, onCancelListener, formatArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a progress dialog.
|
||||
*
|
||||
* @param spinner true to use the spinner style
|
||||
* @param context the context
|
||||
* @param messageId the progress message id
|
||||
* @param onCancelListener the cancel listener
|
||||
* @param formatArgs the format arguments for the message id
|
||||
*/
|
||||
private static ProgressDialog createProgressDialog(boolean spinner, Context context,
|
||||
int messageId, DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
|
||||
ProgressDialog progressDialog = new ProgressDialog(context);
|
||||
progressDialog.setCancelable(true);
|
||||
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setMessage(context.getString(messageId, formatArgs));
|
||||
progressDialog.setOnCancelListener(onCancelListener);
|
||||
progressDialog.setProgressStyle(spinner ? ProgressDialog.STYLE_SPINNER
|
||||
: ProgressDialog.STYLE_HORIZONTAL);
|
||||
progressDialog.setTitle(R.string.generic_progress_title);
|
||||
return progressDialog;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,53 +25,42 @@ import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Utilities for EULA.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class EulaUtils {
|
||||
|
||||
private static final String EULA_PREFERENCE_FILE = "eula";
|
||||
|
||||
// Accepting Google mobile terms of service
|
||||
private static final String EULA_PREFERENCE_KEY = "eula.google_mobile_tos_accepted";
|
||||
|
||||
// Google's mobile page
|
||||
private static final String HOST_NAME = "m.google.com";
|
||||
|
||||
private EulaUtils() {}
|
||||
|
||||
public static boolean getEulaValue(Context context) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
|
||||
return preferences.getBoolean(EULA_PREFERENCE_KEY, false);
|
||||
return sharedPreferences.getBoolean(EULA_PREFERENCE_KEY, false);
|
||||
}
|
||||
|
||||
public static void setEulaValue(Context context) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
|
||||
Editor editor = preferences.edit();
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(EULA_PREFERENCE_KEY, true);
|
||||
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
|
||||
}
|
||||
|
||||
public static String getEulaMessage(Context context) {
|
||||
String item1 = String.format(context.getString(R.string.eula_message_item1), HOST_NAME,
|
||||
HOST_NAME, HOST_NAME, HOST_NAME, HOST_NAME);
|
||||
String item3 = String.format(context.getString(R.string.eula_message_item3), HOST_NAME);
|
||||
String footer = String.format(context.getString(R.string.eula_message_footer), HOST_NAME);
|
||||
String copyright = "©" + Calendar.getInstance().get(Calendar.YEAR);
|
||||
return context.getString(R.string.eula_message_date)
|
||||
return context.getString(R.string.eula_date)
|
||||
+ "\n\n"
|
||||
+ context.getString(R.string.eula_message_header)
|
||||
+ "\n\n"
|
||||
+ context.getString(R.string.eula_message_body)
|
||||
+ "\n\n"
|
||||
+ "1. " + item1
|
||||
+ "\n\n"
|
||||
+ "2. " + context.getString(R.string.eula_message_item2)
|
||||
+ "\n\n"
|
||||
+ "3. " + item3
|
||||
+ "\n\n"
|
||||
+ "4. " + context.getString(R.string.eula_message_item4)
|
||||
+ "\n\n"
|
||||
+ footer
|
||||
+ "\n\n"
|
||||
+ copyright;
|
||||
+ context.getString(R.string.eula_body, HOST_NAME)
|
||||
+ "\n\n"
|
||||
+ context.getString(R.string.eula_footer, HOST_NAME)
|
||||
+ "\n\n"
|
||||
+ "©" + Calendar.getInstance().get(Calendar.YEAR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,20 +29,46 @@ import java.io.File;
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
private FileUtils() {}
|
||||
|
||||
/**
|
||||
* The maximum FAT32 path length. See the FAT32 spec at
|
||||
* http://msdn.microsoft.com/en-us/windows/hardware/gg463080
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final int MAX_FAT32_PATH_LENGTH = 260;
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the SD card is available.
|
||||
*/
|
||||
public static boolean isSdCardAvailable() {
|
||||
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the given directory exists by creating it and its parents if
|
||||
* necessary.
|
||||
*
|
||||
* @return whether the directory exists (either already existed or was
|
||||
* successfully created)
|
||||
*/
|
||||
public static boolean ensureDirectoryExists(File dir) {
|
||||
if (dir.exists() && dir.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
if (dir.mkdirs()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a path inside the My Tracks directory in the SD card.
|
||||
*
|
||||
* @param components the path components inside the mytracks directory
|
||||
* @return the full path to the destination
|
||||
*/
|
||||
public String buildExternalDirectoryPath(String... components) {
|
||||
public static String buildExternalDirectoryPath(String... components) {
|
||||
StringBuilder dirNameBuilder = new StringBuilder();
|
||||
dirNameBuilder.append(Environment.getExternalStorageDirectory());
|
||||
dirNameBuilder.append(File.separatorChar);
|
||||
@@ -54,33 +80,6 @@ public class FileUtils {
|
||||
return dirNameBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the SD card is available.
|
||||
*/
|
||||
public boolean isSdCardAvailable() {
|
||||
return Environment.MEDIA_MOUNTED.equals(
|
||||
Environment.getExternalStorageState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the given directory exists by creating it and its parents if
|
||||
* necessary.
|
||||
*
|
||||
* @return whether the directory exists (either already existed or was
|
||||
* successfully created)
|
||||
*/
|
||||
public boolean ensureDirectoryExists(File dir) {
|
||||
if (dir.exists() && dir.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dir.mkdirs()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a filename with the given base name (prefix) and the given
|
||||
* extension, possibly adding a suffix to ensure the file doesn't exist.
|
||||
@@ -90,8 +89,8 @@ public class FileUtils {
|
||||
* @param extension the file's extension
|
||||
* @return the complete file name, without the directory
|
||||
*/
|
||||
public synchronized String buildUniqueFileName(File directory,
|
||||
String fileBaseName, String extension) {
|
||||
public static synchronized String buildUniqueFileName(
|
||||
File directory, String fileBaseName, String extension) {
|
||||
return buildUniqueFileName(directory, fileBaseName, extension, 0);
|
||||
}
|
||||
|
||||
@@ -105,7 +104,8 @@ public class FileUtils {
|
||||
* @param suffix the first numeric suffix to try to use, or 0 for none
|
||||
* @return the complete filename, without the directory
|
||||
*/
|
||||
private String buildUniqueFileName(File directory, String base, String extension, int suffix) {
|
||||
private static String buildUniqueFileName(
|
||||
File directory, String base, String extension, int suffix) {
|
||||
String suffixName = "";
|
||||
if (suffix > 0) {
|
||||
suffixName += "(" + Integer.toString(suffix) + ")";
|
||||
@@ -115,8 +115,8 @@ public class FileUtils {
|
||||
String baseName = sanitizeFileName(base);
|
||||
baseName = truncateFileName(directory, baseName, suffixName);
|
||||
String fullName = baseName + suffixName;
|
||||
|
||||
if (!fileExists(directory, fullName)) {
|
||||
|
||||
if (!new File(directory, fullName).exists()) {
|
||||
return fullName;
|
||||
}
|
||||
return buildUniqueFileName(directory, base, extension, suffix + 1);
|
||||
@@ -131,7 +131,7 @@ public class FileUtils {
|
||||
* @param name name
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String sanitizeFileName(String name) {
|
||||
static String sanitizeFileName(String name) {
|
||||
StringBuffer buffer = new StringBuffer(name.length());
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
int codePoint = name.codePointAt(i);
|
||||
@@ -145,13 +145,13 @@ public class FileUtils {
|
||||
String result = buffer.toString();
|
||||
return result.replaceAll("_+", "_");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if it is a special FAT32 character.
|
||||
*
|
||||
*
|
||||
* @param character the character
|
||||
*/
|
||||
private boolean isSpecialFat32(char character) {
|
||||
private static boolean isSpecialFat32(char character) {
|
||||
switch (character) {
|
||||
case '$':
|
||||
case '%':
|
||||
@@ -191,8 +191,8 @@ public class FileUtils {
|
||||
* @param suffix suffix
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String truncateFileName(File directory, String name, String suffix) {
|
||||
// 1 at the end accounts for the FAT32 filename trailing NUL character
|
||||
static String truncateFileName(File directory, String name, String suffix) {
|
||||
// 1 at the end accounts for the FAT32 filename trailing NUL character
|
||||
int requiredLength = directory.getPath().length() + suffix.length() + 1;
|
||||
if (name.length() + requiredLength > MAX_FAT32_PATH_LENGTH) {
|
||||
int limit = MAX_FAT32_PATH_LENGTH - requiredLength;
|
||||
@@ -201,13 +201,4 @@ public class FileUtils {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a file with the given name exists in the given directory.
|
||||
* This is isolated so it can be overridden in tests.
|
||||
*/
|
||||
protected boolean fileExists(File directory, String fullName) {
|
||||
File file = new File(directory, fullName);
|
||||
return file.exists();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,21 +16,19 @@
|
||||
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.SaveActivity;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -80,12 +78,10 @@ public class PlayTrackUtils {
|
||||
public static void playTrack(Context context, long trackId) {
|
||||
AnalyticsUtils.sendPageViews(context, "/action/play");
|
||||
|
||||
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
|
||||
Intent intent = new Intent(context, SaveActivity.class)
|
||||
.putExtra(SaveActivity.EXTRA_FILE_FORMAT, TrackFileFormat.KML.ordinal())
|
||||
.putExtra(SaveActivity.EXTRA_PLAY_FILE, true)
|
||||
.setAction(context.getString(R.string.track_action_save))
|
||||
.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
|
||||
.putExtra(SaveActivity.EXTRA_TRACK_ID, trackId)
|
||||
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) TrackFileFormat.KML)
|
||||
.putExtra(SaveActivity.EXTRA_PLAY_TRACK, true);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@@ -97,9 +93,9 @@ public class PlayTrackUtils {
|
||||
public static Dialog createInstallEarthDialog(final Context context) {
|
||||
return new AlertDialog.Builder(context)
|
||||
.setCancelable(true)
|
||||
.setMessage(R.string.track_list_play_install_earth_message)
|
||||
.setMessage(R.string.track_detail_install_earth_message)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(android.R.string.ok, new OnClickListener() {
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent();
|
||||
|
||||
@@ -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
|
||||
@@ -22,7 +22,6 @@ import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
@@ -34,26 +33,6 @@ import android.util.Log;
|
||||
*/
|
||||
public class SystemUtils {
|
||||
|
||||
private static final int RELEASE_SIGNATURE_HASHCODE = -1855564782;
|
||||
|
||||
/**
|
||||
* Returns whether or not this is a release build.
|
||||
*/
|
||||
public static boolean isRelease(Context context) {
|
||||
try {
|
||||
Signature [] sigs = context.getPackageManager().getPackageInfo(
|
||||
context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
|
||||
for (Signature sig : sigs) {
|
||||
if (sig.hashCode() == RELEASE_SIGNATURE_HASHCODE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(Constants.TAG, "Unable to get signatures", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the My Tracks version from the manifest.
|
||||
*
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.google.android.apps.mytracks.widgets;
|
||||
import static com.google.android.apps.mytracks.Constants.SETTINGS_NAME;
|
||||
import static com.google.android.apps.mytracks.Constants.TAG;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
@@ -145,7 +145,10 @@ public class TrackWidgetProvider
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.track_widget);
|
||||
|
||||
// Make all of the stats open the mytracks activity.
|
||||
Intent intent = new Intent(context, MyTracks.class);
|
||||
Intent intent = new Intent(context, TrackDetailActivity.class);
|
||||
if (track != null) {
|
||||
intent.putExtra(TrackDetailActivity.TRACK_ID, track.getId());
|
||||
}
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
|
||||
views.setOnClickPendingIntent(R.id.appwidget_track_statistics, pendingIntent);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user