Android Location Service using Kotlin
Add required dependencies in your app level build.gradle file
implementation 'com.google.android.gms:play-services-location:17.0.0' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
Give permission for foreground service and register it in manifest.xml file
<!-- Required for foreground services on P+. --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <service android:name=".LocationUpdatesService" android:enabled="true" android:exported="true" />
Give location permissions in mainifest.xml file
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Check location permissions at runtime
After android marshmallow, Developers must need to request permissions at runtime. Features related to permissions only work if users allow required permissions.
private val MY_PERMISSIONS_REQUEST_LOCATION = 68 private val REQUEST_CHECK_SETTINGS = 129 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this@MainActivity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), MY_PERMISSIONS_REQUEST_LOCATION) } else { Log.e("MainActivity:","Location Permission Already Granted") if (getLocationMode() == 3) { Log.e("MainActivity:","Already set High Accuracy Mode") initializeService() } else { Log.e("MainActivity:","Alert Dialog Shown") showAlertDialog(this@MainActivity) } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { when (requestCode) { MY_PERMISSIONS_REQUEST_LOCATION -> { if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { Log.e("MainActivity:","Location Permission Granted") if (getLocationMode() == 3) { Log.e("MainActivity:","Already set High Accuracy Mode") initializeService() } else { Log.e("MainActivity:","Alert Dialog Shown") showAlertDialog(this@MainActivity) } } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return } } } private fun getLocationMode(): Int { return Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_CHECK_SETTINGS) { initializeService() } }
Show alert dialog if GPS is turned off
If GPS is turned off in device then we need to take user to device gps settings to turn on through this alert dialog.
private fun showAlertDialog(context: Context?) { try { context?.let { val builder = AlertDialog.Builder(it) builder.setTitle(it.resources.getString(R.string.app_name)) .setMessage("Please select High accuracy Location Mode from Mode Settings") .setPositiveButton(it.resources.getString(android.R.string.ok)) { dialog, which -> dialog.dismiss() startActivityForResult(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), REQUEST_CHECK_SETTINGS) } .setIcon(android.R.drawable.ic_dialog_alert) .setCancelable(false) .show() } } catch (e: Exception) { e.printStackTrace() } }
Create location service
override fun onCreate() { super.onCreate() val handlerThread = HandlerThread(TAG) handlerThread.start() mServiceHandler = Handler(handlerThread.looper) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.e(TAG, "Location Service started") val startedFromNotification = intent?.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION, false) // We got here because the user decided to remove location updates from the notification. if (startedFromNotification!!) { removeLocationUpdates() stopSelf() } // Tells the system to not try to recreate the service after it has been killed. return Service.START_NOT_STICKY } override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) mChangingConfiguration = true } override fun onBind(intent: Intent?): IBinder? { Log.e(TAG, "in onBind()") stopForeground(true) mChangingConfiguration = false return mBinder } override fun onRebind(intent: Intent?) { Log.i(TAG, "in onRebind()") stopForeground(true) mChangingConfiguration = false super.onRebind(intent) } override fun onUnbind(intent: Intent?): Boolean { Log.i(TAG, "Last client unbound from service") // Called when the last client (MainActivity in case of this sample) unbinds from this // service. If this method is called due to a configuration change in MainActivity, we // do nothing. Otherwise, we make this service a foreground service. if (!mChangingConfiguration) { Log.i(TAG, "Starting foreground service") startForeground(NOTIFICATION_ID, serviceNotification()) } return true } override fun onDestroy() { super.onDestroy() mServiceHandler?.removeCallbacksAndMessages(null) }
Create foreground service notification
private fun serviceNotification(): Notification { // Get an instance of the Notification manager val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager val intent = Intent(this, LocationUpdatesService::class.java) // Extra to help us figure out if we arrived in onStartCommand via the notification or not. intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true) // Android O requires a Notification Channel. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = getString(R.string.app_name) // Create the channel for the notification val mChannel = NotificationChannel( "location_service_channel", name, NotificationManager.IMPORTANCE_HIGH ) // Set the Notification Channel for the Notification Manager. mNotificationManager?.createNotificationChannel(mChannel) } val builder = NotificationCompat.Builder(this) .setContentTitle("Location Service") .setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setWhen(System.currentTimeMillis()) // Set the Channel ID for Android O. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId("location_service_channel") // Channel ID } else { builder.priority = Notification.PRIORITY_HIGH } return builder.build() }
Create location client to request location updates
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this) mLocationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult?) { super.onLocationResult(locationResult) locationResult?.let { mLocationResult -> onNewLocation(mLocationResult.lastLocation) } } } private fun createLocationRequest() { mLocationRequest = LocationRequest() mLocationRequest?.interval = UPDATE_INTERVAL_IN_MILLISECONDS mLocationRequest?.fastestInterval = FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS mLocationRequest?.priority = LocationRequest.PRIORITY_HIGH_ACCURACY Log.e(TAG, "createLocationRequest") } fun requestLocationUpdates() { Log.e(TAG, "Requesting location updates") startService(Intent(applicationContext, LocationUpdatesService::class.java)) try { mFusedLocationClient?.requestLocationUpdates( mLocationRequest, mLocationCallback, Looper.myLooper() ) } catch (unlikely: SecurityException) { Log.e(TAG, "Lost location permission. Could not request updates. $unlikely") } } private fun removeLocationUpdates() { Log.e(TAG, "Removing location updates") try { mFusedLocationClient?.removeLocationUpdates(mLocationCallback) stopSelf() } catch (unlikely: SecurityException) { Log.e(TAG, "Lost location permission. Could not remove updates. $unlikely") } } private fun getLastLocation() { try { mFusedLocationClient?.lastLocation ?.addOnCompleteListener { task -> if (task.isSuccessful && task.result != null) { currentLocation = task.result } else { Log.e(TAG, "Failed to get location.") } } } catch (unlikely: SecurityException) { Log.e(TAG, "Lost location permission.$unlikely") } }
Add places locations to notify users when they arrive
Here you can add your places through their latitudes and longitudes to notify users when they arrive.
listPlaces = ArrayList<Location>() val location1 = Location("Place 1") location1.latitude = 33.844843 location1.longitude = -116.54911 listPlaces?.add(location1) val location2 = Location("Place 2") location2.latitude = 44.240309 location2.longitude = -91.493619 listPlaces?.add(location2) val location3 = Location("Place 3") location3.latitude = 44.920474 location3.longitude = -93.447851 listPlaces?.add(location3)
Detect new location and check distance in the specified range
Whenever new location is detected, you have to check distance from that location to all locations you already added. If location range is same as you want then you have to store that location in local to check later. Because you will get same location multiple times at regular intervals and you don’t want to notify user everytime.
val sharedPreferences = getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE) val editor = sharedPreferences?.edit() listPlaces?.let { mPlaces -> for (getLocation: Location in mPlaces) { if (sharedPreferences.getString(PLACE_ID, "") != getLocation.provider) { val distance: Float = location.distanceTo(getLocation) if (distance <= locationRange) { Log.e(TAG, "Location Triggered " + getLocation.provider) editor?.putString(PLACE_ID, getLocation.provider) editor?.apply() if (isAppIsInBackground(applicationContext)) { Log.e(TAG, "App In Background " + getLocation.provider) Log.e(TAG, "Send Notification " + getLocation.provider) sendNotification(this, getLocation.provider) } else { Log.e(TAG, "App In Foreground " + getLocation.provider) val pushNotification = Intent("NotifyUser") pushNotification.putExtra("pinned_location_name", getLocation.provider) pushNotification.putExtra("pinned_location_lat", getLocation.latitude.toString()) pushNotification.putExtra("pinned_location_long", getLocation.longitude.toString()) LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification) } } } } }
Send notification to user if app is in background or killed
// Get an instance of the Notification manager val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager // Android O requires a Notification Channel. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = context.getString(R.string.app_name) // Create the channel for the notification val mChannel = NotificationChannel("channel_01", name, NotificationManager.IMPORTANCE_HIGH) // Set the Notification Channel for the Notification Manager. mNotificationManager?.createNotificationChannel(mChannel) } // Create an explicit content Intent that starts the main Activity. val notificationIntent = Intent(context, MainActivity::class.java) // Construct a task stack. val stackBuilder = TaskStackBuilder.create(context) // Add the main Activity to the task stack as the parent. stackBuilder.addParentStack(MainActivity::class.java) // Push the content Intent onto the stack. stackBuilder.addNextIntent(notificationIntent) // Get a PendingIntent containing the entire back stack. val notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) val defaultSoundUri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) // Get a notification builder that's compatible with platform versions >= 4 val builder = NotificationCompat.Builder(context) // Define the notification settings. builder.setSmallIcon(R.mipmap.ic_launcher) // In a real app, you may want to use a library like Volley // to decode the Bitmap. .setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) .setColor(Color.RED) .setContentTitle("You are around at " + placeId) .setSound(defaultSoundUri) .setContentIntent(notificationPendingIntent) // Set the Channel ID for Android O. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId("channel_01") // Channel ID } else { builder.priority = Notification.PRIORITY_HIGH } // Dismiss notification once the user touches it. builder.setAutoCancel(true) // Issue the notification mNotificationManager?.notify(0, builder.build())
Notify user through broadcast receiver if app is in foreground
broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(contxt: Context?, intent: Intent?) { when (intent?.action) { "NotifyUser" -> { try { val name = intent.getStringExtra("pinned_location_name") val lat = intent.getStringExtra("pinned_location_lat") val long = intent.getStringExtra("pinned_location_long") txt_location_name?.text = "You are around at " + name txt_location_lat?.text = "Pinned Location Latitude: " + lat txt_location_long?.text = "Pinned Location Longitude: " + long } catch (e: Exception) { e.printStackTrace() } } } } } } override fun onResume() { super.onResume() val intentFilter = IntentFilter() intentFilter.addAction("NotifyUser") broadcastReceiver?.let { LocalBroadcastManager.getInstance(this).registerReceiver(it, intentFilter) } } override fun onPause() { broadcastReceiver?.let { LocalBroadcastManager.getInstance(this).unregisterReceiver(it) } super.onPause() }
Integrate location service in activity class
// A reference to the service used to get location updates. private var mService: LocationUpdatesService? = null; // Tracks the bound state of the service. private var mBound: Boolean = false // Monitors the state of the connection to the service. private var mServiceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { val binder: LocationUpdatesService.LocalBinder = service as LocationUpdatesService.LocalBinder mService = binder.service mBound = true // Check that the user hasn't revoked permissions by going to Settings. mService?.requestLocationUpdates() } override fun onServiceDisconnected(name: ComponentName?) { mService = null mBound = false } } override fun onStart() { super.onStart() bindService(Intent(this, LocationUpdatesService::class.java), mServiceConnection, Context.BIND_AUTO_CREATE) } override fun onStop() { if (mBound) { // Unbind from the service. This signals to the service that this activity is no longer // in the foreground, and the service can respond by promoting itself to a foreground // service. unbindService(mServiceConnection) mBound = false } super.onStop() }
Final Code
package com.example.locationupdatesservicesample import android.app.* import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.BitmapFactory import android.graphics.Color import android.location.Location; import android.media.RingtoneManager import android.net.Uri import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import androidx.core.app.NotificationCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.util.Log; import androidx.core.app.TaskStackBuilder import androidx.core.content.ContextCompat.getSystemService 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.io.Serializable class LocationUpdatesService : Service() { private val locationRange = 100f private val USER_PREFS = "user_prefs" private val PLACE_ID = "placeId" private val EXTRA_STARTED_FROM_NOTIFICATION = "started_from_notification" private val mBinder = LocalBinder() /** * The desired interval for location updates. Inexact. Updates may be more or less frequent. */ private val UPDATE_INTERVAL_IN_MILLISECONDS: Long = 5000 /** * The fastest rate for active location updates. Updates will never be more frequent * than this value. */ private val FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = UPDATE_INTERVAL_IN_MILLISECONDS / 2 /** * The identifier for the notification displayed for the foreground service. */ private val NOTIFICATION_ID = 12345678 /** * Used to check whether the bound activity has really gone away and not unbound as part of an * orientation change. We create a foreground service notification only if the former takes * place. */ private var mChangingConfiguration = false /** * Contains parameters used by [com.google.android.gms.location.FusedLocationProviderApi]. */ private var mLocationRequest: LocationRequest? = null /** * Provides access to the Fused Location Provider API. */ private var mFusedLocationClient: FusedLocationProviderClient? = null /** * Callback for changes in location. */ private var mLocationCallback: LocationCallback? = null private var mServiceHandler: Handler? = null /** * The current location. */ private var currentLocation: Location? = null private val TAG = "LocationUpdatesService" private var listPlaces: ArrayList<Location>? = null override fun onCreate() { super.onCreate() listPlaces = ArrayList<Location>() val location1 = Location("Place 1") location1.latitude = 33.844843 location1.longitude = -116.54911 listPlaces?.add(location1) val location2 = Location("Place 2") location2.latitude = 44.240309 location2.longitude = -91.493619 listPlaces?.add(location2) val location3 = Location("Place 3") location3.latitude = 44.920474 location3.longitude = -93.447851 listPlaces?.add(location3) mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this) mLocationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult?) { super.onLocationResult(locationResult) locationResult?.let { mLocationResult -> onNewLocation(mLocationResult.lastLocation) } } } createLocationRequest() getLastLocation() val handlerThread = HandlerThread(TAG) handlerThread.start() mServiceHandler = Handler(handlerThread.looper) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.e(TAG, "Location Service started") val startedFromNotification = intent?.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION, false) // We got here because the user decided to remove location updates from the notification. if (startedFromNotification!!) { removeLocationUpdates() stopSelf() } // Tells the system to not try to recreate the service after it has been killed. return Service.START_NOT_STICKY } override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) mChangingConfiguration = true } override fun onBind(intent: Intent?): IBinder? { Log.e(TAG, "in onBind()") stopForeground(true) mChangingConfiguration = false return mBinder } override fun onRebind(intent: Intent?) { Log.i(TAG, "in onRebind()") stopForeground(true) mChangingConfiguration = false super.onRebind(intent) } override fun onUnbind(intent: Intent?): Boolean { Log.i(TAG, "Last client unbound from service") // Called when the last client (MainActivity in case of this sample) unbinds from this // service. If this method is called due to a configuration change in MainActivity, we // do nothing. Otherwise, we make this service a foreground service. if (!mChangingConfiguration) { Log.i(TAG, "Starting foreground service") startForeground(NOTIFICATION_ID, serviceNotification()) } return true } override fun onDestroy() { super.onDestroy() mServiceHandler?.removeCallbacksAndMessages(null) } fun requestLocationUpdates() { Log.e(TAG, "Requesting location updates") startService(Intent(applicationContext, LocationUpdatesService::class.java)) try { mFusedLocationClient?.requestLocationUpdates( mLocationRequest, mLocationCallback, Looper.myLooper() ) } catch (unlikely: SecurityException) { Log.e(TAG, "Lost location permission. Could not request updates. $unlikely") } } private fun removeLocationUpdates() { Log.e(TAG, "Removing location updates") try { mFusedLocationClient?.removeLocationUpdates(mLocationCallback) stopSelf() } catch (unlikely: SecurityException) { Log.e(TAG, "Lost location permission. Could not remove updates. $unlikely") } } private fun getLastLocation() { try { mFusedLocationClient?.lastLocation ?.addOnCompleteListener { task -> if (task.isSuccessful && task.result != null) { currentLocation = task.result } else { Log.e(TAG, "Failed to get location.") } } } catch (unlikely: SecurityException) { Log.e(TAG, "Lost location permission.$unlikely") } } fun onNewLocation(location: Location) { Log.e(TAG, "New location: " + location) currentLocation = location val sharedPreferences = getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE) val editor = sharedPreferences?.edit() listPlaces?.let { mPlaces -> for (getLocation: Location in mPlaces) { if (sharedPreferences.getString(PLACE_ID, "") != getLocation.provider) { val distance: Float = location.distanceTo(getLocation) if (distance <= locationRange) { Log.e(TAG, "Location Triggered " + getLocation.provider) editor?.putString(PLACE_ID, getLocation.provider) editor?.apply() if (isAppIsInBackground(applicationContext)) { Log.e(TAG, "App In Background " + getLocation.provider) Log.e(TAG, "Send Notification " + getLocation.provider) sendNotification(this, getLocation.provider) } else { Log.e(TAG, "App In Foreground " + getLocation.provider) val pushNotification = Intent("NotifyUser") pushNotification.putExtra("pinned_location_name", getLocation.provider) pushNotification.putExtra("pinned_location_lat", getLocation.latitude.toString()) pushNotification.putExtra("pinned_location_long", getLocation.longitude.toString()) LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification) } } } } } } private fun serviceNotification(): Notification { // Get an instance of the Notification manager val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager val intent = Intent(this, LocationUpdatesService::class.java) // Extra to help us figure out if we arrived in onStartCommand via the notification or not. intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true) // Android O requires a Notification Channel. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = getString(R.string.app_name) // Create the channel for the notification val mChannel = NotificationChannel( "location_service_channel", name, NotificationManager.IMPORTANCE_HIGH ) // Set the Notification Channel for the Notification Manager. mNotificationManager?.createNotificationChannel(mChannel) } val builder = NotificationCompat.Builder(this) .setContentTitle("Location Service") .setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setWhen(System.currentTimeMillis()) // Set the Channel ID for Android O. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId("location_service_channel") // Channel ID } else { builder.priority = Notification.PRIORITY_HIGH } return builder.build() } private fun createLocationRequest() { mLocationRequest = LocationRequest() mLocationRequest?.interval = UPDATE_INTERVAL_IN_MILLISECONDS mLocationRequest?.fastestInterval = FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS mLocationRequest?.priority = LocationRequest.PRIORITY_HIGH_ACCURACY Log.e(TAG, "createLocationRequest") } inner class LocalBinder : Binder() { internal val service: LocationUpdatesService get() = this@LocationUpdatesService } fun isAppIsInBackground(context: Context): Boolean { var isInBackground = true try { val am = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager am?.let { val runningProcesses = it.runningAppProcesses for (processInfo in runningProcesses) { if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { for (activeProcess in processInfo.pkgList) { if (activeProcess == context.packageName) { isInBackground = false } } } } } } catch (e: Exception) { e.printStackTrace() } return isInBackground } fun sendNotification(context: Context, placeId: String) { try { // Get an instance of the Notification manager val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager // Android O requires a Notification Channel. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = context.getString(R.string.app_name) // Create the channel for the notification val mChannel = NotificationChannel("channel_01", name, NotificationManager.IMPORTANCE_HIGH) // Set the Notification Channel for the Notification Manager. mNotificationManager?.createNotificationChannel(mChannel) } // Create an explicit content Intent that starts the main Activity. val notificationIntent = Intent(context, MainActivity::class.java) // Construct a task stack. val stackBuilder = TaskStackBuilder.create(context) // Add the main Activity to the task stack as the parent. stackBuilder.addParentStack(MainActivity::class.java) // Push the content Intent onto the stack. stackBuilder.addNextIntent(notificationIntent) // Get a PendingIntent containing the entire back stack. val notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) val defaultSoundUri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) // Get a notification builder that's compatible with platform versions >= 4 val builder = NotificationCompat.Builder(context) // Define the notification settings. builder.setSmallIcon(R.mipmap.ic_launcher) // In a real app, you may want to use a library like Volley // to decode the Bitmap. .setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher)) .setColor(Color.RED) .setContentTitle("You are around at " + placeId) .setSound(defaultSoundUri) .setContentIntent(notificationPendingIntent) // Set the Channel ID for Android O. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId("channel_01") // Channel ID } else { builder.priority = Notification.PRIORITY_HIGH } // Dismiss notification once the user touches it. builder.setAutoCancel(true) // Issue the notification mNotificationManager?.notify(0, builder.build()) } catch (e: Exception) { e.printStackTrace() } } }
package com.example.locationupdatesservicesample import android.Manifest import android.app.Activity import android.app.AlertDialog import android.content.* import android.content.pm.PackageManager import android.location.Location import android.os.Bundle import android.os.IBinder import android.provider.Settings import android.util.Log import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.localbroadcastmanager.content.LocalBroadcastManager import kotlinx.android.synthetic.main.activity_main.* class MainActivity : Activity() { // A reference to the service used to get location updates. private var mService: LocationUpdatesService? = null; // Tracks the bound state of the service. private var mBound: Boolean = false private val MY_PERMISSIONS_REQUEST_LOCATION = 68 private val REQUEST_CHECK_SETTINGS = 129 private var broadcastReceiver: BroadcastReceiver? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this@MainActivity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), MY_PERMISSIONS_REQUEST_LOCATION) } else { Log.e("MainActivity:","Location Permission Already Granted") if (getLocationMode() == 3) { Log.e("MainActivity:","Already set High Accuracy Mode") initializeService() } else { Log.e("MainActivity:","Alert Dialog Shown") showAlertDialog(this@MainActivity) } } broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(contxt: Context?, intent: Intent?) { when (intent?.action) { "NotifyUser" -> { try { val name = intent.getStringExtra("pinned_location_name") val lat = intent.getStringExtra("pinned_location_lat") val long = intent.getStringExtra("pinned_location_long") txt_location_name?.text = "You are around at " + name txt_location_lat?.text = "Pinned Location Latitude: " + lat txt_location_long?.text = "Pinned Location Longitude: " + long } catch (e: Exception) { e.printStackTrace() } } } } } } override fun onResume() { super.onResume() val intentFilter = IntentFilter() intentFilter.addAction("NotifyUser") broadcastReceiver?.let { LocalBroadcastManager.getInstance(this).registerReceiver(it, intentFilter) } } override fun onPause() { broadcastReceiver?.let { LocalBroadcastManager.getInstance(this).unregisterReceiver(it) } super.onPause() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { when (requestCode) { MY_PERMISSIONS_REQUEST_LOCATION -> { if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { Log.e("MainActivity:","Location Permission Granted") if (getLocationMode() == 3) { Log.e("MainActivity:","Already set High Accuracy Mode") initializeService() } else { Log.e("MainActivity:","Alert Dialog Shown") showAlertDialog(this@MainActivity) } } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return } } } private fun showAlertDialog(context: Context?) { try { context?.let { val builder = AlertDialog.Builder(it) builder.setTitle(it.resources.getString(R.string.app_name)) .setMessage("Please select High accuracy Location Mode from Mode Settings") .setPositiveButton(it.resources.getString(android.R.string.ok)) { dialog, which -> dialog.dismiss() startActivityForResult(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), REQUEST_CHECK_SETTINGS) } .setIcon(android.R.drawable.ic_dialog_alert) .setCancelable(false) .show() } } catch (e: Exception) { e.printStackTrace() } } private fun getLocationMode(): Int { return Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_CHECK_SETTINGS) { initializeService() } } // Monitors the state of the connection to the service. private var mServiceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { val binder: LocationUpdatesService.LocalBinder = service as LocationUpdatesService.LocalBinder mService = binder.service mBound = true // Check that the user hasn't revoked permissions by going to Settings. mService?.requestLocationUpdates() } override fun onServiceDisconnected(name: ComponentName?) { mService = null mBound = false } } private fun initializeService(){ bindService(Intent(this, LocationUpdatesService::class.java), mServiceConnection, Context.BIND_AUTO_CREATE) } override fun onStop() { if (mBound) { // Unbind from the service. This signals to the service that this activity is no longer // in the foreground, and the service can respond by promoting itself to a foreground // service. unbindService(mServiceConnection) mBound = false } super.onStop() } }


