diff --git a/feature/nfc/build.gradle.kts b/feature/nfc/build.gradle.kts index e988272a..86e554d5 100644 --- a/feature/nfc/build.gradle.kts +++ b/feature/nfc/build.gradle.kts @@ -15,9 +15,10 @@ dependencies { implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.lifecycle.runtime.compose) + // Permissions + implementation(libs.nordic.permissions.nfc) + implementation(libs.nordic.permissions.wifi) implementation(project(":feature:common")) implementation(project(":feature:ui")) api(project(":lib:nfc")) - implementation(libs.nordic.permissions.nfc) - implementation("androidx.compose.material3:material3:1.3.0-beta02") } \ No newline at end of file diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/mapping/UiMapper.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/mapping/UiMapper.kt index 2ef36024..1e4a1348 100644 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/mapping/UiMapper.kt +++ b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/mapping/UiMapper.kt @@ -35,7 +35,15 @@ fun AuthenticationMode.toDisplayString(): String = when (this) { * @return The list of security types supported to display in the dropdown. */ fun authListToDisplay(): List { - return WifiAuthTypeBelowTiramisu.entries.map { it.toDisplayString() } + // Removed the security types that are not supported (such as WPA-Enterprise). + return listOf( + "Open", + "Shared", + "WPA-Personal", + "WPA2-Personal", + "WPA/WPA2-Personal", + "WPA3-Personal" + ) } /** diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/RequireLocationForWifi.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/RequireLocationForWifi.kt deleted file mode 100644 index 04eb6540..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/RequireLocationForWifi.kt +++ /dev/null @@ -1,36 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionNotAvailableReason -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionState -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view.LocationPermissionRequiredView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.viewmodel.PermissionViewModel - -@Composable -fun RequireLocationForWifi( - onChanged: (Boolean) -> Unit = {}, - contentWithoutLocation: @Composable () -> Unit = { LocationPermissionRequiredView() }, - content: @Composable (isLocationRequiredAndDisabled: Boolean) -> Unit, -) { - val viewModel = hiltViewModel() - val state by viewModel.locationPermission.collectAsStateWithLifecycle() - - LaunchedEffect(state) { - onChanged( - state is WifiPermissionState.Available || - (state as WifiPermissionState.NotAvailable).reason == WifiPermissionNotAvailableReason.DISABLED - ) - } - - when (val s = state) { - WifiPermissionState.Available -> content(false) - is WifiPermissionState.NotAvailable -> when (s.reason) { - WifiPermissionNotAvailableReason.DISABLED -> content(true) - else -> contentWithoutLocation() - } - } -} \ No newline at end of file diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/RequireWifi.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/RequireWifi.kt deleted file mode 100644 index b0eaa2cb..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/RequireWifi.kt +++ /dev/null @@ -1,50 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission - -import android.os.Build -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionNotAvailableReason -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionState -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view.WifiDisabledView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view.WifiNotAvailableView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view.WifiPermissionRequiredView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.viewmodel.PermissionViewModel - -@Composable -fun RequireWifi( - onChanged: (Boolean) -> Unit = {}, - contentWithoutWifi: @Composable (WifiPermissionNotAvailableReason) -> Unit = { - NoWifiView(reason = it) - }, - content: @Composable () -> Unit, -) { - val viewModel = hiltViewModel() - val state by viewModel.wifiState.collectAsStateWithLifecycle() - - LaunchedEffect(state) { - onChanged(state is WifiPermissionState.Available) - } - - when (val s = state) { - WifiPermissionState.Available -> content() - is WifiPermissionState.NotAvailable -> contentWithoutWifi(s.reason) - } -} - -@Composable -private fun NoWifiView( - reason: WifiPermissionNotAvailableReason, -) { - when (reason) { - WifiPermissionNotAvailableReason.NOT_AVAILABLE -> WifiNotAvailableView() - WifiPermissionNotAvailableReason.PERMISSION_REQUIRED -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - WifiPermissionRequiredView() - } - - WifiPermissionNotAvailableReason.DISABLED -> WifiDisabledView() - } -} \ No newline at end of file diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/location/LocationStateManager.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/location/LocationStateManager.kt deleted file mode 100644 index 951d53fd..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/location/LocationStateManager.kt +++ /dev/null @@ -1,80 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.location - -import android.annotation.SuppressLint -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.location.LocationManager -import androidx.core.content.ContextCompat -import androidx.core.location.LocationManagerCompat -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.callbackFlow -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.LocalDataProvider -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.PermissionUtils -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionNotAvailableReason -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionState -import javax.inject.Inject -import javax.inject.Singleton - -private const val REFRESH_PERMISSIONS = - "no.nordicsemi.android.common.permission.REFRESH_LOCATION_PERMISSIONS" - -@Singleton -class LocationStateManager @Inject constructor( - @ApplicationContext private val context: Context, -) { - private val dataProvider = LocalDataProvider(context) - private val utils = PermissionUtils(context, dataProvider) - - @SuppressLint("WrongConstant") - fun locationState() = callbackFlow { - trySend(getLocationState()) - - val locationStateChangeHandler = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - trySend(getLocationState()) - } - } - val filter = IntentFilter().apply { - addAction(LocationManager.MODE_CHANGED_ACTION) - addAction(REFRESH_PERMISSIONS) - } - ContextCompat.registerReceiver( - context, - locationStateChangeHandler, - filter, - ContextCompat.RECEIVER_EXPORTED - ) - awaitClose { - context.unregisterReceiver(locationStateChangeHandler) - } - } - - fun refreshPermission() { - val intent = Intent(REFRESH_PERMISSIONS) - context.sendBroadcast(intent) - } - - fun markLocationPermissionRequested() { - dataProvider.locationPermissionRequested = true - } - - fun isLocationPermissionDeniedForever(context: Context): Boolean { - return utils.isLocationPermissionDeniedForever(context) - } - - private fun getLocationState(): WifiPermissionState { - val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager - return when { - !utils.isLocationPermissionGranted -> - WifiPermissionState.NotAvailable(WifiPermissionNotAvailableReason.PERMISSION_REQUIRED) - - dataProvider.isLocationPermissionRequired && !LocationManagerCompat.isLocationEnabled(lm) -> - WifiPermissionState.NotAvailable(WifiPermissionNotAvailableReason.DISABLED) - - else -> WifiPermissionState.Available - } - } -} \ No newline at end of file diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/LocalDataProvider.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/LocalDataProvider.kt deleted file mode 100644 index 81954914..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/LocalDataProvider.kt +++ /dev/null @@ -1,63 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils - -import android.content.Context -import android.content.SharedPreferences -import android.os.Build -import androidx.annotation.ChecksSdkIntAtLeast -import androidx.core.app.ActivityCompat - -private const val SHARED_PREFS_NAME = "SHARED_PREFS_NAME" - -private const val PREFS_PERMISSION_REQUESTED = "permission_requested" -private const val PREFS_WIFI_PERMISSION_REQUESTED = "wifi_permission_requested" - -internal class LocalDataProvider( - private val context: Context -) { - private val sharedPrefs: SharedPreferences - get() = context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) - - /** - * The first time an app requests a permission there is no 'Don't ask again' checkbox and - * [ActivityCompat.shouldShowRequestPermissionRationale] returns false. - * This situation is similar to a permission being denied forever, so to distinguish both cases - * a flag needs to be saved. - */ - var locationPermissionRequested: Boolean - get() = sharedPrefs.getBoolean(PREFS_PERMISSION_REQUESTED, false) - set(value) { - sharedPrefs.edit().putBoolean(PREFS_PERMISSION_REQUESTED, value).apply() - } - - /** - * The first time an app requests a permission there is no 'Don't ask again' checkbox and - * [ActivityCompat.shouldShowRequestPermissionRationale] returns false. - * This situation is similar to a permission being denied forever, so to distinguish both cases - * a flag needs to be saved. - */ - var wifiPermissionRequested: Boolean - get() = sharedPrefs.getBoolean(PREFS_WIFI_PERMISSION_REQUESTED, false) - set(value) { - sharedPrefs.edit().putBoolean(PREFS_WIFI_PERMISSION_REQUESTED, value).apply() - } - - val isLocationPermissionRequired: Boolean - /** - * Location enabled is required on phones running Android 6 - 11 - * (for example on Nexus and Pixel devices). Initially, Samsung phones didn't require it, - * but that has been fixed for those phones in Android 9. - * Several Wi-Fi APIs require the ACCESS_FINE_LOCATION permission, - * even when your app targets Android 13 or higher. - * - * @return False if it is known that location is not required, true otherwise. - */ - get() = isMarshmallowOrAbove - - val isMarshmallowOrAbove: Boolean - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M) - get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M - - val isTiramisuOrAbove: Boolean - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) - get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -} diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/PermissionUtils.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/PermissionUtils.kt deleted file mode 100644 index 18fe2bdb..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/PermissionUtils.kt +++ /dev/null @@ -1,74 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils - -import android.Manifest -import android.app.Activity -import android.content.Context -import android.content.ContextWrapper -import android.content.pm.PackageManager -import android.net.wifi.WifiManager -import androidx.core.content.ContextCompat - -internal class PermissionUtils( - private val context: Context, - private val dataProvider: LocalDataProvider, -) { - val isWifiEnabled: Boolean - get() = (context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager) - .isWifiEnabled - - val isWifiAvailable: Boolean - get() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI) - - private val isLocationPermissionRequired: Boolean - get() = dataProvider.isMarshmallowOrAbove - - private val isWifiPermissionGranted: Boolean - get() = !dataProvider.isTiramisuOrAbove || - ContextCompat.checkSelfPermission( - context, - Manifest.permission.NEARBY_WIFI_DEVICES - ) == PackageManager.PERMISSION_GRANTED - - val isLocationPermissionGranted: Boolean - get() = !isLocationPermissionRequired || - ContextCompat.checkSelfPermission( - context, - Manifest.permission.ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED - - val areNecessaryWifiPermissionsGranted: Boolean - get() = isWifiPermissionGranted - - fun isWifiPermissionDeniedForever(context: Context): Boolean { - return dataProvider.isTiramisuOrAbove && - !isWifiPermissionGranted && // Wifi permission must be denied - dataProvider.wifiPermissionRequested && // Permission must have been requested before - !context.findActivity() - .shouldShowRequestPermissionRationale(Manifest.permission.NEARBY_WIFI_DEVICES) - } - - fun isLocationPermissionDeniedForever(context: Context): Boolean { - return dataProvider.isMarshmallowOrAbove && - !isLocationPermissionGranted // Location permission must be denied - && dataProvider.locationPermissionRequested // Permission must have been requested before - && !context.findActivity() - .shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) - } - - /** - * Finds the activity from the given context. - * - * https://github.com/google/accompanist/blob/6611ebda55eb2948eca9e1c89c2519e80300855a/permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt#L99 - * - * @throws IllegalStateException if no activity was found. - * @return the activity. - */ - private fun Context.findActivity(): Activity { - var context = this - while (context is ContextWrapper) { - if (context is Activity) return context - context = context.baseContext - } - throw IllegalStateException("no activity") - } -} diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/WifiPermissionState.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/WifiPermissionState.kt deleted file mode 100644 index 833f9199..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/utils/WifiPermissionState.kt +++ /dev/null @@ -1,29 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils - -/** - * Represents the reason for Wi-Fi permission is not available. - */ -enum class WifiPermissionNotAvailableReason { - PERMISSION_REQUIRED, - NOT_AVAILABLE, - DISABLED, -} - -/** - * Represents the state of Wi-Fi permission. - */ -sealed class WifiPermissionState { - - /** - * Represents the Wi-Fi permission is available. - */ - data object Available : WifiPermissionState() - - /** - * Represents the Wi-Fi permission is not available. - * @param reason The reason for Wi-Fi permission is not available. - */ - data class NotAvailable( - val reason: WifiPermissionNotAvailableReason, - ) : WifiPermissionState() -} diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/LocationPermissionRequiredView.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/LocationPermissionRequiredView.kt deleted file mode 100644 index c3af507d..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/LocationPermissionRequiredView.kt +++ /dev/null @@ -1,106 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view - -import android.Manifest -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.provider.Settings -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.LocationOff -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.core.content.ContextCompat -import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.R -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.viewmodel.PermissionViewModel - -@Composable -internal fun LocationPermissionRequiredView() { - val viewModel = hiltViewModel() - val context = LocalContext.current - var permissionDenied by remember { mutableStateOf(viewModel.isLocationPermissionDeniedForever(context)) } - - val launcher = rememberLauncherForActivityResult( - ActivityResultContracts.RequestMultiplePermissions() - ) { - viewModel.markLocationPermissionRequested() - permissionDenied = viewModel.isLocationPermissionDeniedForever(context) - viewModel.refreshLocationPermission() - } - - LocationPermissionRequiredView( - permissionDenied = permissionDenied, - onGrantClicked = { - val requiredPermissions = arrayOf( - Manifest.permission.ACCESS_FINE_LOCATION - ) - launcher.launch(requiredPermissions) - }, - onOpenSettingsClicked = { openPermissionSettings(context) }, - ) -} - -@Composable -internal fun LocationPermissionRequiredView( - permissionDenied: Boolean, - onGrantClicked: () -> Unit, - onOpenSettingsClicked: () -> Unit, -) { - WarningView( - imageVector = Icons.Default.LocationOff, - title = stringResource(id = R.string.location_permission_required), - hint = stringResource(id = R.string.location_permission__required_des), - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - if (!permissionDenied) { - Button(onClick = onGrantClicked) { - Text(text = stringResource(id = R.string.grant_permission)) - } - } else { - Button(onClick = onOpenSettingsClicked) { - Text(text = stringResource(id = R.string.settings)) - } - } - } -} - -private fun openPermissionSettings(context: Context) { - ContextCompat.startActivity( - context, - Intent( - Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", context.packageName, null) - ), - null - ) -} - -@Preview -@Composable -private fun LocationPermissionRequiredView_Preview() { - NordicTheme { - LocationPermissionRequiredView( - permissionDenied = false, - onGrantClicked = { }, - onOpenSettingsClicked = { }, - ) - } -} \ No newline at end of file diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiDisabledView.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiDisabledView.kt deleted file mode 100644 index 1eac0744..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiDisabledView.kt +++ /dev/null @@ -1,49 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view - -import android.content.Context -import android.content.Intent -import android.provider.Settings -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.WifiOff -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.R - -@Composable -internal fun WifiDisabledView() { - WarningView( - imageVector = Icons.Default.WifiOff, - title = stringResource(id = R.string.wifi_disabled), - hint = stringResource(id = R.string.wifi_disabled_des), - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - val context = LocalContext.current - Button(onClick = { enableWifi(context) }) { - Text(text = stringResource(id = R.string.enable_wifi)) - } - } -} - -private fun enableWifi(context: Context) { - context.startActivity(Intent(Settings.ACTION_WIFI_SETTINGS)) -} - -@Preview -@Composable -private fun WifiDisabledViewPreview() { - NordicTheme { - WifiDisabledView() - } -} \ No newline at end of file diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiNotAvailableView.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiNotAvailableView.kt deleted file mode 100644 index 56e8b89e..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiNotAvailableView.kt +++ /dev/null @@ -1,34 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.WifiOff -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.R - -@Composable -internal fun WifiNotAvailableView() { - WarningView( - imageVector = Icons.Default.WifiOff, - title = stringResource(id = R.string.wifi_not_available), - hint = stringResource(id = R.string.wifi_not_available_des), - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) -} - -@Preview -@Composable -private fun WifiNotAvailableView_Preview() { - NordicTheme { - WifiNotAvailableView() - } -} diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiPermissionRequiredView.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiPermissionRequiredView.kt deleted file mode 100644 index e937ef6a..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/view/WifiPermissionRequiredView.kt +++ /dev/null @@ -1,92 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.view - -import android.Manifest -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.provider.Settings -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.annotation.RequiresApi -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.WifiOff -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.core.content.ContextCompat -import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.theme.NordicTheme -import no.nordicsemi.android.common.theme.view.WarningView -import no.nordicsemi.android.wifi.provisioner.feature.nfc.R -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.viewmodel.PermissionViewModel - -@RequiresApi(Build.VERSION_CODES.TIRAMISU) -@Composable -internal fun WifiPermissionRequiredView() { - val viewModel: PermissionViewModel = hiltViewModel() - val context = LocalContext.current - var permissionDenied by remember { mutableStateOf(viewModel.isWifiPermissionDeniedForever(context)) } - - WarningView( - imageVector = Icons.Default.WifiOff, - title = stringResource(id = R.string.wifi_permission_required), - hint = stringResource(id = R.string.wifi_permission_required_des), - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - val requiredPermissions = arrayOf( - Manifest.permission.NEARBY_WIFI_DEVICES, - ) - - val launcher = rememberLauncherForActivityResult( - ActivityResultContracts.RequestMultiplePermissions() - ) { - viewModel.markWifiPermissionRequested() - permissionDenied = viewModel.isWifiPermissionDeniedForever(context) - viewModel.refreshWifiPermission() - } - - if (!permissionDenied) { - Button(onClick = { launcher.launch(requiredPermissions) }) { - Text(text = stringResource(id = R.string.grant_permission)) - } - } else { - Button(onClick = { openPermissionSettings(context) }) { - Text(text = stringResource(id = R.string.settings)) - } - } - } -} - -private fun openPermissionSettings(context: Context) { - ContextCompat.startActivity( - context, - Intent( - Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", context.packageName, null) - ), - null - ) -} - -@RequiresApi(Build.VERSION_CODES.TIRAMISU) -@Preview -@Composable -private fun WifiPermissionRequiredViewPreview() { - NordicTheme { - WifiPermissionRequiredView() - } -} \ No newline at end of file diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/viewmodel/PermissionViewModel.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/viewmodel/PermissionViewModel.kt deleted file mode 100644 index fcdf3b8e..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/viewmodel/PermissionViewModel.kt +++ /dev/null @@ -1,55 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.viewmodel - -import android.content.Context -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.stateIn -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.location.LocationStateManager -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionNotAvailableReason -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionState -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.wifi.WifiStateManager -import javax.inject.Inject - -@HiltViewModel -class PermissionViewModel @Inject constructor( - private val wifiStateManager: WifiStateManager, - private val locationManager: LocationStateManager, -) : ViewModel() { - val wifiState = wifiStateManager.wifiState() - .stateIn( - viewModelScope, SharingStarted.Lazily, - WifiPermissionState.NotAvailable(WifiPermissionNotAvailableReason.NOT_AVAILABLE) - ) - - val locationPermission = locationManager.locationState() - .stateIn( - viewModelScope, SharingStarted.Lazily, - WifiPermissionState.NotAvailable(WifiPermissionNotAvailableReason.NOT_AVAILABLE) - ) - - fun refreshWifiPermission() { - wifiStateManager.refreshPermission() - } - - fun refreshLocationPermission() { - locationManager.refreshPermission() - } - - fun markLocationPermissionRequested() { - locationManager.markLocationPermissionRequested() - } - - fun markWifiPermissionRequested() { - wifiStateManager.markWifiPermissionRequested() - } - - fun isWifiPermissionDeniedForever(context: Context): Boolean { - return wifiStateManager.isWifiPermissionDeniedForever(context) - } - - fun isLocationPermissionDeniedForever(context: Context): Boolean { - return locationManager.isLocationPermissionDeniedForever(context) - } -} diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/wifi/WifiStateManager.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/wifi/WifiStateManager.kt deleted file mode 100644 index 898fe900..00000000 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/permission/wifi/WifiStateManager.kt +++ /dev/null @@ -1,85 +0,0 @@ -package no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.wifi - -import android.annotation.SuppressLint -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.net.wifi.WifiManager -import androidx.core.content.ContextCompat -import androidx.core.content.ContextCompat.RECEIVER_EXPORTED -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.callbackFlow -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.LocalDataProvider -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.PermissionUtils -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionNotAvailableReason -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.utils.WifiPermissionState -import javax.inject.Inject -import javax.inject.Singleton - -private const val REFRESH_PERMISSIONS = - "no.nordicsemi.android.common.permission.REFRESH_WIFI_PERMISSIONS" - -@Singleton -class WifiStateManager @Inject constructor( - @ApplicationContext private val context: Context, -) { - private val dataProvider = LocalDataProvider(context) - private val utils = PermissionUtils(context, dataProvider) - - @SuppressLint("WrongConstant") - fun wifiState() = callbackFlow { - trySend(getWifiPermissionState()) - - val wifiStateChangeHandler = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - trySend(getWifiPermissionState()) - } - } - val filter = IntentFilter().apply { - addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) - addAction(REFRESH_PERMISSIONS) - } - - ContextCompat.registerReceiver( - context, - wifiStateChangeHandler, - filter, - RECEIVER_EXPORTED - ) - - awaitClose { - context.unregisterReceiver(wifiStateChangeHandler) - } - } - - fun refreshPermission() { - val intent = Intent(REFRESH_PERMISSIONS) - context.sendBroadcast(intent) - } - - fun markWifiPermissionRequested() { - dataProvider.wifiPermissionRequested = true - } - - fun isWifiPermissionDeniedForever(context: Context): Boolean { - return utils.isWifiPermissionDeniedForever(context) - } - - private fun getWifiPermissionState() = when { - !utils.isWifiAvailable -> WifiPermissionState.NotAvailable( - WifiPermissionNotAvailableReason.NOT_AVAILABLE - ) - - !utils.areNecessaryWifiPermissionsGranted -> WifiPermissionState.NotAvailable( - WifiPermissionNotAvailableReason.PERMISSION_REQUIRED - ) - - !utils.isWifiEnabled -> WifiPermissionState.NotAvailable( - WifiPermissionNotAvailableReason.DISABLED - ) - - else -> WifiPermissionState.Available - } -} diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/uicomponent/AddWifiManuallyDialog.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/uicomponent/AddWifiManuallyDialog.kt index e6925888..ec0d5c6b 100644 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/uicomponent/AddWifiManuallyDialog.kt +++ b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/uicomponent/AddWifiManuallyDialog.kt @@ -106,7 +106,10 @@ internal fun AddWifiManuallyDialog( null, showPassword = showPassword, onShowPassChange = { showPassword = !showPassword }, - onUpdate = { password = it }, + onUpdate = { + password = it + isPasswordEmpty = password?.isEmpty() == true + }, ) } else { // Clear the password if the authentication mode is open. diff --git a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/view/WifiScannerView.kt b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/view/WifiScannerView.kt index eb2c5012..acbe1d66 100644 --- a/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/view/WifiScannerView.kt +++ b/feature/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/feature/nfc/view/WifiScannerView.kt @@ -56,14 +56,14 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import no.nordicsemi.android.common.permissions.wifi.RequireLocationForWifi +import no.nordicsemi.android.common.permissions.wifi.RequireWifi import no.nordicsemi.android.common.theme.NordicTheme import no.nordicsemi.android.common.theme.view.NordicAppBar import no.nordicsemi.android.common.theme.view.WarningView import no.nordicsemi.android.wifi.provisioner.feature.nfc.R import no.nordicsemi.android.wifi.provisioner.feature.nfc.mapping.Frequency import no.nordicsemi.android.wifi.provisioner.feature.nfc.mapping.toDisplayString -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.RequireLocationForWifi -import no.nordicsemi.android.wifi.provisioner.feature.nfc.permission.RequireWifi import no.nordicsemi.android.wifi.provisioner.feature.nfc.uicomponent.PasswordDialog import no.nordicsemi.android.wifi.provisioner.feature.nfc.uicomponent.RssiIconView import no.nordicsemi.android.wifi.provisioner.feature.nfc.uicomponent.VerticalBlueBar diff --git a/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/NdefMessageBuilder.kt b/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/NdefMessageBuilder.kt index 65442834..bf6fade2 100644 --- a/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/NdefMessageBuilder.kt +++ b/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/NdefMessageBuilder.kt @@ -43,26 +43,25 @@ class NdefMessageBuilder { * @return the NDEF message for the Wi-Fi data. */ fun createNdefMessage(wifiNetwork: WifiData): NdefMessage { - return generateNdefMessage(wifiNetwork) + val record = createWifiRecord(wifiNetwork) + return NdefMessage(arrayOf(record)) } /** - * Generates the NDEF message for the given Wi-Fi network. + * Generates the NDEF record for the given Wi-Fi network. * * @param wifiNetwork the Wi-Fi network to be written to the NDEF message. */ - private fun generateNdefMessage(wifiNetwork: WifiData): NdefMessage { + fun createWifiRecord(wifiNetwork: WifiData): NdefRecord { val payload: ByteArray = generateNdefPayload(wifiNetwork) val empty = byteArrayOf() - val mimeRecord = NdefRecord( + return NdefRecord( NdefRecord.TNF_MIME_MEDIA, NFC_TOKEN_MIME_TYPE.toByteArray(Charset.forName("US-ASCII")), empty, payload ) - - return NdefMessage(arrayOf(mimeRecord)) } /** diff --git a/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/WifiManagerRepository.kt b/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/WifiManagerRepository.kt index 2480ccdb..7d8933a0 100644 --- a/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/WifiManagerRepository.kt +++ b/lib/nfc/src/main/java/no/nordicsemi/android/wifi/provisioner/nfc/WifiManagerRepository.kt @@ -22,7 +22,7 @@ import no.nordicsemi.android.wifi.provisioner.nfc.domain.Success class WifiManagerRepository( context: Context, ) { - private var wifiManager: WifiManager = + private val wifiManager: WifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager private val _networkState = MutableStateFlow(Success(emptyList())) diff --git a/settings.gradle.kts b/settings.gradle.kts index a04196ea..baf5a284 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,7 +48,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.11.9") + from("no.nordicsemi.android.gradle:version-catalog:1.11.10") } } }