GPS service is not enabled on Android versions 12 and above.
Everything worked correctly on earlier versions
I have read this documentation,
but I'm not sure if I have a problem with android:foregroundServiceType since I basically don't use it in the application.
So here is some of my code AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
And code of my vm:
private fun enableGps(activity: FragmentActivity) {
val manager = activity.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(manager)) {
locationManager.enableGps(activity)
}
}
And here is the implementation of the method itself:
override fun enableGps(activity: FragmentActivity) {
if (googleApiClient == null) {
googleApiClient = GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks {
override fun onConnected(bundle: Bundle?) {}
override fun onConnectionSuspended(i: Int) {
googleApiClient?.connect()
}
})
.addOnConnectionFailedListener { connectionResult ->
Timber.e("${connectionResult.errorCode}")
}.build()
googleApiClient?.connect()
}
val locationRequest = LocationRequest.create()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest.interval = 10000
locationRequest.fastestInterval = 5000
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
builder.setAlwaysShow(true)
googleApiClient?.let {
val result: PendingResult<LocationSettingsResult> =
LocationServices.SettingsApi.checkLocationSettings(it, builder.build())
result.setResultCallback { result ->
val status: Status = result.status
when (status.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
// Show the dialog by calling startResolutionForResult(),
status.startResolutionForResult(
activity,
REQUEST_LOCATION
)
} catch (e: IntentSender.SendIntentException) {
Timber.e(e)
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
activity.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
}
}
}
}
}
UPD
Here is full code of my DeviceLocationManager, maybe I 'm requesting my permissions somehow wrong here:
https://gist.github.com/mnewlive/0c0a0c1f7ccb26fe58fd6b0fa5dd1dda
I rewrote the following method because it was deprecated:
override fun isLocationProviderActive(): Boolean {
val providerInfo = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.LOCATION_PROVIDERS_ALLOWED
)
return providerInfo?.isNotEmpty() == true
}
to
override fun isLocationStateEnabled(): Boolean {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return LocationManagerCompat.isLocationEnabled(locationManager)
}
Related
Improved version!
The following code shows how to ACCESS_FINE_LOCATION. While longer than my original code it is preferred because it stops GPS updates when the App is Paused or Stopped.
Due to issues with the Android Studio emulators it also includes a simple watchdog timer reporting the GPS Updates have stopped if no updates are received for approximately 10 seconds
const val PERMISSIONS_REQUEST_ACCESS_LOCATION = 99 // any positive value
class MainActivity : AppCompatActivity() {
private lateinit var tvCurrentTime : TextView
private lateinit var tvSpeed : TextView
private lateinit var tvBearing : TextView
private var myTimer: Timer = Timer()
private var sHourMinuteSecond : String = "00:00:00"
private lateinit var fusedLocationClient : FusedLocationProviderClient
private lateinit var locationCallback: LocationCallback
private var gpsWatchDog : Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvCurrentTime = findViewById<View>(R.id.show_time_textview) as TextView
tvSpeed = findViewById<View>(R.id.speed_textview) as TextView
tvBearing = findViewById<View>(R.id.bearing_textview) as TextView
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this#MainActivity) as FusedLocationProviderClient
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
processNewLocation(locationResult.lastLocation) // only process the lastLocation (may be others)
}
}
}
private fun checkPermissionsForGPSUpdates() {
if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
// Android wants us to request both COARSE and FINE permissions
writeToLog("checkSelfPermission Permission Not Granted so requesting!")
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), PERMISSIONS_REQUEST_ACCESS_LOCATION)
// During requestPermission OnPause() is called
// When user grants permission OnResume() is called which once again calls checkPermissionsForGPSUpdates() when GPS updates should commence
return
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
// only run if we have FINE location
writeToLog("ACCESS_FINE_LOCATION Permission Granted start GPS updates !")
val locationRequest: LocationRequest = LocationRequest.create().apply {
interval = 1000
fastestInterval = 500
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
try {
if (!fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()).isSuccessful) {
writeToLog("requestLocationUpdates() was unsuccessful") // Stackoverflow indicates this may actually be a google error
}
} catch (unlikely: SecurityException) {
writeToLog("Lost location permission. Could not request updates. $unlikely")
}
} else {
writeToLog("Permissions Not Granted so exit")
Toast.makeText(this#MainActivity, "App will not run without GPS" , Toast.LENGTH_LONG).show()
finish()
}
}
private fun updateTimeString() {
// Called by the timer DO NOT put code affecting the UI in here!!!
val systemTime = GregorianCalendar()
sHourMinuteSecond = SimpleDateFormat("HH:mm:ss").format(systemTime.timeInMillis)
// Use the runOnUiThread method to update the display
this.runOnUiThread(updateDisplay)
}
private val updateDisplay = Runnable {
// This method runs in the same thread as the User Interface
// due to continuous crashing of the emulator has been written to be short
tvCurrentTime.text = sHourMinuteSecond
if (++gpsWatchDog > 10) {
tvBearing.text = "No GPS"
}
}
private fun processNewLocation(latestLocation : Location) {
// reset the watchdog since we have received a GPS update
gpsWatchDog = 0
// do something with the latest location
// for now just show the GPS time
val sSpeed = SimpleDateFormat("HH:mm:ss").format(latestLocation.time)
tvSpeed.text = sSpeed
val sBearing = latestLocation.bearing.roundToInt().toString() // no decimal places
tvBearing.text = sBearing
}
override fun onResume() {
super.onResume()
writeToLog("Called onResume()")
// start the GPS updates (after checking user permission)
checkPermissionsForGPSUpdates()
val currentSystemTime = System.currentTimeMillis()
// start a timer to update the time
myTimer = Timer()
val delayToNextWholeSecond = ((currentSystemTime / 1000) + 1) * 1000 - currentSystemTime // Synchronise to whole seconds
myTimer.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
updateTimeString()
}
}, delayToNextWholeSecond, 1000)
}
override fun onPause() {
super.onPause()
writeToLog("onPause()")
myTimer.cancel() // stop the existing timer
myTimer.purge()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
override fun onStop() {
super.onStop()
writeToLog("onStop()")
myTimer.cancel()
myTimer.purge()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
private fun writeToLog(message : String) {
Log.d("SmallGPSDemo", message)
}
}
The original very short example is shown below only to show Android's new RequestPermission() to obtain permissions (and start updates)
class MainActivity : AppCompatActivity() {
private var debugTextView : TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
debugTextView = findViewById<View>(R.id.debug_textview) as TextView
// this checks permission and starts the location updates
requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
private fun processNewLocation(latestLocation : Location) {
// do something with the location
debugTextView!!.text = SimpleDateFormat("hh:mm:ss.SSS").format(System.currentTimeMillis())
}
#SuppressLint("MissingPermission")
private val requestPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// Permissions granted so start GPS updates
val locationRequest: LocationRequest = LocationRequest.create().apply {
interval = 1000
fastestInterval = 500
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
for (location in locationResult.locations) {
processNewLocation(location) // Settings should only give one location but ...
}
}
}
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(this#MainActivity)
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
} else {
Log.d("DEBUG", "permission denied ")
Toast.makeText(this#MainActivity, "This activity will not run unless you allow GPS", Toast.LENGTH_LONG).show()
finish()
}
}
}
Reminder add location permissions to the AndroidManifest file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.reallysmallgpscode">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.ReallySmallGPSCode">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Hopefully others find the code useful
Project API requirements: API 23+.
Location details are available and they work for API 26+, but for API 23 don't.
I have just read the article Get user's location in android API 23
However, tried different solutions and still have no result. What could be the reason?
class TabFragment1 : Fragment() {
var lat: Double = 0.0
var long: Double = 0.0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.let {}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
super.onCreate(savedInstanceState)
val v: View = inflater.inflate(R.layout.fragment_tab1, container, false)
val spinner1 = v.spinnerCategory as Spinner
val buttonSearch = v.buttonSearch as Button
// Search button
buttonSearch.setOnClickListener {
// GET LOCATION
val g = this.activity?.let {
it1 -> geo(it1)
}
if (g != null) {
activity?.let {
if (g[0] == 0.0 && g[1] == 0.0) {
println(" g result ${g[0]}")
alert(getString(R.string.error_no_location))
// Request location updates
if (ActivityCompat.checkSelfPermission(it, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
var permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
permissions = permissions.plus(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
ActivityCompat.requestPermissions(context as Activity, permissions, 0)
}
} else {
// segue
val intent = Intent(it, PostTableViewActivity::class.java)
intent.putExtra("long", g[1].toString())
intent.putExtra("lat", g[0].toString())
startActivity(intent)
}
}
} else {
alert("TurnOn Geolocation!")
}
}
return v
}
geo.kt:
fun geo(context: Context): DoubleArray? {
val locationManager: LocationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val providers: List<String> = locationManager.getProviders(true)
var location: Location? = null
for (i in providers.size - 1 downTo 0) {
if (ActivityCompat.checkSelfPermission(context.applicationContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(context.applicationContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
println(" NO PERMS?? ")
var permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
permissions = permissions.plus(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
ActivityCompat.requestPermissions(context as Activity, permissions,0)
return null
}
location = locationManager.getLastKnownLocation(providers[i])
if (location != null)
break
}
val gps = DoubleArray(2)
if (location != null) {
println(" GEO FUNC has lat, long - ?? ${location.latitude}, ${location.longitude}")
gps[0] = location.latitude
gps[1] = location.longitude
}
return gps
}
API 23 console result:
I/System.out: g result 0.0
API 26 console result:
I/System.out: GEO FUNC has lat, long - ?? 37.421998333333335, -122.08400000000002
Manifest:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
Only this works for API == 23
1. build.gradle
implementation 'com.google.android.gms:play-services-location:17.1.0'
2. permission
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3. context -> gps[latitude, longitude]
private fun requestCurrentLocation(context: Context): DoubleArray? {
val fusedLocationClient: FusedLocationProviderClient by lazy {
LocationServices.getFusedLocationProviderClient(context)
}
var cancellationTokenSource = CancellationTokenSource()
val gps = DoubleArray(2)
// Check Fine permission
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
// Main code
val currentLocationTask: Task<Location> = fusedLocationClient.getCurrentLocation(
PRIORITY_HIGH_ACCURACY,
cancellationTokenSource.token
)
currentLocationTask.addOnCompleteListener { task: Task<Location> ->
val result = if (task.isSuccessful) {
val result: Location = task.result
gps[0] = result.latitude
gps[1] = result.longitude
"Location (success): ${result.latitude}, ${result.longitude}"
} else {
val exception = task.exception
"Location (failure): $exception"
}
println("getCurrentLocation() result: $result")
}
} else {
// Request fine location permission (full code below).
}
return gps
}
For the past months I have been developing an app which a feature of it implements BLE connection from the app to a device. Suddenly today, after a few scans, the app returns everytime 0 device despite obtaining before like 5 or 6.
I have been thinking that it was a permission problem related with coarse location, however, the changes I made it was so little that it should not be affected, but that does not matter
As I followed the bluetooth guide that Google offers amongst other guides, this is my code:
onCreate method:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_guide)
handler = Handler()
devicesResult = ArrayList()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
// Android M Permission check
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
val builder = AlertDialog.Builder(this)
builder.setTitle("This app needs location access")
builder.setMessage("Please grant location access so this app can detect beacons.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener { requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), PERMISSION_REQUEST_COARSE_LOCATION) }
builder.show()
}else{
if (areLocationServicesEnabled(this))
{
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
{
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show()
finish()
}
bluetoothAdapter?.takeIf { it.isDisabled }?.apply {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, 1)
}
//scanLeDevice(true)
}
}
}
}
onRequestPermissionsResult method:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>, grantResults: IntArray
) {
when (requestCode) {
PERMISSION_REQUEST_COARSE_LOCATION -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Log.d(FragmentActivity.TAG, "coarse location permission granted")
} else {
val builder = AlertDialog.Builder(this)
builder.setTitle("Functionality limited")
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener { }
builder.show()
}
return
}
}
}
areLocationServicesEnabled method:
private fun areLocationServicesEnabled(context:Context):Boolean {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return try {
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} catch (e:Exception) {
e.printStackTrace()
false
}
}
onResume method:
override fun onResume() {
super.onResume()
if (bluetoothAdapter != null && !bluetoothAdapter!!.isEnabled)
{
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, 1)
}
mLEScanner = bluetoothAdapter!!.bluetoothLeScanner
scanLeDevice(true)
}
scanLeDevice method:
private fun scanLeDevice(enable:Boolean) {
if (enable)
{
handler!!.postDelayed({
mLEScanner!!.stopScan(mScanCallback)
//selectDevice()
}, SCAN_PERIOD)
mLEScanner!!.startScan(mScanCallback)
}
else
{
mLEScanner!!.stopScan(mScanCallback)
}
}
mScanCallback attribute:
private val mScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
Log.i("callbackType", callbackType.toString())
Log.i("result", result.toString())
devicesResult!!.add(result)
}
override fun onBatchScanResults(results: List<ScanResult>) {
for (sr in results) {
Log.i("ScanResult - Results", sr.toString())
}
}
override fun onScanFailed(errorCode: Int) {
Log.e("Scan Failed", "Error Code: $errorCode")
}
}
As I said before, I expected to show and save on an Array called deviceResult every one of them, however, suddenly, no devices seems to appear after each new scan.
First of all, sorry for the big delay to answer muy question.
After trying not to use the scan more than 6 times between 30 seconds as #Emil pointed out on the last comment fo my question, I finally could make my project work, thanks #Emil.
I have an activity, that is trigered by an event and I use this code, to be able to display it while my device is locked.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
val pm=getSystemService(Context.POWER_SERVICE)as PowerManager
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"FireApp:test2").acquire(1000000)
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
}else{
this.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
}
It is working fine with my device being locked, until i want to display the LocationSettingsRequest dialog using this code:
fun displayLocationSettingsRequest(context: Context) {
val googleApiClient = GoogleApiClient.Builder(context)
.addApi(LocationServices.API).build()
googleApiClient.connect()
val locationRequest = LocationRequest.create()
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
locationRequest.setInterval(5000)
locationRequest.setFastestInterval(2000)
val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest)
builder.setAlwaysShow(true)
val result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build())
result.setResultCallback (object: ResultCallback<LocationSettingsResult> {
override fun onResult(result: LocationSettingsResult) {
val status = result.status
if (status.statusCode == LocationSettingsStatusCodes.SUCCESS) {
Log.d("Martin", "Lokacija je vključena!")
}
if (status.statusCode == LocationSettingsStatusCodes.RESOLUTION_REQUIRED) {
try {
status.startResolutionForResult(this#medAlarmom2, 0x1)
} catch (e: IntentSender.SendIntentException) {
Log.d("Martin", "PendingIntent unable to execute request.")
}
}
if (status.statusCode == LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE) Log.d("Martin", "Location settings are inadequate, and cannot be fixed here. Dialog "+"not created.")
}
})
}
I can't find a good solution, to be able to display this dialog without unlocking the device. Anyone has a solution for this one?
Thanks a milion!
Martin
I have a React-Native application that needs to use GPS location. Below there is a LocationFetcher class that is responsivle for getting current location euther by forcing new location or getting the last one.
Let's say getLocation() method is being invoked at the fixed interval. When I turn off the GPS I would get the messages No location provider found., which is fine. But when I turn the GPS on I would get no messages at all. What is more the promise is neither resolved nor rejected. I could toggle GPS many times and let's say on about 5th time all the promises would be resolved with a current location, then I repeat the process and again and have no location at all.
Sometimes launching another app that uses GPS, for example GPS Viewer would instantly resolve all the promises and sometimes it doesn't. Often toggling GPS on and off would cause no issues and sometimes it would many. Sometimes turning off the Network would have been causing this problem for like 15 minutes and later on it would have no affect.
Did anyone have such problems with GPS?
class LocationFetcher(
val context: Context
) {
companion object {
val LOG_TAG = "LOC_FETCHER"
}
/**
* Gets a location "synchronously" as a Promise
*/
fun getLocation(forceNewLocation: Boolean, promise: Promise) {
try {
if (!areProvidersAvailable()) {
promise.reject(NATIVE_ERROR, "No location provider found.")
return
}
if (!checkForPlayServices()) {
promise.reject(NATIVE_ERROR, "Install Google Play Services First and Try Again.")
return
}
if (!hasPermissions()) {
promise.reject(NATIVE_ERROR, "Appropriate permissions not given.")
return
}
/* --------- */
if (forceNewLocation) {
forceSingleGPSLocationUpdate(promise)
return
}
getLastGPSLocation(promise)
} catch (ex: Exception) {
Log.e(TAG, "Native Location Module ERR - " + ex.toString())
promise.reject(NATIVE_ERROR, ex.toString())
}
}
#RequiresPermission(
anyOf = [
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
]
)
fun getLastGPSLocation(
promise: Promise
) {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
if (locationManager === null) {
Log.e(LOG_TAG, "Location Manager is null")
promise.reject(LOG_TAG, Exception("Location Manager is null"))
return
}
try {
val lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if (lastKnownLocation === null) {
Log.e(LOG_TAG, "Last known location is null")
promise.reject(LOG_TAG, "Last known location is null");
return
}
Log.v(LOG_TAG, "Resolving promise with location")
promise.resolve(convertLocationToJSON(lastKnownLocation))
} catch (e: SecurityException) {
Log.e(LOG_TAG, e.message, e)
promise.reject(LOG_TAG, e)
return
} catch (e: Exception) {
Log.e(LOG_TAG, e.message, e)
promise.reject(LOG_TAG, e)
return
}
}
#RequiresPermission(
anyOf = [
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
]
)
fun forceSingleGPSLocationUpdate(
promise: Promise
) {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
if (locationManager === null) {
Log.e(LOG_TAG, "Location Manager is null")
promise.reject(LOG_TAG, Exception("Location Manager is null"))
return
}
try {
val locationListener = object : LocationListener {
override fun onLocationChanged(location: Location?) {
if (location === null) {
Log.e(LOG_TAG, "Location changed is null")
promise.reject(LOG_TAG, Exception("Location changed is null"))
return
}
Log.v(LOG_TAG, "Resolving promise with location")
promise.resolve(convertLocationToJSON(location))
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, locationListener, null)
} catch (e: SecurityException) {
Log.e(LOG_TAG, e.message, e)
promise.reject(LOG_TAG, e)
return
} catch (e: Exception) {
Log.e(LOG_TAG, e.message, e)
promise.reject(LOG_TAG, e)
return
}
}
fun areProvidersAvailable(): Boolean {
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return try {
lm.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} catch (ex: Exception) {
Log.e(LOG_TAG, ex.toString())
false
}
}
fun hasPermissions(): Boolean {
return ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
// ~ https://stackoverflow.com/questions/
// 22493465/check-if-correct-google-play-service-available-unfortunately-application-has-s
internal fun checkForPlayServices(): Boolean {
val googleApiAvailability = GoogleApiAvailability.getInstance()
val resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context)
if (resultCode != ConnectionResult.SUCCESS) {
if (googleApiAvailability.isUserResolvableError(resultCode)) {
val map = WritableNativeMap().also {
it.putInt("resultCode", resultCode)
it.putInt("resolutionRequest", PLAY_SERVICES_RESOLUTION_REQUEST)
}
sendLocalEventToModule(LOCAL_PLAY_SERVICES_ERROR, map)
}
return false
}
return true
}
internal fun convertLocationToJSON(l: Location?): WritableMap {
if (l === null) {
return WritableNativeMap().also {
it.putString("error", "Received location was null")
}
}
return WritableNativeMap().also {
it.putDouble("latitude", l.latitude)
it.putDouble("longitude", l.longitude)
it.putDouble("accuracy", l.accuracy.toDouble())
it.putDouble("altitude", l.altitude)
it.putDouble("bearing", l.bearing.toDouble())
it.putString("provider", l.provider)
it.putDouble("speed", l.speed.toDouble())
it.putString("timestamp", l.time.toString())
}
}
internal fun sendLocalEventToModule(eventName: String, data: WritableMap) {
val intent = Intent(eventName).also {
it.putExtra("data", WritableMapWrapper(data))
}
Log.v(TAG, "Sending local event ${eventName}")
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
}
}
I don't know if this is important, but we are getting location via Foreground Service, which is presented below.
class ForegroundLocationService : Service() {
lateinit var locationFetcher : LocationFetcher
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_NOT_STICKY
}
override fun onTaskRemoved(rootIntent: Intent?) {
stopSelf()
}
override fun onCreate() {
super.onCreate()
locationFetcher = LocationFetcher(applicationContext)
/**
* Saving this [ForegroundLocationService] reference to the static variable,
* because when binding a Service using [bindService] it would not stop the
* this service, even though the app would close
*/
LocationModule.foregroundLocationService = this
showNotification()
Log.v(TAG, "Creating Foreground Location Service")
}
override fun onDestroy() {
super.onDestroy()
LocationModule.foregroundLocationService = null
locationFetcher.stopLocationUpdates()
Log.v(TAG, "Destroying Foreground Location Service")
}
fun showNotification() {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
} else {
// In Android versions before Oreo channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val notification = NotificationCompat.Builder(this, channelId)
.setOngoing(true)
.setContentTitle(NOTIFICATION_TITLE)
.setSmallIcon(R.mipmap.ic_notification)
.setTicker(NOTIFICATION_TITLE)
.build()
Log.v(TAG, "Showing a notification")
startForeground(NOTIFICATION_ID, notification)
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String {
val channelId = "app_gps_service"
val channelName = NOTIFICATION_TITLE
val chan = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_LOW
)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
Log.v(TAG, "Created notification channel, because SDK version is ${Build.VERSION.SDK_INT}")
return channelId
}
companion object {
val TAG = "ForegroundLocationSvc"
val NOTIFICATION_ID = 101
val NOTIFICATION_TITLE = "GPS Service"
#JvmStatic
fun start(context: Context) {
Log.v(TAG, "Starting Foreground Location Service")
val intent = Intent(context, ForegroundLocationService::class.java)
context.startService(intent)
}
}
}
Below There is our manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.myapp">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:name="android.hardware.location.gps" />
<!-- push notifications permissions -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<permission
android:name="com.ticketing.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="com.ticketing.permission.C2D_MESSAGE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<permission
android:name="android.permission.ACCESS_COARSE_LOCATION"
android:protectionLevel="signature"/>
<permission
android:name="android.permission.ACCESS_FINE_LOCATION"
android:protectionLevel="signature"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
<!-- GPS Receiver -->
<receiver android:name=".gps.GpsLocationReceiver">
<intent-filter>
<action android:name="android.location.PROVIDERS_CHANGED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<service
android:name=".location.ForegroundLocationService"
android:description="#string/foreground_location_service_desc"
android:exported="false"
android:stopWithTask="false">
</service>
</application>
</manifest>