Skip to content

Commit

Permalink
#17 in progress
Browse files Browse the repository at this point in the history
- permissions refactor and some tips recordings for kalinowice
  • Loading branch information
mjureczko committed Feb 28, 2025
1 parent 5b6d065 commit 59e3777
Show file tree
Hide file tree
Showing 24 changed files with 160 additions and 161 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
package pl.marianjureczko.poszukiwacz.permissions

import android.Manifest
import android.annotation.SuppressLint
import android.os.Build
import androidx.annotation.RequiresApi
import pl.marianjureczko.poszukiwacz.R

object RequirementsForBluetooth : Requirements {
override fun getPermission(): String = Manifest.permission.BLUETOOTH
override fun getMessage(): Int = R.string.missing_bluetooth_permission
override fun shouldRequestOnThiDevice(): Boolean = true
}

object RequirementsForBluetoothConnect : Requirements {
@SuppressLint("InlinedApi")
@RequiresApi(Build.VERSION_CODES.S)
override fun getPermission(): String = Manifest.permission.BLUETOOTH_CONNECT
override fun getMessage(): Int = R.string.missing_bluetooth_permission
override fun shouldRequestOnThiDevice(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
}

object RequirementsForNearbyWifiDevices : Requirements {
@SuppressLint("InlinedApi")
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun getPermission(): String = Manifest.permission.NEARBY_WIFI_DEVICES
override fun getMessage(): Int = R.string.missing_bluetooth_permission
override fun shouldRequestOnThiDevice(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
}

object RequirementsForBluetoothScan : Requirements {
@RequiresApi(Build.VERSION_CODES.S)
override fun getPermission(): String = Manifest.permission.BLUETOOTH_SCAN
override fun getMessage(): Int = R.string.missing_bluetooth_permission
override fun shouldRequestOnThiDevice(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ import pl.marianjureczko.poszukiwacz.R
object RequirementsForRecordingSound: Requirements {
override fun getPermission(): String = Manifest.permission.RECORD_AUDIO
override fun getMessage(): Int = R.string.missing_photo_and_audio_permission
override fun shouldRequestOnThiDevice(): Boolean = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,24 @@

package pl.marianjureczko.poszukiwacz.screen.bluetooth

import android.annotation.SuppressLint
import android.widget.Toast
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import pl.marianjureczko.poszukiwacz.R
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForBluetooth
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForBluetoothConnect
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForBluetoothScan
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForNearbyWifiDevices
import pl.marianjureczko.poszukiwacz.shared.GoToFacebook
import pl.marianjureczko.poszukiwacz.shared.GoToGuide
import pl.marianjureczko.poszukiwacz.ui.components.TopBar
import pl.marianjureczko.poszukiwacz.ui.handlePermission

// Manifest.permission.BLUETOOTH
// if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)) {
// permissions.add(Manifest.permission.BLUETOOTH_CONNECT)
// }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// permissions.add(Manifest.permission.NEARBY_WIFI_DEVICES)
// }

@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun BluetoothScreen(
Expand All @@ -39,21 +30,24 @@ fun BluetoothScreen(
val viewModel: BluetoothViewModel = hiltViewModel()
val state: State<BluetoothState> = viewModel.state

val bluetoothPermission: PermissionState = handlePermission(RequirementsForBluetooth)
viewModel.addBluetoothPermission(bluetoothPermission)
if (state.value.permissions.canAskForBluetoothScanPermission()) {
viewModel.addBluetoothScanPermission(handlePermission(RequirementsForBluetoothScan))
}
if (state.value.permissions.canAskForBluetoothConnectPermission()) {
viewModel.addBluetoothConnectPermission(handlePermission(RequirementsForBluetoothConnect))
}
if (state.value.permissions.canAskForNearbyWifiDevicesPermission()) {
viewModel.addNearbyWifiDevicesPermission(handlePermission(RequirementsForNearbyWifiDevices))
}

if (state.value.permissions.canInitViewModel()) {
LaunchedEffect(Unit) {
viewModel.init()
val toRequest = viewModel.getPermissionRequirements()
if (toRequest != null) {
if (toRequest.shouldRequestOnThiDevice()) {
handlePermission(toRequest) {
viewModel.goToNextPermission(toRequest)
}
} else {
viewModel.goToNextPermission(toRequest)
}
} else {
val context = LocalContext.current
if (viewModel.permissionsHandler.allPermissionsGranted(context)) {
LaunchedEffect(Unit) {
viewModel.init()
}
} else {
Toast.makeText(context, R.string.bluetooth_permission_not_given, Toast.LENGTH_LONG).show()
navController.navigateUp()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,84 +1,17 @@
package pl.marianjureczko.poszukiwacz.screen.bluetooth

import android.os.Build
import androidx.compose.runtime.Composable
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.shouldShowRationale
import pl.marianjureczko.poszukiwacz.model.Route

@OptIn(ExperimentalPermissionsApi::class)
data class BluetoothState(
val permissions: Permissions = Permissions(),
val route: Route? = null,
val mode: Mode,
val messages: List<String> = emptyList(),
val devices: List<String> = emptyList(),
val deviceIsSelected: Boolean = false,
val permissionToRequestIndex: Int = 0,
) {
fun addMessage(msg: String): BluetoothState = copy(messages = messages + msg)
fun shouldShowDeviceSelection(): Boolean {
return devices.isNotEmpty() && !deviceIsSelected
}
}

//TODO t: what will happen when permission not granted
@OptIn(ExperimentalPermissionsApi::class)
data class Permissions(
val bluetoothPermission: PermissionState? = null,
val bluetoothScanPermission: PermissionState? = null,
val bluetoothConnectPermission: PermissionState? = null,
val nearbyWifiDevicesPermission: PermissionState? = null,
) {
//TODO t: remove duplication, use canAskForNextPermission fun
//TODO check SDK version
@Composable
fun canAskForBluetoothScanPermission(): Boolean {
return bluetoothPermissionGranted()
&& !scanPermissionGranted()
}

@Composable
fun canAskForBluetoothConnectPermission(): Boolean {
return sdkRequiringScanPermission()
&& scanPermissionGranted()
&& !connectPermissionGranted()
}

@Composable
fun canAskForNearbyWifiDevicesPermission(): Boolean {
return sdkRequiringCanAskForNearbyWifiDevices()
&& connectPermissionGranted()
&& !nearbyWifiDevicesPermissionGranted()
}

@Composable
fun canInitViewModel(): Boolean {
return scanPermissionGranted()
&& (!sdkRequiringScanPermission() || connectPermissionGranted())
&& (!sdkRequiringScanPermission() || nearbyWifiDevicesPermissionGranted())
}

@Composable
private fun bluetoothPermissionGranted() =
bluetoothPermission?.status?.isGranted == true || bluetoothPermission?.status?.shouldShowRationale == true

@Composable
private fun scanPermissionGranted() =
bluetoothScanPermission?.status?.isGranted == true || bluetoothScanPermission?.status?.shouldShowRationale == true

@Composable
private fun sdkRequiringScanPermission() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S

@Composable
private fun sdkRequiringCanAskForNearbyWifiDevices() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU

@Composable
private fun connectPermissionGranted() =
bluetoothConnectPermission?.status?.isGranted == true || bluetoothConnectPermission?.status?.shouldShowRationale == true

@Composable
fun nearbyWifiDevicesPermissionGranted() =
nearbyWifiDevicesPermission?.status?.isGranted == true || nearbyWifiDevicesPermission?.status?.shouldShowRationale == true
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import pl.marianjureczko.poszukiwacz.R
import pl.marianjureczko.poszukiwacz.model.Route
import pl.marianjureczko.poszukiwacz.permissions.Requirements
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForBluetooth
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForBluetoothConnect
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForBluetoothScan
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForNearbyWifiDevices
import pl.marianjureczko.poszukiwacz.shared.di.IoDispatcher
import pl.marianjureczko.poszukiwacz.shared.port.StorageHelper
import pl.marianjureczko.poszukiwacz.shared.port.XmlHelper
import pl.marianjureczko.poszukiwacz.ui.PermissionsHandler
import javax.inject.Inject

const val PARAMETER_MODE = "mode"
Expand All @@ -44,7 +47,6 @@ interface RouteReader {
@HiltViewModel
class BluetoothViewModel @Inject constructor(
private val stateHandle: SavedStateHandle,
private val xmlHelper: XmlHelper,
private val storage: StorageHelper,
private val resources: Resources,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
Expand All @@ -53,6 +55,14 @@ class BluetoothViewModel @Inject constructor(
private var _state: MutableState<BluetoothState> = mutableStateOf(createState())
private val bluetooth: Bluetooth = Bluetooth()
private var devices: List<BluetoothDevice>? = null
val permissionsHandler: PermissionsHandler = PermissionsHandler(
listOf(
RequirementsForBluetooth,
RequirementsForBluetoothScan,
RequirementsForBluetoothConnect,
RequirementsForNearbyWifiDevices,
)
)

val state: State<BluetoothState>
get() = _state
Expand All @@ -72,7 +82,7 @@ class BluetoothViewModel @Inject constructor(
}
} catch (e: BluetoothException) {
val msg = resources.getString(e.msgId)
Log.w(TAG, "$msg", e)
Log.w(TAG, msg, e)
_state.value = state.value.addMessage(msg)
}
}
Expand All @@ -99,32 +109,17 @@ class BluetoothViewModel @Inject constructor(
}
}

@OptIn(ExperimentalPermissionsApi::class)
fun addBluetoothPermission(permission: PermissionState) {
_state.value = state.value.copy(
permissions = state.value.permissions.copy(bluetoothPermission = permission)
)
}

@OptIn(ExperimentalPermissionsApi::class)
fun addBluetoothConnectPermission(permission: PermissionState) {
_state.value = state.value.copy(
permissions = state.value.permissions.copy(bluetoothConnectPermission = permission)
)
fun goToNextPermission(previous: Requirements) {
val nextPermissionIndex = permissionsHandler.requestNextPermission(previous)
_state.value = _state.value.copy(permissionToRequestIndex = nextPermissionIndex)
}

@OptIn(ExperimentalPermissionsApi::class)
fun addNearbyWifiDevicesPermission(permission: PermissionState) {
_state.value = state.value.copy(
permissions = state.value.permissions.copy(nearbyWifiDevicesPermission = permission)
)
}
fun getPermissionRequirements(): Requirements? =
permissionsHandler.getPermissionRequirements(state.value.permissionToRequestIndex)

@OptIn(ExperimentalPermissionsApi::class)
fun addBluetoothScanPermission(permission: PermissionState) {
_state.value = state.value.copy(
permissions = state.value.permissions.copy(bluetoothScanPermission = permission)
)
override fun readRuteFromConnectedSocket(socket: BluetoothSocket) {
storage.extractZipStream(socket.inputStream, ProgressPrinter(this))
print(R.string.extracted_everything)
}

private fun createState(): BluetoothState {
Expand All @@ -138,9 +133,4 @@ class BluetoothViewModel @Inject constructor(
mode = mode,
)
}

override fun readRuteFromConnectedSocket(socket: BluetoothSocket) {
storage.extractZipStream(socket.inputStream, ProgressPrinter(this))
print(R.string.extracted_everything)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,20 @@ package pl.marianjureczko.poszukiwacz.screen.treasureseditor
import android.annotation.SuppressLint
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.isGranted
import pl.marianjureczko.poszukiwacz.R
import pl.marianjureczko.poszukiwacz.model.TreasureDescription
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForDoingTipPhoto
import pl.marianjureczko.poszukiwacz.permissions.RequirementsForRecordingSound
import pl.marianjureczko.poszukiwacz.shared.GoToFacebook
import pl.marianjureczko.poszukiwacz.shared.GoToGuide
import pl.marianjureczko.poszukiwacz.ui.canAskForNextPermission
import pl.marianjureczko.poszukiwacz.ui.components.TopBar
import pl.marianjureczko.poszukiwacz.ui.handlePermission
import pl.marianjureczko.poszukiwacz.ui.isPermissionGranted

@OptIn(ExperimentalPermissionsApi::class)
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
Expand All @@ -29,11 +28,15 @@ fun TreasureEditorScreen(
) {
val viewModel: TreasureEditorViewModel = hiltViewModel()
val state = viewModel.state.value
val cameraPermission: PermissionState = handlePermission(RequirementsForDoingTipPhoto)
var microphonePermission: PermissionState? = null
if (canAskForNextPermission(cameraPermission)) {
microphonePermission = handlePermission(RequirementsForRecordingSound)

val toRequest = viewModel.getNextPermissionRequest()
if (toRequest != null) {
handlePermission(toRequest) {
viewModel.requestNextPermission(toRequest)
}
}
val cameraPermission = isPermissionGranted(RequirementsForDoingTipPhoto, LocalContext.current)
val microphonePermission = isPermissionGranted(RequirementsForRecordingSound, LocalContext.current)
Scaffold(
topBar = {
TopBar(
Expand All @@ -46,8 +49,8 @@ fun TreasureEditorScreen(
content = { _ ->
TreasureEditorScreenBody(
state = state,
cameraPermissionGranted = cameraPermission.status.isGranted,
recordingPermissionGranted = microphonePermission?.status?.isGranted ?: false,
cameraPermissionGranted = cameraPermission,
recordingPermissionGranted = microphonePermission,
hideOverrideSoundTipDialog = { viewModel.hideOverrideSoundTipDialog() },
hideOverridePhotoDialog = { viewModel.hideOverridePhotoDialog() },
showOverridePhotoDialog = { td: TreasureDescription -> viewModel.showOverridePhotoDialog(td) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ data class TreasureEditorState(
val treasureWithPhotoTipToOverride: TreasureDescription? = null,
val showOverrideSoundTipDialog: Boolean = false,
val treasureWithSoundTipToOverride: TreasureDescription? = null,
val permissionToRequestIndex: Int = 0,
) {
fun locationBarData(): LocationBarData {
val formatter = CoordinatesFormatter()
Expand Down
Loading

0 comments on commit 59e3777

Please sign in to comment.