地理围栏是现实世界地理区域的虚拟边界。当位置感知设备穿过此虚拟边界时,地理围栏是一种通知方式。
即使在 ios (Flutter) 中应用程序终止后也能持续跟踪位置并生成本地通知
我正在开发一个flutter应用程序来执行地理围栏并生成本地通知,即使应用程序处于后台或终止时也是如此。我使用background_services、flutter_local_notification、geolocator
Android.Manifest.xml Android.Manifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <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" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Geo" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true"> </activity> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <service android:name=".MapsActivity" android:foregroundServiceType="location" /> <!-- Any inner elements would go here. --> <!-- Before you run your application, you need a Google Maps API key. To get one, follow the directions here: https://developers.google.com/maps/documentation/android-sdk/get-api-key Once you have your API key (it starts with "AIza"), define a new property in your project's local.properties file (e.g. MAPS_API_KEY=Aiza...), and replace the "YOUR_API_KEY" string in this file with "${MAPS_API_KEY}". --> <meta-data android:name="com.google.android.geo.API_KEY" android:value="${MAPS_API_KEY}" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <activity android:name=".MapsActivity" android:exported="true" android:label="@string/title_activity_maps"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".GeofenceBroadcastReceiver" android:exported="true" android:permission="TODO"> <intent-filter> <action android:name="com.google.android.gms.location.ACTION_GEOFENCE_TRANSITION" /> </intent-filter> </receiver> </application> </manifest> MapsActivity package com.example.geo import android.os.Build import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.annotation.RequiresApi import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.MarkerOptions import com.example.geo.databinding.ActivityMapsBinding import android.Manifest import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Intent import android.content.pm.PackageManager import android.graphics.Color import android.location.Location import android.os.Looper import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import com.google.android.gms.common.internal.Constants import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.Geofence import com.google.android.gms.location.GeofencingClient import com.google.android.gms.location.GeofencingRequest 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 com.google.android.gms.maps.model.CircleOptions import com.google.android.gms.maps.model.Marker const val CHANNEL_ID = "GeoChannel" class MapsActivity : AppCompatActivity(), OnMapReadyCallback { private lateinit var mMap: GoogleMap private lateinit var binding: ActivityMapsBinding private lateinit var geofencingClient: GeofencingClient private val REQUEST_CODE = 0 private val geo_Id = "MyGeofence" private val entry_latitude = 26.267775 private val entry_longitude = 81.505404 private val geoRad = 282F private val expiration_time = 10000000000000 private lateinit var fusedLocationClient: FusedLocationProviderClient private lateinit var locationCallback: LocationCallback private var permissions = arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.POST_NOTIFICATIONS ) private var geofencelist : MutableList<Geofence> = mutableListOf() @RequiresApi(Build.VERSION_CODES.Q) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMapsBinding.inflate(layoutInflater) setContentView(binding.root) //Notification channel if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Create the NotificationChannel. val name = "Work" val descriptionText = "geofencing" val importance = NotificationManager.IMPORTANCE_HIGH val mChannel = NotificationChannel(CHANNEL_ID, name, importance) mChannel.description = descriptionText // Register the channel with the system. You can't change the importance // or other notification behaviors after this. val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(mChannel) //mChannel. } val textTitle = "You hae checked in " val textContent = "dggdgfdf" // Create an explicit intent for an Activity in your app. val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) // val builder = NotificationCompat.Builder(this, CHANNEL_ID) // .setSmallIcon(R.drawable.notification_icon) // .setContentTitle("My notification") // .setContentText("Hello World!") // .setPriority(NotificationCompat.PRIORITY_DEFAULT) // // Set the intent that fires when the user taps the notification. // .setContentIntent(pendingIntent) // .setAutoCancel(true) //Location fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) //Location permission when { permissions.all { ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED } -> { // Fine and coarse location permissions granted } permissions.any { ActivityCompat.shouldShowRequestPermissionRationale(this, it) } -> { // Show rationale for fine/coarse location // showInContextUI(...) } else -> { // Request fine/coarse location permissions ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE) } } //For background location // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // // Check for background location on API 29+ // ActivityCompat.requestPermissions( // this, // arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), // REQUEST_CODE // ) // } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if ( ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_BACKGROUND_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { // All permissions granted, perform action // performAction(...) } // ActivityCompat.shouldShowRequestPermissionRationale( // this, // Manifest.permission.ACCESS_BACKGROUND_LOCATION // ) -> { // // Show rationale for background location // //showInContextUI(...) else {// Request background location permission ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), REQUEST_CODE ) } } //Geofencing geofencingClient = LocationServices.getGeofencingClient(this) geofencelist.add(Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId(geo_Id) // Set the circular region of this geofence. .setCircularRegion( entry_latitude, entry_longitude, geoRad ) // Set the expiration duration of the geofence. This geofence gets automatically // removed after this period of time. .setExpirationDuration(expiration_time) // Set the transition types of interest. Alerts are only generated for these // transition. We track entry and exit transitions in this sample. .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT ) .setLoiteringDelay(10000) // Create the geofence. .build()) val geofencePendingIntent: PendingIntent by lazy { val intent = Intent(this, GeofenceBroadcastReceiver::class.java) // intent.action = ACTION_GEOFENCE_EVENT val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE } else { PendingIntent.FLAG_UPDATE_CURRENT } PendingIntent.getBroadcast(this, 0, intent, flags) } geofencingClient.addGeofences(getGeofencingRequest(), geofencePendingIntent).run { addOnSuccessListener { // Geofences added val fence = LatLng(entry_latitude, entry_longitude) mMap.addMarker(MarkerOptions().position(fence).title("Marker in center")) mMap.moveCamera(CameraUpdateFactory.newLatLng(fence)) val geofenceCircle = mMap.addCircle( CircleOptions() .center(LatLng(entry_latitude, entry_longitude)) .radius(geoRad.toDouble()) .strokeColor(Color.GREEN) .fillColor(Color.argb(64, 0, 125, 0)) ) } addOnFailureListener { // Failed to add geofences // ... } } // Obtain the SupportMapFragment and get notified when the map is ready to be used. val mapFragment = supportFragmentManager .findFragmentById(R.id.map) as SupportMapFragment mapFragment.getMapAsync(this) } /** * Manipulates the map once available. * This callback is triggered when the map is ready to be used. * This is where we can add markers or lines, add listeners or move the camera. In this case, * we just add a marker near Sydney, Australia. * If Google Play services is not installed on the device, the user will be prompted to install * it inside the SupportMapFragment. This method will only be triggered once the user has * installed Google Play services and returned to the app. */ override fun onMapReady(googleMap: GoogleMap) { mMap = googleMap enableMyLocation() // Live location fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { for (location in locationResult.locations) { val cur = LatLng(location.latitude, location.longitude) mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(cur,20f)) } } } startLocationUpdates() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_CODE -> { // If request is cancelled, the result arrays are empty. if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // Permission is granted. Continue the action or workflow // in your app. } else { // Explain to the user that the feature is unavailable because // the feature requires a permission that the user has denied. // At the same time, respect the user's decision. Don't link to // system settings in an effort to convince the user to change // their decision. } return } // Add other 'when' lines to check for other // permissions this app might request. else -> { // Ignore all other requests. } } } private fun getGeofencingRequest(): GeofencingRequest { return GeofencingRequest.Builder().apply { setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER) addGeofences(geofencelist) }.build() } private fun startLocationUpdates() { // ... request location permissions ... val locationRequest = LocationRequest.create().apply { interval = 100// Update interval in milliseconds fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } if (ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return } fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()) } private fun enableMyLocation() { // Check if location permissions are granted if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { // Enable the blue dot on the map if permissions are granted mMap.isMyLocationEnabled = true } else { // Request location permissions if not granted ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE) } } companion object { internal const val ACTION_GEOFENCE_EVENT = "com.google.android.gms.location.ACTION_GEOFENCE_TRANSITION" } } const val TAGED = "TAGED" GeofenceBroadcastReceiver package com.example.geo import android.content.BroadcastReceiver import android.content.ContentValues.TAG import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.util.Log import androidx.core.app.ActivityCompat import androidx.core.app.NotificationManagerCompat import com.google.android.gms.location.Geofence import com.google.android.gms.location.GeofenceStatusCodes import com.google.android.gms.location.GeofencingEvent import android.Manifest import androidx.core.app.NotificationCompat import com.example.geo.MapsActivity.Companion.ACTION_GEOFENCE_EVENT class GeofenceBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { Log.d(TAGED, "GeofenceBroadcastReceiver triggered") // if (intent != null) { // if (intent.action == ACTION_GEOFENCE_EVENT) { val geofencingEvent = intent?.let { GeofencingEvent.fromIntent(it) } if (geofencingEvent != null && geofencingEvent.hasError()) { val errorMessage = GeofenceStatusCodes.getStatusCodeString(geofencingEvent.errorCode) Log.e(TAGED, "Geofencing error: $errorMessage") return } val geofenceTransition = geofencingEvent?.geofenceTransition Log.d(TAGED, "Geofence transition: $geofenceTransition") if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ) { // Get the geofences that were triggered. A single event can trigger multiple geofences. val triggeringGeofences = geofencingEvent.triggeringGeofences // Get the transition details as a String. val geofenceTransitionDetails = triggeringGeofences?.let { getGeofenceTransitionDetails( context!!, geofenceTransition, it ) } // Send notification and log the transition details. if (geofenceTransitionDetails != null) { sendNotification(context!!, geofenceTransitionDetails) } if (geofenceTransitionDetails != null) { Log.i(TAGED, geofenceTransitionDetails) } } else { Log.e(TAGED, "Geofence transition not handled: $geofenceTransition") } // } // } } private fun sendNotification(context:Context, message: String?) { val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.notification_icon) // Replace with your notification icon .setContentTitle("Geofence Alert") .setContentText(message) .setPriority(NotificationCompat.PRIORITY_HIGH) .setAutoCancel(true) val notificationManager = NotificationManagerCompat.from(context) if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { // Consider requesting the missing permission here return } val notificationId = 123 // You can use a unique ID for each notification notificationManager.notify(notificationId, builder.build()) } private fun getGeofenceTransitionDetails( context: Context,geofenceTransition: Int, triggeringGeofences: List<Geofence> ): String { val geofenceTransitionString = when (geofenceTransition) { Geofence.GEOFENCE_TRANSITION_ENTER -> "Entered" Geofence.GEOFENCE_TRANSITION_EXIT -> "Exited" Geofence.GEOFENCE_TRANSITION_DWELL -> "Dwell" else -> "Unknown Transition" } // Get the Ids of each geofence that was triggered. val triggeringGeofencesIdsList = mutableListOf<String>() for (geofence in triggeringGeofences) { triggeringGeofencesIdsList.add(geofence.requestId) } val triggeringGeofencesIdsString = triggeringGeofencesIdsList.joinToString(", ") return "Geofence transition: $geofenceTransitionString - $triggeringGeofencesIdsString" } } 我尝试获取日志消息,我收到了这些消息 2024-08-25 23:30:36.829 13719-13719 TAGED com.example.geo D GeofenceBroadcastReceiver triggered 2024-08-25 23:30:36.829 13719-13719 TAGED com.example.geo D Geofence transition: null 2024-08-25 23:30:36.829 13719-13719 TAGED com.example.geo E Geofence transition not handled: null why I 1。检查意图操作 <receiver android:name=".GeofenceBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="com.google.android.gms.location.ACTION_GEOFENCE_TRANSITION" /> </intent-filter> </receiver> 在您的 GeofenceBroadcastReceiver 中,检查以下内容: if (intent?.action == "com.google.android.gms.location.ACTION_GEOFENCE_TRANSITION") { // Your geofence processing code here } 2。检查地理围栏事件错误 val geofencingEvent = GeofencingEvent.fromIntent(intent) if (geofencingEvent.hasError()) { val errorMessage = GeofenceStatusCodes.getStatusCodeString(geofencingEvent.errorCode) Log.e(TAGED, "Geofencing error: $errorMessage") return }
GeoFencing:BroadcastReceiver 无法触发
我尝试从Android开发人员运行此应用程序:https://developer.android.com/codelabs/advanced-android-kotlin-training-geofencing?authuser=2#0 正在添加地理围栏,但什么也没有
我想使用 .htaccess 来阻止某个国家/地区,并且如果任何其他国家/地区将访问者重定向到另一个网址
我有这个代码来阻止来自墨西哥的人访问我的域名,但我想知道是否可以添加另一行,将他们重定向到另一个 URL(电子邮件营销页面上的登陆页面)...
我正在使用 google-maps-flutter 插件开发 flutter 项目,我想检查用户位置是否位于我在地图上创建的多边形内。有一个使用 JavaScript api 的简单方法(
我需要在 flutter 中实现地理围栏来预订餐厅,并在应用程序关闭时跟踪餐厅,并且它应该在后台运行,任何人都可以建议一些好的包装...
反应原生:错误不变违规:找不到 RNBackgroundGeolocation.watchPosition 的 cbID 747 和 callID 373 的回调
我目前正在我的 React Native 应用程序中使用react-native-background-geolocation 库来进行地理围栏并更新用户在 Google 地图上的当前位置。一切正常...
不确定这是否与我尝试在react-native模块中使用它有关,它或多或少是一样的——除非在ReactContextBaseJavaModule中初始化它是一个问题。 一切似乎...
iOS 应用程序使用地理围栏来通知用户预定义的附近位置。允许应用程序错过某些位置(用户没有收到有关附近的通知...
我正在开发 Flutter 应用程序,并且正在尝试实现地理围栏功能。我希望能够跟踪用户何时进入或退出预定义的地理区域并执行某些操作...
我制作了一个用于地理围栏的安卓应用程序。 它运作良好,因为我将所有位置传感器和活动搜索移动到与我也放在前台的应用程序关联的服务。 我拿了...
我正在构建一个 Google Maps Javascript API 应用程序,允许用户绘制多条折线的路径,计算角度,然后尝试弯曲角度,模拟车辆转弯......
Geofence - 触发“GEOFENCE_NOT_AVAILABLE”的问题
我使用地理围栏使用应用程序,自从我开始使用它以来,我开始广播接收器转换,当我关闭 GPS 位置时,我收到代码为 GEOFENCE_NOT_AVAILABLE 的警报...
我正在尝试找到一种方法来检查一对纬度/经度坐标是否在一个区域内(由其他纬度/经度坐标生成)。 例如,如果我的区域是用这些坐标生成的矩形...
GeoFencing 在 Flutter 中无法与 Geolocator 一起使用
我在调试构建 flutter 应用程序时遇到此错误。 因为 easy_geofencing 0.2.0 依赖于 geolocator ^7.7.1 并且没有版本的 easy_geofencing match >0.2.0 <0.3.0, easy_geofencing ^0...
我正在寻找一种在移动设备之间获得一米精度的方法,到目前为止还没有找到。我正在寻找gps和基于位置的地理围栏作为一种选择,但我不认为我会得到 ...
我一直在开发一个应用程序,当一个周界被跨越时,它可以将Geofence信息记录在一个文本文件中。由于某些原因,总是取我的Geofence提醒数组中第一个值的名字......。
是系统将它们持久化,还是我必须在重启后再次添加它们?我在https:/developer.android.comtraininglocationgeofencing...的文档中没有找到任何关于这方面的内容。
我是ionic 4的新手,我需要在我的项目中添加地理围栏。添加插件后,无法构建应用程序,并且返回上述错误。 cordova-android:8.1.0,java版本:1 ....