Chances are high you landed on the above page first before this article. COVID has brought out the developer in us and almost everyone wants to create an app that monitors user’s whereabouts in the background. If you are new to android development, like every optimistic programmer you start coding.
There’s an easy workaround to the restriction shall you wish to implement it. Enter: Foreground Services. Let’s dive into the implementation.
Disclaimer: Traditionally getting the current location is implemented by Fused Location Client, but for this article let’s focus on the service. The API implements it for us in the backend along with other fallbacks.
Step 1: Install the Library
implementation 'io.nlopez.smartlocation:library:3.3.3'
Step 2: Adding permissions
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Step 3: Implementing the service class
class LocationService : Service() {
private var builder : LocationParams.Builder?=null
private var mNotificationManager: NotificationManager? = null
override fun onBind(intent: Intent?): IBinder {
throw UnsupportedOperationException("Not yet implemented")
}
override fun onCreate() {
super.onCreate()
builder = LocationParams.Builder()
.setAccuracy(LocationAccuracy.HIGH)
.setInterval(Constants.LOCATION_UPDATE_INTERVAL_IN_MILLISECONDS.toLong())
mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val mChannel = NotificationChannel(Constants.NOTIFICATION_CHANNEL_ID, Constants.NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT)
mNotificationManager!!.createNotificationChannel(mChannel)
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(1, notification)
SmartLocation.with(this)
.location()
.continuous()
.config(builder!!.build())
.start { location ->
Toast.makeText(this, location.latitude.toString() + " " + location.longitude.toString(), Toast.LENGTH_SHORT).show()
Log.w(TAG,location.toString())
}
return START_STICKY
}
private val notification: Notification
get() {
val builder = NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL_ID)
.setContentText(getString(R.string.body_text))
.setContentTitle(getString(R.string.header_text))
.setOngoing(true)
.setSmallIcon(R.drawable.ic_notification)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
builder.setChannelId(Constants.NOTIFICATION_CHANNEL_ID)
return builder.build()
}
companion object{
private val TAG = LocationService::class.java.simpleName
}
}/**
Please define the following constants when you use the code
Constants.NOTIFICATION_CHANNEL_ID
Constants.NOTIFICATION_CHANNEL_NAME
Constants.LOCATION_UPDATE_INTERVAL_IN_MILLISECONDS
*/
In a gist, my onCreate does all the heavy lifting and creates a persistent notification while onStart handles getting the location and showing the toast.
Step 4: Registering the service in MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (!Helper.isLocationPermissionGranted(this))
requestPermissions()
else
registerService()
}
private fun registerService(){
val serviceIntent = Intent(this, LocationService::class.java)
startService(serviceIntent)
} private fun requestPermissions(){/**handle request permission and onRequest permission result accoringly*/}
Step 5: Registering the service in Manifest
<service
android:name=".services.LocationService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location" />
There you have it. Persistent background location in the background. I’ll love to keep the article updated in case there is something that I missed.
Stay Safe❤