Skip to content

Commit

Permalink
Code Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
umer0586 committed Feb 20, 2025
1 parent 577fb35 commit ff33515
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
Expand All @@ -44,19 +45,20 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.github.umer0586.droidpad.data.ExternalData
import com.github.umer0586.droidpad.ui.theme.DroidPadTheme
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun QRCodeScannerScreen(
viewModel: QRScannerScreenViewModel,
Expand All @@ -68,6 +70,37 @@ fun QRCodeScannerScreen(
}

val uiState by viewModel.uiState.collectAsState()
val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)

//If shouldShowRationale == true,
// 1. It means that the app should show an explanation before requesting the permission again. This usually happens when the user has denied the permission once but has not selected "Don't ask again".
//If shouldShowRationale == false, it means that:
// 1. The permission has never been requested before.
// 2. The user granted the permission.
// 3. The user denied the permission and selected "Don't ask again" (in this case, further requests will not show a system dialog).

LaunchedEffect(cameraPermissionState.status) {
if (!cameraPermissionState.status.isGranted) {
if (cameraPermissionState.status.shouldShowRationale) {
// Show rationale for permission
viewModel.onEvent(QRScannerScreenEvent.OnShouldShowRationale)
} else {
// Check if permission is permanently denied
if (!cameraPermissionState.status.isGranted && !cameraPermissionState.status.shouldShowRationale) {
// Permission is permanently denied, guide user to app settings
viewModel.onEvent(QRScannerScreenEvent.OnPermissionPermanentlyDenied)
} else {
// Request permission
cameraPermissionState.launchPermissionRequest()
}
}
} else { // cameraPermissionState.status.isGranted
// Permission is granted
viewModel.onEvent(QRScannerScreenEvent.OnCameraPermissionGranted)
}

cameraPermissionState.launchPermissionRequest()
}

QRCodeScannerScreenContent(
uiState = uiState,
Expand All @@ -79,6 +112,10 @@ fun QRCodeScannerScreen(
onBackPress?.invoke()
}

is QRScannerScreenEvent.OnGrantPermissionClick -> {
cameraPermissionState.launchPermissionRequest()
}

else -> {}
}

Expand All @@ -87,8 +124,7 @@ fun QRCodeScannerScreen(


}
// Because of its dependency on PermissionState not all of its content could be viewed in a Preview Mode
@OptIn(ExperimentalPermissionsApi::class)

@Composable
fun QRCodeScannerScreenContent(
uiState: QRScannerScreenState,
Expand All @@ -98,20 +134,6 @@ fun QRCodeScannerScreenContent(
BackHandler {
onEvent(QRScannerScreenEvent.OnBackPress)
}

val cameraPermissionState =
if (!LocalInspectionMode.current)
rememberPermissionState(Manifest.permission.CAMERA)
else
null

if(!LocalInspectionMode.current){
LaunchedEffect(Unit) {
cameraPermissionState?.launchPermissionRequest()
}
}


// Launcher for the QR code scanner
val scanLauncher =
rememberLauncherForActivityResult(contract = ScanContract()) { result: ScanIntentResult ->
Expand All @@ -121,22 +143,14 @@ fun QRCodeScannerScreenContent(

}

// Skip if being previewed in Preview mode
if (!LocalInspectionMode.current) {
LaunchedEffect(key1 = cameraPermissionState?.status) {
if (cameraPermissionState?.status == PermissionStatus.Granted) {

scanLauncher.launch(
ScanOptions().apply {
setDesiredBarcodeFormats(ScanOptions.QR_CODE)
setCameraId(0)
setBeepEnabled(false)
})


} else if (cameraPermissionState?.status?.shouldShowRationale == true) {
cameraPermissionState.launchPermissionRequest()
}
if(uiState.cameraPermissionGranted){
LaunchedEffect(Unit) {
scanLauncher.launch(
ScanOptions().apply {
setDesiredBarcodeFormats(ScanOptions.QR_CODE)
setCameraId(0)
setBeepEnabled(false)
})
}
}

Expand All @@ -148,14 +162,19 @@ fun QRCodeScannerScreenContent(
contentAlignment = Alignment.Center
) {

if( cameraPermissionState?.status != PermissionStatus.Granted && !LocalInspectionMode.current){
if(uiState.cameraPermissionPermanentlyDenied){
Column(
modifier = Modifier.align(Alignment.Center),
modifier = Modifier
.fillMaxWidth(0.7f)
.align(Alignment.Center),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
val context = LocalContext.current
Text("Camera permission required")
Text(
"You have denied camera permission permanently. Please grant it from settings.",
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = {
Expand All @@ -166,7 +185,27 @@ fun QRCodeScannerScreenContent(
context.startActivity(intent)
}
) {
Text("Grant")
Text("Open Settings")
}
}
}
else if(uiState.shouldShowRationale){
Column(
modifier = Modifier
.fillMaxWidth(0.7f)
.align(Alignment.Center),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
"Camera permission is required to scan QR codes",
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = { onEvent(QRScannerScreenEvent.OnGrantPermissionClick) }
) {
Text("Grant Permission")
}
}
}
Expand All @@ -181,6 +220,7 @@ fun QRCodeScannerScreenContent(
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Failed to Decode !")
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = {
scanLauncher.launch(
Expand All @@ -206,6 +246,7 @@ fun QRCodeScannerContentPreview(modifier: Modifier = Modifier) {
DroidPadTheme {
QRCodeScannerScreenContent(
uiState = QRScannerScreenState(
cameraPermissionPermanentlyDenied = true,
decodingSuccess = false,
decodingFailed = true,
decoding = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,19 @@ import kotlinx.coroutines.withContext
data class QRScannerScreenState(
val decoding: Boolean = false,
val decodingSuccess: Boolean = false,
val decodingFailed: Boolean = false
val decodingFailed: Boolean = false,
val cameraPermissionGranted: Boolean = false,
val shouldShowRationale: Boolean = false,
val cameraPermissionPermanentlyDenied: Boolean = false,
)

sealed interface QRScannerScreenEvent {
data class OnQrCodeScanned(val data: String) : QRScannerScreenEvent
data object OnBackPress : QRScannerScreenEvent
data object OnCameraPermissionGranted : QRScannerScreenEvent
data object OnShouldShowRationale : QRScannerScreenEvent
data object OnPermissionPermanentlyDenied : QRScannerScreenEvent
data object OnGrantPermissionClick: QRScannerScreenEvent
}


Expand Down Expand Up @@ -97,6 +104,24 @@ class QRScannerScreenViewModel : ViewModel() {
}
}

is QRScannerScreenEvent.OnCameraPermissionGranted -> {
_uiState.update {
it.copy(cameraPermissionGranted = true)
}
}

is QRScannerScreenEvent.OnShouldShowRationale -> {
_uiState.update {
it.copy(shouldShowRationale = true)
}
}

is QRScannerScreenEvent.OnPermissionPermanentlyDenied -> {
_uiState.update {
it.copy(cameraPermissionPermanentlyDenied = true)
}
}

else -> {}
}
}
Expand Down

0 comments on commit ff33515

Please sign in to comment.