将位置设置为“仅在使用中”时允许时,地理林无法添加地理林。
我们已经实施了一个地理围栏系统,我们的CMS后端操作员可以通过FCM将Geofence数据发送到应用程序。在您在下面看到的Firebaseservice类中,每次发送此类通知时,它都会触发handleslentnotification方法,然后尝试创建一个地理位置对象并将其添加到Geodencing Client。
我们要求在入职过程中访问位置访问,如果未授予位置权限,我们只是忽略了无声通知有效负载。当用户选择“允许所有时间”的位置权限选项时,一切似乎都可以正常工作。收到有效载荷,并创建地理位置并将其添加到客户端以进行跟踪。 当用户选择“仅在使用中允许”时,就会发生问题。
在这种情况下,当我们尝试将Geofence添加到GeoFencingClient中时,它总是通过抛出以下堆栈跟踪而失败:
2022-05-05 11:27:48.107 13685-13685/com.threenitas.ewayprototype I/EwayApp: New location received: 37.95690471,23.72790214
2022-05-05 11:27:55.580 13685-14907/com.threenitas.ewayprototype D/MyFirebaseMessagingService: body = "{\"Id\":\"1234654\",\"Latitude\":37.3495,\"Longitude\":23.4646,\"Radius\":100,\"StatusCode\":\"EnableEvent\",\"Title\":\"test\",\"Description\":\"test\"}"
2022-05-05 11:27:55.593 13685-14907/com.threenitas.ewayprototype D/MyFirebaseMessagingService: New Geofence created: Geofence[CIRCLE id:1234654 transitions:3 37.349500, 23.464600 9999999933815813000000000000000000000m, resp=0s, dwell=-1ms, @-1]
2022-05-05 11:27:55.616 13685-13685/com.threenitas.ewayprototype D/GeofencingManager: Failed to add geofence with id 1234654 to client!
2022-05-05 11:27:55.616 13685-13685/com.threenitas.ewayprototype W/System.err: com.google.android.gms.common.api.ApiException: 1004:
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.common.api.internal.TaskUtil.setResultOrApiException(com.google.android.gms:play-services-base@@18.0.1:4)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.location.zzat.setResult(com.google.android.gms:play-services-location@@18.0.0:2)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.internal.location.zzaw.zzb(com.google.android.gms:play-services-location@@18.0.0:3)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.internal.location.zzaj.zza(com.google.android.gms:play-services-location@@18.0.0:9)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.internal.location.zzb.onTransact(com.google.android.gms:play-services-location@@18.0.0:3)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at android.os.Binder.execTransactInternal(Binder.java:1195)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at android.os.Binder.execTransact(Binder.java:1159)
2022-05-05 11:27:58.087 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 6.101ms
2022-05-05 11:28:06.071 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 5.875ms
2022-05-05 11:28:06.089 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 8.474ms
2022-05-05 11:28:06.106 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 6.250ms
2022-05-05 11:28:06.136 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 6.388ms
这是Google结束的设计决定,例如此错误跟踪器帖子似乎声称或我在实现中缺少一些东西?
app.kt:
class App : Application() {
lateinit var appContext: Context
lateinit var preferences: SharedPreferences
lateinit var editor: SharedPreferences.Editor
lateinit var geofencingClient: GeofencingClient
val inactiveGeofenceIds = arrayListOf<String>()
val geofenceEventInfo = mutableMapOf<String, GeoEventData>()
val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(appContext, GeofenceBroadcastReceiver::class.java)
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
PendingIntent.getBroadcast(appContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
val locationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
Log.i("EwayApp", "New location received: ${location.latitude},${location.longitude}")
}
override fun onLocationChanged(locations: MutableList<Location>) {}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
fun isGeofencingClientInitialized() = this::geofencingClient.isInitialized
...........
}
firbasemessagingservice:
class MyFirebaseMessagingService : FirebaseMessagingService() {
private val TAG = MyFirebaseMessagingService::class.java.simpleName
private var geofencingManager: GeofencingManager = getKoin().get()
override fun onNewToken(token: String) {
super.onNewToken(token)
NotificationHub.getInstance(App.instance)
.registerForPushNotifications(this, fetchIntegrationIdForLetsPlace())
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// Check if message contains a data payload
remoteMessage.data.isNotEmpty().let {
handleNotification(remoteMessage.data)
}
}
private fun handleNotification(jsonData: MutableMap<String, String>) {
val title = jsonData["Title"]
val description = jsonData["Description"]
val body = jsonData["Body"]
if (title!!.isEmpty()) {
if (hasLocationPermission()) handleSilentNotification(body)
else Log.d(TAG, "No location permission has been granted!")
} else {
showNotification(
title,
description!!
)
}
}
private fun hasLocationPermission() = ContextCompat.checkSelfPermission(
App.instance.appContext,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
private fun showNotification(
title: String,
description: String
) {
// val intentForNotification = Intent(application, SplashScreen::class.java)
// intentForNotification.putExtra("notificationInboxBody", inboxBody)
// intentForNotification.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val hub = NotificationHub.getInstance(App.instance)
val id = hub.lastRandomId ?: 0
hub.lastRandomId = id + 1
//val pendingIntent = PendingIntent.getActivity(application, hub.lastRandomId!! /* Request code */, intentForNotification, PendingIntent.FLAG_UPDATE_CURRENT)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(application, "channel1")
.setSmallIcon(R.drawable.splash_screen_logo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setPriority(Notification.PRIORITY_HIGH)
.setCategory(Notification.CATEGORY_MESSAGE)
.setDefaults(Notification.DEFAULT_ALL)
//.setContentIntent(pendingIntent)
.setContentText(description)
with(NotificationManagerCompat.from(this)) {
// notificationId is a unique int for each notification that you must define
notify(id, notificationBuilder.build())
}
}
private fun handleSilentNotification(body: String?) {
Log.d(TAG, "body = $body")
val geoEventData = jsonToGeoEvent(body)
if (geoEventData == null) {
Log.d(TAG, "Geo event data conversion failed!")
return;
}
if (!App.instance.isGeofencingClientInitialized()) { // app is killed , store the payload for later use
Log.d(TAG, "persist called")
persistGeoData(geoEventData)
return
}
if (geoEventData.statusCode == GeoEventStatus.DisableEvent.name) {
geofencingManager.removeGeofence(geoEventData.id)
return
}
val geofence = geofencingManager.createGeofence(geoEventData)
geofencingManager.storeGeofenceInfo(geofence, geoEventData)
Log.d(TAG, "New Geofence created: $geofence")
geofencingManager.addGeofenceToClient(geofence)
}
private fun persistGeoData(geoEventData: GeoEventData) {
val gson = Gson()
val serializedData = gson.toJson(geoEventData, GeoEventData::class.java)
App.instance.inactiveGeofenceIds.add(geoEventData.id)
App.instance.preferences.edit().putString(geoEventData.id, serializedData).apply()
}
private fun jsonToGeoEvent(body: String?): GeoEventData? {
if (body == null) return null
val gson = Gson()
val json = body.replace("\\", "")
val jo = JSONObject(json.substring(1, json.length - 1))
return gson.fromJson(jo.toString(), GeoEventData::class.java)
}
}
geofencingManagerimpl:
class GeofencingManagerImpl : GeofencingManager {
private val tag: String
get() = GeofencingManager::class.java.simpleName
@SuppressLint("MissingPermission")
override fun addGeofenceToClient(geofence: Geofence) {
App.instance.geofencingClient.addGeofences(
getGeofencingRequest(geofence),
App.instance.geofencePendingIntent
).run {
addOnSuccessListener {
Log.d(tag, "Geofence with id ${geofence.requestId} added to client!")
}
addOnFailureListener {
Log.d(tag, "Failed to add geofence with id ${geofence.requestId} to client!")
it.printStackTrace()
}
}
}
override fun createGeofence(geoEventData: GeoEventData) =
Geofence.Builder()
.setRequestId(geoEventData.id)
.setCircularRegion(
geoEventData.latitude,
geoEventData.longitude,
geoEventData.radius
)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.build()
override fun storeGeofenceInfo(
geofence: Geofence,
geoEventData: GeoEventData
) {
App.instance.geofenceEventInfo[geofence.requestId] = geoEventData
}
override fun getGeofencingRequest(geofence: Geofence): GeofencingRequest {
return GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofence(geofence)
.build()
}
override fun removeGeofence(id: String) {
App.instance.geofenceEventInfo.remove(id)
App.instance.geofencingClient.removeGeofences(listOf(id)).run {
addOnSuccessListener {
Log.d(tag, "Geofence with id $id removed from client!")
}
addOnFailureListener {
Log.d(tag, "Failed to remove geofence with id $id from client!")
it.printStackTrace()
}
}
}
}
Broadcastreceiver:
class GeofenceBroadcastReceiver : BroadcastReceiver() {
private val TAG = GeofenceBroadcastReceiver::class.java.simpleName
override fun onReceive(context: Context?, intent: Intent?) {
val geofencingEvent = GeofencingEvent.fromIntent(intent!!)
if (geofencingEvent.hasError()) {
val errorMessage = GeofenceStatusCodes
.getStatusCodeString(geofencingEvent.errorCode)
Log.e(TAG, errorMessage)
return
}
val geofenceTransition = geofencingEvent.geofenceTransition
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT
) {
val triggeringGeofences = geofencingEvent.triggeringGeofences
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
// user entered or is already inside a geofence
for (geofence in triggeringGeofences) {
val eventData = App.instance.geofenceEventInfo[geofence.requestId] ?: continue
if (eventData.statusCode == GeoEventStatus.EnableEvent.name) showNotification(
eventData.title, eventData.description
)
}
Log.d(TAG, "User entered geofence!")
} else {
Log.d(TAG, "User exited geofence!")
}
} else {
Log.e(
TAG, "Invalid transition type"
)
}
}
private fun showNotification(title: String, description: String) {
// val intentForNotification = Intent(application, SplashScreen::class.java)
// intentForNotification.putExtra("notificationInboxBody", inboxBody)
// intentForNotification.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val hub = NotificationHub.getInstance(App.instance)
val id = hub.lastRandomId ?: 0
hub.lastRandomId = id + 1
//val pendingIntent = PendingIntent.getActivity(application, hub.lastRandomId!! /* Request code */, intentForNotification, PendingIntent.FLAG_UPDATE_CURRENT)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(App.instance.appContext, "channel1")
.setSmallIcon(R.drawable.splash_screen_logo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setPriority(Notification.PRIORITY_HIGH)
.setCategory(Notification.CATEGORY_MESSAGE)
.setDefaults(Notification.DEFAULT_ALL)
//.setContentIntent(pendingIntent)
.setContentText(description)
with(NotificationManagerCompat.from(App.instance.appContext)) {
// notificationId is a unique int for each notification that you must define
notify(id, notificationBuilder.build())
}
}
}
subtest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.threenitas.ewayprototype">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:name=".App"
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.EWayPrototype"
android:usesCleartextTraffic="true">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
<receiver android:name=".geofencing.GeofenceBroadcastReceiver"/>
<service android:foregroundServiceType="location" android:enabled="true" android:name=".geofencing.GeofenceForegroundService"/>
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<activity
android:name=".MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
</application>
</manifest>
We have implemented a Geofencing system where our CMS backend operators can send geofence data to the app through FCM. Inside the FirebaseService class that you see below, every time such a notification is sent it triggers the handleSilentNotification method which then attempts to create a Geofence object and add it to the Geofencing client.
We are requesting location access during the onboarding process and if the location permission was not granted, we just ignore the silent notification payload. When the user selects the "Allow all the time" location permission option, everything seems to work fine. Payloads are received, and geofences are created and added to the client for tracking.
The issue occurs when the user selects "Allow only while in use".
In this case, when we attempt to add the geofence to the GeofencingClient, it always fails by throwing the following stack trace:
2022-05-05 11:27:48.107 13685-13685/com.threenitas.ewayprototype I/EwayApp: New location received: 37.95690471,23.72790214
2022-05-05 11:27:55.580 13685-14907/com.threenitas.ewayprototype D/MyFirebaseMessagingService: body = "{\"Id\":\"1234654\",\"Latitude\":37.3495,\"Longitude\":23.4646,\"Radius\":100,\"StatusCode\":\"EnableEvent\",\"Title\":\"test\",\"Description\":\"test\"}"
2022-05-05 11:27:55.593 13685-14907/com.threenitas.ewayprototype D/MyFirebaseMessagingService: New Geofence created: Geofence[CIRCLE id:1234654 transitions:3 37.349500, 23.464600 9999999933815813000000000000000000000m, resp=0s, dwell=-1ms, @-1]
2022-05-05 11:27:55.616 13685-13685/com.threenitas.ewayprototype D/GeofencingManager: Failed to add geofence with id 1234654 to client!
2022-05-05 11:27:55.616 13685-13685/com.threenitas.ewayprototype W/System.err: com.google.android.gms.common.api.ApiException: 1004:
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.common.api.internal.TaskUtil.setResultOrApiException(com.google.android.gms:play-services-base@@18.0.1:4)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.location.zzat.setResult(com.google.android.gms:play-services-location@@18.0.0:2)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.internal.location.zzaw.zzb(com.google.android.gms:play-services-location@@18.0.0:3)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.internal.location.zzaj.zza(com.google.android.gms:play-services-location@@18.0.0:9)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at com.google.android.gms.internal.location.zzb.onTransact(com.google.android.gms:play-services-location@@18.0.0:3)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at android.os.Binder.execTransactInternal(Binder.java:1195)
2022-05-05 11:27:55.617 13685-13685/com.threenitas.ewayprototype W/System.err: at android.os.Binder.execTransact(Binder.java:1159)
2022-05-05 11:27:58.087 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 6.101ms
2022-05-05 11:28:06.071 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 5.875ms
2022-05-05 11:28:06.089 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 8.474ms
2022-05-05 11:28:06.106 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 6.250ms
2022-05-05 11:28:06.136 13685-14822/com.threenitas.ewayprototype W/s.ewayprototyp: Suspending all threads took: 6.388ms
Is this a design decision on google's end like this bug tracker post seems to claim or am I missing something in my implementation?
App.kt:
class App : Application() {
lateinit var appContext: Context
lateinit var preferences: SharedPreferences
lateinit var editor: SharedPreferences.Editor
lateinit var geofencingClient: GeofencingClient
val inactiveGeofenceIds = arrayListOf<String>()
val geofenceEventInfo = mutableMapOf<String, GeoEventData>()
val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(appContext, GeofenceBroadcastReceiver::class.java)
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
PendingIntent.getBroadcast(appContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
val locationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
Log.i("EwayApp", "New location received: ${location.latitude},${location.longitude}")
}
override fun onLocationChanged(locations: MutableList<Location>) {}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
fun isGeofencingClientInitialized() = this::geofencingClient.isInitialized
...........
}
FirebaseMessagingService:
class MyFirebaseMessagingService : FirebaseMessagingService() {
private val TAG = MyFirebaseMessagingService::class.java.simpleName
private var geofencingManager: GeofencingManager = getKoin().get()
override fun onNewToken(token: String) {
super.onNewToken(token)
NotificationHub.getInstance(App.instance)
.registerForPushNotifications(this, fetchIntegrationIdForLetsPlace())
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// Check if message contains a data payload
remoteMessage.data.isNotEmpty().let {
handleNotification(remoteMessage.data)
}
}
private fun handleNotification(jsonData: MutableMap<String, String>) {
val title = jsonData["Title"]
val description = jsonData["Description"]
val body = jsonData["Body"]
if (title!!.isEmpty()) {
if (hasLocationPermission()) handleSilentNotification(body)
else Log.d(TAG, "No location permission has been granted!")
} else {
showNotification(
title,
description!!
)
}
}
private fun hasLocationPermission() = ContextCompat.checkSelfPermission(
App.instance.appContext,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
private fun showNotification(
title: String,
description: String
) {
// val intentForNotification = Intent(application, SplashScreen::class.java)
// intentForNotification.putExtra("notificationInboxBody", inboxBody)
// intentForNotification.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val hub = NotificationHub.getInstance(App.instance)
val id = hub.lastRandomId ?: 0
hub.lastRandomId = id + 1
//val pendingIntent = PendingIntent.getActivity(application, hub.lastRandomId!! /* Request code */, intentForNotification, PendingIntent.FLAG_UPDATE_CURRENT)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(application, "channel1")
.setSmallIcon(R.drawable.splash_screen_logo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setPriority(Notification.PRIORITY_HIGH)
.setCategory(Notification.CATEGORY_MESSAGE)
.setDefaults(Notification.DEFAULT_ALL)
//.setContentIntent(pendingIntent)
.setContentText(description)
with(NotificationManagerCompat.from(this)) {
// notificationId is a unique int for each notification that you must define
notify(id, notificationBuilder.build())
}
}
private fun handleSilentNotification(body: String?) {
Log.d(TAG, "body = $body")
val geoEventData = jsonToGeoEvent(body)
if (geoEventData == null) {
Log.d(TAG, "Geo event data conversion failed!")
return;
}
if (!App.instance.isGeofencingClientInitialized()) { // app is killed , store the payload for later use
Log.d(TAG, "persist called")
persistGeoData(geoEventData)
return
}
if (geoEventData.statusCode == GeoEventStatus.DisableEvent.name) {
geofencingManager.removeGeofence(geoEventData.id)
return
}
val geofence = geofencingManager.createGeofence(geoEventData)
geofencingManager.storeGeofenceInfo(geofence, geoEventData)
Log.d(TAG, "New Geofence created: $geofence")
geofencingManager.addGeofenceToClient(geofence)
}
private fun persistGeoData(geoEventData: GeoEventData) {
val gson = Gson()
val serializedData = gson.toJson(geoEventData, GeoEventData::class.java)
App.instance.inactiveGeofenceIds.add(geoEventData.id)
App.instance.preferences.edit().putString(geoEventData.id, serializedData).apply()
}
private fun jsonToGeoEvent(body: String?): GeoEventData? {
if (body == null) return null
val gson = Gson()
val json = body.replace("\\", "")
val jo = JSONObject(json.substring(1, json.length - 1))
return gson.fromJson(jo.toString(), GeoEventData::class.java)
}
}
GeofencingManagerImpl:
class GeofencingManagerImpl : GeofencingManager {
private val tag: String
get() = GeofencingManager::class.java.simpleName
@SuppressLint("MissingPermission")
override fun addGeofenceToClient(geofence: Geofence) {
App.instance.geofencingClient.addGeofences(
getGeofencingRequest(geofence),
App.instance.geofencePendingIntent
).run {
addOnSuccessListener {
Log.d(tag, "Geofence with id ${geofence.requestId} added to client!")
}
addOnFailureListener {
Log.d(tag, "Failed to add geofence with id ${geofence.requestId} to client!")
it.printStackTrace()
}
}
}
override fun createGeofence(geoEventData: GeoEventData) =
Geofence.Builder()
.setRequestId(geoEventData.id)
.setCircularRegion(
geoEventData.latitude,
geoEventData.longitude,
geoEventData.radius
)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.build()
override fun storeGeofenceInfo(
geofence: Geofence,
geoEventData: GeoEventData
) {
App.instance.geofenceEventInfo[geofence.requestId] = geoEventData
}
override fun getGeofencingRequest(geofence: Geofence): GeofencingRequest {
return GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofence(geofence)
.build()
}
override fun removeGeofence(id: String) {
App.instance.geofenceEventInfo.remove(id)
App.instance.geofencingClient.removeGeofences(listOf(id)).run {
addOnSuccessListener {
Log.d(tag, "Geofence with id $id removed from client!")
}
addOnFailureListener {
Log.d(tag, "Failed to remove geofence with id $id from client!")
it.printStackTrace()
}
}
}
}
BroadcastReceiver:
class GeofenceBroadcastReceiver : BroadcastReceiver() {
private val TAG = GeofenceBroadcastReceiver::class.java.simpleName
override fun onReceive(context: Context?, intent: Intent?) {
val geofencingEvent = GeofencingEvent.fromIntent(intent!!)
if (geofencingEvent.hasError()) {
val errorMessage = GeofenceStatusCodes
.getStatusCodeString(geofencingEvent.errorCode)
Log.e(TAG, errorMessage)
return
}
val geofenceTransition = geofencingEvent.geofenceTransition
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT
) {
val triggeringGeofences = geofencingEvent.triggeringGeofences
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
// user entered or is already inside a geofence
for (geofence in triggeringGeofences) {
val eventData = App.instance.geofenceEventInfo[geofence.requestId] ?: continue
if (eventData.statusCode == GeoEventStatus.EnableEvent.name) showNotification(
eventData.title, eventData.description
)
}
Log.d(TAG, "User entered geofence!")
} else {
Log.d(TAG, "User exited geofence!")
}
} else {
Log.e(
TAG, "Invalid transition type"
)
}
}
private fun showNotification(title: String, description: String) {
// val intentForNotification = Intent(application, SplashScreen::class.java)
// intentForNotification.putExtra("notificationInboxBody", inboxBody)
// intentForNotification.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val hub = NotificationHub.getInstance(App.instance)
val id = hub.lastRandomId ?: 0
hub.lastRandomId = id + 1
//val pendingIntent = PendingIntent.getActivity(application, hub.lastRandomId!! /* Request code */, intentForNotification, PendingIntent.FLAG_UPDATE_CURRENT)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(App.instance.appContext, "channel1")
.setSmallIcon(R.drawable.splash_screen_logo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setPriority(Notification.PRIORITY_HIGH)
.setCategory(Notification.CATEGORY_MESSAGE)
.setDefaults(Notification.DEFAULT_ALL)
//.setContentIntent(pendingIntent)
.setContentText(description)
with(NotificationManagerCompat.from(App.instance.appContext)) {
// notificationId is a unique int for each notification that you must define
notify(id, notificationBuilder.build())
}
}
}
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.threenitas.ewayprototype">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:name=".App"
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.EWayPrototype"
android:usesCleartextTraffic="true">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
<receiver android:name=".geofencing.GeofenceBroadcastReceiver"/>
<service android:foregroundServiceType="location" android:enabled="true" android:name=".geofencing.GeofenceForegroundService"/>
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<activity
android:name=".MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
</application>
</manifest>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Android Geopencing 文档>
,则第二次权限为“允许所有时间”权限。
The Android geofencing documentation clearly says:
That second permission is the "Allow all the time" permission.