skip to Main Content

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


  1. Chosen as BEST ANSWER

    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

    var message = {
    "registration_ids": [
        fcmToken
    ],
    "webpush": {
        "headers": {
            "Urgency": "high"
        }
    },
    "android": {
        "priority": "high"
    },
    "priority": 10
    

    }


  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search