package fi.pyppe.android.gps;
import java.io.File;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;
import org.apache.commons.validator.UrlValidator;
import fi.pyppe.android.gps.listen.PyppeGPSLocationListener;
import fi.pyppe.android.gps.util.KMLWriter;
import fi.pyppe.android.gps.util.Utilities;
import fi.pyppe.android.gps.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CompoundButton.OnCheckedChangeListener;
enum TargetActivity {
MAPS,
UPLOAD,
EMAIL
}
/**
* <p>The main Activity.</p>
*
* <p>Shows Location-data, and the start/stop Button.</p>
*
* @author Pyppe
*/
public class PyppeGPSActivity extends Activity implements OnCheckedChangeListener {
// --- User preferences ---
private boolean preferWirelessNetworks;
private int minimumSeconds;
// --- Location-related Listeners and Managers ---
private PyppeGPSLocationListener gpsLocationListener;
private PyppeGPSLocationListener wirelessNetworkLocationListener;
public LocationManager gpsLocationManager;
public LocationManager wirelessNetworkLocationManager;
private Location currentLocation;
private boolean wirelessNetworkEnabled;
private boolean gpsEnabled;
private boolean isUsingGps;
// --- KML-writer ---
private KMLWriter kmlWriter;
private String currentFileName;
// --- Misc ---
private String currentUploadURL;
private NotificationManager gpsNotifyManager;
private int NOTIFICATION_ID;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
kmlWriter = new KMLWriter(this);
setContentView(R.layout.main);
ToggleButton buttonOnOff = (ToggleButton) findViewById(R.id.buttonOnOff);
buttonOnOff.setOnCheckedChangeListener(this);
refreshUserPreferences();
}
/**
* Handler for the Start/Stop button.
*/
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
try {
TextView buttonLabel = (TextView) findViewById(R.id.buttonOnOffText);
if (isChecked) {
refreshUserPreferences();
notifyStatusbar();
resetCurrentFileName();
startLocationManagers();
buttonLabel.setText("Stop");
} else {
removeNotification();
stopLocationManagers();
kmlWriter.finishCurrentKML();
buttonLabel.setText("Start");
}
clearForm();
} catch (Exception ex) {
setStatusText("Error: " + ex.getMessage());
}
}
/**
* Manages the notification in the status bar
*/
private void notifyStatusbar() {
gpsNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
showNotificationInStatusbar();
}
/**
* Hides the notification icon in the status bar if it's visible.
*/
private void removeNotification() {
try {
gpsNotifyManager.cancelAll();
} catch (Exception ex) {
}
}
/**
* Shows a notification icon in the status bar
*/
private void showNotificationInStatusbar() {
// What happens when the notification item is clicked
Intent contentIntent = new Intent(this, PyppeGPSActivity.class);
PendingIntent pending = PendingIntent.getActivity(getBaseContext(), 0, contentIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
Notification notification = new Notification(R.drawable.status25, null, System.currentTimeMillis());
notification.flags |= Notification.FLAG_ONGOING_EVENT;
String contentText = PyppeGPSConstants.APP_NAME + " is running.";
if (currentLocation != null) {
NumberFormat nf = new DecimalFormat("###.######");
contentText = nf.format(currentLocation.getLatitude()) + "," + nf.format(currentLocation.getLongitude());
}
notification.setLatestEventInfo(getBaseContext(), PyppeGPSConstants.APP_NAME + " is running", contentText, pending);
gpsNotifyManager.notify(NOTIFICATION_ID, notification);
}
/**
* Refreshes User Preferences.
*/
public void refreshUserPreferences() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
preferWirelessNetworks = prefs.getBoolean("preferencePreferWirelessNetworks", false);
String minimumSecondsString = prefs.getString("preferenceLogFrequency", "5");
if (minimumSecondsString != null && minimumSecondsString.length() > 0) {
minimumSeconds = Integer.valueOf(minimumSecondsString);
} else {
minimumSeconds = 60;
}
TextView logFrequencyText = (TextView) findViewById(R.id.logFrequencyText);
if (minimumSeconds > 0) {
logFrequencyText.setText("Every " + minimumSeconds + " seconds");
} else {
logFrequencyText.setText("As frequently as possible");
}
if ((currentFileName != null && currentFileName.length() > 0)) {
TextView tvLogFile = (TextView) findViewById(R.id.logFileText);
tvLogFile.setText("/sdcard/" + PyppeGPSConstants.OUTPUT_FOLDER + "/\n" + currentFileName);
}
}
/**
* Show Toast-message, when user hides the application with phone's Back-button.
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
Toast.makeText(getBaseContext(),
PyppeGPSConstants.APP_NAME + " is still running. You can exit the application from the menu.", Toast.LENGTH_LONG).show();
return true;
}
/*
if (keyCode == KeyEvent.KEYCODE_D) {
insertMockGPSData();
return true;
}
*/
return super.onKeyDown(keyCode, event);
}
@SuppressWarnings("unused")
private void insertMockGPSData() {
setStatusText("Started");
String providerName = "GPS";
setProviderAndTimeText(providerName + ": " + new Date().toLocaleString());
String longitude = "22.94";
String latitude = "61.59";
Random generator = new Random();
for (int i = 0; i < 13; i++) {
latitude += generator.nextInt(10);
longitude += generator.nextInt(10);
}
setLatitudeText(longitude);
setLongitudeText(latitude);
String altitude = "83." + generator.nextInt(10);
setAltitudeText(altitude + " m");
String speedKmH = "2" + generator.nextInt(4);
setSpeedText(speedKmH + " km/h");
if (generator.nextBoolean()) {
setDirectionText("\u2191");
} else {
setDirectionText("\u2197");
}
setAccuracyText("\u00b1 4.0 m");
int number = 9 + generator.nextInt(3);
setSatellitesText(String.valueOf(number));
}
/**
* Create Options-menu.
*
* @see options_menu.xml
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
/**
* Called when one of the menu items is selected.
*/
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
switch (itemId) {
case R.id.menuSettings:
Intent settingsActivity = new Intent(getBaseContext(), SettingsActivity.class);
startActivityForResult(settingsActivity, SettingsActivity.REFRESH_PREFERENCES);
break;
case R.id.menuTagLocation:
kmlWriter.showTagLocationDialog();
break;
case R.id.menuSendEmail:
showFilePickerDialog(TargetActivity.EMAIL);
break;
case R.id.menuUpload:
if (isValidUploadURL()) {
showFilePickerDialog(TargetActivity.UPLOAD);
} else {
Utilities.showMessageDialog("Cannot upload", "Please define a valid Upload URL in Settings-menu.", this);
}
break;
case R.id.menuViewMap:
showFilePickerDialog(TargetActivity.MAPS);
break;
case R.id.menuExit:
removeNotification();
stopLocationManagers();
System.exit(0);
break;
}
return false;
}
private boolean isValidUploadURL() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
currentUploadURL = preferences.getString("preferenceUploadURL", "http://");
if (currentUploadURL.equals("http://")) {
return false;
}
String[] schemes = {"http","https"};
UrlValidator urlValidator = new UrlValidator(schemes);
if (urlValidator.isValid(currentUploadURL)) {
return true;
} else {
return false;
}
}
private void showFilePickerDialog(TargetActivity targetActivity) {
try {
final File gpxFolder = new File(Environment.getExternalStorageDirectory(), PyppeGPSConstants.OUTPUT_FOLDER);
final TargetActivity activity = targetActivity;
if (gpxFolder.exists()) {
String[] enumeratedFiles = gpxFolder.list();
List<String> fileList = new ArrayList<String>(Arrays.asList(enumeratedFiles));
Collections.sort(fileList);
Collections.reverse(fileList);
final String[] files = fileList.toArray(new String[0]);
final Dialog dialog = new Dialog(this);
if (activity.equals(TargetActivity.UPLOAD)) {
dialog.setTitle("Choose file to upload");
} else if (activity.equals(TargetActivity.MAPS)) {
dialog.setTitle("Choose file to view");
} else {
dialog.setTitle("Choose file to email");
}
dialog.setContentView(R.layout.filelist);
ListView thelist = (ListView) dialog.findViewById(R.id.listViewFiles);
thelist.setAdapter(new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_single_choice, files));
thelist.setOnItemClickListener(new OnItemClickListener() {
@SuppressWarnings("unchecked")
public void onItemClick(AdapterView av, View v, int index, long arg) {
dialog.dismiss();
final String chosenFileName = files[index];
if (activity.equals(TargetActivity.UPLOAD)) {
final AlertDialog alertDialog = new AlertDialog.Builder(PyppeGPSActivity.this).create();
alertDialog.setTitle("Upload");
final EditText input = new EditText(PyppeGPSActivity.this);
alertDialog.setView(input);
alertDialog.setMessage("Give description of file:");
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent settingsActivity = new Intent(getBaseContext(), UploadActivity.class);
settingsActivity.putExtra(UploadActivity.FILE_DESCRIPTION, input.getText().toString());
settingsActivity.putExtra(UploadActivity.FILE_DATA, new File(gpxFolder, chosenFileName));
settingsActivity.putExtra(UploadActivity.UPLOAD_URL, currentUploadURL);
startActivity(settingsActivity);
return;
}
});
alertDialog.setButton2("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Canceled.
}
});
alertDialog.show();
} else if (activity.equals(TargetActivity.MAPS)) {
Intent mapsActivity = new Intent(getBaseContext(), MapsActivity.class);
mapsActivity.putExtra("kmlFile", new File(gpxFolder, chosenFileName));
startActivity(mapsActivity);
return;
} else {
Intent sendEmailIntent = new Intent(Intent.ACTION_SEND);
sendEmailIntent.putExtra(Intent.EXTRA_SUBJECT, "Data from " + PyppeGPSConstants.APP_NAME);
sendEmailIntent.putExtra(Intent.EXTRA_STREAM,
Uri.parse("file://" + Environment.getExternalStorageDirectory() + "/" + PyppeGPSConstants.OUTPUT_FOLDER + "/" + chosenFileName ));
sendEmailIntent.setType("application/vnd.google-earth.kml+xml");
startActivity(Intent.createChooser(sendEmailIntent, "Send mail..."));
}
}
});
dialog.show();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SettingsActivity.REFRESH_PREFERENCES) {
refreshUserPreferences();
}
}
/**
* Starts the LocationManagers.
*/
public void startLocationManagers() {
refreshUserPreferences();
gpsLocationListener = new PyppeGPSLocationListener(this);
wirelessNetworkLocationListener = new PyppeGPSLocationListener(this);
gpsLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
wirelessNetworkLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
checkWirelessNetworkAndGpsStatus();
if (gpsEnabled && !preferWirelessNetworks) {
gpsLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minimumSeconds * 1000, 0F, gpsLocationListener);
gpsLocationManager.addGpsStatusListener(gpsLocationListener);
isUsingGps = true;
} else if (wirelessNetworkEnabled) {
isUsingGps = false;
wirelessNetworkLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, minimumSeconds * 1000, 0F, wirelessNetworkLocationListener);
} else {
isUsingGps = false;
setStatusText("No Location-provider available");
return;
}
setStatusText("Started");
}
private void checkWirelessNetworkAndGpsStatus() {
wirelessNetworkEnabled = wirelessNetworkLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
gpsEnabled = gpsLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
/**
* Stops the LocationManagers.
*/
private void stopLocationManagers() {
if (wirelessNetworkLocationListener != null) {
wirelessNetworkLocationManager.removeUpdates(wirelessNetworkLocationListener);
}
if (gpsLocationListener != null) {
gpsLocationManager.removeUpdates(gpsLocationListener);
gpsLocationManager.removeGpsStatusListener(gpsLocationListener);
}
setStatusText("Stopped");
}
/**
* Sets the current file name based on user preference.
*/
private void resetCurrentFileName() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
currentFileName = sdf.format(new Date()) + ".kml";
}
/**
* Clears the table, removes all values.
*/
private void clearForm() {
String text = "";
setLatitudeText(text);
setLongitudeText(text);
setProviderAndTimeText(text);
setAltitudeText(text);
setSpeedText(text);
setSatellitesText(text);
setDirectionText(text);
setAccuracyText(text);
}
/**
* Sets the Location Provider status label.
*
* @param message
* Status message
*/
public void setStatusText(String message) {
TextView textView = (TextView) findViewById(R.id.statusText);
textView.setText(message);
}
/**
* Shows the number of Satellites in the UI.
*
* @param number
* Number of satellites
*/
public void setNumberOfSatellites(int number) {
TextView textView = (TextView) findViewById(R.id.satellitesText);
textView.setText(String.valueOf(number));
}
private void setLatitudeText(String text) {
TextView textView = (TextView) findViewById(R.id.latitudeText);
textView.setText(text);
}
private void setLongitudeText(String text) {
TextView textView = (TextView) findViewById(R.id.longitudeText);
textView.setText(text);
}
private void setProviderAndTimeText(String text) {
TextView textView = (TextView) findViewById(R.id.providerAndTimeText);
textView.setText(text);
}
private void setAltitudeText(String text) {
TextView textView = (TextView) findViewById(R.id.altitudeText);
textView.setText(text);
}
private void setSpeedText(String text) {
TextView textView = (TextView) findViewById(R.id.speedText);
textView.setText(text);
}
private void setSatellitesText(String text) {
TextView textView = (TextView) findViewById(R.id.satellitesText);
textView.setText(text);
}
private void setDirectionText(String text) {
TextView textView = (TextView) findViewById(R.id.directionText);
textView.setText(text);
}
private void setAccuracyText(String text) {
TextView textView = (TextView) findViewById(R.id.accuracyText);
textView.setText(text);
}
/**
* Update current location.
*
* @param location
* New Location
*/
public void updateLocation(Location location) {
currentLocation = location;
try {
notifyStatusbar();
String providerName = location.getProvider();
if (providerName.equalsIgnoreCase(LocationManager.GPS_PROVIDER)) {
providerName = "GPS";
} else {
providerName = "Wireless networks";
}
setProviderAndTimeText(providerName + ": " + new Date().toLocaleString());
setLatitudeText(String.valueOf(location.getLatitude()));
setLongitudeText(String.valueOf(location.getLongitude()));
if (location.hasAltitude()) {
double altitude = location.getAltitude();
setAltitudeText(String.valueOf(altitude) + " m");
} else {
setAltitudeText("-");
}
if (location.hasSpeed()) {
int speedKmH = (int) (location.getSpeed() * 3.6);
setSpeedText(String.valueOf(speedKmH) + " km/h");
} else {
setSpeedText("-");
}
setDirectionText(Utilities.getDirectionArrow(location));
if (!isUsingGps) {
setSatellitesText("-");
}
if (location.hasAccuracy()) {
float accuracy = location.getAccuracy();
setAccuracyText("\u00b1 " + String.valueOf(accuracy) + " m");
} else {
setAccuracyText("-");
}
kmlWriter.writeToFile(location);
refreshUserPreferences();
resetManagersIfRequired();
} catch (Exception ex) {
setStatusText("Error in displaying location info: " + ex.getMessage());
}
}
public String getCurrentFileName() {
return currentFileName;
}
public Location getCurrentLocation() {
return currentLocation;
}
/**
* Stops GPS Location Manager, then starts it.
*/
public void restartLocationManagers() {
stopLocationManagers();
startLocationManagers();
}
/**
* Checks to see if providers have been enabled and switches providers based
* on user preferences.
*/
private void resetManagersIfRequired() {
checkWirelessNetworkAndGpsStatus();
if (isUsingGps && preferWirelessNetworks) {
restartLocationManagers();
} else if (gpsEnabled && !isUsingGps && !preferWirelessNetworks) {
restartLocationManagers();
}
}
}
|