使用地理围栏时如何从 BroadcastReceiver 通知调用活动

如何解决使用地理围栏时如何从 BroadcastReceiver 通知调用活动

我根据地理围栏文档定义了一个 broacastReceiver,以便在用户与地理围栏交互时接收进入和退出更新。我的问题是该应用程序将在路上使用,因此,当用户开车并进入地理围栏时,我会收到有关它的通知,当他退出时也会发生同样的情况。但是,当收到退出事件时,我需要从客户端和 Google 地图地图中删除触发的地理围栏。这两个都存在于我的 MapsActivity(这是我根据文档设置接收器和事件通知流程的地方),所以我想从接收器调用我的活动的 removeGeofences(...) 方法。我查看了大量关于此问题的帖子,但似乎没有一个涵盖地理围栏用例。我尝试通过代码动态声明接收器,而不是通过清单静态声明,但在这种情况下,我需要在地理围栏中找不到的意图过滤器。关于如何实现这一目标的任何想法?

广播接收器:

class GeofenceReceiver : broadcastReceiver() {
    private val TAG = GeofenceReceiver::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,"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 mMap: GoogleMap? = null
    private var geofenceClient: GeofencingClient? = null
    private var geofenceList: ArrayList<Geofence> = arraylistof()
    private var geofenceMapMarks: MutableMap<String,Pair<Marker,Circle>> = mutableMapOf()
    private var currLocationMarker: Marker? = null

    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this,GeofenceReceiver::class.java)
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences()
        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 onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)

        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)

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

    @SuppressLint("InlinedApi")
    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))
        geofenceMapMarks["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(Geofence.NEVER_EXPIRE)

                // 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 (geofenceMapMarks.keys.contains(id)) geofenceMapMarks.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() {
        super.onDestroy()
        mMap = null
        geofenceClient = null
    }

    @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
                    )
                } else grantFailed = true
            }
            3 -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this,"GRANTED second",Toast.LENGTH_SHORT).show()
                } 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
    }
}

清单:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.spap.geofencepoc">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.GeoFencePoC"
        tools:ignore="AllowBackup">

        <receiver android:name=".receivers.GeofenceReceiver"/>

        <Meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity
            android:name=".presentation.MapsActivity"
            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>
    </application>

</manifest>

解决方法

您可以使用 SharesPreference 将触发的 geoFenceId 通知给 MainActivity。

class GeofenceReceiver : BroadcastReceiver() {
    private val TAG = GeofenceReceiver::class.java.simpleName
    private var geoFencePref: SharedPreferences? = null
    private val triggeredExitGeofenceIds: HashSet<String> = HashSet()
    private var triggedGeofenceIdsList: ArrayList<String> = ArrayList()

    override fun onReceive(context: Context?,intent: Intent?) {
        geoFencePref = context?.getSharedPreferences("TriggerdExitedId",Context.MODE_PRIVATE)
        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
            storeGeofenceTransitionDetails(geofenceTransition,triggeringGeofences)


            for (geofence in triggeringGeofences) Log.d(TAG,"Geofence ${geofence.requestId} triggered!")

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

    private fun storeGeofenceTransitionDetails(
        geofenceTransition: Int,triggeredGeofences: List<Geofence>
    ) {
        triggeredExitGeofenceIds.clear()
        for (geofence in triggeredGeofences) {
            triggedGeofenceIdsList.add(geofence.requestId)
            if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
                triggeredExitGeofenceIds.add(geofence.requestId)
            }
        }
        geoFencePref?.edit()?.putStringSet("geoFenceId",triggeredExitGeofenceIds)?.apply()
    }
}  

//然后在 MainActivity 中注册 sharedpreference 来监听变化。

class MapsActivity : AppCompatActivity(),OnMapReadyCallback,SharedPreferences.OnSharedPreferenceChangeListener {

....

    override fun onStart() {
        super.onStart()
        requestPermissionsIfNeeded()
        geoFencePref = getSharedPreferences("TriggerdExitedId",Context.MODE_PRIVATE)
        geoFencePref!!.registerOnSharedPreferenceChangeListener(this)
    }

....

    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?,key: String?) {
        val triggeredExitFences: HashSet<String>
        val triggeredGeofences = ArrayList<String>()
        if (key != null) {
            Log.d("onSharedChanged: ",key)
        }
        if (key.equals("geoFenceId")) {
            triggeredExitFences = geoFencePref?.getStringSet("geoFenceId",null) as HashSet<String>
            if(triggeredExitFences.isEmpty()) Log.d("onSharedChanged: ","no exit fences triggered")
            triggeredGeofences.addAll(triggeredExitFences)
            for(fence in triggeredExitFences) Log.d("onSharedChanged: ","ID: $fence triggered!")
            //Here you can call removeGeoFencesFromClient() to unRegister geoFences and removeGeofencesFromMap() to remove marker.
            // removeGeofencesFromClient(triggerdIdList);
            // removeGeofencesFromMap(triggerdIdList);
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?