Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6cfc0aa2 authored by Marcus Hoffmann's avatar Marcus Hoffmann Committed by Marvin W.
Browse files

request BLUETOOTH_SCAN and ADVERTISE permissions on Android 12

This also adds a warning notification when the app doesn't have the
required permission after an OS update.
parent 4a5c9849
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -105,7 +105,11 @@ class ExposureNotificationsConfirmActivity : AppCompatActivity() {
    private var permissionNeedsHandling: Boolean = false
    private var permissionRequestCode = 33
    private val permissions by lazy {
        if (Build.VERSION.SDK_INT >= 29) {
        if (Build.VERSION.SDK_INT >= 31){
            arrayOf("android.permission.BLUETOOTH_ADVERTISE", "android.permission.BLUETOOTH_SCAN", "android.permission.ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")

        }
        else if (Build.VERSION.SDK_INT >= 29) {
            arrayOf("android.permission.ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")
        } else {
            arrayOf("android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")
+27 −2
Original line number Diff line number Diff line
@@ -8,12 +8,13 @@ package org.microg.gms.nearby.core.ui
import android.bluetooth.BluetoothAdapter
import android.content.Context.LOCATION_SERVICE
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.provider.Settings
import android.util.Log
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.location.LocationManagerCompat
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope
@@ -31,6 +32,7 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
    private lateinit var exposureEnableInfo: Preference
    private lateinit var exposureBluetoothOff: Preference
    private lateinit var exposureLocationOff: Preference
    private lateinit var exposureNearbyNotGranted: Preference
    private lateinit var exposureBluetoothUnsupported: Preference
    private lateinit var exposureBluetoothNoAdvertisement: Preference
    private lateinit var exposureApps: PreferenceCategory
@@ -41,6 +43,7 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
    private val handler = Handler()
    private val updateStatusRunnable = Runnable { updateStatus() }
    private val updateContentRunnable = Runnable { updateContent() }
    private var permissionRequestCode = 33

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        addPreferencesFromResource(R.xml.preferences_exposure_notifications)
@@ -50,6 +53,7 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
        exposureEnableInfo = preferenceScreen.findPreference("pref_exposure_enable_info") ?: exposureEnableInfo
        exposureBluetoothOff = preferenceScreen.findPreference("pref_exposure_error_bluetooth_off") ?: exposureBluetoothOff
        exposureLocationOff = preferenceScreen.findPreference("pref_exposure_error_location_off") ?: exposureLocationOff
        exposureNearbyNotGranted = preferenceScreen.findPreference("pref_exposure_error_nearby_not_granted") ?: exposureNearbyNotGranted
        exposureBluetoothUnsupported = preferenceScreen.findPreference("pref_exposure_error_bluetooth_unsupported") ?: exposureBluetoothUnsupported
        exposureBluetoothNoAdvertisement = preferenceScreen.findPreference("pref_exposure_error_bluetooth_no_advertise") ?: exposureBluetoothNoAdvertisement
        exposureApps = preferenceScreen.findPreference("prefcat_exposure_apps") ?: exposureApps
@@ -80,12 +84,28 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
            true
        }

        exposureNearbyNotGranted.onPreferenceClickListener = Preference.OnPreferenceClickListener {
            val nearbyPermissions = arrayOf("android.permission.BLUETOOTH_ADVERTISE", "android.permission.BLUETOOTH_SCAN")
            requestPermissions(nearbyPermissions, ++permissionRequestCode)
            true
        }

        collectedRpis.onPreferenceClickListener = Preference.OnPreferenceClickListener {
            findNavController().navigate(requireContext(), R.id.openExposureRpis)
            true
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == this.permissionRequestCode) {
            updateStatus()
            // Tell the NotifyService that it should update the notification
            val intent = Intent(NOTIFICATION_UPDATE_ACTION)
            requireContext().sendBroadcast(intent)
        }
    }

    override fun onResume() {
        super.onResume()

@@ -110,6 +130,11 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
            val bluetoothSupported = ScannerService.isSupported(appContext)
            val advertisingSupported = if (bluetoothSupported == true) AdvertiserService.isSupported(appContext) else bluetoothSupported

            val nearbyPermissions = arrayOf("android.permission.BLUETOOTH_ADVERTISE", "android.permission.BLUETOOTH_SCAN")
            val nearbyPermissionsGranted = Build.VERSION.SDK_INT >= 31 || nearbyPermissions.all {
                ContextCompat.checkSelfPermission(appContext, it) == PackageManager.PERMISSION_GRANTED
            }
            exposureNearbyNotGranted.isVisible = enabled && !nearbyPermissionsGranted
            exposureLocationOff.isVisible = enabled && bluetoothSupported != false && !LocationManagerCompat.isLocationEnabled(appContext.getSystemService(LOCATION_SERVICE) as LocationManager)
            exposureBluetoothOff.isVisible = enabled && bluetoothSupported == null && !turningBluetoothOn
            exposureBluetoothUnsupported.isVisible = enabled && bluetoothSupported == false
+2 −0
Original line number Diff line number Diff line
@@ -76,4 +76,6 @@ Your identity or test result won&apos;t be shared with other people."</string>
    <string name="exposure_confirm_bluetooth_description">Bluetooth needs to be enabled.</string>
    <string name="exposure_confirm_location_description">Location access is required.</string>
    <string name="exposure_confirm_button">Enable</string>
    <string name="pref_exposure_error_nearby_not_granted_title">New Permissions required</string>
    <string name="pref_exposure_error_nearby_not_granted_description">Tap to grant required permissions to Exposure Notifications</string>
</resources>
+8 −0
Original line number Diff line number Diff line
@@ -31,6 +31,14 @@
        app:isPreferenceVisible="false"
        tools:isPreferenceVisible="true" />

    <Preference
        android:icon="@drawable/ic_info_outline"
        android:key="pref_exposure_error_nearby_not_granted"
        android:title="@string/pref_exposure_error_nearby_not_granted_title"
        android:summary="@string/pref_exposure_error_nearby_not_granted_description"
        app:isPreferenceVisible="false"
        tools:isPreferenceVisible="true" />

    <Preference
        android:icon="@drawable/ic_alert"
        android:key="pref_exposure_error_bluetooth_unsupported"
+15 −3
Original line number Diff line number Diff line
@@ -147,7 +147,11 @@ class AdvertiserService : LifecycleService() {
                        .setTxPowerLevel(AdvertisingSetParameters.TX_POWER_LOW)
                        .setConnectable(false)
                        .build()
                try {
                    advertiser.startAdvertisingSet(params, data, null, null, null, setCallback as AdvertisingSetCallback)
                } catch (e: SecurityException) {
                    Log.e(TAG, "Couldn't start advertising: Need android.permission.BLUETOOTH_ADVERTISE permission.", )
                }
            } else {
                nextSend = nextSend.coerceAtMost(180000)
                val settings = Builder()
@@ -156,7 +160,11 @@ class AdvertiserService : LifecycleService() {
                        .setTxPowerLevel(ADVERTISE_TX_POWER_LOW)
                        .setConnectable(false)
                        .build()
                try {
                    advertiser.startAdvertising(settings, data, callback)
                } catch (e: SecurityException) {
                    Log.e(TAG, "Couldn't start advertising: Need android.permission.BLUETOOTH_ADVERTISE permission.", )
                }
            }
            synchronized(this) { advertising = true }
            sendingBytes = payload
@@ -204,7 +212,11 @@ class AdvertiserService : LifecycleService() {
        advertising = false
        if (Build.VERSION.SDK_INT >= 26) {
            wantStartAdvertising = true
            try {
                advertiser?.stopAdvertisingSet(setCallback as AdvertisingSetCallback)
            } catch (e: SecurityException) {
                Log.i(TAG, "Tried calling stopAdvertisingSet without android.permission.BLUETOOTH_ADVERTISE permission.", )
            }
        } else {
            advertiser?.stopAdvertising(callback)
        }
Loading