// Copyright 2013 Geoffrey Buttercrumbs
//
// 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.geoffreybuttercrumbs.arewethereyet;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.*;
import android.os.PowerManager.WakeLock;
import android.os.Process;
public class AlarmService extends Service {
private ServiceHandler mServiceHandler;
private static int ONGOING_NOTIFICATION = 1;
private static Location loc;
private static int radius;
private static Uri tone;
private static LocationManager locationManager;
private static LocationListener myLocationListener;
private static long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 0; // in Meters
private static long MINIMUM_TIME_BETWEEN_UPDATE = 1000 * 30 * 1; // in Milliseconds
private static final int ONE_MINUTE = 1000 * 60 * 1;
private static final String RADIUS = "radius";
private static final String LOC = "loc";
private static final String TONE = "tone";
private static final String ADDRESS = "address";
private MediaPlayer mMediaPlayer;
private boolean everAlarmed = false;
private static Location oldLocation;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
myLocationListener = new MyLocationListener();
/**
* Start the location manager...
*/
makeManager();
/**
* Do a check to see if we are already in the zone (TODO eventually we could use this to toggle to OutOfZone alarm.)
*/
if(locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) != null){
checkLoc(locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER));
}
else if (locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) != null)
checkLoc(locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
Looper mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle extras = intent.getExtras();
radius = extras.getInt(RADIUS);
loc = (Location) extras.get(LOC);
tone = (Uri) extras.get(TONE);
String address = (String) extras.get(ADDRESS);
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
/**
* Start running the service in the foreground so it will not be killed.
*/
CharSequence tickerText;
if (address != null)
tickerText = getText(R.string.ticker_text)+ ": " + address;
else
tickerText = getText(R.string.ticker_text)+ ": Address unknown";
Notification notification = new Notification(R.drawable.ic_stat_alarm, tickerText, System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ZonePicker.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
CharSequence nt;
if (address != null)
nt = getText(R.string.notification_title) + ": " + address;
else
nt = getText(R.string.notification_title) + ": Address unknown";
notification.setLatestEventInfo(this, nt, getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION, notification);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
locationManager.removeUpdates(myLocationListener);
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
}
public class MyLocationListener implements LocationListener {
public void onLocationChanged(Location location) {
if (isBetterLocation(location, oldLocation)){
checkLoc(location);
oldLocation = location;
}
}
public void onStatusChanged(String s, int i, Bundle b) {
}
public void onProviderDisabled(String s) {
}
public void onProviderEnabled(String s) {
}
}
public void checkLoc(Location location){
// oldDistanceLogic(location);
distanceLogic(location);
if ((loc.distanceTo(location) <= radius) && (!everAlarmed)){
final WakeLock wl = AlarmWakeLock.createFullWakeLock(AlarmService.this);
wl.acquire();
Intent dialogIntent = new Intent(getBaseContext(), Alarm.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//TESTING TODO REMOVE
try
{
Thread.sleep(500); // do nothing
}
catch(InterruptedException e)
{
e.printStackTrace();
}
playAudio();
getApplication().startActivity(dialogIntent);
everAlarmed = true;
wl.release();
}
}
private void distanceLogic(Location location) {
if (Math.round((loc.distanceTo(location)-radius)/1000) != Math.sqrt(MINIMUM_TIME_BETWEEN_UPDATE/1000) && !everAlarmed) {
if (Math.round((loc.distanceTo(location)-radius)/1000) != 0){
MINIMUM_TIME_BETWEEN_UPDATE = (long) (Math.pow(Math.round((loc.distanceTo(location)-radius)/1000), 2)*1000);
//If time gets larger than 12.15min, that is too much.
if (MINIMUM_TIME_BETWEEN_UPDATE > 1000 * 27 * 27)
MINIMUM_TIME_BETWEEN_UPDATE = 1000 * 27 * 27;
makeManager();
}
}
}
//Archived in favor of exponential ramp (this one is stepped)
/* private void oldDistanceLogic(Location location) {
if (loc.distanceTo(location)-radius <= 10000 && loc.distanceTo(location)-radius > 5000 && MINIMUM_TIME_BETWEEN_UPDATE != 60000 && everAlarmed == false) {
MINIMUM_TIME_BETWEEN_UPDATE = 60000;
makeManager();
//Testing
Toast.makeText(getApplicationContext(), "Distantce:" + loc.distanceTo(location) + " Radius:" + radius, Toast.LENGTH_SHORT).show();
}
if (loc.distanceTo(location)-radius <= 5000 && loc.distanceTo(location)-radius > 1000 && MINIMUM_TIME_BETWEEN_UPDATE > 30000 && everAlarmed == false) {
MINIMUM_TIME_BETWEEN_UPDATE = 30000;
makeManager();
//Testing
Toast.makeText(getApplicationContext(), "Distantce :" + loc.distanceTo(location) + " Radius:" + radius, Toast.LENGTH_SHORT).show();
}
if (loc.distanceTo(location)-radius <= 1000 && MINIMUM_TIME_BETWEEN_UPDATE > 1000 && everAlarmed == false) {
MINIMUM_TIME_BETWEEN_UPDATE = 1000;
makeManager();
//Testing
Toast.makeText(getApplicationContext(), "Distantce :" + loc.distanceTo(location) + " Radius:" + radius, Toast.LENGTH_SHORT).show();
}
}*/
private void playAudio() {
try {
// Uri alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
// if(alert == null){
// alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
// if(alert == null){
// alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
// }
// }
Uri alert;
if (tone != null){
alert = tone ;
}
else
alert = null;
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(this, alert);
final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mMediaPlayer.setLooping(true);
mMediaPlayer.prepare();
mMediaPlayer.start();
}
// mMediaPlayer.start();
} catch (Exception e) {
// Log.e("ex", "error: " + e.getMessage(), e);
}
}
/** Determines whether one Location reading is better than the current Location fix
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > ONE_MINUTE;
boolean isSignificantlyOlder = timeDelta < -ONE_MINUTE;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
private void makeManager() {
locationManager.removeUpdates(myLocationListener);
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MINIMUM_TIME_BETWEEN_UPDATE,
MINIMUM_DISTANCECHANGE_FOR_UPDATE,
myLocationListener
);
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MINIMUM_TIME_BETWEEN_UPDATE,
MINIMUM_DISTANCECHANGE_FOR_UPDATE,
myLocationListener
);
}
}