Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android periodic location updates using workmanager

I am using a WorkManager as follows -

class LocationWorker(
    ctx: Context, params: WorkerParameters
) : CoroutineWorker(ctx, params), KoinComponent {

    private val locationDataRepository: LocationDataRepository by inject()

    override suspend fun doWork(): Result {
        return try {
            locationDataRepository.triggerLocationUpdates()
            Result.success()
        } catch (e: Exception) {
            Result.failure()
        }
    }
}

I trigger the Worker as -

val myWorker =
            PeriodicWorkRequestBuilder<LocationWorker>(
                15,
                TimeUnit.MINUTES
            ).addTag(
                "location"
            ).build()
        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "location",
            ExistingPeriodicWorkPolicy.KEEP,
            myWorker
        )

As you see, the WorkManager minimum period is 15 minutes. I want to track the location for very short intervals say every few seconds and also I want the location to be tracked even when the phone screen is off. Is WorkManager the right choice for my requirements or would you suggest me some other API?

like image 527
Ma2340 Avatar asked Sep 20 '19 18:09

Ma2340


People also ask

How can I get continuous updates of current location in Android?

In order to receive location updates from a NETWORK_PROVIDER or GPS_PROVIDER , you must request the user's permission by declaring either the ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission, respectively, in your Android manifest file.

What is the use of WorkManager in Android?

WorkManager is intended for work that is required to run reliably even if the user navigates off a screen, the app exits, or the device restarts. For example: Sending logs or analytics to backend services. Periodically syncing application data with a server.

Which interface do we use to get the location updates?

The LocationListener interface, which is part of the Android Locations API is used for receiving notifications from the LocationManager when the location has changed. The LocationManager class provides access to the systems location services.

What is difference between WorkManager and coroutines?

The WorkManager API makes it easy to specify deferrable, asynchronous tasks and when they should run. These APIs let you create a task and hand it off to WorkManager to run immediately or at an appropriate time. On the other hand, coroutines are designed to compute a given task only immediately and asynchronously.


1 Answers

It might help you it works even when the app is killed..I'm just a tad concerned when device enters doze mode as gps cannot be accessed when device is stationary

foreground service main purpose is to run with a persistent notification when the app is killed

LocationService.class

public class LocationService extends Service implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000 * 30; //30 secs
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    protected static final String TAG = "LocationUpdateService";
    public static final int HORIZONTAL_ACCURACY_IN_METERS = 100;

    /**
     * The identifier for the notification displayed for the foreground service.
     */
    private static final int NOTIFICATION_ID = 12345678;

    public static boolean mRequestingLocationUpdates = false;
    public boolean isEnded = false;
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;



    private SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
    private double latitude = 0;
    private double longitude = 0;
    private float[] results1 = new float[1];
    private float distanceInMeters = 0;
    private Handler mHandler;


    @Override
    public void onCreate() {
        super.onCreate();


        String CHANNEL_ID = "FOREGROUND_CHANNEL";
        if (Build.VERSION.SDK_INT >= 26) {

            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "location_notification",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        }

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setOngoing(true)
                .setContentTitle("G Star")

                .setSmallIcon(R.drawable.gm_noti_logo)
                .setContentText("Running").build();

        startForeground(NOTIFICATION_ID, notification);
        Utility.getInstance().makeDescriptiveLogs("ON CREATE WAS HIT");

        sendLocationDataToServerPeriodically();


    }

    private void sendLocationDataToServerPeriodically() {


//getting the alarm manager
        AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        //creating a new intent specifying the broadcast receiver
        Intent intentLR = new Intent(this, PostLocationReceiver.class);

        PendingIntent pi = PendingIntent.getBroadcast(this, 0, intentLR,
                PendingIntent.FLAG_UPDATE_CURRENT);


        if (android.os.Build.VERSION.SDK_INT >= 23) {
            assert am != null;
            am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
        } else if (Build.VERSION.SDK_INT >= 19) {
            if (am != null) {
                am.setInexactRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        } else {
            if (am != null) {
                am.setRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        }


    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        isEnded = false;

        Utility.getInstance().makeDescriptiveLogs("ONSTART COMMAND WAS HIT");
        buildGoogleApiClient();
        if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) {
            startLocationUpdates();
        }
        mHandler = new Handler();
        return START_STICKY;
    }

    @Override
    public void onConnected(Bundle bundle) {
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        // The connection to Google Play services was lost for some reason. We call connect() to
        // attempt to re-establish the connection.

        mGoogleApiClient.connect();
    }

    @Override
    public void onLocationChanged(Location location) {

        if (location.getAccuracy() < HORIZONTAL_ACCURACY_IN_METERS)
            updateUI(location);
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
        // onConnectionFailed.

    }

    protected synchronized void buildGoogleApiClient() {

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        createLocationRequest();
    }

    /**
     * Updates the latitude, the longitude, and the last location time in the UI.
     */
    private void updateUI(Location mCurrentLocation) {



        mHandler.post(() -> {
            /*GET DEVICE CURRENT BATTERY LEVEL*/
            int batteryPercent = Utility.getInstance().getBatteryPercentage(LocationService.this);


            /*  CALCULATE DISTANCE BETWEEN LAT LONG INTERVALS*/
            if (latitude != 0 && longitude != 0) {
                Location.distanceBetween(latitude, longitude, mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), results1);
                distanceInMeters = results1[0];
            }


            latitude = mCurrentLocation.getLatitude();
            longitude = mCurrentLocation.getLongitude();

            /*CHECK IF DEVICE HAS ACTIVE INTERNET CONNECTION*/
            String networkStatus = Utility.getInstance().checkConnection(LocationService.this) ? "1" : "0";


            /*CHECK NETWORK SIGNAL STRENGTH*/
            String signalStrength = Utility.getInstance().getSignalStrength(LocationService.this);

            SQLiteDBHandler db = SQLiteDBHandler.getInstance(LocationService.this);
            db.insertDeviceLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? mCurrentLocation.getSpeedAccuracyMetersPerSecond() : mCurrentLocation.getSpeed(), sdf.format(Calendar.getInstance().getTime()), distanceInMeters, batteryPercent, networkStatus, signalStrength);


        });

    }

    protected void createLocationRequest() {
        mGoogleApiClient.connect();
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setSmallestDisplacement(5);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    /**
     * Requests location updates from the FusedLocationApi.
     */
    public void startLocationUpdates() {
        if (!mRequestingLocationUpdates) {
            mRequestingLocationUpdates = true;

            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }


            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);

            isEnded = true;
        }
    }

    /**
     * Removes location updates from the FusedLocationApi.
     */
    public void stopLocationUpdates() {
        if (mRequestingLocationUpdates) {
            mRequestingLocationUpdates = false;



            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

        }
    }

    @Override
    public void onDestroy() {

        mHandler.removeCallbacksAndMessages(null);

        stopLocationUpdates();

        super.onDestroy();
    }


}
like image 149
coderBox Avatar answered Oct 12 '22 07:10

coderBox