forked from upstream-mirrors/OpenTracks
Handles interrupt and error cleanup during import
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
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.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A DialogFragment to show information about My Tracks.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AboutDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface AboutCaller {
|
||||
|
||||
/**
|
||||
* Called when about license is invoked.
|
||||
*/
|
||||
public void onAboutLicense();
|
||||
}
|
||||
|
||||
public static final String ABOUT_DIALOG_TAG = "aboutDialog";
|
||||
|
||||
private AboutCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (AboutCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + AboutCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
fragmentActivity = getActivity();
|
||||
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.about, null);
|
||||
TextView aboutVersion = (TextView) view.findViewById(R.id.about_version);
|
||||
aboutVersion.setText(SystemUtils.getMyTracksVersion(fragmentActivity));
|
||||
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(
|
||||
R.string.about_license, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
caller.onAboutLicense();
|
||||
}
|
||||
}).setPositiveButton(R.string.generic_ok, null).setTitle(R.string.help_about).setView(view)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.ChartView;
|
||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||
import com.google.android.apps.mytracks.content.TrackDataType;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsUpdater;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.ZoomControls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* A fragment to display track chart to the user.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
public static final String CHART_FRAGMENT_TAG = "chartFragment";
|
||||
|
||||
private final ArrayList<double[]> pendingPoints = new ArrayList<double[]>();
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
// Stats gathered from the received data
|
||||
private TripStatisticsUpdater tripStatisticsUpdater;
|
||||
private long startTime;
|
||||
|
||||
private boolean metricUnits = true;
|
||||
private boolean reportSpeed = true;
|
||||
private int recordingDistanceInterval = PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT;
|
||||
|
||||
// Modes of operation
|
||||
private boolean chartByDistance = true;
|
||||
private boolean[] chartShow = new boolean[] { true, true, true, true, true, true };
|
||||
|
||||
// UI elements
|
||||
private ChartView chartView;
|
||||
private ZoomControls zoomControls;
|
||||
|
||||
/**
|
||||
* A runnable that will enable/disable zoom controls and orange pointer as
|
||||
* appropriate and redraw.
|
||||
*/
|
||||
private final Runnable updateChart = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isResumed() || trackDataHub == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||
chartView.setShowPointer(isSelectedTrackRecording());
|
||||
chartView.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
/*
|
||||
* Create a chartView here to store data thus won't need to reload all the
|
||||
* data on every onStart or onResume.
|
||||
*/
|
||||
chartView = new ChartView(getActivity());
|
||||
};
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.chart, container, false);
|
||||
zoomControls = (ZoomControls) view.findViewById(R.id.chart_zoom_controls);
|
||||
zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
zoomIn();
|
||||
}
|
||||
});
|
||||
zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
zoomOut();
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
ViewGroup layout = (ViewGroup) getActivity().findViewById(R.id.chart_view_layout);
|
||||
LayoutParams layoutParams = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
layout.addView(chartView, layoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
checkChartSettings();
|
||||
getActivity().runOnUiThread(updateChart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
ViewGroup layout = (ViewGroup) getActivity().findViewById(R.id.chart_view_layout);
|
||||
layout.removeView(chartView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackUpdated(Track track) {
|
||||
if (isResumed()) {
|
||||
if (track == null || track.getTripStatistics() == null) {
|
||||
startTime = -1L;
|
||||
return;
|
||||
}
|
||||
startTime = track.getTripStatistics().getStartTime();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTrackPoints() {
|
||||
if (isResumed()) {
|
||||
tripStatisticsUpdater = startTime != -1L ? new TripStatisticsUpdater(startTime) : null;
|
||||
pendingPoints.clear();
|
||||
chartView.reset();
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
chartView.resetScroll();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledInTrackPoint(Location location) {
|
||||
if (isResumed()) {
|
||||
double[] data = new double[ChartView.NUM_SERIES + 1];
|
||||
fillDataPoint(location, data);
|
||||
pendingPoints.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledOutTrackPoint(Location location) {
|
||||
if (isResumed()) {
|
||||
fillDataPoint(location, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit(Location location) {
|
||||
if (isResumed()) {
|
||||
fillDataPoint(location, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPointsDone() {
|
||||
if (isResumed()) {
|
||||
chartView.addDataPoints(pendingPoints);
|
||||
pendingPoints.clear();
|
||||
runOnUiThread(updateChart);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWaypoints() {
|
||||
if (isResumed()) {
|
||||
chartView.clearWaypoints();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypoint(Waypoint waypoint) {
|
||||
if (isResumed() && waypoint != null && LocationUtils.isValidLocation(waypoint.getLocation())) {
|
||||
chartView.addWaypoint(waypoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypointsDone() {
|
||||
if (isResumed()) {
|
||||
runOnUiThread(updateChart);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMetricUnitsChanged(boolean metric) {
|
||||
if (isResumed()) {
|
||||
if (metricUnits == metric) {
|
||||
return false;
|
||||
}
|
||||
metricUnits = metric;
|
||||
chartView.setMetricUnits(metricUnits);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
chartView.requestLayout();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReportSpeedChanged(boolean speed) {
|
||||
if (isResumed()) {
|
||||
if (reportSpeed == speed) {
|
||||
return false;
|
||||
}
|
||||
reportSpeed = speed;
|
||||
chartView.setReportSpeed(reportSpeed);
|
||||
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
||||
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
||||
setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed);
|
||||
setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
chartView.requestLayout();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRecordingGpsAccuracy(int minRequiredAccuracy) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRecordingDistanceIntervalChanged(int value) {
|
||||
if (isResumed()) {
|
||||
if (recordingDistanceInterval == value) {
|
||||
return false;
|
||||
}
|
||||
recordingDistanceInterval = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the chart settings.
|
||||
*/
|
||||
private void checkChartSettings() {
|
||||
boolean needUpdate = false;
|
||||
if (chartByDistance != PreferencesUtils.isChartByDistance(getActivity())) {
|
||||
chartByDistance = !chartByDistance;
|
||||
chartView.setChartByDistance(chartByDistance);
|
||||
reloadTrackDataHub();
|
||||
needUpdate = true;
|
||||
}
|
||||
if (setSeriesEnabled(ChartView.ELEVATION_SERIES, PreferencesUtils.getBoolean(getActivity(),
|
||||
R.string.chart_show_elevation_key, PreferencesUtils.CHART_SHOW_ELEVATION_DEFAULT))) {
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
||||
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
||||
if (setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed)) {
|
||||
needUpdate = true;
|
||||
}
|
||||
if (setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed)) {
|
||||
needUpdate = true;
|
||||
}
|
||||
if (setSeriesEnabled(ChartView.POWER_SERIES, PreferencesUtils.getBoolean(
|
||||
getActivity(), R.string.chart_show_power_key, PreferencesUtils.CHART_SHOW_POWER_DEFAULT))) {
|
||||
needUpdate = true;
|
||||
}
|
||||
if (setSeriesEnabled(ChartView.CADENCE_SERIES, PreferencesUtils.getBoolean(getActivity(),
|
||||
R.string.chart_show_cadence_key, PreferencesUtils.CHART_SHOW_CADENCE_DEFAULT))) {
|
||||
needUpdate = true;
|
||||
}
|
||||
if (setSeriesEnabled(ChartView.HEART_RATE_SERIES, PreferencesUtils.getBoolean(getActivity(),
|
||||
R.string.chart_show_heart_rate_key, PreferencesUtils.CHART_SHOW_HEART_RATE_DEFAULT))) {
|
||||
needUpdate = true;
|
||||
}
|
||||
if (needUpdate) {
|
||||
chartView.postInvalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the series enabled value.
|
||||
*
|
||||
* @param index the series index
|
||||
* @param value the value
|
||||
* @return true if changed
|
||||
*/
|
||||
private boolean setSeriesEnabled(int index, boolean value) {
|
||||
if (chartShow[index] != value) {
|
||||
chartShow[index] = value;
|
||||
chartView.setChartValueSeriesEnabled(index, value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
||||
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE, TrackDataType.PREFERENCE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void pauseTrackDataHub() {
|
||||
trackDataHub.unregisterTrackDataListener(this);
|
||||
trackDataHub = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selected track is recording. Needs to be synchronized
|
||||
* because trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized boolean isSelectedTrackRecording() {
|
||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void reloadTrackDataHub() {
|
||||
if (trackDataHub != null) {
|
||||
trackDataHub.reloadDataForListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To zoom in.
|
||||
*/
|
||||
private void zoomIn() {
|
||||
chartView.zoomIn();
|
||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||
}
|
||||
|
||||
/**
|
||||
* To zoom out.
|
||||
*/
|
||||
private void zoomOut() {
|
||||
chartView.zoomOut();
|
||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a runnable on the UI thread if possible.
|
||||
*
|
||||
* @param runnable the runnable
|
||||
*/
|
||||
private void runOnUiThread(Runnable runnable) {
|
||||
FragmentActivity fragmentActivity = getActivity();
|
||||
if (fragmentActivity != null) {
|
||||
fragmentActivity.runOnUiThread(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a location, fill in a data point, an array of double[]. <br>
|
||||
* data[0] = time/distance <br>
|
||||
* data[1] = elevation <br>
|
||||
* data[2] = speed <br>
|
||||
* data[3] = pace <br>
|
||||
* data[4] = heart rate <br>
|
||||
* data[5] = cadence <br>
|
||||
* data[6] = power <br>
|
||||
*
|
||||
* @param location the location
|
||||
* @param data the data point to fill in, can be null
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void fillDataPoint(Location location, double data[]) {
|
||||
double timeOrDistance = Double.NaN;
|
||||
double elevation = Double.NaN;
|
||||
double speed = Double.NaN;
|
||||
double pace = Double.NaN;
|
||||
double heartRate = Double.NaN;
|
||||
double cadence = Double.NaN;
|
||||
double power = Double.NaN;
|
||||
|
||||
if (tripStatisticsUpdater != null) {
|
||||
tripStatisticsUpdater.addLocation(location, recordingDistanceInterval, PreferencesUtils
|
||||
.getInt(getActivity(), R.string.stats_weight_key, PreferencesUtils.STATS_WEIGHT_DEFAULT));
|
||||
TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
if (chartByDistance) {
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
timeOrDistance = distance;
|
||||
} else {
|
||||
timeOrDistance = tripStatistics.getTotalTime();
|
||||
}
|
||||
|
||||
elevation = tripStatisticsUpdater.getSmoothedElevation();
|
||||
if (!metricUnits) {
|
||||
elevation *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
|
||||
speed = tripStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
|
||||
if (!metricUnits) {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
}
|
||||
if (location instanceof MyTracksLocation
|
||||
&& ((MyTracksLocation) location).getSensorDataSet() != null) {
|
||||
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
||||
if (sensorDataSet.hasHeartRate()
|
||||
&& sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorDataSet.getHeartRate().hasValue()) {
|
||||
heartRate = sensorDataSet.getHeartRate().getValue();
|
||||
}
|
||||
if (sensorDataSet.hasCadence()
|
||||
&& sensorDataSet.getCadence().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorDataSet.getCadence().hasValue()) {
|
||||
cadence = sensorDataSet.getCadence().getValue();
|
||||
}
|
||||
if (sensorDataSet.hasPower()
|
||||
&& sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING
|
||||
&& sensorDataSet.getPower().hasValue()) {
|
||||
power = sensorDataSet.getPower().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
data[0] = timeOrDistance;
|
||||
data[1] = elevation;
|
||||
data[2] = speed;
|
||||
data[3] = pace;
|
||||
data[4] = heartRate;
|
||||
data[5] = cadence;
|
||||
data[6] = power;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ChartView getChartView() {
|
||||
return chartView;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setTripStatisticsUpdater(long time) {
|
||||
tripStatisticsUpdater = new TripStatisticsUpdater(time);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setChartView(ChartView view) {
|
||||
chartView = view;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setMetricUnits(boolean value) {
|
||||
metricUnits = value;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setReportSpeed(boolean value) {
|
||||
reportSpeed = value;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setChartByDistance(boolean value) {
|
||||
chartByDistance = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.gms.auth.UserRecoverableAuthException;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A non UI fragment to check permission.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class CheckPermissionFragment extends Fragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface CheckPermissionCaller {
|
||||
|
||||
/**
|
||||
* Called when check permission is done.
|
||||
*
|
||||
* @param scope the permission scope
|
||||
* @param success true if success
|
||||
* @param intent if not success, intent to prompt for permission
|
||||
*/
|
||||
public void onCheckPermissionDone(String scope, boolean success, Intent intent);
|
||||
}
|
||||
|
||||
public static final String CHECK_PERMISSION_TAG = "checkPermission";
|
||||
|
||||
private static final String TAG = CheckPermissionFragment.class.getSimpleName();
|
||||
private static final String KEY_ACCOUNT_NAME = "accountName";
|
||||
private static final String KEY_SCOPE = "scope";
|
||||
|
||||
private CheckPermissionCaller caller;
|
||||
|
||||
public static CheckPermissionFragment newInstance(String accountName, String scope) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_ACCOUNT_NAME, accountName);
|
||||
bundle.putString(KEY_SCOPE, scope);
|
||||
|
||||
CheckPermissionFragment checkPermissionFragment = new CheckPermissionFragment();
|
||||
checkPermissionFragment.setArguments(bundle);
|
||||
return checkPermissionFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (CheckPermissionCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + CheckPermissionCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Only want one instance
|
||||
setRetainInstance(true);
|
||||
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String accountName = getArguments().getString(KEY_ACCOUNT_NAME);
|
||||
String scope = getArguments().getString(KEY_SCOPE);
|
||||
try {
|
||||
SendToGoogleUtils.getGoogleAccountCredential(getActivity(), accountName, scope);
|
||||
finish(scope, true, null);
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
Intent intent = null;
|
||||
try {
|
||||
// HACK: UserRecoverableAuthException.getIntent can throw a null pointer exception.
|
||||
intent = e.getIntent();
|
||||
} catch (Exception e1) {
|
||||
Log.e(TAG, "Exception in getIntent", e1);
|
||||
}
|
||||
finish(scope, false, intent);
|
||||
} catch (GoogleAuthException e) {
|
||||
Log.e(TAG, "GoogleAuthException", e);
|
||||
finish(scope, false, null);
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
finish(scope, false, e.getIntent());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
finish(scope, false, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private void finish(final String scope, final boolean success, final Intent intent) {
|
||||
getFragmentManager().popBackStack();
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
caller.onCheckPermissionDone(scope, success, intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* A DialogFragment to choose an account.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ChooseAccountDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ChooseAccountCaller {
|
||||
|
||||
/**
|
||||
* Called when choose account is done.
|
||||
*/
|
||||
public void onChooseAccountDone();
|
||||
}
|
||||
|
||||
public static final String CHOOSE_ACCOUNT_DIALOG_TAG = "chooseAccount";
|
||||
|
||||
private ChooseAccountCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
private Account[] accounts;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ChooseAccountCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + ChooseAccountCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = getActivity();
|
||||
accounts = AccountManager.get(fragmentActivity).getAccountsByType(Constants.ACCOUNT_TYPE);
|
||||
|
||||
if (accounts.length == 1) {
|
||||
PreferencesUtils.setString(fragmentActivity, R.string.google_account_key, accounts[0].name);
|
||||
dismiss();
|
||||
caller.onChooseAccountDone();
|
||||
return;
|
||||
}
|
||||
|
||||
String googleAccount = PreferencesUtils.getString(
|
||||
fragmentActivity, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
if (accounts[i].name.equals(googleAccount)) {
|
||||
dismiss();
|
||||
caller.onChooseAccountDone();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
if (accounts.length == 0) {
|
||||
return new AlertDialog.Builder(fragmentActivity).setMessage(
|
||||
R.string.send_google_no_account_message).setTitle(R.string.send_google_no_account_title)
|
||||
.setPositiveButton(R.string.generic_ok, null).create();
|
||||
}
|
||||
String[] choices = new String[accounts.length];
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
choices[i] = accounts[i].name;
|
||||
}
|
||||
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(
|
||||
R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
PreferencesUtils.setString(
|
||||
fragmentActivity, R.string.google_account_key, accounts[position].name);
|
||||
caller.onChooseAccountDone();
|
||||
}
|
||||
}).setSingleChoiceItems(choices, 0, null)
|
||||
.setTitle(R.string.send_google_choose_account_title).create();
|
||||
}
|
||||
}
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.ShareCompat;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A DialogFragment to choose an activity.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ChooseActivityDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ChooseActivityCaller {
|
||||
|
||||
/**
|
||||
* Called when choose activity is done.
|
||||
*/
|
||||
public void onChooseActivityDone(String packageName, String className);
|
||||
}
|
||||
|
||||
public static final String CHOOSE_ACTIVITY_DIALOG_TAG = "chooseActivityDialog";
|
||||
|
||||
private ChooseActivityCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
private PackageManager packageManager;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ChooseActivityCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + ChooseActivityCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
fragmentActivity = getActivity();
|
||||
packageManager = fragmentActivity.getPackageManager();
|
||||
List<DisplayInfo> displayInfos = getDisplayInfos();
|
||||
|
||||
ArrayAdapter<DisplayInfo> arrayAdapter = new ArrayAdapter<DisplayInfo>(fragmentActivity,
|
||||
R.layout.choose_activity_list_item, R.id.choose_activity_list_item_text1, displayInfos) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View view;
|
||||
if (convertView == null) {
|
||||
view = fragmentActivity.getLayoutInflater()
|
||||
.inflate(R.layout.choose_activity_list_item, parent, false);
|
||||
} else {
|
||||
view = convertView;
|
||||
}
|
||||
DisplayInfo displayInfo = getItem(position);
|
||||
TextView text1 = (TextView) view.findViewById(R.id.choose_activity_list_item_text1);
|
||||
TextView text2 = (TextView) view.findViewById(R.id.choose_activity_list_item_text2);
|
||||
ImageView icon = (ImageView) view.findViewById(R.id.choose_activity_list_item_icon);
|
||||
text1.setText(displayInfo.primaryLabel);
|
||||
if (displayInfo.secondaryLabel != null) {
|
||||
text2.setVisibility(View.VISIBLE);
|
||||
text2.setText(displayInfo.secondaryLabel);
|
||||
} else {
|
||||
text2.setVisibility(View.GONE);
|
||||
}
|
||||
icon.setImageDrawable(displayInfo.icon);
|
||||
return view;
|
||||
}
|
||||
};
|
||||
return new AlertDialog.Builder(fragmentActivity).setSingleChoiceItems(
|
||||
arrayAdapter, 0, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
AlertDialog alertDialog = (AlertDialog) dialog;
|
||||
DisplayInfo displayInfo = (DisplayInfo) alertDialog.getListView()
|
||||
.getItemAtPosition(which);
|
||||
ActivityInfo activityInfo = displayInfo.resolveInfo.activityInfo;
|
||||
dismiss();
|
||||
caller.onChooseActivityDone(
|
||||
activityInfo.applicationInfo.packageName, activityInfo.name);
|
||||
}
|
||||
}).setTitle(R.string.share_track_share_url_title).create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
super.onCancel(dialog);
|
||||
caller.onChooseActivityDone(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display info.
|
||||
*/
|
||||
private List<DisplayInfo> getDisplayInfos() {
|
||||
List<DisplayInfo> displayInfos = new ArrayList<DisplayInfo>();
|
||||
Intent intent = ShareCompat.IntentBuilder.from(fragmentActivity)
|
||||
.setType(IntentUtils.TEXT_PLAIN_TYPE).getIntent();
|
||||
List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(
|
||||
intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
if (resolveInfos != null && resolveInfos.size() > 0) {
|
||||
int size = resolveInfos.size();
|
||||
ResolveInfo firstResolveInfo = resolveInfos.get(0);
|
||||
for (int i = 1; i < size; i++) {
|
||||
ResolveInfo resolveInfo = resolveInfos.get(i);
|
||||
if (firstResolveInfo.priority != resolveInfo.priority
|
||||
|| firstResolveInfo.isDefault != resolveInfo.isDefault) {
|
||||
while (i < size) {
|
||||
resolveInfos.remove(i);
|
||||
size--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (size > 1) {
|
||||
ResolveInfo.DisplayNameComparator displayNameComparator = new ResolveInfo.DisplayNameComparator(
|
||||
packageManager);
|
||||
Collections.sort(resolveInfos, displayNameComparator);
|
||||
}
|
||||
|
||||
firstResolveInfo = resolveInfos.get(0);
|
||||
int start = 0;
|
||||
CharSequence firstLabel = firstResolveInfo.loadLabel(packageManager);
|
||||
for (int i = 1; i < size; i++) {
|
||||
if (firstLabel == null) {
|
||||
firstLabel = firstResolveInfo.activityInfo.packageName;
|
||||
}
|
||||
ResolveInfo resolveInfo = resolveInfos.get(i);
|
||||
CharSequence label = resolveInfo.loadLabel(packageManager);
|
||||
if (label == null) {
|
||||
label = resolveInfo.activityInfo.packageName;
|
||||
}
|
||||
if (label.equals(firstLabel)) {
|
||||
continue;
|
||||
}
|
||||
processGroup(resolveInfos, displayInfos, start, i - 1);
|
||||
firstResolveInfo = resolveInfo;
|
||||
firstLabel = label;
|
||||
start = i;
|
||||
}
|
||||
// Process last group
|
||||
processGroup(resolveInfos, displayInfos, start, size - 1);
|
||||
}
|
||||
return displayInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains display info.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private final class DisplayInfo {
|
||||
private ResolveInfo resolveInfo;
|
||||
private CharSequence primaryLabel;
|
||||
private CharSequence secondaryLabel;
|
||||
private Drawable icon;
|
||||
|
||||
public DisplayInfo(ResolveInfo resolveInfo, CharSequence primaryLabel,
|
||||
CharSequence secondaryLabel, Drawable icon) {
|
||||
this.resolveInfo = resolveInfo;
|
||||
this.primaryLabel = primaryLabel;
|
||||
this.secondaryLabel = secondaryLabel;
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a group of items with the same label.
|
||||
*
|
||||
* @param resolveInfos list of resolve infos
|
||||
* @param displayInfos list of display infos
|
||||
* @param start start index
|
||||
* @param end end index
|
||||
*/
|
||||
private void processGroup(
|
||||
List<ResolveInfo> resolveInfos, List<DisplayInfo> displayInfos, int start, int end) {
|
||||
ResolveInfo startResolveInfo = resolveInfos.get(start);
|
||||
CharSequence primaryLabel = startResolveInfo.loadLabel(packageManager);
|
||||
Drawable icon = startResolveInfo.loadIcon(packageManager);
|
||||
|
||||
int num = end - start + 1;
|
||||
if (num == 1) {
|
||||
// Only one, set the secondary label to null
|
||||
displayInfos.add(new DisplayInfo(startResolveInfo, primaryLabel, null, icon));
|
||||
} else {
|
||||
// Decide package name or application name for the secondary label
|
||||
boolean usePackageName = false;
|
||||
CharSequence appName = startResolveInfo.activityInfo.applicationInfo.loadLabel(
|
||||
packageManager);
|
||||
if (appName == null) {
|
||||
usePackageName = true;
|
||||
} else {
|
||||
// Use HashSet to track duplicates
|
||||
HashSet<CharSequence> duplicates = new HashSet<CharSequence>();
|
||||
duplicates.add(appName);
|
||||
for (int i = start + 1; i <= end; i++) {
|
||||
ResolveInfo resolveInfo = resolveInfos.get(i);
|
||||
CharSequence name = resolveInfo.activityInfo.applicationInfo.loadLabel(packageManager);
|
||||
if ((name == null) || (duplicates.contains(name))) {
|
||||
usePackageName = true;
|
||||
break;
|
||||
} else {
|
||||
duplicates.add(name);
|
||||
}
|
||||
}
|
||||
// Clear HashSet for later use
|
||||
duplicates.clear();
|
||||
}
|
||||
for (int i = start; i <= end; i++) {
|
||||
ResolveInfo resolveInfo = resolveInfos.get(i);
|
||||
CharSequence secondaryLabel = usePackageName ? resolveInfo.activityInfo.packageName
|
||||
: resolveInfo.activityInfo.applicationInfo.loadLabel(packageManager);
|
||||
displayInfos.add(new DisplayInfo(resolveInfo, primaryLabel, secondaryLabel, icon));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.TrackIconUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.GridView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A DialogFragment to choose an activity type.
|
||||
*
|
||||
* @author apoorvn
|
||||
*/
|
||||
public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author apoorvn
|
||||
*/
|
||||
public interface ChooseActivityTypeCaller {
|
||||
|
||||
/**
|
||||
* Called when choose activity type is done.
|
||||
*/
|
||||
public void onChooseActivityTypeDone(String iconValue);
|
||||
}
|
||||
|
||||
public static final String CHOOSE_ACTIVITY_TYPE_DIALOG_TAG = "chooseActivityType";
|
||||
|
||||
private ChooseActivityTypeCaller caller;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ChooseActivityTypeCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(activity.toString() + " must implement "
|
||||
+ ChooseActivityTypeCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
GridView gridView =
|
||||
(GridView) getActivity().getLayoutInflater().inflate(R.layout.choose_activity_type, null);
|
||||
|
||||
final List<String> iconValues = TrackIconUtils.getAllIconValues();
|
||||
List<Integer> imageIds = new ArrayList<Integer>();
|
||||
for (String iconValue : iconValues) {
|
||||
imageIds.add(TrackIconUtils.getIconDrawable(iconValue));
|
||||
}
|
||||
|
||||
Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeResource(getResources(), R.drawable.ic_track_airplane, options);
|
||||
int padding = 32;
|
||||
int width = options.outWidth + 2 * padding;
|
||||
int height = options.outHeight + 2 * padding;
|
||||
gridView.setColumnWidth(width);
|
||||
|
||||
ChooseActivityTypeImageAdapter imageAdapter =
|
||||
new ChooseActivityTypeImageAdapter(getActivity(), imageIds, width, height, padding);
|
||||
gridView.setAdapter(imageAdapter);
|
||||
gridView.setOnItemClickListener(new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
dismiss();
|
||||
caller.onChooseActivityTypeDone(iconValues.get(position));
|
||||
}
|
||||
});
|
||||
|
||||
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
|
||||
.setTitle(R.string.track_edit_activity_type_hint).setView(gridView).create();
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Image adapter for choosing an activity type.
|
||||
*
|
||||
* @author apoorvn
|
||||
*/
|
||||
public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
|
||||
private final Context context;
|
||||
private final List<Integer> imageIds;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int padding;
|
||||
|
||||
public ChooseActivityTypeImageAdapter(
|
||||
Context context, List<Integer> imageIds, int width, int height, int padding) {
|
||||
this.context = context;
|
||||
this.imageIds = imageIds;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.padding = padding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return imageIds.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ImageView imageView;
|
||||
if (convertView == null) {
|
||||
imageView = new ImageView(context);
|
||||
} else {
|
||||
imageView = (ImageView) convertView;
|
||||
}
|
||||
imageView.setImageResource(imageIds.get(position));
|
||||
imageView.setMinimumHeight(height);
|
||||
imageView.setMinimumWidth(width);
|
||||
imageView.setPadding(padding, padding, padding, padding);
|
||||
return imageView;
|
||||
}
|
||||
}
|
||||
+104
@@ -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.fragments;
|
||||
|
||||
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.support.v4.app.DialogFragment;
|
||||
|
||||
/**
|
||||
* A DialogFragment to confirm delete.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ConfirmDeleteDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ConfirmDeleteCaller {
|
||||
|
||||
/**
|
||||
* Called when confirm delete is done.
|
||||
*
|
||||
* @param trackIds list of track ids to delete. To delete all, set to size 1
|
||||
* with trackIds[0] == -1L
|
||||
*/
|
||||
public void onConfirmDeleteDone(long[] trackIds);
|
||||
}
|
||||
|
||||
public static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
||||
private static final String KEY_TRACK_IDS = "trackIds";
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param trackIds list of track ids to delete. To delete all, set to size 1
|
||||
* with trackIds[0] == -1L
|
||||
*/
|
||||
public static ConfirmDeleteDialogFragment newInstance(long[] trackIds) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
|
||||
|
||||
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
|
||||
deleteTrackDialogFragment.setArguments(bundle);
|
||||
return deleteTrackDialogFragment;
|
||||
}
|
||||
|
||||
private ConfirmDeleteCaller caller;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ConfirmDeleteCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + ConfirmDeleteCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
||||
int titleId;
|
||||
int messageId;
|
||||
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
||||
titleId = R.string.track_delete_all_confirm_title;
|
||||
messageId = R.string.track_delete_all_confirm_message;
|
||||
} else {
|
||||
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title
|
||||
: R.string.track_delete_one_confirm_title;
|
||||
messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message
|
||||
: R.string.track_delete_one_confirm_message;
|
||||
}
|
||||
return DialogUtils.createConfirmationDialog(
|
||||
getActivity(), titleId, getString(messageId), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
caller.onConfirmDeleteDone(trackIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+106
@@ -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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A DialogFragment to confirm playing a track.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ConfirmPlayDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ConfirmPlayCaller {
|
||||
|
||||
/**
|
||||
* Called when confirm play is done.
|
||||
*/
|
||||
public void onConfirmPlayDone(long[] trackIds);
|
||||
}
|
||||
|
||||
public static final String CONFIRM_PLAY_DIALOG_TAG = "confirmPlayDialog";
|
||||
|
||||
private static final String KEY_TRACK_IDS = "trackIds";
|
||||
|
||||
private CheckBox checkBox;
|
||||
|
||||
public static ConfirmPlayDialogFragment newInstance(long[] trackIds) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
|
||||
|
||||
ConfirmPlayDialogFragment confirmDialogFragment = new ConfirmPlayDialogFragment();
|
||||
confirmDialogFragment.setArguments(bundle);
|
||||
return confirmDialogFragment;
|
||||
}
|
||||
|
||||
private ConfirmPlayCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ConfirmPlayCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + ConfirmPlayCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.confirm_dialog, null);
|
||||
TextView textView = (TextView) view.findViewById(R.id.confirm_dialog_message);
|
||||
textView.setText(R.string.track_detail_play_confirm_message);
|
||||
checkBox = (CheckBox) view.findViewById(R.id.confirm_dialog_check_box);
|
||||
|
||||
return DialogUtils.createConfirmationDialog(fragmentActivity,
|
||||
R.string.track_detail_play_confirm_title, view, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
||||
PreferencesUtils.setBoolean(
|
||||
fragmentActivity, R.string.confirm_play_earth_key, !checkBox.isChecked());
|
||||
caller.onConfirmPlayDone(trackIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
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.DialogInterface.OnClickListener;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* A DialogFrament to confirm sync to Google Drive.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ConfirmSyncDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ConfirmSyncCaller {
|
||||
|
||||
/**
|
||||
* Called when confirm sync is done.
|
||||
*/
|
||||
public void onConfirmSyncDone(boolean enable);
|
||||
}
|
||||
|
||||
public static final String CONFIRM_SYNC_DIALOG_TAG = "confirmSyncDialog";
|
||||
|
||||
private ConfirmSyncCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ConfirmSyncCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + ConfirmSyncCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = getActivity();
|
||||
if (PreferencesUtils.getBoolean(
|
||||
fragmentActivity, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT)) {
|
||||
dismiss();
|
||||
caller.onConfirmSyncDone(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
String googleAccount = PreferencesUtils.getString(
|
||||
fragmentActivity, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
|
||||
String message = getString(
|
||||
R.string.sync_drive_confirm_message, googleAccount, getString(R.string.my_tracks_app_name));
|
||||
return new AlertDialog.Builder(fragmentActivity).setMessage(message)
|
||||
.setNegativeButton(R.string.generic_no, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
caller.onConfirmSyncDone(false);
|
||||
}
|
||||
}).setPositiveButton(R.string.generic_yes, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
caller.onConfirmSyncDone(true);
|
||||
}
|
||||
}).setTitle(R.string.sync_drive_title).create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface arg0) {
|
||||
caller.onConfirmSyncDone(false);
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
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.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* A DialogFragment to delete marker.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class DeleteMarkerDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface DeleteMarkerCaller {
|
||||
|
||||
/**
|
||||
* Called when delete marker is done.
|
||||
*/
|
||||
public void onDeleteMarkerDone();
|
||||
}
|
||||
|
||||
public static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
||||
private static final String KEY_MARKER_IDS = "markerIds";
|
||||
|
||||
public static DeleteMarkerDialogFragment newInstance(long[] markerIds) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLongArray(KEY_MARKER_IDS, markerIds);
|
||||
|
||||
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment();
|
||||
deleteMarkerDialogFragment.setArguments(bundle);
|
||||
return deleteMarkerDialogFragment;
|
||||
}
|
||||
|
||||
private DeleteMarkerCaller caller;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (DeleteMarkerCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + DeleteMarkerCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final FragmentActivity fragmentActivity = getActivity();
|
||||
final long[] markerIds = getArguments().getLongArray(KEY_MARKER_IDS);
|
||||
int titleId = markerIds.length > 1 ? R.string.generic_delete_selected_confirm_title
|
||||
: R.string.marker_delete_one_confirm_title;
|
||||
int messageId = markerIds.length > 1 ? R.string.marker_delete_multiple_confirm_message
|
||||
: R.string.marker_delete_one_confirm_message;
|
||||
return DialogUtils.createConfirmationDialog(
|
||||
fragmentActivity, titleId, getString(messageId), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (long markerId : markerIds) {
|
||||
MyTracksProviderUtils.Factory.get(fragmentActivity)
|
||||
.deleteWaypoint(markerId, new DescriptionGeneratorImpl(fragmentActivity));
|
||||
}
|
||||
caller.onDeleteMarkerDone();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
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.DialogInterface.OnClickListener;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* A DialogFrament to enable sync to Google Drive.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class EnableSyncDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface EnableSyncCaller {
|
||||
|
||||
/**
|
||||
* Called when enable sync is done.
|
||||
*/
|
||||
public void onEnableSyncDone(boolean enable);
|
||||
}
|
||||
|
||||
public static final String ENABLE_SYNC_DIALOG_TAG = "enableSyncDialog";
|
||||
|
||||
private EnableSyncCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (EnableSyncCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + EnableSyncCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = getActivity();
|
||||
if (PreferencesUtils.getBoolean(
|
||||
fragmentActivity, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT)) {
|
||||
dismiss();
|
||||
caller.onEnableSyncDone(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return new AlertDialog.Builder(fragmentActivity).setMessage(R.string.sync_drive_enable_message)
|
||||
.setNegativeButton(R.string.generic_no, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
caller.onEnableSyncDone(false);
|
||||
}
|
||||
}).setPositiveButton(R.string.generic_yes, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
caller.onEnableSyncDone(true);
|
||||
}
|
||||
}).setTitle(R.string.sync_drive_title).create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface arg0) {
|
||||
caller.onEnableSyncDone(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.EulaUtils;
|
||||
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.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A DialogFragment to show EULA.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class EulaDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface EulaCaller {
|
||||
|
||||
/**
|
||||
* Called when eula is done.
|
||||
*/
|
||||
public void onEulaDone();
|
||||
}
|
||||
|
||||
public static final String EULA_DIALOG_TAG = "eulaDialog";
|
||||
private static final String KEY_HAS_ACCEPTED = "hasAccepted";
|
||||
private static final String GOOGLE_URL = "m.google.com";
|
||||
private static final String KOREAN = "ko";
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link EulaDialogFragment}.
|
||||
*
|
||||
* @param hasAccepted true if the user has accepted the eula.
|
||||
*/
|
||||
public static EulaDialogFragment newInstance(boolean hasAccepted) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(KEY_HAS_ACCEPTED, hasAccepted);
|
||||
|
||||
EulaDialogFragment eulaDialogFragment = new EulaDialogFragment();
|
||||
eulaDialogFragment.setArguments(bundle);
|
||||
return eulaDialogFragment;
|
||||
}
|
||||
|
||||
private EulaCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (EulaCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + EulaCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
fragmentActivity = getActivity();
|
||||
|
||||
boolean hasAccepted = getArguments().getBoolean(KEY_HAS_ACCEPTED);
|
||||
|
||||
SpannableString message = new SpannableString(getEulaText());
|
||||
Linkify.addLinks(message, Linkify.WEB_URLS);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(fragmentActivity).setMessage(message)
|
||||
.setTitle(R.string.eula_title);
|
||||
|
||||
if (hasAccepted) {
|
||||
builder.setPositiveButton(R.string.generic_ok, null);
|
||||
} else {
|
||||
builder.setNegativeButton(R.string.eula_decline, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
caller.onEulaDone();
|
||||
}
|
||||
}).setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}).setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
EulaUtils.setAcceptEula(fragmentActivity);
|
||||
caller.onEulaDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
TextView textView = (TextView) getDialog().findViewById(android.R.id.message);
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
textView.setTextAppearance(fragmentActivity, R.style.TextSmall);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface arg0) {
|
||||
caller.onEulaDone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the EULA text.
|
||||
*/
|
||||
private String getEulaText() {
|
||||
String tos = getString(R.string.eula_date) + "\n\n" + getString(R.string.eula_body, GOOGLE_URL)
|
||||
+ "\n\n" + getString(R.string.eula_footer, GOOGLE_URL) + "\n\n"
|
||||
+ getString(R.string.eula_copyright_year);
|
||||
boolean isKorean = getResources().getConfiguration().locale.getLanguage().equals(KOREAN);
|
||||
if (isKorean) {
|
||||
tos += "\n\n" + getString(R.string.eula_korean);
|
||||
}
|
||||
return tos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
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.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.View;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.RadioGroup.OnCheckedChangeListener;
|
||||
|
||||
/**
|
||||
* A DialogFragment to export a track.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ExportDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Export types.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public enum ExportType {
|
||||
GOOGLE_MAPS, GOOGLE_FUSION_TABLES, GOOGLE_SPREADSHEET, EXTERNAL_STORAGE
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ExportCaller {
|
||||
|
||||
/**
|
||||
* Called when export is done.
|
||||
*/
|
||||
public void onExportDone(ExportType exportType, TrackFileFormat trackFileFormat);
|
||||
}
|
||||
|
||||
public static final String EXPORT_DIALOG_TAG = "export";
|
||||
|
||||
private ExportCaller caller;
|
||||
private RadioGroup exportTypeOptions;
|
||||
private RadioGroup exportGoogleMapsOptions;
|
||||
private RadioGroup exportGoogleFusionTablesOptions;
|
||||
private RadioGroup exportExternalStorageOptions;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ExportCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + ExportCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
FragmentActivity fragmentActivity = getActivity();
|
||||
|
||||
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.export, null);
|
||||
|
||||
exportTypeOptions = (RadioGroup) view.findViewById(R.id.export_type_options);
|
||||
exportGoogleMapsOptions = (RadioGroup) view.findViewById(R.id.export_google_maps_options);
|
||||
exportGoogleFusionTablesOptions = (RadioGroup) view.findViewById(
|
||||
R.id.export_google_fusion_tables_options);
|
||||
exportExternalStorageOptions = (RadioGroup) view.findViewById(
|
||||
R.id.export_external_storage_options);
|
||||
|
||||
exportTypeOptions.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
||||
exportGoogleMapsOptions.setVisibility(
|
||||
checkedId == R.id.export_google_maps ? View.VISIBLE : View.GONE);
|
||||
exportGoogleFusionTablesOptions.setVisibility(
|
||||
checkedId == R.id.export_google_fusion_tables ? View.VISIBLE : View.GONE);
|
||||
exportExternalStorageOptions.setVisibility(
|
||||
checkedId == R.id.export_external_storage ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
});
|
||||
ExportType exportType = ExportType.valueOf(PreferencesUtils.getString(
|
||||
fragmentActivity, R.string.export_type_key, PreferencesUtils.EXPORT_TYPE_DEFAULT));
|
||||
exportTypeOptions.check(getExportTypeId(exportType));
|
||||
|
||||
boolean exportGoogleMapsPublic = PreferencesUtils.getBoolean(fragmentActivity,
|
||||
R.string.export_google_maps_public_key, PreferencesUtils.EXPORT_GOOGLE_MAPS_PUBLIC_DEFAULT);
|
||||
exportGoogleMapsOptions.check(
|
||||
exportGoogleMapsPublic ? R.id.export_google_maps_public : R.id.export_google_maps_unlisted);
|
||||
|
||||
boolean exportGoogleFusionTablesPublic = PreferencesUtils.getBoolean(fragmentActivity,
|
||||
R.string.export_google_fusion_tables_public_key,
|
||||
PreferencesUtils.EXPORT_GOOGLE_FUSION_TABLES_PUBLIC_DEFAULT);
|
||||
exportGoogleFusionTablesOptions.check(
|
||||
exportGoogleFusionTablesPublic ? R.id.export_google_fusion_tables_public
|
||||
: R.id.export_google_fusion_tables_private);
|
||||
|
||||
setExternalStorageOption(
|
||||
(RadioButton) view.findViewById(R.id.export_external_storage_kml), TrackFileFormat.KML);
|
||||
setExternalStorageOption(
|
||||
(RadioButton) view.findViewById(R.id.export_external_storage_gpx), TrackFileFormat.GPX);
|
||||
setExternalStorageOption(
|
||||
(RadioButton) view.findViewById(R.id.export_external_storage_csv), TrackFileFormat.CSV);
|
||||
setExternalStorageOption(
|
||||
(RadioButton) view.findViewById(R.id.export_external_storage_tcx), TrackFileFormat.TCX);
|
||||
TrackFileFormat trackFileFormat = TrackFileFormat.valueOf(PreferencesUtils.getString(
|
||||
fragmentActivity, R.string.export_external_storage_format_key,
|
||||
PreferencesUtils.EXPORT_EXTERNAL_STORAGE_FORMAT_DEFAULT));
|
||||
exportExternalStorageOptions.check(getExternalStorageFormatId(trackFileFormat));
|
||||
|
||||
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(
|
||||
R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.menu_export, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
FragmentActivity context = getActivity();
|
||||
int id = exportTypeOptions.getCheckedRadioButtonId();
|
||||
ExportType type = getExportType(id);
|
||||
TrackFileFormat format = null;
|
||||
|
||||
PreferencesUtils.setString(context, R.string.export_type_key, type.name());
|
||||
if (id == R.id.export_google_maps) {
|
||||
PreferencesUtils.setBoolean(context, R.string.export_google_maps_public_key,
|
||||
exportGoogleMapsOptions.getCheckedRadioButtonId()
|
||||
== R.id.export_google_maps_public);
|
||||
} else if (id == R.id.export_google_fusion_tables) {
|
||||
PreferencesUtils.setBoolean(context, R.string.export_google_fusion_tables_public_key,
|
||||
exportGoogleFusionTablesOptions.getCheckedRadioButtonId()
|
||||
== R.id.export_google_fusion_tables_public);
|
||||
} else if (id == R.id.export_external_storage) {
|
||||
format = getTrackFileFormat(exportExternalStorageOptions.getCheckedRadioButtonId());
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.export_external_storage_format_key, format.name());
|
||||
}
|
||||
caller.onExportDone(type, format);
|
||||
}
|
||||
}).setTitle(R.string.export_title).setView(view).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an external storage option.
|
||||
*
|
||||
* @param radioButton the radio button
|
||||
* @param trackFileFormat the track file format
|
||||
*/
|
||||
private void setExternalStorageOption(RadioButton radioButton, TrackFileFormat trackFileFormat) {
|
||||
radioButton.setText(getString(R.string.export_external_storage_option, trackFileFormat.name(),
|
||||
FileUtils.getDirectoryDisplayName(trackFileFormat.getExtension())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the external storage format id from a track file format.
|
||||
*
|
||||
* @param trackFileFormat the track file format
|
||||
*/
|
||||
private int getExternalStorageFormatId(TrackFileFormat trackFileFormat) {
|
||||
switch (trackFileFormat) {
|
||||
case KML:
|
||||
return R.id.export_external_storage_kml;
|
||||
case GPX:
|
||||
return R.id.export_external_storage_gpx;
|
||||
case CSV:
|
||||
return R.id.export_external_storage_csv;
|
||||
default:
|
||||
return R.id.export_external_storage_tcx;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track file format from an external storage format id.
|
||||
*
|
||||
* @param externalStorageFormatId the external storage format id
|
||||
*/
|
||||
private TrackFileFormat getTrackFileFormat(int externalStorageFormatId) {
|
||||
switch (externalStorageFormatId) {
|
||||
case R.id.export_external_storage_kml:
|
||||
return TrackFileFormat.KML;
|
||||
case R.id.export_external_storage_gpx:
|
||||
return TrackFileFormat.GPX;
|
||||
case R.id.export_external_storage_csv:
|
||||
return TrackFileFormat.CSV;
|
||||
default:
|
||||
return TrackFileFormat.TCX;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the export type id from an export type.
|
||||
*
|
||||
* @param exportType the export type
|
||||
*/
|
||||
private int getExportTypeId(ExportType exportType) {
|
||||
switch (exportType) {
|
||||
case GOOGLE_MAPS:
|
||||
return R.id.export_google_maps;
|
||||
case GOOGLE_FUSION_TABLES:
|
||||
return R.id.export_google_fusion_tables;
|
||||
case GOOGLE_SPREADSHEET:
|
||||
return R.id.export_google_spreadsheets;
|
||||
default:
|
||||
return R.id.export_external_storage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the export type from an export type id.
|
||||
*
|
||||
* @param exportTypeId the export type id
|
||||
*/
|
||||
private ExportType getExportType(int exportTypeId) {
|
||||
switch (exportTypeId) {
|
||||
case R.id.export_google_maps:
|
||||
return ExportType.GOOGLE_MAPS;
|
||||
case R.id.export_google_fusion_tables:
|
||||
return ExportType.GOOGLE_FUSION_TABLES;
|
||||
case R.id.export_google_spreadsheets:
|
||||
return ExportType.GOOGLE_SPREADSHEET;
|
||||
default:
|
||||
return ExportType.EXTERNAL_STORAGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
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.DialogInterface.OnClickListener;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
|
||||
/**
|
||||
* A DialogFragment to select a file type, gpx, kml, etc.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class FileTypeDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface FileTypeCaller {
|
||||
|
||||
/**
|
||||
* Called when file type selection is done.
|
||||
*/
|
||||
public void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat);
|
||||
}
|
||||
|
||||
public static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
||||
|
||||
private static final String KEY_MENU_ID = "menuId";
|
||||
private static final String KEY_TITLE_ID = "titleId";
|
||||
private static final String KEY_OPTION_ID = "optionId";
|
||||
private static final String KEY_SIZE = "size";
|
||||
|
||||
public static FileTypeDialogFragment newInstance(
|
||||
int menuId, int titleId, int optionId, int size) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(KEY_MENU_ID, menuId);
|
||||
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||
bundle.putInt(KEY_OPTION_ID, optionId);
|
||||
bundle.putInt(KEY_SIZE, size);
|
||||
|
||||
FileTypeDialogFragment fileTypeDialogFragment = new FileTypeDialogFragment();
|
||||
fileTypeDialogFragment.setArguments(bundle);
|
||||
return fileTypeDialogFragment;
|
||||
}
|
||||
|
||||
private FileTypeCaller caller;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (FileTypeCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + FileTypeCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
int size = getArguments().getInt(KEY_SIZE);
|
||||
int optionId = getArguments().getInt(KEY_OPTION_ID);
|
||||
final int titleId = getArguments().getInt(KEY_TITLE_ID);
|
||||
final int menuId = getArguments().getInt(KEY_MENU_ID);
|
||||
String[] choices = new String[size];
|
||||
TrackFileFormat[] trackFileFormats = TrackFileFormat.values();
|
||||
for (int i = 0; i < choices.length; i++) {
|
||||
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
||||
choices[i] = getString(optionId, trackFileFormat.name(),
|
||||
FileUtils.getDirectoryDisplayName(trackFileFormat.getExtension()));
|
||||
}
|
||||
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
caller.onFileTypeDone(menuId, TrackFileFormat.values()[position]);
|
||||
}
|
||||
}).setSingleChoiceItems(choices, 0, null).setTitle(titleId).create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* A DialogFragment to configure frequency.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class FrequencyDialogFragment extends DialogFragment {
|
||||
|
||||
public static final String FREQUENCY_DIALOG_TAG = "frequencyDialog";
|
||||
|
||||
private static final String KEY_PREFERENCE_ID = "preferenceId";
|
||||
private static final String KEY_DEFAULT_VALUE = "defaultValue";
|
||||
private static final String KEY_TITLE_ID = "titleId";
|
||||
|
||||
public static FrequencyDialogFragment newInstance(
|
||||
int preferenceId, int defaultValue, int titleId) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(KEY_PREFERENCE_ID, preferenceId);
|
||||
bundle.putInt(KEY_DEFAULT_VALUE, defaultValue);
|
||||
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||
|
||||
FrequencyDialogFragment frequencyDialogFragment = new FrequencyDialogFragment();
|
||||
frequencyDialogFragment.setArguments(bundle);
|
||||
return frequencyDialogFragment;
|
||||
}
|
||||
|
||||
private FragmentActivity fragmentActivity;
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
fragmentActivity = getActivity();
|
||||
|
||||
final int preferenceId = getArguments().getInt(KEY_PREFERENCE_ID);
|
||||
int defaultValue = getArguments().getInt(KEY_DEFAULT_VALUE);
|
||||
int titleId = getArguments().getInt(KEY_TITLE_ID);
|
||||
int frequencyValue = PreferencesUtils.getInt(fragmentActivity, preferenceId, defaultValue);
|
||||
|
||||
return new AlertDialog.Builder(fragmentActivity)
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
int listIndex = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
PreferencesUtils.setInt(fragmentActivity, preferenceId, getFrequencyValue(listIndex));
|
||||
}
|
||||
})
|
||||
.setSingleChoiceItems(getFrequencyDisplayOptions(), getListIndex(frequencyValue), null)
|
||||
.setTitle(titleId)
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the frequency display options.
|
||||
*/
|
||||
private String[] getFrequencyDisplayOptions() {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(fragmentActivity);
|
||||
return StringUtils.getFrequencyOptions(fragmentActivity, metricUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list index for a frequency value. Returns 0 if the value is not on
|
||||
* the list.
|
||||
*/
|
||||
private int getListIndex(int frequencyValue) {
|
||||
String[] values = getResources().getStringArray(R.array.frequency_values);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (frequencyValue == Integer.parseInt(values[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the frequency value from a list index.
|
||||
*
|
||||
* @param listIndex the list index
|
||||
*/
|
||||
private int getFrequencyValue(int listIndex) {
|
||||
String[] values = getResources().getStringArray(R.array.frequency_values);
|
||||
return Integer.parseInt(values[listIndex]);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* A DialogFragment to install Google Earth.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class InstallEarthDialogFragment extends DialogFragment {
|
||||
|
||||
public static final String INSTALL_EARTH_DIALOG_TAG = "installEarthDialog";
|
||||
|
||||
private FragmentActivity fragmentActivity;
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
fragmentActivity = getActivity();
|
||||
return new AlertDialog.Builder(fragmentActivity).setMessage(
|
||||
R.string.track_detail_install_earth_message).setNegativeButton(R.string.generic_no, null)
|
||||
.setPositiveButton(R.string.generic_yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent().setData(
|
||||
Uri.parse(GoogleEarthUtils.GOOGLE_EARTH_MARKET_URL));
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(
|
||||
fragmentActivity, R.string.track_detail_install_earth_error, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}).setTitle(R.string.track_detail_install_earth_title).create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.MarkerEditActivity;
|
||||
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.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.apps.mytracks.util.PhotoUtils;
|
||||
import com.google.android.apps.mytracks.util.StatsUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* A fragment to show marker details.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class MarkerDetailFragment extends Fragment {
|
||||
|
||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||
private static final String KEY_MARKER_ID = "markerId";
|
||||
|
||||
public static MarkerDetailFragment newInstance(long markerId) {
|
||||
MarkerDetailFragment fragment = new MarkerDetailFragment();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong(KEY_MARKER_ID, markerId);
|
||||
fragment.setArguments(bundle);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private MyTracksProviderUtils myTracksProviderUtils;
|
||||
private Waypoint waypoint;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
||||
if (markerId == -1L) {
|
||||
Log.d(TAG, "invalid marker id");
|
||||
getFragmentManager().popBackStack();
|
||||
return;
|
||||
}
|
||||
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
setHasOptionsMenu(true);
|
||||
return inflater.inflate(R.layout.marker_detail_fragment, container, false);
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Need to update the waypoint in case returning after an edit
|
||||
updateWaypoint(true);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.marker_detail, menu);
|
||||
|
||||
updateWaypoint(false);
|
||||
|
||||
Track track = myTracksProviderUtils.getTrack(waypoint.getTrackId());
|
||||
boolean isSharedWithMe = track != null ? track.isSharedWithMe() : true;
|
||||
|
||||
menu.findItem(R.id.marker_detail_edit).setVisible(!isSharedWithMe);
|
||||
menu.findItem(R.id.marker_detail_delete).setVisible(!isSharedWithMe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
long markerId = getArguments().getLong(KEY_MARKER_ID);
|
||||
FragmentActivity fragmentActivity = getActivity();
|
||||
Intent intent;
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.marker_detail_show_on_map:
|
||||
intent = IntentUtils.newIntent(fragmentActivity, TrackDetailActivity.class)
|
||||
.putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.marker_detail_edit:
|
||||
intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class)
|
||||
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.marker_detail_delete:
|
||||
DeleteMarkerDialogFragment.newInstance(new long[] { markerId })
|
||||
.show(getChildFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the waypoint.
|
||||
*
|
||||
* @param refresh true to always update
|
||||
*/
|
||||
private void updateWaypoint(boolean refresh) {
|
||||
if (refresh || waypoint == null) {
|
||||
waypoint = myTracksProviderUtils.getWaypoint(getArguments().getLong(KEY_MARKER_ID));
|
||||
if (waypoint == null) {
|
||||
Log.d(TAG, "waypoint is null");
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI.
|
||||
*/
|
||||
private void update() {
|
||||
View waypointSection = getView().findViewById(R.id.marker_detail_waypoint_section);
|
||||
View statisticsSection = getView().findViewById(R.id.marker_detail_statistics_section);
|
||||
|
||||
if (waypoint.getType() == WaypointType.WAYPOINT) {
|
||||
waypointSection.setVisibility(View.VISIBLE);
|
||||
statisticsSection.setVisibility(View.GONE);
|
||||
|
||||
ImageView imageView = (ImageView) getView().findViewById(R.id.marker_detail_waypoint_photo);
|
||||
String photoUrl = waypoint.getPhotoUrl();
|
||||
if (photoUrl == null || photoUrl.equals("")) {
|
||||
imageView.setVisibility(View.GONE);
|
||||
} else {
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
Display defaultDisplay = getActivity().getWindowManager().getDefaultDisplay();
|
||||
@SuppressWarnings("deprecation")
|
||||
int displayWidth = defaultDisplay.getWidth();
|
||||
@SuppressWarnings("deprecation")
|
||||
int displayHeight = defaultDisplay.getHeight();
|
||||
PhotoUtils.setImageVew(imageView, Uri.parse(photoUrl), displayWidth, displayHeight);
|
||||
}
|
||||
|
||||
TextView name = (TextView) getView().findViewById(R.id.marker_detail_waypoint_name);
|
||||
name.setText(getString(R.string.generic_name_line, waypoint.getName()));
|
||||
|
||||
TextView markerType = (TextView) getView()
|
||||
.findViewById(R.id.marker_detail_waypoint_marker_type);
|
||||
markerType.setText(
|
||||
getString(R.string.marker_detail_waypoint_marker_type, waypoint.getCategory()));
|
||||
|
||||
TextView description = (TextView) getView()
|
||||
.findViewById(R.id.marker_detail_waypoint_description);
|
||||
description.setText(getString(R.string.generic_description_line, waypoint.getDescription()));
|
||||
} else {
|
||||
waypointSection.setVisibility(View.GONE);
|
||||
statisticsSection.setVisibility(View.VISIBLE);
|
||||
|
||||
TextView name = (TextView) getView().findViewById(R.id.marker_detail_statistics_name);
|
||||
name.setText(getString(R.string.generic_name_line, waypoint.getName()));
|
||||
|
||||
StatsUtils.setTripStatisticsValues(getActivity(), waypoint.getTripStatistics());
|
||||
StatsUtils.setLocationValues(getActivity(), waypoint.getLocation(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,760 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.MapOverlay;
|
||||
import com.google.android.apps.mytracks.MarkerDetailActivity;
|
||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||
import com.google.android.apps.mytracks.content.TrackDataType;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.services.MyTracksLocationManager;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.GoogleLocationUtils;
|
||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.gms.location.LocationListener;
|
||||
import com.google.android.gms.maps.CameraUpdate;
|
||||
import com.google.android.gms.maps.CameraUpdateFactory;
|
||||
import com.google.android.gms.maps.GoogleMap;
|
||||
import com.google.android.gms.maps.GoogleMap.OnCameraChangeListener;
|
||||
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
|
||||
import com.google.android.gms.maps.LocationSource;
|
||||
import com.google.android.gms.maps.LocationSource.OnLocationChangedListener;
|
||||
import com.google.android.gms.maps.SupportMapFragment;
|
||||
import com.google.android.gms.maps.model.CameraPosition;
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
import com.google.android.gms.maps.model.LatLngBounds;
|
||||
import com.google.android.gms.maps.model.Marker;
|
||||
import com.google.android.gms.maps.model.Polyline;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* A fragment to display map to the user.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class MyTracksMapFragment extends SupportMapFragment implements TrackDataListener {
|
||||
|
||||
public static final String MAP_FRAGMENT_TAG = "mapFragment";
|
||||
|
||||
private static final String CURRENT_LOCATION_KEY = "current_location_key";
|
||||
private static final String
|
||||
KEEP_CURRENT_LOCATION_VISIBLE_KEY = "keep_current_location_visible_key";
|
||||
|
||||
private static final float DEFAULT_ZOOM_LEVEL = 18f;
|
||||
|
||||
// Google's latitude and longitude
|
||||
private static final double DEFAULT_LATITUDE = 37.423;
|
||||
private static final double DEFAULT_LONGITUDE = -122.084;
|
||||
|
||||
private static final int MAP_VIEW_PADDING = 32;
|
||||
|
||||
// States from TrackDetailActivity, set in onResume
|
||||
private long trackId;
|
||||
private long markerId;
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
// Current location
|
||||
private Location currentLocation;
|
||||
private Location lastTrackPoint;
|
||||
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
|
||||
|
||||
/**
|
||||
* True to continue keeping the current location visible on the screen.
|
||||
* <p>
|
||||
* Set to true when <br>
|
||||
* 1. user clicks on the my location button <br>
|
||||
* 2. first location during a recording <br>
|
||||
* Set to false when <br>
|
||||
* 1. showing a marker <br>
|
||||
* 2. user manually zooms/pans
|
||||
*/
|
||||
private boolean keepCurrentLocationVisible;
|
||||
|
||||
private OnLocationChangedListener onLocationChangedListener;
|
||||
|
||||
// Current track
|
||||
private Track currentTrack;
|
||||
|
||||
// Current paths
|
||||
private ArrayList<Polyline> paths = new ArrayList<Polyline>();
|
||||
boolean reloadPaths = true;
|
||||
|
||||
// UI elements
|
||||
private GoogleMap googleMap;
|
||||
private MapOverlay mapOverlay;
|
||||
private View mapView;
|
||||
private ImageButton myLocationImageButton;
|
||||
private TextView messageTextView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setHasOptionsMenu(true);
|
||||
mapOverlay = new MapOverlay(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mapView = super.onCreateView(inflater, container, savedInstanceState);
|
||||
View layout = inflater.inflate(R.layout.map, container, false);
|
||||
RelativeLayout mapContainer = (RelativeLayout) layout.findViewById(R.id.map_container);
|
||||
mapContainer.addView(mapView, 0);
|
||||
|
||||
/*
|
||||
* For Froyo (2.2) and Gingerbread (2.3), need a transparent FrameLayout on
|
||||
* top for view pager to work correctly.
|
||||
*/
|
||||
FrameLayout frameLayout = new FrameLayout(getActivity());
|
||||
frameLayout.setBackgroundColor(getResources().getColor(android.R.color.transparent));
|
||||
mapContainer.addView(frameLayout,
|
||||
new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
|
||||
myLocationImageButton = (ImageButton) layout.findViewById(R.id.map_my_location);
|
||||
myLocationImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final MyTracksLocationManager myTracksLocationManager = new MyTracksLocationManager(
|
||||
getActivity(), Looper.myLooper(), true);
|
||||
if (!myTracksLocationManager.isAllowed()) {
|
||||
String setting = getString(
|
||||
GoogleLocationUtils.isAvailable(getActivity()) ? R.string.gps_google_location_settings
|
||||
: R.string.gps_location_access);
|
||||
Toast.makeText(
|
||||
getActivity(), getString(R.string.my_location_no_gps, setting), Toast.LENGTH_LONG)
|
||||
.show();
|
||||
myTracksLocationManager.close();
|
||||
} else {
|
||||
myTracksLocationManager.requestLastLocation(new LocationListener() {
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
myTracksLocationManager.close();
|
||||
keepCurrentLocationVisible = true;
|
||||
setCurrentLocation(location);
|
||||
updateCurrentLocation(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
messageTextView = (TextView) layout.findViewById(R.id.map_message);
|
||||
return layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
keepCurrentLocationVisible = savedInstanceState.getBoolean(
|
||||
KEEP_CURRENT_LOCATION_VISIBLE_KEY, false);
|
||||
if (keepCurrentLocationVisible) {
|
||||
Location location = (Location) savedInstanceState.getParcelable(CURRENT_LOCATION_KEY);
|
||||
if (location != null) {
|
||||
setCurrentLocation(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, after onCreateView, getMap will not return null and we can
|
||||
* initialize googleMap. However, onActivityCreated can be called multiple
|
||||
* times, e.g., when the user switches tabs. With
|
||||
* GoogleMapOptions.useViewLifecycleInFragment == false, googleMap lifecycle
|
||||
* is tied to the fragment lifecycle and the same googleMap object is
|
||||
* returned in getMap. Thus we only need to initialize googleMap once, when
|
||||
* it is null.
|
||||
*/
|
||||
if (googleMap == null) {
|
||||
googleMap = getMap();
|
||||
googleMap.setMyLocationEnabled(true);
|
||||
|
||||
/*
|
||||
* My Tracks needs to handle the onClick event when the my location button
|
||||
* is clicked. Currently, the API doesn't allow handling onClick event,
|
||||
* thus hiding the default my location button and providing our own.
|
||||
*/
|
||||
googleMap.getUiSettings().setMyLocationButtonEnabled(false);
|
||||
googleMap.setIndoorEnabled(true);
|
||||
googleMap.setOnMarkerClickListener(new OnMarkerClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onMarkerClick(Marker marker) {
|
||||
if (isResumed()) {
|
||||
String title = marker.getTitle();
|
||||
if (title != null && title.length() > 0) {
|
||||
long id = Long.valueOf(title);
|
||||
Context context = getActivity();
|
||||
Intent intent = IntentUtils.newIntent(context, MarkerDetailActivity.class)
|
||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, id);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
googleMap.setLocationSource(new LocationSource() {
|
||||
|
||||
@Override
|
||||
public void activate(OnLocationChangedListener listener) {
|
||||
onLocationChangedListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
onLocationChangedListener = null;
|
||||
}
|
||||
});
|
||||
googleMap.setOnCameraChangeListener(new OnCameraChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onCameraChange(CameraPosition cameraPosition) {
|
||||
if (isResumed() && keepCurrentLocationVisible && currentLocation != null
|
||||
&& !isLocationVisible(currentLocation)) {
|
||||
keepCurrentLocationVisible = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// First obtain the states from TrackDetailActivity
|
||||
trackId = ((TrackDetailActivity) getActivity()).getTrackId();
|
||||
markerId = ((TrackDetailActivity) getActivity()).getMarkerId();
|
||||
resumeTrackDataHub();
|
||||
|
||||
MyTracksLocationManager myTracksLocationManager = new MyTracksLocationManager(
|
||||
getActivity(), Looper.myLooper(), false);
|
||||
boolean isGpsProviderEnabled = myTracksLocationManager.isGpsProviderEnabled();
|
||||
myTracksLocationManager.close();
|
||||
|
||||
if (googleMap != null) {
|
||||
|
||||
// Update map type
|
||||
int mapType = PreferencesUtils.getInt(
|
||||
getActivity(), R.string.map_type_key, PreferencesUtils.MAP_TYPE_DEFAUlT);
|
||||
googleMap.setMapType(mapType);
|
||||
ApiAdapterFactory.getApiAdapter().invalidMenu(getActivity());
|
||||
|
||||
// Disable my location if gps is disabled
|
||||
googleMap.setMyLocationEnabled(isGpsProviderEnabled);
|
||||
}
|
||||
|
||||
// setWarningMessage depends on resumeTrackDataHub being invoked beforehand
|
||||
setWarningMessage(isGpsProviderEnabled);
|
||||
|
||||
currentTrack = MyTracksProviderUtils.Factory.get(getActivity()).getTrack(trackId);
|
||||
mapOverlay.setShowEndMarker(!isSelectedTrackRecording());
|
||||
if (markerId != -1L) {
|
||||
showMarker(markerId);
|
||||
} else {
|
||||
if (keepCurrentLocationVisible && currentLocation != null && isSelectedTrackRecording()) {
|
||||
updateCurrentLocation(true);
|
||||
} else {
|
||||
showTrack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
outState.putBoolean(KEEP_CURRENT_LOCATION_VISIBLE_KEY, keepCurrentLocationVisible);
|
||||
if (currentLocation != null) {
|
||||
/*
|
||||
* currentLocation is a MyTracksLocation object, which cannot be
|
||||
* unmarshalled. Thus creating a Location object before placing it in the
|
||||
* bundle.
|
||||
*/
|
||||
outState.putParcelable(CURRENT_LOCATION_KEY, new Location(currentLocation));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflator) {
|
||||
menuInflator.inflate(R.menu.map, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
if (googleMap != null) {
|
||||
int id;
|
||||
switch (googleMap.getMapType()) {
|
||||
case GoogleMap.MAP_TYPE_NORMAL:
|
||||
id = R.id.menu_map;
|
||||
break;
|
||||
case GoogleMap.MAP_TYPE_SATELLITE:
|
||||
id = R.id.menu_satellite;
|
||||
break;
|
||||
case GoogleMap.MAP_TYPE_HYBRID:
|
||||
id = R.id.menu_satellite_with_streets;
|
||||
break;
|
||||
case GoogleMap.MAP_TYPE_TERRAIN:
|
||||
id = R.id.menu_terrain;
|
||||
break;
|
||||
default:
|
||||
id = R.id.menu_map;
|
||||
}
|
||||
MenuItem menuItem = menu.findItem(id);
|
||||
if (menuItem != null) {
|
||||
menuItem.setChecked(true);
|
||||
}
|
||||
}
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
||||
int type = GoogleMap.MAP_TYPE_NORMAL;
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.menu_map:
|
||||
type = GoogleMap.MAP_TYPE_NORMAL;
|
||||
break;
|
||||
case R.id.menu_satellite:
|
||||
type = GoogleMap.MAP_TYPE_SATELLITE;
|
||||
break;
|
||||
case R.id.menu_satellite_with_streets:
|
||||
type = GoogleMap.MAP_TYPE_HYBRID;
|
||||
break;
|
||||
case R.id.menu_terrain:
|
||||
type = GoogleMap.MAP_TYPE_TERRAIN;
|
||||
break;
|
||||
default:
|
||||
return super.onOptionsItemSelected(menuItem);
|
||||
}
|
||||
if (googleMap != null) {
|
||||
googleMap.setMapType(type);
|
||||
menuItem.setChecked(true);
|
||||
PreferencesUtils.setInt(getActivity(), R.string.map_type_key, type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackUpdated(Track track) {
|
||||
currentTrack = track;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTrackPoints() {
|
||||
lastTrackPoint = null;
|
||||
if (isResumed()) {
|
||||
mapOverlay.clearPoints();
|
||||
reloadPaths = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledInTrackPoint(final Location location) {
|
||||
lastTrackPoint = location;
|
||||
if (isResumed()) {
|
||||
mapOverlay.addLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledOutTrackPoint(Location location) {
|
||||
lastTrackPoint = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit(Location location) {
|
||||
if (isResumed()) {
|
||||
mapOverlay.addSegmentSplit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPointsDone() {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed() && googleMap != null && currentTrack != null) {
|
||||
boolean hasStartMarker = mapOverlay.update(
|
||||
googleMap, paths, currentTrack.getTripStatistics(), reloadPaths);
|
||||
|
||||
/*
|
||||
* If has the start marker, then don't need to reload the paths each
|
||||
* time
|
||||
*/
|
||||
if (hasStartMarker) {
|
||||
reloadPaths = false;
|
||||
}
|
||||
|
||||
if (lastTrackPoint != null && isSelectedTrackRecording()) {
|
||||
boolean firstLocation = setCurrentLocation(lastTrackPoint);
|
||||
if (firstLocation) {
|
||||
keepCurrentLocationVisible = true;
|
||||
}
|
||||
updateCurrentLocation(firstLocation);
|
||||
setWarningMessage(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWaypoints() {
|
||||
if (isResumed()) {
|
||||
mapOverlay.clearWaypoints();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypoint(Waypoint waypoint) {
|
||||
if (isResumed() && waypoint != null && LocationUtils.isValidLocation(waypoint.getLocation())) {
|
||||
mapOverlay.addWaypoint(waypoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypointsDone() {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed() && googleMap != null && currentTrack != null) {
|
||||
mapOverlay.update(googleMap, paths, currentTrack.getTripStatistics(), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMetricUnitsChanged(boolean metric) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReportSpeedChanged(boolean reportSpeed) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRecordingGpsAccuracy(int newValue) {
|
||||
recordingGpsAccuracy = newValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRecordingDistanceIntervalChanged(int minRecordingDistance) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub. Needs to be synchronized because the trackDataHub
|
||||
* can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
||||
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE, TrackDataType.PREFERENCE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the trackDataHub. Needs to be synchronized because the trackDataHub
|
||||
* can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void pauseTrackDataHub() {
|
||||
if (trackDataHub != null) {
|
||||
trackDataHub.unregisterTrackDataListener(this);
|
||||
}
|
||||
trackDataHub = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selected track is recording. Needs to be synchronized
|
||||
* because the trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized boolean isSelectedTrackRecording() {
|
||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current location.
|
||||
*
|
||||
* @param location the location
|
||||
* @return true if this is the first location
|
||||
*/
|
||||
private boolean setCurrentLocation(Location location) {
|
||||
boolean firstLocation = false;
|
||||
if (currentLocation == null && location != null) {
|
||||
firstLocation = true;
|
||||
}
|
||||
currentLocation = location;
|
||||
return firstLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current location.
|
||||
*
|
||||
* @param forceZoom true to force zoom to the current location regardless of
|
||||
* the keepCurrentLocationVisible policy
|
||||
*/
|
||||
private void updateCurrentLocation(final boolean forceZoom) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (!isResumed() || googleMap == null || onLocationChangedListener == null
|
||||
|| currentLocation == null) {
|
||||
return;
|
||||
}
|
||||
onLocationChangedListener.onLocationChanged(currentLocation);
|
||||
if (forceZoom || (keepCurrentLocationVisible && !isLocationVisible(currentLocation))) {
|
||||
LatLng latLng = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
|
||||
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, DEFAULT_ZOOM_LEVEL));
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the current track by moving the camera over the track.
|
||||
*/
|
||||
private void showTrack() {
|
||||
if (googleMap == null || currentTrack == null) {
|
||||
return;
|
||||
}
|
||||
if (currentTrack.getNumberOfPoints() < 2) {
|
||||
googleMap.moveCamera(
|
||||
CameraUpdateFactory.newLatLngZoom(getDefaultLatLng(), googleMap.getMinZoomLevel()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapView.getWidth() == 0 || mapView.getHeight() == 0) {
|
||||
if (mapView.getViewTreeObserver().isAlive()) {
|
||||
mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
ApiAdapterFactory.getApiAdapter()
|
||||
.removeGlobalLayoutListener(mapView.getViewTreeObserver(), this);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
moveCameraOverTrack();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
moveCameraOverTrack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the camera over the current track.
|
||||
*/
|
||||
private void moveCameraOverTrack() {
|
||||
/**
|
||||
* Check all the required variables.
|
||||
*/
|
||||
if (googleMap == null || currentTrack == null || currentTrack.getNumberOfPoints() < 2
|
||||
|| mapView == null || mapView.getWidth() == 0 || mapView.getHeight() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
TripStatistics tripStatistics = currentTrack.getTripStatistics();
|
||||
int latitudeSpanE6 = tripStatistics.getTop() - tripStatistics.getBottom();
|
||||
int longitudeSpanE6 = tripStatistics.getRight() - tripStatistics.getLeft();
|
||||
if (latitudeSpanE6 > 0 && latitudeSpanE6 < 180E6 && longitudeSpanE6 > 0
|
||||
&& longitudeSpanE6 < 360E6) {
|
||||
LatLng southWest = new LatLng(
|
||||
tripStatistics.getBottomDegrees(), tripStatistics.getLeftDegrees());
|
||||
LatLng northEast = new LatLng(
|
||||
tripStatistics.getTopDegrees(), tripStatistics.getRightDegrees());
|
||||
LatLngBounds bounds = LatLngBounds.builder().include(southWest).include(northEast).build();
|
||||
|
||||
/**
|
||||
* Note cannot call CameraUpdateFactory.newLatLngBounds(LatLngBounds
|
||||
* bounds, int padding) if the map view has not undergone layout. Thus
|
||||
* calling CameraUpdateFactory.newLatLngBounds(LatLngBounds bounds, int
|
||||
* width, int height, int padding) after making sure that mapView is valid
|
||||
* in the above code.
|
||||
*/
|
||||
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(
|
||||
bounds, mapView.getWidth(), mapView.getHeight(), MAP_VIEW_PADDING);
|
||||
googleMap.moveCamera(cameraUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a marker by moving the camera over the marker.
|
||||
*
|
||||
* @param id the marker id
|
||||
*/
|
||||
private void showMarker(final long id) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isResumed() || googleMap == null) {
|
||||
return;
|
||||
}
|
||||
MyTracksProviderUtils MyTracksProviderUtils = Factory.get(getActivity());
|
||||
Waypoint waypoint = MyTracksProviderUtils.getWaypoint(id);
|
||||
if (waypoint != null && waypoint.getLocation() != null) {
|
||||
Location location = waypoint.getLocation();
|
||||
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
|
||||
keepCurrentLocationVisible = false;
|
||||
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, DEFAULT_ZOOM_LEVEL);
|
||||
googleMap.moveCamera(cameraUpdate);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default LatLng.
|
||||
*/
|
||||
private LatLng getDefaultLatLng() {
|
||||
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(getActivity());
|
||||
Location location = myTracksProviderUtils.getLastValidTrackPoint();
|
||||
if (location != null) {
|
||||
return new LatLng(location.getLatitude(), location.getLongitude());
|
||||
}
|
||||
return new LatLng(DEFAULT_LATITUDE, DEFAULT_LONGITUDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the location is visible. Needs to run on the UI thread.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
private boolean isLocationVisible(Location location) {
|
||||
if (location == null || googleMap == null) {
|
||||
return false;
|
||||
}
|
||||
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
|
||||
return googleMap.getProjection().getVisibleRegion().latLngBounds.contains(latLng);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the warning message.
|
||||
*
|
||||
* @param isGpsProviderEnabled true if gps provider is enabled
|
||||
*/
|
||||
private void setWarningMessage(boolean isGpsProviderEnabled) {
|
||||
String message;
|
||||
if (!isSelectedTrackRecording()) {
|
||||
message = null;
|
||||
} else {
|
||||
if (isGpsProviderEnabled) {
|
||||
boolean hasFix;
|
||||
boolean hasGoodFix;
|
||||
if (currentLocation == null) {
|
||||
hasFix = false;
|
||||
hasGoodFix = false;
|
||||
} else {
|
||||
hasFix = !LocationUtils.isLocationOld(currentLocation);
|
||||
hasGoodFix = currentLocation.hasAccuracy()
|
||||
&& currentLocation.getAccuracy() < recordingGpsAccuracy;
|
||||
}
|
||||
if (!hasFix) {
|
||||
message = getString(R.string.gps_wait_for_signal);
|
||||
} else if (!hasGoodFix) {
|
||||
message = getString(R.string.gps_wait_for_better_signal);
|
||||
} else {
|
||||
message = null;
|
||||
}
|
||||
} else {
|
||||
String setting = getString(
|
||||
GoogleLocationUtils.isAvailable(getActivity()) ? R.string.gps_google_location_settings
|
||||
: R.string.gps_location_access);
|
||||
message = getString(R.string.gps_disabled, setting);
|
||||
}
|
||||
}
|
||||
|
||||
if (message == null) {
|
||||
messageTextView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
messageTextView.setText(message);
|
||||
messageTextView.setVisibility(View.VISIBLE);
|
||||
if (isGpsProviderEnabled) {
|
||||
messageTextView.setOnClickListener(null);
|
||||
} else {
|
||||
Toast.makeText(getActivity(), R.string.gps_not_found, Toast.LENGTH_LONG).show();
|
||||
|
||||
// Click to show the location source settings
|
||||
messageTextView.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = GoogleLocationUtils.isAvailable(getActivity()) ? new Intent(
|
||||
GoogleLocationUtils.ACTION_GOOGLE_LOCATION_SETTINGS)
|
||||
: new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2013 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
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.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.widget.SimpleCursorAdapter;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.FilterQueryProvider;
|
||||
import android.widget.MultiAutoCompleteTextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* A DialogFragment to share a track.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ShareTrackDialogFragment extends DialogFragment {
|
||||
|
||||
/**
|
||||
* Interface for caller of this dialog fragment.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public interface ShareTrackCaller {
|
||||
|
||||
/**
|
||||
* Called when share track is done.
|
||||
*
|
||||
* @param emails the added emails
|
||||
* @param makePublic true to make the track public
|
||||
*/
|
||||
public void onShareTrackDone(String emails, boolean makePublic);
|
||||
}
|
||||
|
||||
public static final String SHARE_TRACK_DIALOG_TAG = "shareTrackDialog";
|
||||
|
||||
private static final String KEY_TRACK_ID = "trackId";
|
||||
|
||||
public static ShareTrackDialogFragment newInstance(long trackId) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong(KEY_TRACK_ID, trackId);
|
||||
|
||||
ShareTrackDialogFragment shareleTrackDialogFragment = new ShareTrackDialogFragment();
|
||||
shareleTrackDialogFragment.setArguments(bundle);
|
||||
return shareleTrackDialogFragment;
|
||||
}
|
||||
|
||||
private ShareTrackCaller caller;
|
||||
private FragmentActivity fragmentActivity;
|
||||
private MultiAutoCompleteTextView multiAutoCompleteTextView;
|
||||
private CheckBox publicCheckBox;
|
||||
private CheckBox inviteCheckBox;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
try {
|
||||
caller = (ShareTrackCaller) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(
|
||||
activity.toString() + " must implement " + ShareTrackCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
fragmentActivity = getActivity();
|
||||
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.share_track, null);
|
||||
|
||||
multiAutoCompleteTextView = (MultiAutoCompleteTextView) view.findViewById(
|
||||
R.id.share_track_emails);
|
||||
multiAutoCompleteTextView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
|
||||
|
||||
SimpleCursorAdapter adapter = new SimpleCursorAdapter(fragmentActivity,
|
||||
R.layout.add_emails_item, getCursor(fragmentActivity, null), new String[] {
|
||||
ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA },
|
||||
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
|
||||
adapter.setCursorToStringConverter(new SimpleCursorAdapter.CursorToStringConverter() {
|
||||
@Override
|
||||
public CharSequence convertToString(Cursor cursor) {
|
||||
int index = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
|
||||
return cursor.getString(index).trim();
|
||||
}
|
||||
});
|
||||
adapter.setFilterQueryProvider(new FilterQueryProvider() {
|
||||
@Override
|
||||
public Cursor runQuery(CharSequence constraint) {
|
||||
return getCursor(fragmentActivity, constraint);
|
||||
}
|
||||
});
|
||||
multiAutoCompleteTextView.setAdapter(adapter);
|
||||
|
||||
publicCheckBox = (CheckBox) view.findViewById(R.id.share_track_public);
|
||||
publicCheckBox.setChecked(PreferencesUtils.getBoolean(
|
||||
fragmentActivity, R.string.share_track_public_key,
|
||||
PreferencesUtils.SHARE_TRACK_PUBLIC_DEFAULT));
|
||||
|
||||
inviteCheckBox = (CheckBox) view.findViewById(R.id.share_track_invite);
|
||||
inviteCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
multiAutoCompleteTextView.setVisibility(isChecked ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
});
|
||||
inviteCheckBox.setChecked(PreferencesUtils.getBoolean(
|
||||
fragmentActivity, R.string.share_track_invite_key,
|
||||
PreferencesUtils.SHARE_TRACK_INVITE_DEFAULT));
|
||||
|
||||
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(
|
||||
R.string.generic_cancel, null)
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (!publicCheckBox.isChecked() && !inviteCheckBox.isChecked()) {
|
||||
Toast.makeText(fragmentActivity, R.string.share_track_no_selection, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
String acl = multiAutoCompleteTextView.getText().toString().trim();
|
||||
if (!publicCheckBox.isChecked() && acl.equals("")) {
|
||||
Toast.makeText(fragmentActivity, R.string.share_track_no_emails, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
PreferencesUtils.setBoolean(
|
||||
fragmentActivity, R.string.share_track_public_key, publicCheckBox.isChecked());
|
||||
PreferencesUtils.setBoolean(
|
||||
fragmentActivity, R.string.share_track_invite_key, inviteCheckBox.isChecked());
|
||||
caller.onShareTrackDone(acl, publicCheckBox.isChecked());
|
||||
}
|
||||
}).setTitle(R.string.share_track_title).setView(view).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cursor
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param constraint the constraint
|
||||
*/
|
||||
private Cursor getCursor(Activity activity, CharSequence constraint) {
|
||||
String order = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
|
||||
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'";
|
||||
if (constraint != null) {
|
||||
selection += " AND (" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE '%" + constraint
|
||||
+ "%' OR " + ContactsContract.CommonDataKinds.Email.DATA + " LIKE '%" + constraint
|
||||
+ "%' )";
|
||||
}
|
||||
String[] projection = new String[] { ContactsContract.Contacts._ID,
|
||||
ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA };
|
||||
Uri uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
|
||||
return activity.getContentResolver().query(uri, projection, selection, null, order);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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.fragments;
|
||||
|
||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TrackDataHub;
|
||||
import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||
import com.google.android.apps.mytracks.content.TrackDataType;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.StatsUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* A fragment to display track statistics to the user.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
public static final String STATS_FRAGMENT_TAG = "statsFragment";
|
||||
|
||||
private static final int ONE_SECOND = 1000;
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
private Handler handler;
|
||||
|
||||
private Location lastLocation = null;
|
||||
private TripStatistics lastTripStatistics = null;
|
||||
private int recordingGpsAccuracy = PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT;
|
||||
|
||||
// A runnable to update the total time field.
|
||||
private final Runnable updateTotalTime = new Runnable() {
|
||||
public void run() {
|
||||
if (isResumed() && isSelectedTrackRecording()) {
|
||||
if (!isSelectedTrackPaused() && lastTripStatistics != null) {
|
||||
StatsUtils.setTotalTimeValue(getActivity(), System.currentTimeMillis()
|
||||
- lastTripStatistics.getStopTime() + lastTripStatistics.getTotalTime());
|
||||
}
|
||||
handler.postDelayed(this, ONE_SECOND);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.stats, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
handler = new Handler();
|
||||
updateUi(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
if (isSelectedTrackRecording()) {
|
||||
handler.post(updateTotalTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
handler.removeCallbacks(updateTotalTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackUpdated(final Track track) {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
lastTripStatistics = track != null ? track.getTripStatistics() : null;
|
||||
updateUi(getActivity());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTrackPoints() {
|
||||
lastLocation = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledInTrackPoint(Location location) {
|
||||
lastLocation = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledOutTrackPoint(Location location) {
|
||||
lastLocation = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit(Location location) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTrackPointsDone() {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
if (!isSelectedTrackRecording() || isSelectedTrackPaused()) {
|
||||
lastLocation = null;
|
||||
}
|
||||
|
||||
if (lastLocation != null) {
|
||||
boolean hasFix = !LocationUtils.isLocationOld(lastLocation);
|
||||
boolean hasGoodFix = lastLocation.hasAccuracy()
|
||||
&& lastLocation.getAccuracy() < recordingGpsAccuracy;
|
||||
|
||||
if (!hasFix || !hasGoodFix) {
|
||||
lastLocation = null;
|
||||
}
|
||||
}
|
||||
StatsUtils.setLocationValues(getActivity(), lastLocation, isSelectedTrackRecording());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWaypoints() {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypoint(Waypoint wpt) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewWaypointsDone() {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMetricUnitsChanged(final boolean metric) {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
updateUi(getActivity());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onReportSpeedChanged(final boolean speed) {
|
||||
if (isResumed()) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isResumed()) {
|
||||
updateUi(getActivity());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRecordingGpsAccuracy(int newValue) {
|
||||
recordingGpsAccuracy = newValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRecordingDistanceIntervalChanged(int minRecordingDistance) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
||||
TrackDataType.PREFERENCE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void pauseTrackDataHub() {
|
||||
trackDataHub.unregisterTrackDataListener(this);
|
||||
trackDataHub = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selected track is recording. Needs to be synchronized
|
||||
* because trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized boolean isSelectedTrackRecording() {
|
||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selected track is paused. Needs to be synchronized
|
||||
* because trackDataHub can be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized boolean isSelectedTrackPaused() {
|
||||
return trackDataHub != null && trackDataHub.isSelectedTrackPaused();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI.
|
||||
*/
|
||||
private void updateUi(FragmentActivity activity) {
|
||||
StatsUtils.setTripStatisticsValues(activity, lastTripStatistics);
|
||||
StatsUtils.setLocationValues(activity, lastLocation, isSelectedTrackRecording());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user