diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0e8590d..fb912c6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,9 @@ android:supportsRtl="true" android:theme="@style/Theme.WearSync" tools:targetApi="31"> + diff --git a/app/src/main/java/org/hydev/wearsync/ActivityPermissions.kt b/app/src/main/java/org/hydev/wearsync/ActivityPermissions.kt new file mode 100644 index 0000000..81bb8f4 --- /dev/null +++ b/app/src/main/java/org/hydev/wearsync/ActivityPermissions.kt @@ -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, 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 + { + 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 { + return requiredPermissions().filter { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/hydev/wearsync/ActivityScan.kt b/app/src/main/java/org/hydev/wearsync/ActivityScan.kt index a9640c3..762501c 100644 --- a/app/src/main/java/org/hydev/wearsync/ActivityScan.kt +++ b/app/src/main/java/org/hydev/wearsync/ActivityScan.kt @@ -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): Array { - val missingPermissions: MutableList = ArrayList() - for (requiredPermission in requiredPermissions) { - if (checkSelfPermission(requiredPermission) != PackageManager.PERMISSION_GRANTED) { - missingPermissions.add(requiredPermission) - } - } - return missingPermissions.toTypedArray() - } - - private val requiredPermissions: Array - 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, - 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 - } } \ No newline at end of file diff --git a/app/src/main/java/org/hydev/wearsync/Extensions.kt b/app/src/main/java/org/hydev/wearsync/Extensions.kt index d9776af..6a9947b 100644 --- a/app/src/main/java/org/hydev/wearsync/Extensions.kt +++ b/app/src/main/java/org/hydev/wearsync/Extensions.kt @@ -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() \ No newline at end of file + .setAction("Action", null).show() + +fun Context.blueMan() = getSystemService(AppCompatActivity.BLUETOOTH_SERVICE) as BluetoothManager diff --git a/app/src/main/java/org/hydev/wearsync/MainActivity.kt b/app/src/main/java/org/hydev/wearsync/MainActivity.kt index 62610b6..c904a0e 100644 --- a/app/src/main/java/org/hydev/wearsync/MainActivity.kt +++ b/app/src/main/java/org/hydev/wearsync/MainActivity.kt @@ -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 } diff --git a/app/src/main/res/layout/activity_permissions.xml b/app/src/main/res/layout/activity_permissions.xml new file mode 100644 index 0000000..69346e5 --- /dev/null +++ b/app/src/main/res/layout/activity_permissions.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file