TP-73923 | Permission handling on qr and pay to contacts screen (#11795)

This commit is contained in:
Shaurya Rehan
2024-07-16 20:01:37 +05:30
committed by GitHub
parent fe7f4b161c
commit 6bddc99f86
11 changed files with 102 additions and 14 deletions

View File

@@ -17,11 +17,14 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState
sealed interface NaviPermissionResult {
data object AllGranted : NaviPermissionResult
// Here user has hard denied permission. App cannot ask permisison again..
// Here user has hard denied permission. App cannot ask permisison again..
data object HardDenied : NaviPermissionResult
// Here user has denied permission so app can show a UI before asking for permission again.
data object ShowRationale : NaviPermissionResult
data object None : NaviPermissionResult
}
fun handlePermissionResult(

View File

@@ -204,7 +204,8 @@ fun PaymentSummaryScreenV2(
naviPayAnalytics = NaviPayAnalytics.INSTANCE.NaviPayPermission(),
)
}
NaviPermissionResult.ShowRationale -> {}
NaviPermissionResult.ShowRationale,
NaviPermissionResult.None -> {}
}
}

View File

@@ -285,6 +285,9 @@ fun QrScannerScreen(
)
)
}
else -> {
// Do nothing
}
}
}

View File

@@ -7,6 +7,8 @@
package com.navi.pay.management.moneytransfer.scanpay.ui
import android.Manifest
import android.content.pm.PackageManager
import android.view.ScaleGestureDetector
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -77,6 +79,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
@@ -140,7 +143,6 @@ import com.navi.pay.utils.clearBackStackUpToAndNavigate
import com.navi.pay.utils.clickableDebounce
import com.navi.pay.utils.contactInitials
import com.navi.pay.utils.launchPermissionSettingsScreen
import com.navi.pay.utils.redirectToSettings
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.flow.collectLatest
@@ -165,6 +167,7 @@ fun QrScannerScreenV2(
qrScannerViewModelV2.clickedContactPhoneNumber.collectAsStateWithLifecycle()
val frequentTransactionsHeading by
qrScannerViewModelV2.frequentTransactionsHeading.collectAsStateWithLifecycle()
val permissionResult by qrScannerViewModelV2.permissionResult.collectAsStateWithLifecycle()
val onBackClick = { navigator.navigateUp(naviPayActivity) }
@@ -236,6 +239,26 @@ fun QrScannerScreenV2(
qrScannerViewModelV2.navigateToNextScreen.collect { navigator.navigate(direction = it) }
}
LaunchedEffect(Unit) {
val isPermissionGranted =
ContextCompat.checkSelfPermission(
naviPayActivity.baseContext,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
val shouldShowRationale =
ActivityCompat.shouldShowRequestPermissionRationale(
naviPayActivity,
Manifest.permission.CAMERA
)
// If permission is not granted and shouldShowRationale is false, the permission is hard
// denied
if (!isPermissionGranted && !shouldShowRationale) {
qrScannerViewModelV2.updatePermissionResult(NaviPermissionResult.HardDenied)
}
}
val bottomSheetStateHolder by
qrScannerViewModelV2.bottomSheetStateHolder.collectAsStateWithLifecycle()
val turnOffCamera by qrScannerViewModelV2.shouldTurnOffCamera.collectAsStateWithLifecycle()
@@ -285,7 +308,7 @@ fun QrScannerScreenV2(
naviPaySessionAttributes =
qrScannerViewModelV2.getNaviPaySessionAttributes()
)
// Camera screen will be shown on recomposition
qrScannerViewModelV2.updatePermissionResult(it)
}
NaviPermissionResult.HardDenied -> {
naviPayAnalytics.onCameraPermissionDenied(
@@ -293,6 +316,7 @@ fun QrScannerScreenV2(
naviPaySessionAttributes =
qrScannerViewModelV2.getNaviPaySessionAttributes()
)
qrScannerViewModelV2.updatePermissionResult(it)
}
NaviPermissionResult.ShowRationale -> {
naviPayAnalytics.onCameraPermissionDenied(
@@ -300,12 +324,16 @@ fun QrScannerScreenV2(
naviPaySessionAttributes =
qrScannerViewModelV2.getNaviPaySessionAttributes()
)
qrScannerViewModelV2.updatePermissionResult(it)
}
else -> {
// Do nothing
}
}
}
val onAllowPermissionButtonClick = {
if (cameraPermissionsState.redirectToSettings()) {
if (permissionResult is NaviPermissionResult.HardDenied) {
naviPayActivity.launchPermissionSettingsScreen()
naviPayAnalytics.onAppSettingsScreenLaunched(
naviPaySessionAttributes = qrScannerViewModelV2.getNaviPaySessionAttributes()

View File

@@ -29,6 +29,7 @@ import com.navi.pay.common.model.view.NaviPayErrorButtonConfig
import com.navi.pay.common.model.view.NaviPayErrorConfig
import com.navi.pay.common.model.view.NaviPaySessionHelper
import com.navi.pay.common.model.view.NaviPayVmData
import com.navi.pay.common.model.view.NaviPermissionResult
import com.navi.pay.common.repository.CommonRepository
import com.navi.pay.common.setup.NaviPayCustomerStatusHandler
import com.navi.pay.common.setup.model.NaviPayCustomerStatus
@@ -134,6 +135,10 @@ constructor(
private val _clickedContactPhoneNumber = MutableStateFlow("")
val clickedContactPhoneNumber = _clickedContactPhoneNumber.asStateFlow()
private val _permissionResult =
MutableStateFlow<NaviPermissionResult>(NaviPermissionResult.None)
val permissionResult = _permissionResult.asStateFlow()
private val frequentTransactionsTotalEntries =
MutableStateFlow(naviPayDefaultConfig.config.frequentTransactionsTotalEntries)
@@ -589,6 +594,10 @@ constructor(
}
}
}
fun updatePermissionResult(permissionResult: NaviPermissionResult) {
_permissionResult.update { permissionResult }
}
}
enum class QrScannerBottomSheetUIStateV2 {

View File

@@ -253,6 +253,9 @@ fun PayToContactsScreen(
)
)
}
else -> {
// Do nothing
}
}
}

View File

@@ -7,6 +7,8 @@
package com.navi.pay.management.paytocontacts.ui
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
@@ -54,6 +56,8 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -107,7 +111,6 @@ import com.navi.pay.utils.SELF_TRANSFER
import com.navi.pay.utils.contactInitials
import com.navi.pay.utils.customHide
import com.navi.pay.utils.launchPermissionSettingsScreen
import com.navi.pay.utils.redirectToSettings
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.android.awaitFrame
@@ -152,6 +155,7 @@ fun PayToContactsScreenV2(
payToContactsViewModelV2.showFrequentTransactionsSection.collectAsStateWithLifecycle()
val shouldShowYourContactsTitle by
payToContactsViewModelV2.shouldShowYourContactsTitle.collectAsStateWithLifecycle()
val permissionResult by payToContactsViewModelV2.permissionResult.collectAsStateWithLifecycle()
val isSelfTransferCtaVisible by
payToContactsViewModelV2.isSelfTransferCtaVisible.collectAsStateWithLifecycle()
@@ -235,6 +239,26 @@ fun PayToContactsScreenV2(
isDelayedOnboardingExperimentEnabled(bundle = naviPayActivity.intent.extras)
}
LaunchedEffect(Unit) {
val isPermissionGranted =
ContextCompat.checkSelfPermission(
naviPayActivity.baseContext,
Manifest.permission.READ_CONTACTS
) == PackageManager.PERMISSION_GRANTED
val shouldShowRationale =
ActivityCompat.shouldShowRequestPermissionRationale(
naviPayActivity,
Manifest.permission.READ_CONTACTS
)
// If permission is not granted and shouldShowRationale is false, the permission is hard
// denied
if (!isPermissionGranted && !shouldShowRationale) {
payToContactsViewModelV2.updatePermissionResult(NaviPermissionResult.HardDenied)
}
}
val readContactsPermissionsState =
rememberMultiplePermissions(
permissions =
@@ -248,6 +272,7 @@ fun PayToContactsScreenV2(
naviPaySessionAttributes =
payToContactsViewModelV2.getNaviPaySessionAttributes()
)
payToContactsViewModelV2.updatePermissionResult(it)
}
NaviPermissionResult.HardDenied -> {
naviPayAnalytics.onPayToContactsPermissionDenied(
@@ -255,6 +280,7 @@ fun PayToContactsScreenV2(
naviPaySessionAttributes =
payToContactsViewModelV2.getNaviPaySessionAttributes()
)
payToContactsViewModelV2.updatePermissionResult(it)
}
NaviPermissionResult.ShowRationale -> {
naviPayAnalytics.onPayToContactsPermissionDenied(
@@ -262,12 +288,16 @@ fun PayToContactsScreenV2(
naviPaySessionAttributes =
payToContactsViewModelV2.getNaviPaySessionAttributes()
)
payToContactsViewModelV2.updatePermissionResult(it)
}
else -> {
// Do nothing
}
}
}
val onAllowPermissionButtonClicked = {
if (readContactsPermissionsState.redirectToSettings()) {
if (permissionResult is NaviPermissionResult.HardDenied) {
naviPayActivity.launchPermissionSettingsScreen()
naviPayAnalytics.onAppSettingsScreenLaunched(
naviPaySessionAttributes = payToContactsViewModelV2.getNaviPaySessionAttributes()

View File

@@ -26,6 +26,7 @@ import com.navi.pay.common.model.network.ValidateVpaRequest
import com.navi.pay.common.model.network.ValidateVpaResponse
import com.navi.pay.common.model.view.NaviPaySessionHelper
import com.navi.pay.common.model.view.NaviPayVmData
import com.navi.pay.common.model.view.NaviPermissionResult
import com.navi.pay.common.repository.CommonRepository
import com.navi.pay.common.usecase.LitmusExperimentsUseCase
import com.navi.pay.common.usecase.NaviPayConfigUseCase
@@ -140,6 +141,10 @@ constructor(
private val _apiErrorMessage = MutableSharedFlow<String>()
val apiErrorMessage = _apiErrorMessage.asSharedFlow()
private val _permissionResult =
MutableStateFlow<NaviPermissionResult>(NaviPermissionResult.None)
val permissionResult = _permissionResult.asStateFlow()
private val _isSavedContactListNonEmpty = MutableStateFlow(true)
val isSavedContactListNonEmpty = _isSavedContactListNonEmpty.asStateFlow()
@@ -792,6 +797,10 @@ constructor(
)
}
}
fun updatePermissionResult(permissionResult: NaviPermissionResult) {
_permissionResult.update { permissionResult }
}
}
enum class PayToContactsUIStateV2 {

View File

@@ -130,6 +130,9 @@ fun NaviPayOnboardingScreen(
)
)
}
else -> {
// do nothing
}
}
}

View File

@@ -273,6 +273,9 @@ fun NaviPayOnboardingScreenV2(
)
)
}
else -> {
// Do nothing
}
}
}
@@ -349,6 +352,9 @@ fun NaviPayOnboardingScreenV2(
)
)
}
else -> {
// Do nothing
}
}
}

View File

@@ -54,8 +54,6 @@ import androidx.paging.compose.LazyPagingItems
import coil.request.ImageRequest
import com.google.accompanist.navigation.material.BottomSheetNavigator
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
@@ -661,11 +659,6 @@ fun String.getIfscForBankUptime(): String {
fun getDefaultPayeeEntity() = PayeeEntity(name = "Default", vpa = "Default")
@OptIn(ExperimentalPermissionsApi::class)
fun MultiplePermissionsState.redirectToSettings(): Boolean {
return this.shouldShowRationale && !this.allPermissionsGranted
}
fun Activity.launchPermissionSettingsScreen() {
val intent =
Intent(