[O] Split permissions activity
This commit is contained in:
@@ -18,6 +18,9 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.WearSync"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ActivityPermissions"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ActivityScan"
|
||||
android:exported="false" />
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.hydev.wearsync
|
||||
|
||||
import android.Manifest
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class ActivityPermissions : AppCompatActivity()
|
||||
{
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_permissions)
|
||||
|
||||
checkPermissions()
|
||||
}
|
||||
|
||||
private fun checkPermissions() {
|
||||
val missing = getMissingPermissions()
|
||||
if (missing.isNotEmpty()) {
|
||||
requestPermissions(missing.toTypedArray(), ACCESS_LOCATION_REQUEST)
|
||||
}
|
||||
else finish()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
println(grantResults.map { it })
|
||||
|
||||
// Check if all permission were granted
|
||||
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) finish()
|
||||
else {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("Location permission is required for scanning Bluetooth peripherals")
|
||||
.setMessage("Please grant permissions")
|
||||
.setPositiveButton("Retry") { dialogInterface, _ ->
|
||||
dialogInterface.cancel()
|
||||
checkPermissions()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ACCESS_LOCATION_REQUEST = 2
|
||||
|
||||
fun Context.hasPermissions() = getMissingPermissions().isEmpty()
|
||||
|
||||
fun Context.requiredPermissions(): Array<String>
|
||||
{
|
||||
val sdk = applicationInfo.targetSdkVersion
|
||||
return if (SDK_INT >= Build.VERSION_CODES.S && sdk >= Build.VERSION_CODES.S) {
|
||||
arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
|
||||
} else if (SDK_INT >= Build.VERSION_CODES.Q && sdk >= Build.VERSION_CODES.Q) {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
} else arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
}
|
||||
|
||||
fun Context.getMissingPermissions(): List<String> {
|
||||
return requiredPermissions().filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,10 @@
|
||||
package org.hydev.wearsync
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.welie.blessed.BluetoothPeripheral
|
||||
import kotlinx.coroutines.*
|
||||
@@ -25,7 +12,6 @@ import kotlinx.coroutines.flow.consumeAsFlow
|
||||
import org.hydev.wearsync.bles.BluetoothHandler
|
||||
import org.hydev.wearsync.bles.ObservationUnit
|
||||
import org.hydev.wearsync.databinding.ActivityScanBinding
|
||||
import timber.log.Timber
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@@ -35,50 +21,18 @@ class ActivityScan : AppCompatActivity() {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private val dateFormat: DateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm:ss", Locale.ENGLISH)
|
||||
private val enableBluetoothRequest =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
// Bluetooth has been enabled
|
||||
checkPermissions()
|
||||
} else {
|
||||
// Bluetooth has not been enabled, try again
|
||||
askToEnableBluetooth()
|
||||
}
|
||||
}
|
||||
|
||||
private val bluetoothManager by lazy { getSystemService(BLUETOOTH_SERVICE) as BluetoothManager }
|
||||
|
||||
private val blueMan by lazy { blueMan() }
|
||||
private lateinit var bluetoothHandler: BluetoothHandler
|
||||
|
||||
|
||||
private fun askToEnableBluetooth() {
|
||||
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||
enableBluetoothRequest.launch(enableBtIntent)
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityScanBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
registerReceiver(
|
||||
locationServiceStateReceiver,
|
||||
IntentFilter(LocationManager.MODE_CHANGED_ACTION)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (bluetoothManager.adapter != null) {
|
||||
if (!isBluetoothEnabled) askToEnableBluetooth() else checkPermissions()
|
||||
} else {
|
||||
Timber.e("This device has no Bluetooth hardware")
|
||||
}
|
||||
initBluetoothHandler()
|
||||
}
|
||||
|
||||
private val isBluetoothEnabled: Boolean
|
||||
get() = bluetoothManager.adapter?.isEnabled ?: false
|
||||
|
||||
private val central get() = bluetoothHandler.central
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@@ -89,7 +43,7 @@ class ActivityScan : AppCompatActivity() {
|
||||
println("OnCreate called, Initializing...")
|
||||
|
||||
// List bonded device addresses
|
||||
val pairedDevices = bluetoothManager.adapter.bondedDevices.toList()
|
||||
val pairedDevices = blueMan.adapter.bondedDevices.toList()
|
||||
val pairedAddresses = pairedDevices.map { it.address }.toSet()
|
||||
|
||||
// Scan devices
|
||||
@@ -213,134 +167,4 @@ class ActivityScan : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterReceiver(locationServiceStateReceiver)
|
||||
}
|
||||
|
||||
private val locationServiceStateReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val action = intent.action
|
||||
if (action != null && action == LocationManager.MODE_CHANGED_ACTION) {
|
||||
val isEnabled = areLocationServicesEnabled()
|
||||
Timber.i("Location service state changed to: %s", if (isEnabled) "on" else "off")
|
||||
checkPermissions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPeripheral(peripheralAddress: String): BluetoothPeripheral {
|
||||
return bluetoothHandler.central.getPeripheral(peripheralAddress)
|
||||
}
|
||||
|
||||
private fun checkPermissions() {
|
||||
val missingPermissions = getMissingPermissions(requiredPermissions)
|
||||
if (missingPermissions.isNotEmpty()) {
|
||||
requestPermissions(missingPermissions, ACCESS_LOCATION_REQUEST)
|
||||
} else {
|
||||
checkIfLocationIsNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMissingPermissions(requiredPermissions: Array<String>): Array<String> {
|
||||
val missingPermissions: MutableList<String> = ArrayList()
|
||||
for (requiredPermission in requiredPermissions) {
|
||||
if (checkSelfPermission(requiredPermission) != PackageManager.PERMISSION_GRANTED) {
|
||||
missingPermissions.add(requiredPermission)
|
||||
}
|
||||
}
|
||||
return missingPermissions.toTypedArray()
|
||||
}
|
||||
|
||||
private val requiredPermissions: Array<String>
|
||||
get() {
|
||||
val targetSdkVersion = applicationInfo.targetSdkVersion
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && targetSdkVersion >= Build.VERSION_CODES.S) {
|
||||
arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && targetSdkVersion >= Build.VERSION_CODES.Q) {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
} else arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
}
|
||||
|
||||
private fun checkIfLocationIsNeeded() {
|
||||
val targetSdkVersion = applicationInfo.targetSdkVersion
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && targetSdkVersion < Build.VERSION_CODES.S) {
|
||||
// Check if Location services are on because they are required to make scanning work for SDK < 31
|
||||
if (checkLocationServices()) {
|
||||
initBluetoothHandler()
|
||||
}
|
||||
} else {
|
||||
initBluetoothHandler()
|
||||
}
|
||||
}
|
||||
|
||||
private fun areLocationServicesEnabled(): Boolean {
|
||||
val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
locationManager.isLocationEnabled
|
||||
} else {
|
||||
val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
val isNetworkEnabled =
|
||||
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
isGpsEnabled || isNetworkEnabled
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLocationServices(): Boolean {
|
||||
return if (!areLocationServicesEnabled()) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("Location services are not enabled")
|
||||
.setMessage("Scanning for Bluetooth peripherals requires locations services to be enabled.") // Want to enable?
|
||||
.setPositiveButton("Enable") { dialogInterface, _ ->
|
||||
dialogInterface.cancel()
|
||||
startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ ->
|
||||
// if this button is clicked, just close
|
||||
// the dialog box and do nothing
|
||||
dialog.cancel()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
|
||||
// Check if all permission were granted
|
||||
var allGranted = true
|
||||
for (result in grantResults) {
|
||||
if (result != PackageManager.PERMISSION_GRANTED) {
|
||||
allGranted = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allGranted) {
|
||||
checkIfLocationIsNeeded()
|
||||
} else {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle("Location permission is required for scanning Bluetooth peripherals")
|
||||
.setMessage("Please grant permissions")
|
||||
.setPositiveButton("Retry") { dialogInterface, _ ->
|
||||
dialogInterface.cancel()
|
||||
checkPermissions()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_ENABLE_BT = 1
|
||||
private const val ACCESS_LOCATION_REQUEST = 2
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
package org.hydev.wearsync
|
||||
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
fun View.snack(msg: String) = Snackbar.make(this, msg, Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show()
|
||||
.setAction("Action", null).show()
|
||||
|
||||
fun Context.blueMan() = getSystemService(AppCompatActivity.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package org.hydev.wearsync
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.hydev.wearsync.ActivityPermissions.Companion.hasPermissions
|
||||
import org.hydev.wearsync.databinding.ActivityMainBinding
|
||||
|
||||
class MainActivity : AppCompatActivity()
|
||||
@@ -17,6 +20,21 @@ class MainActivity : AppCompatActivity()
|
||||
val pref get() = getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val mac get() = pref.getString("device", "None")
|
||||
|
||||
val enableBluetoothRequest =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
// Bluetooth has been enabled
|
||||
} else {
|
||||
// Bluetooth has not been enabled, try again
|
||||
ensureBluetooth()
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureBluetooth() {
|
||||
if (blueMan().adapter?.isEnabled == false)
|
||||
enableBluetoothRequest.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE))
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
@@ -35,13 +53,20 @@ class MainActivity : AppCompatActivity()
|
||||
|
||||
println("owo")
|
||||
}
|
||||
|
||||
if (!hasPermissions()) startActivity(Intent(this, ActivityPermissions::class.java))
|
||||
}
|
||||
|
||||
override fun onResume()
|
||||
{
|
||||
super.onResume()
|
||||
ensureBluetooth()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean
|
||||
{
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ActivityPermissions">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Required permissions are not available.\nPlease allow permissions"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user