在自定义 BroadcastReceiver 中接收地理围栏事件

问题描述

我正在使用 Google 的地理围栏 API 在我的 geofenceReceiver 类中接收地理围栏事件,但我还需要从我的活动中调用一个方法,所以我想我会在实例化过程中将它作为参数传递。问题是文档仅向您展示了如何使用清单中声明的​​静态接收器来执行此操作。当我尝试使用意图过滤器,手动注册和取消注册接收器并从 pendingIntent 块发送广播时,块内的代码没有被调用,因此我没有收到任何更新。但是,当通过清单静态声明接收器时,我可以很好地接收更新。我在这里遗漏了一些明显的东西吗?

地理围栏接收器:

class GeofenceReceiver(private val activity: MapsActivity?) : broadcastReceiver() {
    private val TAG = GeofenceReceiver::class.java.simpleName

    override fun onReceive(context: Context?,intent: Intent?) {
        Log.d(TAG,"update received! Activity: $activity")
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        if (geofencingEvent.hasError()) {
            val errorMessage = GeofenceStatusCodes
                .getStatusCodeString(geofencingEvent.errorCode)
            Log.e(TAG,"error: $errorMessage")
            return
        }

        // Get the transition type.
        val geofenceTransition = geofencingEvent.geofenceTransition

        // Test that the reported transition was of interest.
        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

            val ids = arraylistof<String>()
            for (geofence in triggeringGeofences) {
                Log.d(TAG,"Geofence ${geofence.requestId} triggered!")
                ids.add(geofence.requestId)
            }

            if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER)
                Log.d(TAG,"User entered geofence!")
            else {
                Log.d(TAG,"User exited geofence!")
                //activity.removeGeofences(ids)
            }
        } else {
            // Log the error.
            Log.e(TAG,"Invalid transition")
        }
    }

地图活动:

class MapsActivity : AppCompatActivity(),OnMapReadyCallback {

