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" />
2
Answers
The onMessageReceived(remoteMessage: RemoteMessage) is not tigered when the app is in background or killed state in this case i send the FCM with high priority then it worked even if the app is not running
}
There is an old official repository here which should give you what you need, but bear in mind this repository has been archived and replaced by a newer version (details in the linked repo) which uses a different method.