user3542927
user3542927

Reputation: 51

getting location updates with background service

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

Answers (1)

Daniel Nugent
Daniel Nugent

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

Related Questions