    private var serviceRunning = false
    private var mMap: GoogleMap? = null
    private var geofenceClient: GeofencingClient? = null
    private var geofenceList: ArrayList<Geofence> = arraylistof()
    private var markerCirclePairs: MutableMap<String,Pair<Marker,Circle>> = mutableMapOf()
    private var currLocationMarker: Marker? = null
    private val geofenceReceiver = GeofenceReceiver(this)

    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this,GeofenceReceiver::class.java)
        intent.action = "com.spap.geofencepoc.GEOFENCE_EVENT_ACTION"
        Log.d("MapsActivity","Pending intent block called")
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences().
        sendbroadcast(intent)
        PendingIntent.getbroadcast(this,intent,PendingIntent.FLAG_UPDATE_CURRENT)
    }

    private val mLocationListener: LocationListener = LocationListener {
        currLocationMarker?.remove()
        currLocationMarker = mMap?.addMarker(
            MarkerOptions().position(LatLng(it.latitude,it.longitude)).title("Current Location")
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
        )
        animateCameraToLocation(currLocationMarker?.position!!)
    }

    override fun onStart() {
        super.onStart()
        requestPermissionsIfNeeded()
    }

    override fun onPause() {
        super.onPause()
        if (!serviceRunning) {
            Intent(this,GeofenceBackgroundService::class.java).also { intent ->
                startService(intent)
            }
            serviceRunning = true
        }
        (getSystemService(LOCATION_SERVICE) as LocationManager).removeUpdates(mLocationListener)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)
        val filter = IntentFilter("com.spap.geofencepoc.GEOFENCE_EVENT_ACTION")
        registerReceiver(geofenceReceiver,filter)
        geofenceClient = LocationServices.getGeofencingClient(this)
        // 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)
    }

    override fun onResume() {
        super.onResume()
        val mLocationManager = getSystemService(LOCATION_SERVICE) as LocationManager
        if (!shouldRequestPermissions()) subscribetoLiveCurrentLocation(mLocationManager)
    }

    @SuppressLint("MissingPermission")
    private fun subscribetoLiveCurrentLocation(mLocationManager: LocationManager) {
        mLocationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER,1000,50F,mLocationListener
        )
    }

    private fun requestPermissionsIfNeeded() {
        if (!shouldRequestPermissions()) return

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
            ActivityCompat.requestPermissions(
                this,arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),2
            )
        else ActivityCompat.requestPermissions(
            this,1
        )
    }

    private fun shouldRequestPermissions(): Boolean = ContextCompat.checkSelfPermission(
        this,Manifest.permission.ACCESS_FINE_LOCATION
    ) != PackageManager.PERMISSION_GRANTED
            && ContextCompat.checkSelfPermission(
        this,Manifest.permission.ACCESS_BACKGROUND_LOCATION
    ) != PackageManager.PERMISSION_GRANTED

    /**
     * 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

        mMap?.setonMapClickListener { latlong ->
            drawGeofenceOnMap(latlong)
            addGeofencetoList(latlong)
        }
    }

    private fun drawGeofenceOnMap(latlong: LatLng) {
        val marker = mMap?.addMarker(MarkerOptions().position(latlong).title("Geofence"))
        val circle = mMap?.addCircle(
            CircleOptions().center(latlong).radius(defaultGeofenceRadius.todouble()).strokeColor(
                resources.getColor(android.R.color.holo_red_light,null)
            ).fillColor(
                resources.getColor(android.R.color.transparent,null)
            )
        )
        mMap?.moveCamera(CameraUpdateFactory.newLatLng(latlong))
        markerCirclePairs.put("Geofence #${(geofenceList.size + 1)}",Pair(marker!!,circle!!))
    }

    private fun animateCameraToLocation(latlong: LatLng) {
        val cameraPosition = CameraPosition.Builder()
            .target(latlong)
            .zoom(17f)
            .build()

        mMap?.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
    }

    private fun addGeofencetoList(latlong: LatLng) {
        geofenceList.add(
            Geofence.Builder()
                // Set the request ID of the geofence. This is a string to identify this
                // geofence.
                .setRequestId("Geofence #" + (geofenceList.size + 1))

                // Set the circular region of this geofence.
                .setCircularRegion(
                    latlong.latitude,latlong.longitude,defaultGeofenceRadius
                )

                // Set the expiration duration of the geofence. This geofence gets automatically
                // removed after this period of time.
                .setExpirationDuration(10000)

                // 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)

                // Create the geofence.
                .build()
        )
    }

    private fun getGeofencingRequest(): GeofencingRequest {
        return GeofencingRequest.Builder().apply {
            setinitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            addGeofences(geofenceList)
        }.build()
    }

    private fun addGeofences() {
        if (ActivityCompat.checkSelfPermission(
                this,Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            Toast.makeText(this,"Need to grant permissions to continue",Toast.LENGTH_LONG).show()
            requestPermissionsIfNeeded()
            return
        }
        geofenceClient?.addGeofences(getGeofencingRequest(),geofencePendingIntent)?.run {
            addOnSuccessListener {
                Toast.makeText(this@MapsActivity,"Geofences added!",Toast.LENGTH_LONG).show()
            }
            addOnFailureListener {
                Toast.makeText(this@MapsActivity,"Failed to add geofences!",Toast.LENGTH_LONG)
                    .show()
                Log.d("MAPSACTIVITY",it.message.toString())
            }
        }
    }

    fun removeGeofences(ids: ArrayList<String>) {
        removeGeofencesFromClient(ids)
        removeGeofencesFromMap(ids)
    }

    private fun removeGeofencesFromMap(ids: ArrayList<String>) {
        for (id in ids) {
            if (markerCirclePairs.keys.contains(id)) markerCirclePairs.remove(id)
        }
    }

    private fun removeGeofencesFromClient(ids: ArrayList<String>) {
        for (fence in geofenceList) {
            if (ids.contains(fence.requestId)) geofenceList.remove(fence)
        }

        geofenceClient?.removeGeofences(ids)
            ?.addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Toast.makeText(
                        this,"Geofences have been removed!",Toast.LENGTH_LONG
                    ).show()
                } else {
                    Toast.makeText(
                        this,"Failed to remove geofences",Toast.LENGTH_LONG
                    ).show()
                    Log.d("MAPSACTIVITY","error ==> " + task.exception)
                }
            }
    }

    override fun onDestroy() {
        Toast.makeText(this,"activity destroyed!",Toast.LENGTH_SHORT).show()
        super.onDestroy()
        unregisterReceiver(geofenceReceiver)
        mMap = null
        geofenceClient = null
        serviceRunning = false
        stopService(Intent(this,GeofenceBackgroundService::class.java))
    }

    @SuppressLint("InlinedApi")
    override fun onRequestPermissionsResult(
        requestCode: Int,permissions: Array<out String>,grantResults: IntArray
    ) {
        var grantFailed = false
        when (requestCode) {
            1 -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this,"GRANTED",Toast.LENGTH_SHORT).show()
                } else grantFailed = true
            }
            2 -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this,"GRANTED first",Toast.LENGTH_SHORT).show()
                    ActivityCompat.requestPermissions(
                        this,arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),3
                    )
                    subscribetoLiveCurrentLocation(getSystemService(LOCATION_SERVICE) as LocationManager)
                } else grantFailed = true
            }
            3 -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this,"GRANTED second",Toast.LENGTH_SHORT).show()
                    subscribetoLiveCurrentLocation(getSystemService(LOCATION_SERVICE) as LocationManager)
                } else grantFailed = true
            }
        }
        if (grantFailed) {
            if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) ||
                shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
            ) {
                Toast.makeText(this,"Show permission rationale",Toast.LENGTH_LONG).show()
            } else Toast.makeText(
                this,"You must grant the requested permissions to continue",Toast.LENGTH_SHORT
            ).show()
        }
    }

    override fun onoptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.addGeofence -> {
                addGeofences()
                true
            }
            else -> super.onoptionsItemSelected(item)
        }
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val inflater = menuInflater
        inflater.inflate(R.menu.options_menu,menu)
        return true
    }
}

我按照官方文档在活动的 onCreate() 中注册接收器并在其 onDestroy() 中取消注册,我正在按照适当的命名约定指定一个意图过滤器。尽管以这种方式执行操作时不会调用 pendingIntent 块。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...