Reputation: 51
i have just started to learn how to use services, and i am trying to build an app which sets off an alarm when the device is in a certain distance from a location that the user defined. i read a couple of tutorial and wrote this:
package com.example.fgps;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.IntentSender;
import android.database.Cursor;
import android.location.Location;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.model.LatLng;
public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
public static final String TAG = BackgroundLocationService.class.getSimpleName();
private List<LocationAlarm> alarms;
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
private GoogleApiClient mGoogleApiClient;
private boolean mInProgress;
private LocationRequest mLocationRequest;
public void onCreate(){
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
// Create the LocationRequest object
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(3 * 1000) // 3 seconds, in milliseconds
.setFastestInterval(1 * 500); // 1/2 second, in milliseconds
openDB();
Cursor c =Mdb.getAllRows();
LatLng latLng;
for(alarms = new ArrayList<LocationAlarm>(); !c.isAfterLast(); c.moveToNext()) {
latLng=new LatLng(c.getDouble(c.getColumnIndex(c.getColumnName(2))),c.getDouble(c.getColumnIndex(c.getColumnName(3))));
alarms.add(new LocationAlarm(c.getInt(c.getColumnIndex(c.getColumnName(4))) != 0, latLng, c.getDouble(c.getColumnIndex(c.getColumnName(5))), c.getString(c.getColumnIndex(c.getColumnName(1)))));
}
}
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
private void setUpLocationClientIfNeeded() {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onConnected(Bundle bundle) {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
else {
handleNewLocation(location);
}
}
@Override
public void onConnectionSuspended(int i) {
}
public void onConnectionFailed(ConnectionResult connectionResult) {
mInProgress = false;
// * Google Play services can resolve some errors it detects.
// * If the error has a resolution, try sending an Intent to
// * start a Google Play services activity that can resolve
//* error.
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(null, CONNECTION_FAILURE_RESOLUTION_REQUEST);
// * Thrown if Google Play services canceled the original
// * PendingIntent
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
//* If no resolution is available, display a dialog to the
// * user with the error.
Log.i(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
}
}
@Override
public void onDestroy(){
mInProgress = false;
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
super.onDestroy();
}
@Override
public void onLocationChanged(Location location) {
handleNewLocation(location);
}
private DBAdapter Mdb;
private void handleNewLocation(Location location) {
Log.d(TAG, location.toString());
int c=0;
for(int i=0;i<alarms.size();i++)
{
if(alarms.get(i).getActive())
{
break;
}
else{
c++;
}
}
if(c==alarms.size())
stopSelf();
double currentLatitude = location.getLatitude();
double currentLongitude = location.getLongitude();
LatLng latLng = new LatLng(currentLatitude, currentLongitude);
for(int i=0;i<alarms.size();i++)
{
if(alarms.get(i).getActive() && (distanceCal(latLng, alarms.get(i).getLatlng())<=alarms.get(i).getDistance()))
{
setOffAlarm();
}
}
}
private void setOffAlarm() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 1);
//Create a new PendingIntent and add it to the AlarmManager
Intent intent = new Intent(this, AlarmReceiverActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
12345, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am =
(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
pendingIntent);
}
private double distanceCal(LatLng Mloc,LatLng destination)
{
final int R = 6371; // Radius of the earth
Double lat1 = Mloc.latitude;
Double lon1 = Mloc.longitude;
Double lat2 = destination.latitude;
Double lon2 = destination.longitude;
Double latDistance = toRad(lat2-lat1);
Double lonDistance = toRad(lon2-lon1);
Double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
Double distance = R * c;
return distance;
}
private Double toRad(Double value) {
return value * Math.PI / 180;
}
private void openDB(){
Mdb = new DBAdapter(this);
Mdb.open();
}
}
when i run the app in the emulator, the app crashes and in the log cat written "google client is not connected yet". how do i solve it? in addition is this service even built correctly?
edit:----------------------------------------------------
i changed the setoffalarm():
private void setOffAlarm(int index) {
Log.d("setoff", "got here");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 1);
//Create a new PendingIntent and add it to the AlarmManager
Intent intent = new Intent(this, AlarmReceiverActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
index, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am =
(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
pendingIntent);
}
i also add :
for(int i=0;i<alarms.size();i++)
{
if(alarms.get(i).getActive() && (distanceCal(latLng, alarms.get(i).getLatlng())<=alarms.get(i).getDistance()))
{
setOffAlarm(i);
}
}
but it still won't activate the alarm, any suggestions?
edit 2-------------------------------------------
the alarmreceiver class:
package com.example.fgps;
import java.io.IOException;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
/**
* This activity will be called when the alarm is triggered.
*
*/
public class AlarmReceiverActivity extends Activity {
private MediaPlayer mMediaPlayer;
private PowerManager.WakeLock wl;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
Log.d("alarm", "got here");
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.alarm);
Button stopAlarm = (Button) findViewById(R.id.stopAlarm);
stopAlarm.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View arg0, MotionEvent arg1) {
mMediaPlayer.stop();
finish();
return false;
}
});
playSound(this, getAlarmUri());
}
@Override
protected void onStop() {
super.onStop();
wl.release();
}
private void playSound(Context context, Uri alert) {
mMediaPlayer = new MediaPlayer();
try {
mMediaPlayer.setDataSource(context, alert);
final AudioManager audioManager = (AudioManager) context
.getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mMediaPlayer.prepare();
mMediaPlayer.start();
}
} catch (IOException e) {
System.out.println("OOPS");
}
}
//Get an alarm sound. Try for an alarm. If none set, try notification,
//Otherwise, ringtone.
private Uri getAlarmUri() {
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);
}
}
return alert;
}
}
"alarm, got here" doesn't appear in the log cat
Upvotes: 2
Views: 4114
Reputation: 43342
The problem is that you are calling setUpLocationClientIfNeeded()
when the API is not connected yet.
To fix the error, just wait until the onConnected()
callback before calling setUpLocationClientIfNeeded()
.
So, remove it from onStartCommand()
:
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
//remove this:
//setUpLocationClientIfNeeded();
if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
And then add it to onConnected()
:
@Override
public void onConnected(Bundle bundle) {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
else {
handleNewLocation(location);
}
//add this here!
setUpLocationClientIfNeeded();
}
Edit: Regarding the alarms not firing, it looks like you're only creating one alarm, and each alarm in the list overwrites the previous one.
You will need to give each PendingIntent a different requestCode
in order to fix that.
See documentation for PendingIntent.getActivity() here.
So, you should add a parameter to setAlarm()
, you can just use the index in the alarms
List:
for(int i=0;i<alarms.size();i++)
{
if(alarms.get(i).getActive() && (distanceCal(latLng, alarms.get(i).getLatlng())<=alarms.get(i).getDistance()))
{
setOffAlarm(i); //give the index here
}
}
Then, in setOffAlarm()
, use the index passed in to the parameter as the requestCode
instead of hard coding it to 12345
:
private void setOffAlarm(int index) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 1);
//Create a new PendingIntent and add it to the AlarmManager
Intent intent = new Intent(this, AlarmReceiverActivity.class);
//don't use 12345 for requestCode, this will overwrite previous alarm
//PendingIntent pendingIntent = PendingIntent.getActivity(this,
// 12345, intent, PendingIntent.FLAG_CANCEL_CURRENT);
//use index instead:
PendingIntent pendingIntent = PendingIntent.getActivity(this,
index, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am =
(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
pendingIntent);
}
Upvotes: 1