I want to start a service for location updates in background what i want to achieve is i want to start the service when the user recevice notification from firebase FCM in onMessageReceived(remoteMessage: RemoteMessage)
when the application is in forground the onMessageReceived start the service but when the app is killed or not in forground the location is not updated or the service does not run in the background. What is the proper approach to get the user location in background when the app is in background or in killed state
<— Firebase Messaging —>
@AndroidEntryPoint
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
if (remoteMessage.data.isNotEmpty()) {
Log.d("FirebaseLogs", "Remote message data: ${remoteMessage.data}")
Intent(this, LocationBackgroundService::class.java).also { intent ->
intent.action = LocationBackgroundService.ACTION_START
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
}
} else {
Log.d("FirebaseLogs", "Remote message data is empty")
}
}
override fun onNewToken(token: String) {
super.onNewToken(token)
SharedPrefDataStore.init().setValue(this, FCM_TOKEN, token)
}
}
<— This is my service class —>
@AndroidEntryPoint
class LocationBackgroundService : Service() {
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private lateinit var locationClient: LocationClient
private lateinit var db: FirebaseFirestore
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
db = FirebaseFirestore.getInstance()
locationClient = DefaultLocationClient(
applicationContext,
LocationServices.getFusedLocationProviderClient(applicationContext)
)
createNotificationChannel()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when(intent?.action){
ACTION_START -> start()
ACTION_STOP -> stop()
}
//return START_STICKY
return super.onStartCommand(intent, flags, startId)
}
private fun start() {
val pendingIntent: PendingIntent =
Intent(this, MainActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(
this, 0, notificationIntent,
PendingIntent.FLAG_IMMUTABLE
)
}
val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle("Tracking location...")
.setContentText("")
.setSmallIcon(R.drawable.we_care_small)
.setOngoing(true)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
locationClient
.getLocationUpdates(10000L)
.catch { e -> e.printStackTrace() }
.onEach { location ->
val lat = location.latitude.toString()
val long = location.longitude.toString()
val geoPoint = GeoPoint(location.latitude, location.longitude)
saveUserLocation(geoPoint)
val updatedNotification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle("Tracking location...")
.setContentText("Location: ($lat, $long)")
.setSmallIcon(R.drawable.we_care_small)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build()
notificationManager.notify(1, updatedNotification)
}
.launchIn(serviceScope)
startForeground(1, notification.build())
}
private fun createNotificationChannel() {
val channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = "Channel for location service"
}
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
private fun stop() {
stopForeground(true)
stopSelf()
}
override fun onDestroy() {
super.onDestroy()
serviceScope.cancel()
}
private fun saveUserLocation(geoPoint: GeoPoint) {
db.collection(USER_LOCATION)
.document(Firebase.auth.currentUser?.uid.toString())
.update("geo_location", geoPoint)
.addOnCompleteListener {
Log.d(TAG, "Save UserLocation from Service :: " + it.isSuccessful)
}
.addOnFailureListener {
Log.d(TAG, "Fail to save UserLocation :: " + it.message)
}
}
companion object {
const val ACTION_START = "ACTION_START"
const val ACTION_STOP = "ACTION_STOP"
}
}
<— Manifest —>
<service
android:name=".service.LocationBackgroundService"
android:enabled="true"
android:foregroundServiceType="location"
android:exported="false"
tools:ignore="ForegroundServicePermission" />