diff --git a/app/build.gradle b/app/build.gradle index 63ae30dd..9f4ebc24 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,7 +62,6 @@ dependencies { implementation project(':profile_rscs') implementation project(':profile_scanner') - implementation project(':lib_permission') implementation project(":lib_theme") implementation project(":lib_utils") implementation project(":lib_service") diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt index d0f048ca..5eeb527d 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt @@ -2,6 +2,7 @@ package no.nordicsemi.android.nrftoolbox import android.app.Activity import androidx.activity.OnBackPressedCallback +import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -14,70 +15,44 @@ import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import androidx.navigation.navArgument import no.nordicsemi.android.bps.view.BPSScreen import no.nordicsemi.android.cgms.view.CGMScreen import no.nordicsemi.android.csc.view.CSCScreen import no.nordicsemi.android.gls.view.GLSScreen import no.nordicsemi.android.hrs.view.HRSScreen import no.nordicsemi.android.hts.view.HTSScreen -import no.nordicsemi.android.permission.bonding.view.BondingScreen -import no.nordicsemi.android.permission.view.BluetoothNotAvailableScreen -import no.nordicsemi.android.permission.view.BluetoothNotEnabledScreen -import no.nordicsemi.android.permission.view.RequestPermissionScreen import no.nordicsemi.android.prx.view.PRXScreen import no.nordicsemi.android.rscs.view.RSCSScreen -import no.nordicsemi.android.scanner.view.ScanDeviceScreen -import no.nordicsemi.android.scanner.view.ScanDeviceScreenResult import no.nordicsemi.android.theme.view.CloseIconAppBar -import no.nordicsemi.android.utils.exhaustive @Composable internal fun HomeScreen() { val navController = rememberNavController() - val viewModel = hiltViewModel() - val continueAction: () -> Unit = { viewModel.finish() } - val state = viewModel.state.collectAsState().value - - BackHandler { viewModel.navigateUp() } - - NavHost(navController = navController, startDestination = NavDestination.HOME.id) { - composable(NavDestination.HOME.id) { HomeView { viewModel.navigate(it) } } - composable(NavDestination.CSC.id) { CSCScreen { viewModel.navigateUp() } } - composable(NavDestination.HRS.id) { HRSScreen { viewModel.navigateUp() } } - composable(NavDestination.HTS.id) { HTSScreen { viewModel.navigateUp() } } - composable(NavDestination.GLS.id) { GLSScreen { viewModel.navigateUp() } } - composable(NavDestination.BPS.id) { BPSScreen { viewModel.navigateUp() } } - composable(NavDestination.PRX.id) { PRXScreen { viewModel.navigateUp() } } - composable(NavDestination.RSCS.id) { RSCSScreen { viewModel.navigateUp() } } - composable(NavDestination.CGMS.id) { CGMScreen { viewModel.navigateUp() } } - composable(NavDestination.REQUEST_PERMISSION.id) { RequestPermissionScreen(continueAction) } - composable(NavDestination.BLUETOOTH_NOT_AVAILABLE.id) { BluetoothNotAvailableScreen { viewModel.finish() } } - composable(NavDestination.BLUETOOTH_NOT_ENABLED.id) { - BluetoothNotEnabledScreen(continueAction) + val activity = LocalContext.current as Activity + BackHandler { + if (navController.currentDestination?.navigatorName != NavDestination.HOME.id) { + navController.popBackStack() + } else { + activity.finish() } - composable( - NavDestination.DEVICE_NOT_CONNECTED.id, - arguments = listOf(navArgument("args") { type = NavType.StringType }) - ) { - ScanDeviceScreen(it.arguments?.getString(ARGS_KEY)!!) { - when (it) { - ScanDeviceScreenResult.OK -> viewModel.finish() - ScanDeviceScreenResult.CANCEL -> viewModel.navigateUp() - }.exhaustive - } - } - composable(NavDestination.BONDING.id) { BondingScreen(continueAction) } } - LaunchedEffect(state) { - navController.navigate(state.url) + val goHome = { navController.navigate(NavDestination.HOME.id) } + + NavHost(navController = navController, startDestination = NavDestination.HOME.id) { + composable(NavDestination.HOME.id) { HomeView { goHome() } } + composable(NavDestination.CSC.id) { CSCScreen { goHome() } } + composable(NavDestination.HRS.id) { HRSScreen { goHome() } } + composable(NavDestination.HTS.id) { HTSScreen { goHome() } } + composable(NavDestination.GLS.id) { GLSScreen { goHome() } } + composable(NavDestination.BPS.id) { BPSScreen { goHome() } } + composable(NavDestination.PRX.id) { PRXScreen { goHome() } } + composable(NavDestination.RSCS.id) { RSCSScreen { goHome() } } + composable(NavDestination.CGMS.id) { CGMScreen { goHome() } } } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt index dd510234..fafc7fd2 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt @@ -11,10 +11,5 @@ enum class NavDestination(val id: String, val pairingRequired: Boolean) { BPS("bps-screen", false), PRX("prx-screen", true), RSCS("rscs-screen", false), - CGMS("cgms-screen", false), - REQUEST_PERMISSION("request-permission", false), - BLUETOOTH_NOT_AVAILABLE("bluetooth-not-available", false), - BLUETOOTH_NOT_ENABLED("bluetooth-not-enabled", false), - DEVICE_NOT_CONNECTED("device-not-connected/{$ARGS_KEY}", false), - BONDING("bonding", false); + CGMS("cgms-screen", false); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavigationViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavigationViewModel.kt deleted file mode 100644 index 80f81f4e..00000000 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavigationViewModel.kt +++ /dev/null @@ -1,99 +0,0 @@ -package no.nordicsemi.android.nrftoolbox - -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import no.nordicsemi.android.bps.repository.BPS_SERVICE_UUID -import no.nordicsemi.android.cgms.repository.CGMS_UUID -import no.nordicsemi.android.csc.service.CYCLING_SPEED_AND_CADENCE_SERVICE_UUID -import no.nordicsemi.android.gls.repository.GLS_SERVICE_UUID -import no.nordicsemi.android.hrs.service.HR_SERVICE_UUID -import no.nordicsemi.android.hts.service.HT_SERVICE_UUID -import no.nordicsemi.android.permission.tools.NordicBleScanner -import no.nordicsemi.android.permission.tools.PermissionHelper -import no.nordicsemi.android.permission.tools.ScannerStatus -import no.nordicsemi.android.permission.viewmodel.BluetoothPermissionState -import no.nordicsemi.android.prx.service.IMMEDIATE_ALERT_SERVICE_UUID -import no.nordicsemi.android.rscs.service.RSCS_SERVICE_UUID -import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder -import javax.inject.Inject - -@HiltViewModel -class NavigationViewModel @Inject constructor( - private val bleScanner: NordicBleScanner, - private val permissionHelper: PermissionHelper, - private val selectedDevice: SelectedBluetoothDeviceHolder -): ViewModel() { - - val state= MutableStateFlow(NavigationTarget(NavDestination.HOME)) - private var targetDestination = NavDestination.HOME - - fun navigate(destination: NavDestination) { - targetDestination = destination - navigateToNextScreen() - } - - fun navigateUp() { - targetDestination = NavDestination.HOME - state.value = NavigationTarget(NavDestination.HOME) - } - - fun finish() { - if (state.value.destination != targetDestination) { - navigateToNextScreen() - } - } - - private fun getBluetoothState(): BluetoothPermissionState { - return if (!permissionHelper.isRequiredPermissionGranted()) { - BluetoothPermissionState.PERMISSION_REQUIRED - } else when (bleScanner.getBluetoothStatus()) { - ScannerStatus.NOT_AVAILABLE -> BluetoothPermissionState.BLUETOOTH_NOT_AVAILABLE - ScannerStatus.DISABLED -> BluetoothPermissionState.BLUETOOTH_NOT_ENABLED - ScannerStatus.ENABLED -> selectedDevice.device?.let { - if (targetDestination.pairingRequired && selectedDevice.isBondingRequired()) { - BluetoothPermissionState.BONDING_REQUIRED - } else { - BluetoothPermissionState.READY - } - } ?: BluetoothPermissionState.DEVICE_NOT_CONNECTED - } - } - - private fun navigateToNextScreen() { - val destination = when (getBluetoothState()) { - BluetoothPermissionState.PERMISSION_REQUIRED -> NavDestination.REQUEST_PERMISSION - BluetoothPermissionState.BLUETOOTH_NOT_AVAILABLE -> NavDestination.BLUETOOTH_NOT_AVAILABLE - BluetoothPermissionState.BLUETOOTH_NOT_ENABLED -> NavDestination.BLUETOOTH_NOT_ENABLED - BluetoothPermissionState.DEVICE_NOT_CONNECTED -> NavDestination.DEVICE_NOT_CONNECTED - BluetoothPermissionState.BONDING_REQUIRED -> NavDestination.BONDING - BluetoothPermissionState.READY -> targetDestination - } - - val args = if (destination == NavDestination.DEVICE_NOT_CONNECTED) { - createServiceId(targetDestination) - } else { - null - } - state.tryEmit(NavigationTarget(destination, args)) - } - - private fun createServiceId(destination: NavDestination): String { - return when (destination) { - NavDestination.CSC -> CYCLING_SPEED_AND_CADENCE_SERVICE_UUID.toString() - NavDestination.HRS -> HR_SERVICE_UUID.toString() - NavDestination.HTS -> HT_SERVICE_UUID.toString() - NavDestination.GLS -> GLS_SERVICE_UUID.toString() - NavDestination.BPS -> BPS_SERVICE_UUID.toString() - NavDestination.RSCS -> RSCS_SERVICE_UUID.toString() - NavDestination.PRX -> IMMEDIATE_ALERT_SERVICE_UUID.toString() - NavDestination.CGMS -> CGMS_UUID.toString() - NavDestination.HOME, - NavDestination.REQUEST_PERMISSION, - NavDestination.BLUETOOTH_NOT_AVAILABLE, - NavDestination.BLUETOOTH_NOT_ENABLED, - NavDestination.BONDING, - NavDestination.DEVICE_NOT_CONNECTED -> throw IllegalArgumentException("There is no serivce related to the destination: $destination") - } - } -} diff --git a/lib_permission/build.gradle b/lib_permission/build.gradle deleted file mode 100644 index e1151501..00000000 --- a/lib_permission/build.gradle +++ /dev/null @@ -1,15 +0,0 @@ -apply from: rootProject.file("library.gradle") -apply plugin: 'kotlin-parcelize' - -dependencies { - implementation project(":lib_utils") - implementation project(":lib_theme") - implementation project(":lib_service") - - implementation libs.material - implementation libs.google.permissions - - implementation libs.bundles.compose - implementation libs.compose.lifecycle - implementation libs.compose.activity -} diff --git a/lib_permission/src/androidTest/java/no/nordicsemi/android/permission/ExampleInstrumentedTest.kt b/lib_permission/src/androidTest/java/no/nordicsemi/android/permission/ExampleInstrumentedTest.kt deleted file mode 100644 index 29f4fc83..00000000 --- a/lib_permission/src/androidTest/java/no/nordicsemi/android/permission/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package no.nordicsemi.android.permission - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("no.nordicsemi.android.scanner.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/lib_permission/src/main/AndroidManifest.xml b/lib_permission/src/main/AndroidManifest.xml deleted file mode 100644 index 1ac23adc..00000000 --- a/lib_permission/src/main/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/HiltModule.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/HiltModule.kt deleted file mode 100644 index d850d0cb..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/HiltModule.kt +++ /dev/null @@ -1,41 +0,0 @@ -package no.nordicsemi.android.permission - -import android.bluetooth.BluetoothAdapter -import android.content.Context -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import no.nordicsemi.android.permission.bonding.repository.BondingStateObserver -import no.nordicsemi.android.permission.tools.PermissionHelper -import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object HiltModule { - - @Provides - fun createNordicBleScanner(): BluetoothAdapter? { - return BluetoothAdapter.getDefaultAdapter() - } - - @Singleton - @Provides - fun createSelectedBluetoothDeviceHolder(): SelectedBluetoothDeviceHolder { - return SelectedBluetoothDeviceHolder() - } - - @Singleton - @Provides - fun createPermissionHelper(@ApplicationContext context: Context): PermissionHelper { - return PermissionHelper(context) - } - - @Singleton - @Provides - fun createBondingStateObserver(@ApplicationContext context: Context): BondingStateObserver { - return BondingStateObserver(context) - } -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/repository/BondingStateObserver.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/repository/BondingStateObserver.kt deleted file mode 100644 index 22a00dd9..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/repository/BondingStateObserver.kt +++ /dev/null @@ -1,57 +0,0 @@ -package no.nordicsemi.android.permission.bonding.repository - -import android.bluetooth.BluetoothDevice -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.util.Log -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.MutableSharedFlow -import no.nordicsemi.android.service.BondingState - -class BondingStateObserver(private val context: Context) { - - val events: MutableSharedFlow = MutableSharedFlow( - extraBufferCapacity = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) - - fun startObserving() { - context.applicationContext.registerReceiver( - broadcastReceiver, - IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED) - ) - } - - fun stopObserving() { - context.applicationContext.unregisterReceiver(broadcastReceiver) - } - - private val broadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - with(intent) { - if (action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) { - val device = getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) - val previousBondState = getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1) - val bondState = getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1) - val bondTransition = "${previousBondState.toBondStateDescription()} to " + bondState.toBondStateDescription() - Log.w("Bond state change", "${device?.address} bond state changed | $bondTransition") - events.tryEmit(BondingStateChangeEvent(device, bondState)) - } - } - } - - private fun Int.toBondStateDescription() = when(this) { - BluetoothDevice.BOND_BONDED -> "BONDED" - BluetoothDevice.BOND_BONDING -> "BONDING" - BluetoothDevice.BOND_NONE -> "NOT BONDED" - else -> "ERROR: $this" - } - } -} - -data class BondingStateChangeEvent(val device: BluetoothDevice?, private val bondStateValue: Int) { - - val bondState = BondingState.create(bondStateValue) -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingErrorView.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingErrorView.kt deleted file mode 100644 index 1c1178a7..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingErrorView.kt +++ /dev/null @@ -1,32 +0,0 @@ -package no.nordicsemi.android.permission.bonding.view - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import no.nordicsemi.android.permission.R -import no.nordicsemi.android.theme.view.ScreenSection - -@Composable -internal fun BondingErrorView() { - ScreenSection { - Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { - Text( - text = stringResource(id = R.string.bonding_error), - textAlign = TextAlign.Center - ) - } - } -} - -@Preview -@Composable -private fun BondingErrorViewPreview() { - BondingErrorView() -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingInProgressView.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingInProgressView.kt deleted file mode 100644 index 53bde7a2..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingInProgressView.kt +++ /dev/null @@ -1,32 +0,0 @@ -package no.nordicsemi.android.permission.bonding.view - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import no.nordicsemi.android.permission.R -import no.nordicsemi.android.theme.view.ScreenSection - -@Composable -internal fun BondingInProgressView() { - ScreenSection { - Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { - Text( - text = stringResource(id = R.string.bonding_in_progress), - textAlign = TextAlign.Center - ) - } - } -} - -@Preview -@Composable -private fun BondingInProgressViewPreview() { - BondingInProgressView() -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingScreen.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingScreen.kt deleted file mode 100644 index 949e1a39..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingScreen.kt +++ /dev/null @@ -1,25 +0,0 @@ -package no.nordicsemi.android.permission.bonding.view - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.permission.bonding.viewmodel.BondingViewModel -import no.nordicsemi.android.service.BondingState -import no.nordicsemi.android.utils.exhaustive - -@Composable -fun BondingScreen(finishAction: () -> Unit) { - val viewModel: BondingViewModel = hiltViewModel() - val state = viewModel.state.collectAsState().value - - LaunchedEffect("start") { - viewModel.bondDevice() - } - - when (state) { - BondingState.BONDING -> BondingInProgressView() - BondingState.BONDED -> finishAction() - BondingState.NONE -> BondingErrorView() - }.exhaustive -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingSuccessView.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingSuccessView.kt deleted file mode 100644 index eadfd1b9..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/view/BondingSuccessView.kt +++ /dev/null @@ -1,32 +0,0 @@ -package no.nordicsemi.android.permission.bonding.view - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import no.nordicsemi.android.permission.R -import no.nordicsemi.android.theme.view.ScreenSection - -@Composable -internal fun BondingSuccessView() { - ScreenSection { - Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { - Text( - text = stringResource(id = R.string.bonding_success), - textAlign = TextAlign.Center - ) - } - } -} - -@Preview -@Composable -private fun BondingSuccessViewPreview() { - BondingSuccessView() -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/viewmodel/BondingViewModel.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/viewmodel/BondingViewModel.kt deleted file mode 100644 index cf022372..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/bonding/viewmodel/BondingViewModel.kt +++ /dev/null @@ -1,43 +0,0 @@ -package no.nordicsemi.android.permission.bonding.viewmodel - -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.permission.bonding.repository.BondingStateObserver -import no.nordicsemi.android.service.BondingState -import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder -import no.nordicsemi.android.theme.viewmodel.CloseableViewModel -import javax.inject.Inject - -@HiltViewModel -class BondingViewModel @Inject constructor( - private val deviceHolder: SelectedBluetoothDeviceHolder, - private val bondingStateObserver: BondingStateObserver -) : CloseableViewModel() { - - val state = MutableStateFlow(deviceHolder.getBondingState()) - - init { - bondingStateObserver.events.onEach { event -> - event.device?.let { - if (it == deviceHolder.device) { - state.tryEmit(event.bondState) - } else { - state.tryEmit(BondingState.NONE) - } - } ?: state.tryEmit(event.bondState) - }.launchIn(viewModelScope) - bondingStateObserver.startObserving() - } - - fun bondDevice() { - deviceHolder.bondDevice() - } - - override fun onCleared() { - super.onCleared() - bondingStateObserver.stopObserving() - } -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/NordicBleScanner.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/NordicBleScanner.kt deleted file mode 100644 index f6eabd8c..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/NordicBleScanner.kt +++ /dev/null @@ -1,17 +0,0 @@ -package no.nordicsemi.android.permission.tools - -import android.annotation.SuppressLint -import android.bluetooth.BluetoothAdapter -import javax.inject.Inject - -@SuppressLint("MissingPermission") -class NordicBleScanner @Inject constructor(private val bleAdapter: BluetoothAdapter?) { - - fun getBluetoothStatus(): ScannerStatus { - return when { - bleAdapter == null -> ScannerStatus.NOT_AVAILABLE - bleAdapter.isEnabled -> ScannerStatus.ENABLED - else -> ScannerStatus.DISABLED - } - } -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/PermissionHelper.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/PermissionHelper.kt deleted file mode 100644 index 5c0cd743..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/PermissionHelper.kt +++ /dev/null @@ -1,21 +0,0 @@ -package no.nordicsemi.android.permission.tools - -import android.Manifest -import android.content.Context -import android.content.pm.PackageManager -import android.os.Build -import androidx.core.content.ContextCompat - -class PermissionHelper(private val context: Context) { - - fun isRequiredPermissionGranted(): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ContextCompat.checkSelfPermission( - context, - Manifest.permission.BLUETOOTH_CONNECT - ) == PackageManager.PERMISSION_GRANTED - } else { - true - } - } -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/ScannerStatus.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/ScannerStatus.kt deleted file mode 100644 index 129126c2..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/tools/ScannerStatus.kt +++ /dev/null @@ -1,5 +0,0 @@ -package no.nordicsemi.android.permission.tools - -enum class ScannerStatus { - ENABLED, DISABLED, NOT_AVAILABLE -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/view/BluetoothNotAvailableScreen.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/view/BluetoothNotAvailableScreen.kt deleted file mode 100644 index 21a0700e..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/view/BluetoothNotAvailableScreen.kt +++ /dev/null @@ -1,80 +0,0 @@ -package no.nordicsemi.android.permission.view - -import android.app.Activity -import android.bluetooth.BluetoothAdapter -import android.content.Intent -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import no.nordicsemi.android.permission.R -import no.nordicsemi.android.theme.view.BackIconAppBar -import no.nordicsemi.android.theme.view.CloseIconAppBar - -@Composable -fun BluetoothNotAvailableScreen(finish: () -> Unit) { - Column { - CloseIconAppBar(stringResource(id = R.string.scanner__request_permission)) { - finish() - } - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(stringResource(R.string.scanner__bluetooth_not_available)) - } - } -} - -@Composable -fun BluetoothNotEnabledScreen(finish: () -> Unit) { - val contract = ActivityResultContracts.StartActivityForResult() - val launcher = rememberLauncherForActivityResult(contract = contract, onResult = { - if (it.resultCode == Activity.RESULT_OK) { - finish() - } - }) - - Column { - BackIconAppBar(stringResource(id = R.string.scanner__request_permission)) { - finish() - } - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Text( - textAlign = TextAlign.Center, - text = stringResource(id = R.string.scanner__bluetooth_not_enabled) - ) - Spacer(Modifier.height(16.dp)) - Text( - textAlign = TextAlign.Center, - text = stringResource(id = R.string.scanner__bluetooth_open_settings_info) - ) - Spacer(Modifier.height(32.dp)) - Button( - onClick = { launcher.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) } - ) { - Text(text = stringResource(id = R.string.scanner__bluetooth_open_settings)) - } - } - } -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/view/NotConnectedView.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/view/NotConnectedView.kt deleted file mode 100644 index d864d378..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/view/NotConnectedView.kt +++ /dev/null @@ -1,51 +0,0 @@ -package no.nordicsemi.android.permission.view - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import no.nordicsemi.android.permission.R - -@Composable -private fun NotConnectedScreen( - connect: () -> Unit -) { - NotConnectedView(connect) -} - -@Composable -private fun NotConnectedView( - connect: () -> Unit -) { - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = stringResource(id = R.string.csc_no_connection)) - Spacer(modifier = Modifier.height(16.dp)) - Button( - onClick = { connect() } - ) { - Text(text = stringResource(id = R.string.csc_connect)) - } - } -} - -@Preview -@Composable -private fun NotConnectedPreview() { - NotConnectedView { } -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/view/RequestPermissionScreen.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/view/RequestPermissionScreen.kt deleted file mode 100644 index e5a6f7ce..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/view/RequestPermissionScreen.kt +++ /dev/null @@ -1,140 +0,0 @@ -package no.nordicsemi.android.permission.view - -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.provider.Settings -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.core.content.ContextCompat.startActivity -import com.google.accompanist.permissions.ExperimentalPermissionsApi -import com.google.accompanist.permissions.PermissionsRequired -import com.google.accompanist.permissions.rememberMultiplePermissionsState -import no.nordicsemi.android.permission.R -import no.nordicsemi.android.theme.view.BackIconAppBar - -@OptIn(ExperimentalPermissionsApi::class) -@Composable -fun RequestPermissionScreen(finish: () -> Unit) { - val permissionsState = rememberMultiplePermissionsState(listOf( - android.Manifest.permission.BLUETOOTH_CONNECT - )) - - Column { - BackIconAppBar(stringResource(id = R.string.scanner__request_permission)) { - finish() - } - - PermissionsRequired( - multiplePermissionsState = permissionsState, - permissionsNotGrantedContent = { PermissionNotGranted { permissionsState.launchMultiplePermissionRequest() } }, - permissionsNotAvailableContent = { PermissionNotAvailable() } - ) { - finish() - } - } -} - -@Composable -private fun PermissionNotGranted(onClick: () -> Unit) { - val doNotShowRationale = rememberSaveable { mutableStateOf(false) } - - if (doNotShowRationale.value) { - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(stringResource(id = R.string.scanner__feature_not_available)) - } - } else { - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - .padding(16.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - textAlign = TextAlign.Center, - text = stringResource(id = R.string.scanner__permission_rationale) - ) - Spacer(modifier = Modifier.height(16.dp)) - Row { - Button(modifier = Modifier.width(100.dp), onClick = { onClick() }) { - Text(stringResource(id = R.string.scanner__button_ok)) - } - Spacer(Modifier.width(16.dp)) - Button(modifier = Modifier.width(100.dp), onClick = { doNotShowRationale.value = true }) { - Text(stringResource(id = R.string.scanner__button_nope)) - } - } - } - } -} - -@Composable -private fun PermissionNotAvailable() { - val context = LocalContext.current - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - textAlign = TextAlign.Center, - text = stringResource(id = R.string.scanner__permission_denied) - ) - Spacer(modifier = Modifier.height(8.dp)) - Button(onClick = { openPermissionSettings(context) }) { - Text(stringResource(id = R.string.scanner__open_settings)) - } - } -} - -private fun openPermissionSettings(context: Context) { - startActivity( - context, - Intent( - Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", context.packageName, null) - ), - null - ) -} - -@Preview -@Composable -private fun PermissionNotGrantedPreview() { - PermissionNotGranted { } -} - -@Preview -@Composable -private fun PermissionNotAvailablePreview() { - PermissionNotAvailable() -} diff --git a/lib_permission/src/main/java/no/nordicsemi/android/permission/viewmodel/BluetoothPermissionState.kt b/lib_permission/src/main/java/no/nordicsemi/android/permission/viewmodel/BluetoothPermissionState.kt deleted file mode 100644 index a0f6b205..00000000 --- a/lib_permission/src/main/java/no/nordicsemi/android/permission/viewmodel/BluetoothPermissionState.kt +++ /dev/null @@ -1,10 +0,0 @@ -package no.nordicsemi.android.permission.viewmodel - -enum class BluetoothPermissionState { - PERMISSION_REQUIRED, - BLUETOOTH_NOT_AVAILABLE, - BLUETOOTH_NOT_ENABLED, - DEVICE_NOT_CONNECTED, - BONDING_REQUIRED, - READY -} diff --git a/lib_permission/src/main/res/values/strings.xml b/lib_permission/src/main/res/values/strings.xml deleted file mode 100644 index 8f942818..00000000 --- a/lib_permission/src/main/res/values/strings.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - BLE devices - - The location permission is required when using Bluetooth LE, because surrounding devices can expose user\'s location. Please grant the permission. - Location permission denied. Please, grant us access on the Settings screen. - Open settings - Feature not available - - List of devices - Scanning failed due to technical reason. - Name: NONE - - No device connected - Connect - - Grant - Deny - - Request permission - - Bluetooth not available. - Bluetooth not enabled. - To enable Bluetooth please open settings. - Open settings - - Bonding in progress. Please follow instruction on the screen. - Bonding success. Please wait for the redirect to chosen profile screen. - We cannot get data from the peripheral without bonding. Please bond the device. - diff --git a/lib_permission/src/test/java/no/nordicsemi/android/permission/ExampleUnitTest.kt b/lib_permission/src/test/java/no/nordicsemi/android/permission/ExampleUnitTest.kt deleted file mode 100644 index b91ce273..00000000 --- a/lib_permission/src/test/java/no/nordicsemi/android/permission/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package no.nordicsemi.android.permission - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/SelectedBluetoothDeviceHolder.kt b/lib_service/src/main/java/no/nordicsemi/android/service/SelectedBluetoothDeviceHolder.kt index d05078fe..1bcf40d6 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/SelectedBluetoothDeviceHolder.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/SelectedBluetoothDeviceHolder.kt @@ -1,8 +1,11 @@ package no.nordicsemi.android.service import android.bluetooth.BluetoothDevice +import javax.inject.Inject +import javax.inject.Singleton -class SelectedBluetoothDeviceHolder { +@Singleton +class SelectedBluetoothDeviceHolder @Inject constructor() { var device: BluetoothDevice? = null private set @@ -11,14 +14,6 @@ class SelectedBluetoothDeviceHolder { return device?.bondState == BluetoothDevice.BOND_NONE } - fun getBondingState(): BondingState { - return when (device?.bondState) { - BluetoothDevice.BOND_BONDED -> BondingState.BONDED - BluetoothDevice.BOND_BONDING -> BondingState.BONDING - else -> BondingState.NONE - } - } - fun bondDevice() { device?.createBond() } @@ -31,18 +26,3 @@ class SelectedBluetoothDeviceHolder { device = null } } - -enum class BondingState { - NONE, BONDING, BONDED; - - companion object { - fun create(value: Int): BondingState { - return when (value) { - BluetoothDevice.BOND_BONDED -> BONDED - BluetoothDevice.BOND_BONDING -> BONDING - BluetoothDevice.BOND_NONE -> NONE - else -> throw IllegalArgumentException("Cannot create BondingState for the value: $value") - } - } - } -} diff --git a/settings.gradle b/settings.gradle index ae53133a..49ec6b31 100644 --- a/settings.gradle +++ b/settings.gradle @@ -73,11 +73,12 @@ include ':profile_prx' include ':profile_rscs' include ':profile_scanner' -include ':lib_permission' include ':lib_service' include ':lib_theme' include ':lib_utils' +include ':scanner' + if (file('../Android-Common-Libraries').exists()) { includeBuild('../Android-Common-Libraries') }