/*
* Copyright (C) 2012 The Serval Project
*
* This file is part of the Serval Maps Software
*
* Serval Maps Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.maps.services;
import java.io.IOException;
import org.servalproject.maps.R;
import org.servalproject.maps.location.JsonLocationWriter;
import org.servalproject.maps.location.LocationCollector;
import org.servalproject.maps.location.MockLocations;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.location.LocationManager;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
/**
* The CoreService class undertakes the core activities that need to be
* undertaken even while activities are not present
*/
public class CoreService extends Service {
/*
* public class level constants
*/
public static final String PREFERENCES_NAME = "core-service";
public static final String PREFERENCES_VALUE = "uptime";
// class level constants
private final int STATUS_NOTIFICATION = 0;
private final String JSON_UPDATE_DELAY_DEFAULT = "60000";
private final boolean V_LOG = false;
private final String TAG = "CoreService";
// class level variables
private LocationCollector locationCollector;
private LocationManager locationManager;
private MockLocations mockLocations = null;
private JsonLocationWriter jsonLocationWriter = null;
private Thread mockLocationsThread = null;
private Thread jsonLocationWriterThread = null;
private SharedPreferences preferences = null;
private Long uptimeStart;
/*
* called when the service is created
*
* (non-Javadoc)
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate() {
// create the necessary supporting variables
locationCollector = new LocationCollector(this.getApplicationContext());
// Acquire a reference to the system Location Manager
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// get the preferences
preferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
// determine if mock locations should be used
if(preferences.getBoolean("preferences_developer_mock_locations", false) == true ) {
if(MockLocations.isMockLocationSet(this) == true) {
try {
mockLocations = new MockLocations(this.getApplicationContext());
Toast.makeText(getApplicationContext(), R.string.system_mock_locations_allowed, Toast.LENGTH_LONG).show();
} catch (IOException e) {
Log.e(TAG, "unable to create MockLocations instance", e);
}
} else {
Toast.makeText(getApplicationContext(), R.string.system_mock_locations_not_allowed, Toast.LENGTH_LONG).show();
Log.e(TAG, "'Allow Mock Locations' setting is not set");
}
}
// determine of JSON output should be created
if(preferences.getBoolean("preferences_map_output_json", false) == true) {
String updateDelay = preferences.getString("preferences_map_output_json_interval", null);
if(updateDelay == null) {
updateDelay = JSON_UPDATE_DELAY_DEFAULT;
}
try {
jsonLocationWriter = new JsonLocationWriter(getApplicationContext(), Long.parseLong(updateDelay));
} catch (IOException e) {
Log.e(TAG, "unable to create jsonLocationWriter instance", e);
}
}
// listen for changes in the preferences
preferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
if(V_LOG) {
if(mockLocations == null) {
Log.v(TAG, "mock locations are not used");
} else {
Log.v(TAG, "mock locations are being used");
}
if(jsonLocationWriter == null) {
Log.v(TAG, "JSON location writing is not occuring");
} else {
Log.v(TAG, "JSON location writing is occuring");
}
Log.v(TAG, "Service Created");
}
}
// listen for changes to the shared preferences
private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
/*
* (non-Javadoc)
* @see android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged(android.content.SharedPreferences, java.lang.String)
*/
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if(V_LOG) {
Log.v(TAG, "a change in shared preferences has been deteceted");
Log.v(TAG, "preference changed: '" + key + "'");
}
// check to see if this is the preference that is of interest
if(key.equals("preferences_developer_mock_locations") == true) {
if(V_LOG) {
Log.v(TAG, "preference changed: 'preferences_developer_mock_locations'");
}
// see if the preference is true
if(preferences.getBoolean("preferences_developer_mock_locations", false) == true ) {
// preference is true so start using mock locations if required
if(mockLocations == null) {
try{
mockLocations = new MockLocations(getApplicationContext());
Thread mockLocationThread = new Thread(mockLocations, "MockLocations");
mockLocationThread.start();
} catch (IOException e) {
Log.e(TAG, "unable to create MockLocations instance", e);
}
}
} else {
// preference is false if of we're doing it stop
if(mockLocations != null) {
mockLocations.requestStop();
mockLocations = null;
}
}
} else if(key.equals("preferences_map_output_json") == true) {
if(V_LOG) {
Log.v(TAG, "preference changed: 'preferences_map_output_json'");
}
if(preferences.getBoolean("preferences_map_output_json", false) == true) {
// preference is true so start outputing json if required
if(jsonLocationWriter == null) {
String updateDelay = preferences.getString("preferences_map_output_json_interval", null);
if(updateDelay == null) {
updateDelay = JSON_UPDATE_DELAY_DEFAULT;
}
try {
jsonLocationWriter = new JsonLocationWriter(getApplicationContext(), Long.parseLong(updateDelay));
jsonLocationWriterThread = new Thread(jsonLocationWriter, "JsonLocationWriter");
jsonLocationWriterThread.start();
} catch (IOException e) {
Log.e(TAG, "unable to create jsonLocationWriter instance", e);
}
}
}
} else if(key.equals("preferences_map_output_json_interval") == true) {
String updateDelay = preferences.getString("preferences_map_output_json_interval", null);
if(updateDelay == null) {
updateDelay = JSON_UPDATE_DELAY_DEFAULT;
}
if(jsonLocationWriter != null) {
jsonLocationWriter.setUpdateDelay(Long.parseLong(updateDelay));
}
}
}
};
/*
* called when the service is started
*
* (non-Javadoc)
* @see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
uptimeStart = System.currentTimeMillis();
if(V_LOG) {
Log.v(TAG, "Service Started");
}
// add the notification icon
addNotification();
if(mockLocations != null) {
mockLocationsThread = new Thread(mockLocations, "MockLocations");
mockLocationsThread.start();
}
if(jsonLocationWriter != null) {
jsonLocationWriterThread = new Thread(jsonLocationWriter, "JsonLocationWriter");
jsonLocationWriterThread.start();
}
// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationCollector);
// If service gets killed, after returning from here, restart
return START_STICKY;
}
// private method used to add the notification icon
private void addNotification() {
// add a notification icon
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//TODO update this with a better icon
//TODO update this with a custom notification with stats
int mNotificationIcon = R.drawable.ic_notification;
CharSequence mTickerText = getString(R.string.system_notification_ticker_text);
long mWhen = System.currentTimeMillis();
// create the notification and set the flag so that it stays up
Notification mNotification = new Notification(mNotificationIcon, mTickerText, mWhen);
mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
// get the content of the notification
CharSequence mNotificationTitle = getString(R.string.system_notification_title);
CharSequence mNotificationContent = getString(R.string.system_notification_content);
// create the intent for the notification
// set flags so that the user returns to this activity and not a new one
Intent mNotificationIntent = new Intent(this, org.servalproject.maps.MapActivity.class);
mNotificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// create a pending intent so that the system can use the above intent at a later time.
PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0, mNotificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
// complete the setup of the notification
mNotification.setLatestEventInfo(getApplicationContext(), mNotificationTitle, mNotificationContent, mPendingIntent);
// add the notification
mNotificationManager.notify(STATUS_NOTIFICATION, mNotification);
}
/*
* called when the service kills the service
*
* (non-Javadoc)
* @see android.app.Service#onDestroy()
*/
@Override
public void onDestroy() {
// tidy up any used resources etc.
// clear the notification
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancel(STATUS_NOTIFICATION);
// stop listening for location updates
locationManager.removeUpdates(locationCollector);
if(mockLocations != null) {
mockLocations.requestStop();
if(mockLocationsThread != null) {
mockLocationsThread.interrupt();
}
}
if(jsonLocationWriter != null) {
jsonLocationWriter.requestStop();
if(jsonLocationWriterThread != null) {
jsonLocationWriterThread.interrupt();
}
}
// update the uptime count
long mUptime = System.currentTimeMillis() - uptimeStart;
SharedPreferences mPreferences = getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
mUptime = mUptime + mPreferences.getLong(PREFERENCES_VALUE, 0);
Editor mEditor = mPreferences.edit();
mEditor.putLong(PREFERENCES_VALUE, mUptime);
mEditor.commit();
super.onDestroy();
if(V_LOG) {
Log.v(TAG, "Service Destroyed");
}
}
/*
* this isn't a bound service so we can safely return null here
*
* (non-Javadoc)
* @see android.app.Service#onBind(android.content.Intent)
*/
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}