Location tracking service: Run every 1 minute send to server every 60 mins

Location tracking service: Run every 1 minute send to server every 60 mins

So as title suggest this are our requirements :

  • Location tracking service

    • which runs continuously in the background and records users Lat, Long every 1 minute and stores this data to server every 60 mins.

    • so the data will be stored in RoomDB for 60 mins

    • then sent to server in the background if internet connection is active

    • after sending data to server and receiving success then clearing RoomDB

Step 1: Room DB Setup

LocationData.java:

import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "location_data")
public class LocationData {
    @PrimaryKey(autoGenerate = true)
    private int id;

    private double latitude;
    private double longitude;
    private long timestamp;

    // Getters and setters


    public LocationData(double latitude, double longitude, long timestamp) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.timestamp = timestamp;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }
}

LocationDao.java:

import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;

import java.util.List;

@Dao
public interface LocationDao {
    @Insert
    void insert(LocationData locationData);

    @Query("DELETE FROM location_data")
    void deleteAll();

    @Query("SELECT * from location_data ORDER BY timestamp ASC")
    List<LocationData> getAllLocations();
}

LocationDatabase.java

import androidx.room.Database;
import androidx.room.RoomDatabase;

@Database(entities = {LocationData.class}, version = 1, exportSchema = false)
public abstract class LocationDatabase extends RoomDatabase {
    public abstract LocationDao locationDao();
}

InsertAsyncTask.java

import android.os.AsyncTask;

public class InsertAsyncTask extends AsyncTask<LocationData, Void, Void> {

    private LocationDao locationDao;

    InsertAsyncTask(LocationDao locationDao) {
        this.locationDao = locationDao;
    }

    @Override
    protected Void doInBackground(LocationData... locationData) {
        locationDao.insert(locationData[0]);
        return null;
    }
}

Step 2: Location Service

LocationTrackingService.java

import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.room.Room;

import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;

import java.util.List;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class LocationTrackingService extends Service {
    private LocationDatabase locationDatabase;
    private FusedLocationProviderClient mFusedLocationClient;
    private LocationCallback mLocationCallback;
    private LocationDao locationDao;
    private static final String TAG = "LocationTrackingService";
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() called");
        locationDatabase = Room.databaseBuilder(this, LocationDatabase.class, "location_database")
                .build();
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        locationDao = locationDatabase.locationDao();
        mLocationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                Log.d(TAG, "onLocationResult() called with: locationResult = [" + locationResult + "]");

                if (locationResult == null) {
                    return;
                }
                for (Location location : locationResult.getLocations()) {
                    LocationData locationData = new LocationData(location.getLatitude(), location.getLongitude(), location.getTime());
                    new InsertAsyncTask(locationDao).execute(locationData);
                }
                startSendingLocationData();

            }
        };
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return Service.START_NOT_STICKY;
        }
        mFusedLocationClient.requestLocationUpdates(getLocationRequest(), mLocationCallback, null);
        return Service.START_STICKY;
    }


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


    private LocationRequest getLocationRequest() {
        LocationRequest locationRequest = new LocationRequest();
        locationRequest.setInterval(60 * 1000); // 60 seconds
        locationRequest.setFastestInterval(30 * 1000); // 30 seconds
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        Log.d(TAG, "getLocationRequest() called");
        return locationRequest;
    }

    private void startSendingLocationData() {
        Log.d(TAG, "startSendingLocationData() called");
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(60 * 1000); // check every 60 seconds
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (isNetworkConnected() && !isDataSentWithinInterval()) {
                        // Get all location data from Room database
                        Log.d(TAG, "run() called isNetworkConnected() && !isDataSentWithinInterval()");
                        List<LocationData> locations = locationDao.getAllLocations();

                        // Send location data to server using Retrofit
                        sendLocationDataToServer(locations);

                        // Store the timestamp of the last sent data in SharedPreferences
                        storeLastSentTimestamp();
                    }


                }
            }
        }).start();
    }
    private boolean isNetworkConnected() {
        Log.d(TAG, "isNetworkConnected() called");
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    }


    // fetch data to send to server
    private void sendLocationDataToServer(List<LocationData> locations) {
        Log.d(TAG, "sendLocationDataToServer() called with: locations = [" + locations + "]");

    //I have commented retrofit code you can do this part just make sure to call deleteAll method from LocationDao only if you want to delete RoomDB.
        // Create Retrofit instance
//        Retrofit retrofit = new Retrofit.Builder()
//                .baseUrl(BASE_URL)
//                .addConverterFactory(GsonConverterFactory.create())
//                .build();
//
//        // Create service
//        LocationService service = retrofit.create(LocationService.class);
//
//        // Send location data to server
//        Call<ResponseBody> call = service.sendLocationData(locations);
//        call.enqueue(new Callback<ResponseBody>() {
//            @Override
//            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
//                if (response.isSuccessful()) {
//                    Log.d(TAG, "Location data sent to server successfully");
//                    // Clear location data from Room database
//                    new ClearLocationsAsyncTask(locationDao).execute();
//                } else {
//                    Log.e(TAG, "Failed to send location data to server");
//                }
//            }
//
//            @Override
//            public void onFailure(Call<ResponseBody> call, Throwable t) {
//                Log.e(TAG, "Failed to send location data to server", t);
//            }
//        });
    }

    private void storeLastSentTimestamp() {
        SharedPreferences preferences = getSharedPreferences("location_data", MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putLong("last_sent_timestamp", System.currentTimeMillis());
        editor.apply();
        Log.d(TAG, "storeLastSentTimestamp() called");
    }

    private boolean isDataSentWithinInterval() {
        Log.d(TAG, "isDataSentWithinInterval() called");
        SharedPreferences preferences = getSharedPreferences("location_data", MODE_PRIVATE);
        long lastSentTimestamp = preferences.getLong("last_sent_timestamp", 0);
        long currentTimestamp = System.currentTimeMillis();
        long interval = 60 * 60 * 1000; // 60 minutes in milliseconds
        return currentTimestamp - lastSentTimestamp < interval;
    }

}

I have commented retrofit code you can do this part just make sure to call deleteAll method from LocationDao only if you want to delete RoomDB.

Step 3: Activate it

Manifest code:

  <application>
...
<service
    android:name=".Services.Location.LocationTrackingService"
    android:enabled="true"
    android:exported="false" />
...
</application>
//Permissions 
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Application Class:

public class MySuperAppApplication extends Application {
    private static Application instance;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
//        DynamicColors.applyToActivitiesIfAvailable(this);
        Intent intent = new Intent(this, LocationTrackingService.class);
        startService(intent);


    }

    public static Context getContext() {
        return instance.getApplicationContext();
    }
}

Did you find this article valuable?

Support saurabh jadhav by becoming a sponsor. Any amount is appreciated!