mirror of https://github.com/YTVanced/VancedMicroG
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.
This commit is contained in:
parent
4a5c98491b
commit
6cfc0aa255
|
@ -105,7 +105,11 @@ class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
||||||
private var permissionNeedsHandling: Boolean = false
|
private var permissionNeedsHandling: Boolean = false
|
||||||
private var permissionRequestCode = 33
|
private var permissionRequestCode = 33
|
||||||
private val permissions by lazy {
|
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")
|
arrayOf("android.permission.ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")
|
||||||
} else {
|
} else {
|
||||||
arrayOf("android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")
|
arrayOf("android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")
|
||||||
|
|
|
@ -8,12 +8,13 @@ package org.microg.gms.nearby.core.ui
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
import android.content.Context.LOCATION_SERVICE
|
import android.content.Context.LOCATION_SERVICE
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import androidx.core.content.ContextCompat
|
||||||
import android.view.View
|
|
||||||
import androidx.core.location.LocationManagerCompat
|
import androidx.core.location.LocationManagerCompat
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -31,6 +32,7 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
private lateinit var exposureEnableInfo: Preference
|
private lateinit var exposureEnableInfo: Preference
|
||||||
private lateinit var exposureBluetoothOff: Preference
|
private lateinit var exposureBluetoothOff: Preference
|
||||||
private lateinit var exposureLocationOff: Preference
|
private lateinit var exposureLocationOff: Preference
|
||||||
|
private lateinit var exposureNearbyNotGranted: Preference
|
||||||
private lateinit var exposureBluetoothUnsupported: Preference
|
private lateinit var exposureBluetoothUnsupported: Preference
|
||||||
private lateinit var exposureBluetoothNoAdvertisement: Preference
|
private lateinit var exposureBluetoothNoAdvertisement: Preference
|
||||||
private lateinit var exposureApps: PreferenceCategory
|
private lateinit var exposureApps: PreferenceCategory
|
||||||
|
@ -41,6 +43,7 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
private val handler = Handler()
|
private val handler = Handler()
|
||||||
private val updateStatusRunnable = Runnable { updateStatus() }
|
private val updateStatusRunnable = Runnable { updateStatus() }
|
||||||
private val updateContentRunnable = Runnable { updateContent() }
|
private val updateContentRunnable = Runnable { updateContent() }
|
||||||
|
private var permissionRequestCode = 33
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
addPreferencesFromResource(R.xml.preferences_exposure_notifications)
|
addPreferencesFromResource(R.xml.preferences_exposure_notifications)
|
||||||
|
@ -50,6 +53,7 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
exposureEnableInfo = preferenceScreen.findPreference("pref_exposure_enable_info") ?: exposureEnableInfo
|
exposureEnableInfo = preferenceScreen.findPreference("pref_exposure_enable_info") ?: exposureEnableInfo
|
||||||
exposureBluetoothOff = preferenceScreen.findPreference("pref_exposure_error_bluetooth_off") ?: exposureBluetoothOff
|
exposureBluetoothOff = preferenceScreen.findPreference("pref_exposure_error_bluetooth_off") ?: exposureBluetoothOff
|
||||||
exposureLocationOff = preferenceScreen.findPreference("pref_exposure_error_location_off") ?: exposureLocationOff
|
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
|
exposureBluetoothUnsupported = preferenceScreen.findPreference("pref_exposure_error_bluetooth_unsupported") ?: exposureBluetoothUnsupported
|
||||||
exposureBluetoothNoAdvertisement = preferenceScreen.findPreference("pref_exposure_error_bluetooth_no_advertise") ?: exposureBluetoothNoAdvertisement
|
exposureBluetoothNoAdvertisement = preferenceScreen.findPreference("pref_exposure_error_bluetooth_no_advertise") ?: exposureBluetoothNoAdvertisement
|
||||||
exposureApps = preferenceScreen.findPreference("prefcat_exposure_apps") ?: exposureApps
|
exposureApps = preferenceScreen.findPreference("prefcat_exposure_apps") ?: exposureApps
|
||||||
|
@ -80,12 +84,28 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exposureNearbyNotGranted.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
val nearbyPermissions = arrayOf("android.permission.BLUETOOTH_ADVERTISE", "android.permission.BLUETOOTH_SCAN")
|
||||||
|
requestPermissions(nearbyPermissions, ++permissionRequestCode)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
collectedRpis.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
collectedRpis.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
findNavController().navigate(requireContext(), R.id.openExposureRpis)
|
findNavController().navigate(requireContext(), R.id.openExposureRpis)
|
||||||
true
|
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() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
|
@ -110,6 +130,11 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
val bluetoothSupported = ScannerService.isSupported(appContext)
|
val bluetoothSupported = ScannerService.isSupported(appContext)
|
||||||
val advertisingSupported = if (bluetoothSupported == true) AdvertiserService.isSupported(appContext) else bluetoothSupported
|
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)
|
exposureLocationOff.isVisible = enabled && bluetoothSupported != false && !LocationManagerCompat.isLocationEnabled(appContext.getSystemService(LOCATION_SERVICE) as LocationManager)
|
||||||
exposureBluetoothOff.isVisible = enabled && bluetoothSupported == null && !turningBluetoothOn
|
exposureBluetoothOff.isVisible = enabled && bluetoothSupported == null && !turningBluetoothOn
|
||||||
exposureBluetoothUnsupported.isVisible = enabled && bluetoothSupported == false
|
exposureBluetoothUnsupported.isVisible = enabled && bluetoothSupported == false
|
||||||
|
|
|
@ -76,4 +76,6 @@ Your identity or test result won't be shared with other people."</string>
|
||||||
<string name="exposure_confirm_bluetooth_description">Bluetooth needs to be enabled.</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_location_description">Location access is required.</string>
|
||||||
<string name="exposure_confirm_button">Enable</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>
|
</resources>
|
||||||
|
|
|
@ -31,6 +31,14 @@
|
||||||
app:isPreferenceVisible="false"
|
app:isPreferenceVisible="false"
|
||||||
tools:isPreferenceVisible="true" />
|
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
|
<Preference
|
||||||
android:icon="@drawable/ic_alert"
|
android:icon="@drawable/ic_alert"
|
||||||
android:key="pref_exposure_error_bluetooth_unsupported"
|
android:key="pref_exposure_error_bluetooth_unsupported"
|
||||||
|
|
|
@ -147,7 +147,11 @@ class AdvertiserService : LifecycleService() {
|
||||||
.setTxPowerLevel(AdvertisingSetParameters.TX_POWER_LOW)
|
.setTxPowerLevel(AdvertisingSetParameters.TX_POWER_LOW)
|
||||||
.setConnectable(false)
|
.setConnectable(false)
|
||||||
.build()
|
.build()
|
||||||
advertiser.startAdvertisingSet(params, data, null, null, null, setCallback as AdvertisingSetCallback)
|
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 {
|
} else {
|
||||||
nextSend = nextSend.coerceAtMost(180000)
|
nextSend = nextSend.coerceAtMost(180000)
|
||||||
val settings = Builder()
|
val settings = Builder()
|
||||||
|
@ -156,7 +160,11 @@ class AdvertiserService : LifecycleService() {
|
||||||
.setTxPowerLevel(ADVERTISE_TX_POWER_LOW)
|
.setTxPowerLevel(ADVERTISE_TX_POWER_LOW)
|
||||||
.setConnectable(false)
|
.setConnectable(false)
|
||||||
.build()
|
.build()
|
||||||
advertiser.startAdvertising(settings, data, callback)
|
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 }
|
synchronized(this) { advertising = true }
|
||||||
sendingBytes = payload
|
sendingBytes = payload
|
||||||
|
@ -204,7 +212,11 @@ class AdvertiserService : LifecycleService() {
|
||||||
advertising = false
|
advertising = false
|
||||||
if (Build.VERSION.SDK_INT >= 26) {
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
wantStartAdvertising = true
|
wantStartAdvertising = true
|
||||||
advertiser?.stopAdvertisingSet(setCallback as AdvertisingSetCallback)
|
try {
|
||||||
|
advertiser?.stopAdvertisingSet(setCallback as AdvertisingSetCallback)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
Log.i(TAG, "Tried calling stopAdvertisingSet without android.permission.BLUETOOTH_ADVERTISE permission.", )
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
advertiser?.stopAdvertising(callback)
|
advertiser?.stopAdvertising(callback)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,3 +42,5 @@ const val CLEANUP_INTERVAL = 24 * 60 * 60 * 1000L
|
||||||
|
|
||||||
const val VERSION_1_0: Byte = 0x40
|
const val VERSION_1_0: Byte = 0x40
|
||||||
const val VERSION_1_1: Byte = 0x50
|
const val VERSION_1_1: Byte = 0x50
|
||||||
|
|
||||||
|
const val NOTIFICATION_UPDATE_ACTION = "org.microg.gms.nearby.UPDATE_NOTIFICATION"
|
||||||
|
|
|
@ -12,6 +12,7 @@ import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -53,9 +54,14 @@ class NotifyService : LifecycleService() {
|
||||||
private fun updateNotification() {
|
private fun updateNotification() {
|
||||||
val location = !LocationManagerCompat.isLocationEnabled(getSystemService(Context.LOCATION_SERVICE) as LocationManager)
|
val location = !LocationManagerCompat.isLocationEnabled(getSystemService(Context.LOCATION_SERVICE) as LocationManager)
|
||||||
val bluetooth = BluetoothAdapter.getDefaultAdapter()?.state.let { it != BluetoothAdapter.STATE_ON && it != BluetoothAdapter.STATE_TURNING_ON }
|
val bluetooth = BluetoothAdapter.getDefaultAdapter()?.state.let { it != BluetoothAdapter.STATE_ON && it != BluetoothAdapter.STATE_TURNING_ON }
|
||||||
Log.d(TAG, "notify: location: $location, bluetooth: $bluetooth")
|
val nearbyPermissions = arrayOf("android.permission.BLUETOOTH_ADVERTISE", "android.permission.BLUETOOTH_SCAN")
|
||||||
|
val permissionNeedsHandling = Build.VERSION.SDK_INT >= 31 && nearbyPermissions.any {
|
||||||
|
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
Log.d( TAG,"notify: location: $location, bluetooth: $bluetooth, permissionNeedsHandling: $permissionNeedsHandling")
|
||||||
|
|
||||||
val text: String = when {
|
val text: String = when {
|
||||||
|
permissionNeedsHandling -> getString(R.string.exposure_notify_off_nearby)
|
||||||
location && bluetooth -> getString(R.string.exposure_notify_off_bluetooth_location)
|
location && bluetooth -> getString(R.string.exposure_notify_off_bluetooth_location)
|
||||||
location -> getString(R.string.exposure_notify_off_location)
|
location -> getString(R.string.exposure_notify_off_location)
|
||||||
bluetooth -> getString(R.string.exposure_notify_off_bluetooth)
|
bluetooth -> getString(R.string.exposure_notify_off_bluetooth)
|
||||||
|
@ -105,6 +111,7 @@ class NotifyService : LifecycleService() {
|
||||||
addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
|
addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
|
||||||
if (Build.VERSION.SDK_INT >= 19) addAction(LocationManager.MODE_CHANGED_ACTION)
|
if (Build.VERSION.SDK_INT >= 19) addAction(LocationManager.MODE_CHANGED_ACTION)
|
||||||
addAction(LocationManager.PROVIDERS_CHANGED_ACTION)
|
addAction(LocationManager.PROVIDERS_CHANGED_ACTION)
|
||||||
|
addAction(NOTIFICATION_UPDATE_ACTION)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,14 +121,18 @@ class ScannerService : LifecycleService() {
|
||||||
Log.i(TAG, "Starting scanner for service $SERVICE_UUID for ${SCANNING_TIME_MS}ms")
|
Log.i(TAG, "Starting scanner for service $SERVICE_UUID for ${SCANNING_TIME_MS}ms")
|
||||||
seenAdvertisements = 0
|
seenAdvertisements = 0
|
||||||
wakeLock.acquire()
|
wakeLock.acquire()
|
||||||
scanner.startScan(
|
try {
|
||||||
listOf(ScanFilter.Builder()
|
scanner.startScan(
|
||||||
.setServiceUuid(SERVICE_UUID)
|
listOf(ScanFilter.Builder()
|
||||||
.setServiceData(SERVICE_UUID, byteArrayOf(0), byteArrayOf(0))
|
.setServiceUuid(SERVICE_UUID)
|
||||||
.build()),
|
.setServiceData(SERVICE_UUID, byteArrayOf(0), byteArrayOf(0))
|
||||||
ScanSettings.Builder().build(),
|
.build()),
|
||||||
callback
|
ScanSettings.Builder().build(),
|
||||||
)
|
callback
|
||||||
|
)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
Log.e(TAG, "Couldn't start ScannerService, need android.permission.BLUETOOTH_SCAN permission.")
|
||||||
|
}
|
||||||
scanning = true
|
scanning = true
|
||||||
lastStartTime = System.currentTimeMillis()
|
lastStartTime = System.currentTimeMillis()
|
||||||
handler.postDelayed(stopLaterRunnable, SCANNING_TIME_MS)
|
handler.postDelayed(stopLaterRunnable, SCANNING_TIME_MS)
|
||||||
|
|
|
@ -7,4 +7,5 @@
|
||||||
<string name="exposure_notify_off_bluetooth">Bluetooth needs to be enabled to receive Exposure Notifications.</string>
|
<string name="exposure_notify_off_bluetooth">Bluetooth needs to be enabled to receive Exposure Notifications.</string>
|
||||||
<string name="exposure_notify_off_location">Location access is required to receive Exposure Notifications.</string>
|
<string name="exposure_notify_off_location">Location access is required to receive Exposure Notifications.</string>
|
||||||
<string name="exposure_notify_off_bluetooth_location">Bluetooth and Location access need to be enabled to receive Exposure Notifications.</string>
|
<string name="exposure_notify_off_bluetooth_location">Bluetooth and Location access need to be enabled to receive Exposure Notifications.</string>
|
||||||
|
<string name="exposure_notify_off_nearby">Exposure Notifications require additional permissions to work</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue