diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt index c23818016..dfdf59ee0 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/addtowallet/AddToWalletScreen.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.android.identity.appsupport.ui.qrcode.ScanQrCodeDialog import com.android.identity.issuance.IssuingAuthorityConfiguration import com.android.identity.issuance.remote.WalletServerProvider import com.android.identity.util.Logger @@ -38,7 +39,6 @@ import com.android.identity_credential.wallet.WalletApplication import com.android.identity_credential.wallet.credentialoffer.extractCredentialIssuerData import com.android.identity_credential.wallet.navigation.WalletDestination import com.android.identity_credential.wallet.ui.ScreenWithAppBarAndBackButton -import com.android.identity_credential.wallet.ui.qrscanner.ScanQrDialog import com.android.identity_credential.wallet.util.getUrlQueryFromCustomSchemeUrl import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -100,13 +100,6 @@ fun AddToWalletScreen( // to navigate after scanning a Qr code navigateToOnComposable.value?.let { route -> onNavigate(route) } - /** - * Helper function for hiding [ScanQrDialog] from current view. - */ - fun dismissScanQrDialog() { - showQrScannerDialog.value = false - } - ScreenWithAppBarAndBackButton( title = stringResource(R.string.add_screen_title), onBackButtonClick = { onNavigate(WalletDestination.PopBackStack.route) } @@ -121,13 +114,10 @@ fun AddToWalletScreen( } else { // compose ScanQrDialog when user taps on "Scan Credential Offer" if (showQrScannerDialog.value) { - ScanQrDialog( - modifier = Modifier - .fillMaxWidth(0.9f) - .fillMaxHeight(0.6f), + ScanQrCodeDialog( title = stringResource(R.string.credential_offer_scan), description = stringResource(id = R.string.credential_offer_details), - onScannedQrCode = { qrCodeTextUrl -> + onCodeScanned = { qrCodeTextUrl -> // filter only for OID4VCI Url schemes. if (qrCodeTextUrl.startsWith(WalletApplication.OID4VCI_CREDENTIAL_OFFER_URL_SCHEME)) { // scanned text is expected to be an encoded Url @@ -145,8 +135,13 @@ fun AddToWalletScreen( } } } + true }, - onClose = { dismissScanQrDialog() } + dismissButton = stringResource(R.string.reader_screen_scan_qr_dialog_dismiss_button), + onDismiss = { showQrScannerDialog.value = false }, + modifier = Modifier + .fillMaxWidth()//0.9f) + .fillMaxHeight(0.6f), ) } else { // not showing [ScanQrDialog] AddToWalletScreenWithIssuerDisplayDatas( diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/reader/ReaderScreen.kt b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/reader/ReaderScreen.kt index 9ded9e389..4b002b2dd 100644 --- a/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/reader/ReaderScreen.kt +++ b/wallet/src/main/java/com/android/identity_credential/wallet/ui/destination/reader/ReaderScreen.kt @@ -63,6 +63,7 @@ import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver +import com.android.identity.appsupport.ui.qrcode.ScanQrCodeDialog import com.android.identity.cbor.Cbor import com.android.identity.cbor.DiagnosticOption import com.android.identity.documenttype.DocumentTypeRepository @@ -81,7 +82,6 @@ import com.android.identity_credential.wallet.navigation.WalletDestination import com.android.identity_credential.wallet.ui.KeyValuePairHtml import com.android.identity_credential.wallet.ui.KeyValuePairText import com.android.identity_credential.wallet.ui.ScreenWithAppBarAndBackButton -import com.android.identity_credential.wallet.ui.qrscanner.ScanQrDialog import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState import kotlinx.datetime.Instant @@ -173,10 +173,15 @@ private fun WaitForEngagement( var dropdownSelected = remember { mutableStateOf(availableRequests[0]) } if (showQrScannerDialog.value) { - ScanQrDialog(title = stringResource(R.string.reader_screen_scan_qr_dialog_title), + ScanQrCodeDialog( + title = stringResource(R.string.reader_screen_scan_qr_dialog_title), description = stringResource(R.string.reader_screen_scan_qr_dialog_text), - onScannedQrCode = { qrCodeText -> model.setQrCode(qrCodeText) }, - onClose = { showQrScannerDialog.value = false } + onCodeScanned = { qrCodeText -> + model.setQrCode(qrCodeText) + true + }, + dismissButton = stringResource(R.string.reader_screen_scan_qr_dialog_dismiss_button), + onDismiss = { showQrScannerDialog.value = false }, ) } diff --git a/wallet/src/main/java/com/android/identity_credential/wallet/ui/qrscanner/ScanQrDialog.kt b/wallet/src/main/java/com/android/identity_credential/wallet/ui/qrscanner/ScanQrDialog.kt deleted file mode 100644 index e055ff411..000000000 --- a/wallet/src/main/java/com/android/identity_credential/wallet/ui/qrscanner/ScanQrDialog.kt +++ /dev/null @@ -1,173 +0,0 @@ -package com.android.identity_credential.wallet.ui.qrscanner - -import android.Manifest -import android.view.ViewGroup -import android.widget.LinearLayout -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -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.foundation.layout.width -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.QrCode -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -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.unit.dp -import androidx.compose.ui.viewinterop.AndroidView -import androidx.compose.ui.window.DialogProperties -import com.android.identity.util.Logger -import com.android.identity_credential.wallet.R -import com.budiyev.android.codescanner.CodeScanner -import com.budiyev.android.codescanner.CodeScannerView -import com.budiyev.android.codescanner.DecodeCallback -import com.budiyev.android.codescanner.ErrorCallback -import com.budiyev.android.codescanner.ScanMode -import com.google.accompanist.permissions.ExperimentalPermissionsApi -import com.google.accompanist.permissions.isGranted -import com.google.accompanist.permissions.rememberPermissionState - -const val TAG = "ScanQrDialog" - -/** - * Composable Dialog that shows a [title], [description], Qr scanner/camera surface, and close button. - * Issues callbacks to [onClose] for dismiss requests (from dialog or close button). When a Qr code - * is successfully scanned, the callback [onScannedQrCode] is invoked with the decoded text. - * - * A [modifier] can be passed for setting the Dialog's dimensions - */ -@Composable -fun ScanQrDialog( - title: String, - description: String, - onClose: () -> Unit, - onScannedQrCode: (String) -> Unit, - modifier: Modifier? = null -) { - // if provided, use the passed-in modifier and set dialog properties that allow for adjustment of dimensions - val (dialogModier: Modifier, dialogProperties: DialogProperties) = - if (modifier != null) { - Pair( - modifier, DialogProperties( - usePlatformDefaultWidth = false, - decorFitsSystemWindows = false - ) - ) - } else { - Pair(Modifier, DialogProperties()) - } - - AlertDialog( - modifier = dialogModier, - properties = dialogProperties, - icon = { - Icon( - Icons.Filled.QrCode, - contentDescription = stringResource(R.string.reader_screen_qr_icon_content_description) - ) - }, - title = { Text(text = title) }, - text = { QrScanner(description, onScannedQrCode) }, - onDismissRequest = { onClose.invoke() }, - confirmButton = {}, - dismissButton = { - TextButton( - onClick = { - onClose.invoke() - } - ) { - Text(stringResource(R.string.reader_screen_scan_qr_dialog_dismiss_button)) - } - } - ) -} - -@OptIn(ExperimentalPermissionsApi::class) -@Composable -private fun QrScanner( - description: String, - onScannedQrCode: (String) -> Unit, -) { - val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA) - if (!cameraPermissionState.status.isGranted) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center, - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - modifier = Modifier.padding(20.dp), - text = stringResource(R.string.reader_screen_scan_qr_dialog_missing_permission_text) - ) - Button( - onClick = { - cameraPermissionState.launchPermissionRequest() - } - ) { - Text(stringResource(R.string.reader_screen_scan_qr_dialog_request_permission_button)) - } - } - } - } else { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center, - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - modifier = Modifier.padding(8.dp), - text = description - ) - Row( - modifier = Modifier - .width(300.dp) - .height(300.dp), - horizontalArrangement = Arrangement.Center - ) { - AndroidView( - modifier = Modifier - .fillMaxSize() - .padding(32.dp), - factory = { context -> - CodeScannerView(context).apply { - val codeScanner = CodeScanner(context, this).apply { - layoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ) - isAutoFocusEnabled = true - isAutoFocusButtonVisible = false - scanMode = ScanMode.SINGLE - decodeCallback = DecodeCallback { result -> - releaseResources() - onScannedQrCode.invoke(result.text) - } - errorCallback = ErrorCallback { error -> - Logger.w(TAG, "Error scanning QR", error) - releaseResources() - } - camera = CodeScanner.CAMERA_BACK - isFlashEnabled = false - } - codeScanner.startPreview() - } - }, - ) - } - } - } - } -} \ No newline at end of file