[O] Split permissions activity
This commit is contained in:
@@ -18,6 +18,9 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.WearSync"
|
android:theme="@style/Theme.WearSync"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".ActivityPermissions"
|
||||||
|
android:exported="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ActivityScan"
|
android:name=".ActivityScan"
|
||||||
android:exported="false" />
|
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
|
package org.hydev.wearsync
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
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.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.provider.Settings
|
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.welie.blessed.BluetoothPeripheral
|
import com.welie.blessed.BluetoothPeripheral
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@@ -25,7 +12,6 @@ import kotlinx.coroutines.flow.consumeAsFlow
|
|||||||
import org.hydev.wearsync.bles.BluetoothHandler
|
import org.hydev.wearsync.bles.BluetoothHandler
|
||||||
import org.hydev.wearsync.bles.ObservationUnit
|
import org.hydev.wearsync.bles.ObservationUnit
|
||||||
import org.hydev.wearsync.databinding.ActivityScanBinding
|
import org.hydev.wearsync.databinding.ActivityScanBinding
|
||||||
import timber.log.Timber
|
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -35,50 +21,18 @@ class ActivityScan : AppCompatActivity() {
|
|||||||
|
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
private val dateFormat: DateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm:ss", Locale.ENGLISH)
|
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 lateinit var bluetoothHandler: BluetoothHandler
|
||||||
|
|
||||||
|
|
||||||
private fun askToEnableBluetooth() {
|
|
||||||
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
|
||||||
enableBluetoothRequest.launch(enableBtIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityScanBinding.inflate(layoutInflater)
|
binding = ActivityScanBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
registerReceiver(
|
|
||||||
locationServiceStateReceiver,
|
|
||||||
IntentFilter(LocationManager.MODE_CHANGED_ACTION)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
initBluetoothHandler()
|
||||||
super.onResume()
|
|
||||||
if (bluetoothManager.adapter != null) {
|
|
||||||
if (!isBluetoothEnabled) askToEnableBluetooth() else checkPermissions()
|
|
||||||
} else {
|
|
||||||
Timber.e("This device has no Bluetooth hardware")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val isBluetoothEnabled: Boolean
|
|
||||||
get() = bluetoothManager.adapter?.isEnabled ?: false
|
|
||||||
|
|
||||||
private val central get() = bluetoothHandler.central
|
private val central get() = bluetoothHandler.central
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
@@ -89,7 +43,7 @@ class ActivityScan : AppCompatActivity() {
|
|||||||
println("OnCreate called, Initializing...")
|
println("OnCreate called, Initializing...")
|
||||||
|
|
||||||
// List bonded device addresses
|
// List bonded device addresses
|
||||||
val pairedDevices = bluetoothManager.adapter.bondedDevices.toList()
|
val pairedDevices = blueMan.adapter.bondedDevices.toList()
|
||||||
val pairedAddresses = pairedDevices.map { it.address }.toSet()
|
val pairedAddresses = pairedDevices.map { it.address }.toSet()
|
||||||
|
|
||||||
// Scan devices
|
// 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
|
package org.hydev.wearsync
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothManager
|
||||||
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
|
||||||
fun View.snack(msg: String) = Snackbar.make(this, msg, Snackbar.LENGTH_LONG)
|
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
|
package org.hydev.wearsync
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.bluetooth.BluetoothAdapter
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import org.hydev.wearsync.ActivityPermissions.Companion.hasPermissions
|
||||||
import org.hydev.wearsync.databinding.ActivityMainBinding
|
import org.hydev.wearsync.databinding.ActivityMainBinding
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity()
|
class MainActivity : AppCompatActivity()
|
||||||
@@ -17,6 +20,21 @@ class MainActivity : AppCompatActivity()
|
|||||||
val pref get() = getSharedPreferences("settings", Context.MODE_PRIVATE)
|
val pref get() = getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||||
val mac get() = pref.getString("device", "None")
|
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")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState: Bundle?)
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
{
|
{
|
||||||
@@ -35,13 +53,20 @@ class MainActivity : AppCompatActivity()
|
|||||||
|
|
||||||
println("owo")
|
println("owo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasPermissions()) startActivity(Intent(this, ActivityPermissions::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume()
|
||||||
|
{
|
||||||
|
super.onResume()
|
||||||
|
ensureBluetooth()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean
|
override fun onCreateOptionsMenu(menu: Menu): Boolean
|
||||||
{
|
{
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
menuInflater.inflate(R.menu.menu_main, menu)
|
menuInflater.inflate(R.menu.menu_main, menu)
|
||||||
|
|
||||||
return true
|
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