Add test cases about TripStatisticsUpdater.addLocation().

This commit is contained in:
youtaol
2013-02-27 18:29:30 +08:00
commit dbfbfb1b1f
910 changed files with 72953 additions and 0 deletions
@@ -0,0 +1,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,142 @@
/*
* 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.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.FilterQueryProvider;
import android.widget.MultiAutoCompleteTextView;
/**
* A DialogFragment to add emails.
*
* @author Jimmy Shih
*/
public class AddEmailsDialogFragment extends DialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface AddEmailsCaller {
/**
* Called when add emails is done.
*
* @param emails the added emails
*/
public void onAddEmailsDone(String emails);
}
public static final String ADD_EMAILS_DIALOG_TAG = "addEmailsDialog";
private static final String KEY_TRACK_ID = "trackId";
public static AddEmailsDialogFragment newInstance(long trackId) {
Bundle bundle = new Bundle();
bundle.putLong(KEY_TRACK_ID, trackId);
AddEmailsDialogFragment addPeopleDialogFragment = new AddEmailsDialogFragment();
addPeopleDialogFragment.setArguments(bundle);
return addPeopleDialogFragment;
}
private AddEmailsCaller caller;
private FragmentActivity fragmentActivity;
private MultiAutoCompleteTextView multiAutoCompleteTextView;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (AddEmailsCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + AddEmailsCaller.class.getSimpleName());
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
fragmentActivity = getActivity();
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.add_emails, null);
multiAutoCompleteTextView = (MultiAutoCompleteTextView) view.findViewById(R.id.add_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);
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) {
String acl = multiAutoCompleteTextView.getText().toString();
caller.onAddEmailsDone(acl);
}
}).setTitle(R.string.share_track_add_emails_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,539 @@
/*
* 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.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 = PreferencesUtils.METRIC_UNITS_DEFAULT;
private boolean reportSpeed = PreferencesUtils.REPORT_SPEED_DEFAULT;
private int minRecordingDistance = PreferencesUtils.MIN_RECORDING_DISTANCE_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 onLocationStateChanged(LocationState state) {
// We don't care.
}
@Override
public void onLocationChanged(Location loc) {
// We don't care.
}
@Override
public void onHeadingChanged(double heading) {
// We don't care.
}
@Override
public void onSelectedTrackChanged(Track track) {
// We don't care.
}
@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();
getActivity().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();
getActivity().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()) {
getActivity().runOnUiThread(updateChart);
}
}
@Override
public boolean onMetricUnitsChanged(boolean metric) {
if (isResumed()) {
if (metricUnits == metric) {
return false;
}
metricUnits = metric;
chartView.setMetricUnits(metricUnits);
getActivity().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);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
chartView.requestLayout();
}
}
});
return true;
}
return false;
}
@Override
public boolean onMinRecordingDistanceChanged(int value) {
if (isResumed()) {
if (minRecordingDistance == value) {
return false;
}
minRecordingDistance = value;
return true;
}
return false;
}
/**
* Checks the chart settings.
*/
private void checkChartSettings() {
boolean needUpdate = false;
if (chartByDistance != PreferencesUtils.getBoolean(getActivity(),
R.string.chart_by_distance_key, PreferencesUtils.CHART_BY_DISTANCE_DEFAULT)) {
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());
}
/**
* 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, minRecordingDistance);
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,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();
}
}
@@ -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_picker_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));
}
}
}
}
@@ -0,0 +1,195 @@
/*
* 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.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.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.RadioButton;
import android.widget.TableRow;
import android.widget.Toast;
/**
* A DialogFragment to choose upload service.
*
* @author Jimmy Shih
*/
public class ChooseUploadServiceDialogFragment extends DialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface ChooseUploadServiceCaller {
/**
* Called when choose upload service is done.
*/
public void onChooseUploadServiceDone(boolean sendDrive, boolean sendMaps,
boolean sendFusionTables, boolean sendSpreadsheets, boolean mapsExistingMap);
}
public static final String CHOOSE_UPLOAD_SERVICE_DIALOG_TAG = "chooseUploadService";
private static final String KEY_HIDE_DRIVE = "hideDrive";
public static ChooseUploadServiceDialogFragment newInstance(boolean hideDrive) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_HIDE_DRIVE, hideDrive);
ChooseUploadServiceDialogFragment chooseUploadServiceDialogFragment = new ChooseUploadServiceDialogFragment();
chooseUploadServiceDialogFragment.setArguments(bundle);
return chooseUploadServiceDialogFragment;
}
private ChooseUploadServiceCaller caller;
private FragmentActivity fragmentActivity;
private boolean hideDrive;
private CheckBox driveCheckBox;
private CheckBox mapsCheckBox;
private CheckBox fusionTablesCheckBox;
private CheckBox spreadsheetsCheckBox;
private TableRow mapsOptionTableRow;
private RadioButton existingMapRadioButton;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (ChooseUploadServiceCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement "
+ ChooseUploadServiceCaller.class.getSimpleName());
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
fragmentActivity = getActivity();
hideDrive = getArguments().getBoolean(KEY_HIDE_DRIVE);
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.choose_upload_service, null);
view.findViewById(R.id.choose_upload_service_drive_options)
.setVisibility(hideDrive ? View.GONE : View.VISIBLE);
driveCheckBox = (CheckBox) view.findViewById(R.id.choose_upload_service_drive);
mapsCheckBox = (CheckBox) view.findViewById(R.id.choose_upload_service_maps);
fusionTablesCheckBox = (CheckBox) view.findViewById(R.id.choose_upload_service_fusion_tables);
spreadsheetsCheckBox = (CheckBox) view.findViewById(R.id.choose_upload_service_spreadsheets);
driveCheckBox.setChecked(PreferencesUtils.getBoolean(
fragmentActivity, R.string.send_to_drive_key, PreferencesUtils.SEND_TO_DRIVE_DEFAULT));
mapsCheckBox.setChecked(PreferencesUtils.getBoolean(
fragmentActivity, R.string.send_to_maps_key, PreferencesUtils.SEND_TO_MAPS_DEFAULT));
fusionTablesCheckBox.setChecked(PreferencesUtils.getBoolean(
fragmentActivity, R.string.send_to_fusion_tables_key,
PreferencesUtils.SEND_TO_FUSION_TABLES_DEFAULT));
spreadsheetsCheckBox.setChecked(PreferencesUtils.getBoolean(
fragmentActivity, R.string.send_to_spreadsheets_key,
PreferencesUtils.SEND_TO_SPREADSHEETS_DEFAULT));
mapsCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton button, boolean checked) {
updateMapsOption();
}
});
mapsOptionTableRow = (TableRow) view.findViewById(R.id.choose_upload_service_maps_options);
RadioButton newMapRadioButton = (RadioButton) view.findViewById(
R.id.choose_upload_service_new_map);
boolean defaultMapPublic = PreferencesUtils.getBoolean(
fragmentActivity, R.string.default_map_public_key, PreferencesUtils.DEFAULT_MAP_PUBLIC_DEFAULT);
newMapRadioButton.setText(defaultMapPublic ? R.string.send_google_new_public_map
: R.string.send_google_new_unlisted_map);
existingMapRadioButton = (RadioButton) view.findViewById(
R.id.choose_upload_service_existing_map);
updateMapsOption();
if (PreferencesUtils.getBoolean(
fragmentActivity, R.string.pick_existing_map_key, PreferencesUtils.PICK_EXISTING_MAP_DEFAULT)) {
existingMapRadioButton.setChecked(true);
} else {
newMapRadioButton.setChecked(true);
}
// Update fusion table
boolean defaultTablePublic = PreferencesUtils.getBoolean(
fragmentActivity, R.string.default_table_public_key, PreferencesUtils.DEFAULT_TABLE_PUBLIC_DEFAULT);
String text = getString(R.string.send_google_fusion_tables) + "\n"
+ getString(defaultTablePublic ? R.string.send_google_fusion_tables_public
: R.string.send_google_fusion_tables_private);
fusionTablesCheckBox.setText(text);
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.send_google_send_now, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PreferencesUtils.setBoolean(
fragmentActivity, R.string.send_to_drive_key, driveCheckBox.isChecked());
PreferencesUtils.setBoolean(
fragmentActivity, R.string.pick_existing_map_key, existingMapRadioButton.isChecked());
PreferencesUtils.setBoolean(
fragmentActivity, R.string.send_to_maps_key, mapsCheckBox.isChecked());
PreferencesUtils.setBoolean(
fragmentActivity, R.string.send_to_fusion_tables_key, fusionTablesCheckBox.isChecked());
PreferencesUtils.setBoolean(
fragmentActivity, R.string.send_to_spreadsheets_key, spreadsheetsCheckBox.isChecked());
if (sendDrive() || mapsCheckBox.isChecked() || fusionTablesCheckBox.isChecked()
|| spreadsheetsCheckBox.isChecked()) {
caller.onChooseUploadServiceDone(sendDrive(), mapsCheckBox.isChecked(),
fusionTablesCheckBox.isChecked(), spreadsheetsCheckBox.isChecked(),
existingMapRadioButton.isChecked());
} else {
Toast.makeText(fragmentActivity, R.string.send_google_no_service_selected, Toast.LENGTH_LONG)
.show();
}
}
}).setTitle(R.string.send_google_title).setView(view).create();
}
/**
* Updates map option.
*/
private void updateMapsOption() {
mapsOptionTableRow.setVisibility(mapsCheckBox.isChecked() ? View.VISIBLE : View.GONE);
}
/**
* True to send to Google Drive.
*/
private boolean sendDrive() {
return !hideDrive && driveCheckBox.isChecked();
}
}
@@ -0,0 +1,121 @@
/*
* 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.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.CheckBox;
import android.widget.TextView;
/**
* A DialogFragment to confirm an action.
*
* @author Jimmy Shih
*/
public class ConfirmDialogFragment extends DialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface ConfirmCaller {
/**
* Called when confirm is done.
*/
public void onConfirmDone(int confirmId, long trackId);
}
public static final String CONFIRM_DIALOG_TAG = "confirmDialog";
private static final String KEY_CONFIRM_ID = "confirmId";
private static final String KEY_DEFAULT_VALUE = "defaultValue";
private static final String KEY_MESSAGE = "message";
private static final String KEY_TRACK_ID = "trackId";
private CheckBox checkBox;
public static ConfirmDialogFragment newInstance(
int confirmId, boolean defaultValue, CharSequence message, long trackId) {
Bundle bundle = new Bundle();
bundle.putInt(KEY_CONFIRM_ID, confirmId);
bundle.putBoolean(KEY_DEFAULT_VALUE, defaultValue);
bundle.putCharSequence(KEY_MESSAGE, message);
bundle.putLong(KEY_TRACK_ID, trackId);
ConfirmDialogFragment confirmDialogFragment = new ConfirmDialogFragment();
confirmDialogFragment.setArguments(bundle);
return confirmDialogFragment;
}
private ConfirmCaller caller;
private FragmentActivity fragmentActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (ConfirmCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + ConfirmCaller.class.getSimpleName());
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int confirmId = getArguments().getInt(KEY_CONFIRM_ID);
boolean defaultValue = getArguments().getBoolean(KEY_DEFAULT_VALUE);
fragmentActivity = getActivity();
if (!PreferencesUtils.getBoolean(fragmentActivity, confirmId, defaultValue)) {
long trackId = getArguments().getLong(KEY_TRACK_ID);
dismiss();
caller.onConfirmDone(confirmId, trackId);
}
}
@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(getArguments().getCharSequence(KEY_MESSAGE));
checkBox = (CheckBox) view.findViewById(R.id.confirm_dialog_check_box);
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(
android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int confirmId = getArguments().getInt(KEY_CONFIRM_ID);
long trackId = getArguments().getLong(KEY_TRACK_ID);
PreferencesUtils.setBoolean(fragmentActivity, confirmId, !checkBox.isChecked());
caller.onConfirmDone(confirmId, trackId);
}
}).setTitle(R.string.generic_confirm_title).setView(view).create();
}
}
@@ -0,0 +1,62 @@
/*
* 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.MyTracksProviderUtils;
import com.google.android.apps.mytracks.io.sync.SyncUtils;
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.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 all tracks.
*
* @author Jimmy Shih
*/
public class DeleteAllTrackDialogFragment extends DialogFragment {
public static final String DELETE_ALL_TRACK_DIALOG_TAG = "deleteAllTrackDialog";
private FragmentActivity fragmentActivity;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
fragmentActivity = getActivity();
return DialogUtils.createConfirmationDialog(fragmentActivity,
R.string.track_list_delete_all_confirm_message, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new Thread(new Runnable() {
@Override
public void run() {
PreferencesUtils.setBoolean(
fragmentActivity, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT);
SyncUtils.disableSync(fragmentActivity);
SyncUtils.clearSyncState(fragmentActivity);
MyTracksProviderUtils.Factory.get(fragmentActivity).deleteAllTracks();
}
}).start();
}
});
}
}
@@ -0,0 +1,98 @@
/*
* 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 one marker.
*
* @author Jimmy Shih
*/
public class DeleteOneMarkerDialogFragment extends DialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface DeleteOneMarkerCaller {
/**
* Called when delete one marker is done.
*/
public void onDeleteOneMarkerDone();
}
public static final String DELETE_ONE_MARKER_DIALOG_TAG = "deleteOneMarkerDialog";
private static final String KEY_MARKER_ID = "markerId";
private static final String KEY_TRACK_ID = "trackId";
public static DeleteOneMarkerDialogFragment newInstance(long markerId, long trackId) {
Bundle bundle = new Bundle();
bundle.putLong(KEY_MARKER_ID, markerId);
bundle.putLong(KEY_TRACK_ID, trackId);
DeleteOneMarkerDialogFragment deleteOneMarkerDialogFragment = new DeleteOneMarkerDialogFragment();
deleteOneMarkerDialogFragment.setArguments(bundle);
return deleteOneMarkerDialogFragment;
}
private DeleteOneMarkerCaller caller;
private FragmentActivity fragmentActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (DeleteOneMarkerCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + DeleteOneMarkerCaller.class.getSimpleName());
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
fragmentActivity = getActivity();
return DialogUtils.createConfirmationDialog(fragmentActivity,
R.string.marker_delete_one_marker_confirm_message, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new Thread(new Runnable() {
@Override
public void run() {
MyTracksProviderUtils.Factory.get(fragmentActivity).deleteWaypoint(
getArguments().getLong(KEY_MARKER_ID),
new DescriptionGeneratorImpl(fragmentActivity));
caller.onDeleteOneMarkerDone();
}
}).start();
}
});
}
}
@@ -0,0 +1,107 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
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 one track.
*
* @author Jimmy Shih
*/
public class DeleteOneTrackDialogFragment extends DialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface DeleteOneTrackCaller {
/**
* Gets the track recording service connection.
*/
public TrackRecordingServiceConnection getTrackRecordingServiceConnection();
/**
* Called when delete one track is done.
*/
public void onDeleteOneTrackDone();
}
public static final String DELETE_ONE_TRACK_DIALOG_TAG = "deleteOneTrackDialog";
private static final String KEY_TRACK_ID = "trackId";
public static DeleteOneTrackDialogFragment newInstance(long trackId) {
Bundle bundle = new Bundle();
bundle.putLong(KEY_TRACK_ID, trackId);
DeleteOneTrackDialogFragment deleteOneTrackDialogFragment = new DeleteOneTrackDialogFragment();
deleteOneTrackDialogFragment.setArguments(bundle);
return deleteOneTrackDialogFragment;
}
private DeleteOneTrackCaller caller;
private FragmentActivity fragmentActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (DeleteOneTrackCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + DeleteOneTrackCaller.class.getSimpleName());
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
fragmentActivity = getActivity();
return DialogUtils.createConfirmationDialog(fragmentActivity,
R.string.track_detail_delete_confirm_message, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final long trackId = getArguments().getLong(KEY_TRACK_ID);
if (trackId
== PreferencesUtils.getLong(fragmentActivity, R.string.recording_track_id_key)) {
TrackRecordingServiceConnectionUtils.stopRecording(
fragmentActivity, caller.getTrackRecordingServiceConnection(), false);
}
new Thread(new Runnable() {
@Override
public void run() {
MyTracksProviderUtils.Factory.get(fragmentActivity).deleteTrack(trackId);
caller.onDeleteOneTrackDone();
}
}).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.
*
* @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.enable_sync_message)
.setNegativeButton(R.string.generic_cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onEnableSyncDone(false);
}
}).setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onEnableSyncDone(true);
}
}).setTitle(R.string.enable_sync_title).create();
}
@Override
public void onCancel(DialogInterface arg0) {
caller.onEnableSyncDone(false);
}
}
@@ -0,0 +1,133 @@
/*
* 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.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 java.util.Locale;
/**
* 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);
AlertDialog.Builder builder = new AlertDialog.Builder(fragmentActivity).setMessage(
getEulaText()).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();
}
}).setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
EulaUtils.setAcceptEula(fragmentActivity);
PreferencesUtils.setBoolean(
fragmentActivity, R.string.metric_units_key, !Locale.US.equals(Locale.getDefault()));
caller.onEulaDone();
}
});
}
return builder.create();
}
@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,111 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.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.getBoolean(
fragmentActivity, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
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]);
}
}
@@ -0,0 +1,65 @@
/*
* 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.SaveActivity;
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(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent().setData(Uri.parse(SaveActivity.GOOGLE_EARTH_MARKET_URL));
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(
fragmentActivity, R.string.track_detail_install_earth_error, Toast.LENGTH_LONG)
.show();
}
}
})
.create();
}
}
@@ -0,0 +1,706 @@
/*
* 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.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.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.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.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 String ZOOM_TO_CURRENT_LOCATION_KEY = "zoom_to_current_location_key";
private static final String MAP_TYPE = "map_type";
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;
private TrackDataHub trackDataHub;
// Current location
private Location currentLocation;
// True to keep the currentLocation visible
private boolean keepCurrentLocationVisible;
// True to zoom to currentLocation when it is available
private boolean zoomToCurrentLocation;
private OnLocationChangedListener onLocationChangedListener;
// For showing a marker
private long markerTrackId = -1L;
private long markerId = -1L;
// 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);
ApiAdapterFactory.getApiAdapter().invalidMenu(getActivity());
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);
myLocationImageButton = (ImageButton) layout.findViewById(R.id.map_my_location);
myLocationImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
forceUpdateLocation();
keepCurrentLocationVisible = true;
zoomToCurrentLocation = true;
updateCurrentLocation();
}
});
messageTextView = (TextView) layout.findViewById(R.id.map_message);
/*
* At this point, after super.onCreateView, getMap will not return null and
* we can initialize googleMap. However, onCreateView 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;
zoomToCurrentLocation = false;
}
}
});
googleMap.moveCamera(
CameraUpdateFactory.newLatLngZoom(getDefaultLatLng(), googleMap.getMinZoomLevel()));
}
return layout;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
keepCurrentLocationVisible = savedInstanceState.getBoolean(
KEEP_CURRENT_LOCATION_VISIBLE_KEY, false);
zoomToCurrentLocation = savedInstanceState.getBoolean(ZOOM_TO_CURRENT_LOCATION_KEY, false);
currentLocation = (Location) savedInstanceState.getParcelable(CURRENT_LOCATION_KEY);
updateCurrentLocation();
if (googleMap != null) {
googleMap.setMapType(savedInstanceState.getInt(MAP_TYPE, GoogleMap.MAP_TYPE_NORMAL));
}
}
}
@Override
public void onResume() {
super.onResume();
resumeTrackDataHub();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (currentLocation != null) {
outState.putParcelable(CURRENT_LOCATION_KEY, currentLocation);
}
outState.putBoolean(KEEP_CURRENT_LOCATION_VISIBLE_KEY, keepCurrentLocationVisible);
outState.putBoolean(ZOOM_TO_CURRENT_LOCATION_KEY, zoomToCurrentLocation);
if (googleMap != null) {
outState.putInt(MAP_TYPE, googleMap.getMapType());
}
}
@Override
public void onPause() {
super.onPause();
pauseTrackDataHub();
}
/**
* Shows the marker on the map.
*
* @param trackId the track id
* @param id the marker id
*/
public void showMarker(long trackId, long id) {
/*
* Synchronize to prevent race condition in changing markerTrackId and
* markerId variables.
*/
synchronized (this) {
if (currentTrack != null && currentTrack.getId() == trackId) {
showMarker(id);
markerTrackId = -1L;
markerId = -1L;
return;
}
markerTrackId = trackId;
markerId = id;
}
}
@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);
}
return true;
}
@Override
public void onLocationStateChanged(final LocationState state) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (!isResumed() || googleMap == null) {
return;
}
boolean myLocationEnabled = true;
if (state == LocationState.DISABLED) {
currentLocation = null;
myLocationEnabled = false;
}
googleMap.setMyLocationEnabled(myLocationEnabled);
String message;
boolean isGpsDisabled;
if (!isSelectedTrackRecording()) {
message = null;
isGpsDisabled = false;
} else {
switch (state) {
case DISABLED:
String setting = getString(GoogleLocationUtils.isAvailable(getActivity())
? R.string.gps_google_location_settings
: R.string.gps_location_access);
message = getString(R.string.gps_disabled, setting);
isGpsDisabled = true;
break;
case NO_FIX:
message = getString(R.string.gps_wait_for_signal);
isGpsDisabled = false;
break;
case BAD_FIX:
message = getString(R.string.gps_wait_for_better_signal);
isGpsDisabled = false;
break;
case GOOD_FIX:
message = null;
isGpsDisabled = false;
break;
default:
throw new IllegalArgumentException("Unexpected state: " + state);
}
}
if (message == null) {
messageTextView.setVisibility(View.GONE);
return;
}
messageTextView.setText(message);
messageTextView.setVisibility(View.VISIBLE);
if (isGpsDisabled) {
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);
}
});
} else {
messageTextView.setOnClickListener(null);
}
}
});
}
}
@Override
public void onLocationChanged(final Location location) {
if (isResumed()) {
if (isSelectedTrackRecording() && currentLocation == null && location != null) {
zoomToCurrentLocation = true;
}
currentLocation = location;
updateCurrentLocation();
}
}
@Override
public void onHeadingChanged(double heading) {
// We don't care.
}
@Override
public void onSelectedTrackChanged(final Track track) {
if (isResumed()) {
currentTrack = track;
boolean hasTrack = track != null;
if (hasTrack) {
mapOverlay.setShowEndMarker(!isSelectedTrackRecording());
synchronized (this) {
/*
* Synchronize to prevent race condition in changing markerTrackId and
* markerId variables.
*/
if (track.getId() == markerTrackId) {
// Show the marker
showMarker(markerId);
markerTrackId = -1L;
markerId = -1L;
} else {
// Show the track
showTrack();
}
}
}
}
}
@Override
public void onTrackUpdated(Track track) {
// We don't care.
}
@Override
public void clearTrackPoints() {
if (isResumed()) {
mapOverlay.clearPoints();
reloadPaths = true;
}
}
@Override
public void onSampledInTrackPoint(final Location location) {
if (isResumed()) {
mapOverlay.addLocation(location);
}
}
@Override
public void onSampledOutTrackPoint(Location location) {
// We don't care.
}
@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) {
mapOverlay.update(googleMap, paths, reloadPaths);
reloadPaths = false;
}
}
});
}
}
@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) {
mapOverlay.update(googleMap, paths, 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 onMinRecordingDistanceChanged(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.SELECTED_TRACK,
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
TrackDataType.LOCATION));
}
/**
* 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();
}
/**
* Forces update location. Needs to be synchronized because the trackDataHub
* can be accessed by multiple threads.
*/
private synchronized void forceUpdateLocation() {
if (trackDataHub != null) {
trackDataHub.forceUpdateLocation();
}
}
/**
* Updates the current location and zoom to it if necessary.
*/
private void updateCurrentLocation() {
getActivity().runOnUiThread(new Runnable() {
public void run() {
if (!isResumed() || googleMap == null || onLocationChangedListener == null
|| currentLocation == null) {
return;
}
onLocationChangedListener.onLocationChanged(currentLocation);
if (zoomToCurrentLocation
|| (keepCurrentLocationVisible && !isLocationVisible(currentLocation))) {
LatLng latLng = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, DEFAULT_ZOOM_LEVEL));
zoomToCurrentLocation = false;
}
};
});
}
/**
* Sets the camera over a track.
*/
private void showTrack() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (!isResumed() || googleMap == null || currentTrack == null
|| currentTrack.getNumberOfPoints() < 2) {
return;
}
/**
* Check that mapView is valid.
*/
if (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 CameraUpdate.newLatLngBounds(LatLngBounds bounds, int padding)
* if the map view has not undergone layout. Thus calling
* CameraUpdate.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);
}
}
});
}
/**
* Sets the camera over a 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;
zoomToCurrentLocation = 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());
} else {
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);
}
}
@@ -0,0 +1,282 @@
/*
* 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.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;
// 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();
}
@Override
public void onPause() {
super.onPause();
pauseTrackDataHub();
handler.removeCallbacks(updateTotalTime);
}
@Override
public void onLocationStateChanged(LocationState state) {
if (isResumed() && state != LocationState.GOOD_FIX) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
lastLocation = null;
StatsUtils.setLocationValues(getActivity(), lastLocation, true);
}
}
});
}
}
@Override
public void onLocationChanged(final Location location) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
if (isSelectedTrackRecording() && !isSelectedTrackPaused()) {
lastLocation = location;
StatsUtils.setLocationValues(getActivity(), location, true);
} else {
if (lastLocation != null) {
lastLocation = null;
StatsUtils.setLocationValues(getActivity(), lastLocation, true);
}
}
}
}
});
}
}
@Override
public void onHeadingChanged(double heading) {
// We don't care.
}
@Override
public void onSelectedTrackChanged(Track track) {
if (isResumed()) {
handler.removeCallbacks(updateTotalTime);
if (isSelectedTrackRecording()) {
handler.post(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() {
// We don't care.
}
@Override
public void onSampledInTrackPoint(Location location) {
// We don't care.
}
@Override
public void onSampledOutTrackPoint(Location location) {
// We don't care.
}
@Override
public void onSegmentSplit(Location location) {
// We don't care.
}
@Override
public void onNewTrackPointsDone() {
// We don't care.
}
@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 onMinRecordingDistanceChanged(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.SELECTED_TRACK,
TrackDataType.TRACKS_TABLE, TrackDataType.LOCATION, 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, true);
}
}
@@ -0,0 +1,81 @@
/*
* 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.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;
/**
* A DialogFrament to show the welcome info.
*
* @author Jimmy Shih
*/
public class WelcomeDialogFragment extends DialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface WelcomeCaller {
/**
* Called when choose account is done.
*/
public void onWelcomeDone();
}
public static final String WELCOME_DIALOG_TAG = "welcomeDialog";
private WelcomeCaller caller;
private FragmentActivity fragmentActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (WelcomeCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + WelcomeCaller.class.getSimpleName());
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
fragmentActivity = getActivity();
return new AlertDialog.Builder(fragmentActivity).setPositiveButton(
R.string.generic_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onWelcomeDone();
}
}).setTitle(R.string.welcome_title).setMessage(R.string.welcome_message).create();
}
@Override
public void onCancel(DialogInterface arg0) {
caller.onWelcomeDone();
}
}