TP-40039 | Internal permission revamp (#8739)
Co-authored-by: Shaurya Rehan <shaurya.rehan@navi.com> Co-authored-by: rahul bhat <rahul.bhat@navi.com> Co-authored-by: shankar yadav <shankar.yadav@navi.com> Co-authored-by: Aditya Narayan Malik <aditya.narayan@navi.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
accompanist-pager = "0.28.0"
|
||||
accompanist-permissions = "0.25.1"
|
||||
accompanist-permissions = "0.32.0"
|
||||
accompanist-systemuicontroller = "0.17.0"
|
||||
androidGradlePlugin = "8.1.2"
|
||||
android-exoplayer = "2.18.1"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.navi.pay.common.model.view
|
||||
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.app.ActivityCompat
|
||||
@@ -9,9 +10,12 @@ import com.google.accompanist.permissions.MultiplePermissionsState
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
|
||||
sealed interface NaviPermissionResult {
|
||||
object AllGranted : NaviPermissionResult
|
||||
object Denied : NaviPermissionResult
|
||||
object ShowRationale : NaviPermissionResult
|
||||
data object AllGranted : NaviPermissionResult
|
||||
//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
|
||||
}
|
||||
|
||||
fun handlePermissionResult(
|
||||
@@ -27,6 +31,7 @@ fun handlePermissionResult(
|
||||
permissionsResultMap.forEach {
|
||||
shouldShowRequestPermissionRationale =
|
||||
ActivityCompat.shouldShowRequestPermissionRationale(activity, it.key)
|
||||
|
||||
if (shouldShowRequestPermissionRationale) {
|
||||
return@forEach
|
||||
}
|
||||
@@ -35,7 +40,7 @@ fun handlePermissionResult(
|
||||
if (shouldShowRequestPermissionRationale) {
|
||||
onResult(NaviPermissionResult.ShowRationale)
|
||||
} else {
|
||||
onResult(NaviPermissionResult.Denied)
|
||||
onResult(NaviPermissionResult.HardDenied)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class PermissionResult(
|
||||
val permissionKey: String, val isGranted: Boolean
|
||||
val permissionKey: String, val isGranted: Boolean, val permissionRevoked: Boolean
|
||||
) : Parcelable
|
||||
@@ -38,6 +38,7 @@ import com.navi.pay.entry.ui.NaviPayAccessDeniedScreen
|
||||
import com.navi.pay.entry.ui.NaviPayMainScreen
|
||||
import com.navi.pay.npcicl.NpciClService
|
||||
import com.navi.pay.onboarding.binding.ui.NaviPayOnboardingActivity
|
||||
import com.navi.pay.permission.model.view.PermissionState
|
||||
import com.navi.pay.permission.utils.PermissionKeys
|
||||
import com.navi.pay.utils.GenericErrorCtaHandler
|
||||
import com.navi.pay.utils.NEEDS_RESULT
|
||||
@@ -184,9 +185,8 @@ class NaviPayActivity : BaseActivity() {
|
||||
genericErrorCtaHandler.destinationsNavigator?.navigate(
|
||||
NaviPayPermissionScreenDestination(
|
||||
permissionKey = PermissionKeys.NON_FIRST_TIME_SCREEN_PERMISSION_KEY,
|
||||
titleText = R.string.intro_permission_title,
|
||||
subTitleText = R.string.intro_permission_subtitle,
|
||||
ctaText = R.string.allow
|
||||
ctaText = R.string.go_to_setting,
|
||||
permissionState = PermissionState.HARD_DENIED
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,9 +10,12 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.plusAssign
|
||||
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
|
||||
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.pay.NavGraphs
|
||||
import com.navi.pay.common.model.view.ErrorVisibilityEvent
|
||||
@@ -22,6 +25,7 @@ import com.navi.pay.common.ui.NaviPayModalBottomSheetLayout
|
||||
import com.navi.pay.common.utils.ErrorEventHandler
|
||||
import com.navi.pay.entry.NaviPayActivity
|
||||
import com.navi.pay.utils.GenericErrorCtaHandler
|
||||
import com.navi.pay.utils.rememberBottomSheetNavigator
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.animations.defaults.RootNavGraphDefaultAnimations
|
||||
import com.ramcosta.composedestinations.animations.rememberAnimatedNavHostEngine
|
||||
@@ -81,46 +85,57 @@ fun NaviPayMainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
NaviPayModalBottomSheetLayout(
|
||||
sheetState = state,
|
||||
sheetContent = {
|
||||
GenericErrorBottomSheetContent(errorEvent, onErrorCtaClick)
|
||||
}
|
||||
) {
|
||||
naviPayActivity.navController = rememberNavController()
|
||||
DestinationsNavHost(
|
||||
startRoute = customerStatusRoute ?: NavGraphs.root.startRoute,
|
||||
navGraph = NavGraphs.root,
|
||||
engine = rememberAnimatedNavHostEngine(rootDefaultAnimations = RootNavGraphDefaultAnimations(
|
||||
enterTransition = {
|
||||
slideIntoContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left,
|
||||
animationSpec = tween(400)
|
||||
)
|
||||
},
|
||||
exitTransition = {
|
||||
slideOutOfContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left,
|
||||
animationSpec = tween(400)
|
||||
)
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIntoContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right,
|
||||
animationSpec = tween(400)
|
||||
)
|
||||
},
|
||||
popExitTransition = {
|
||||
slideOutOfContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right,
|
||||
animationSpec = tween(400)
|
||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||
val navController = rememberNavController().apply {
|
||||
this.navigatorProvider += bottomSheetNavigator
|
||||
}
|
||||
|
||||
ModalBottomSheetLayout(bottomSheetNavigator = bottomSheetNavigator,
|
||||
content = {
|
||||
NaviPayModalBottomSheetLayout(
|
||||
sheetState = state,
|
||||
sheetContent = {
|
||||
GenericErrorBottomSheetContent(
|
||||
errorEvent = errorEvent,
|
||||
onErrorCtaClick = onErrorCtaClick
|
||||
)
|
||||
}
|
||||
)),
|
||||
navController = naviPayActivity.navController,
|
||||
dependenciesContainerBuilder = {
|
||||
dependency(naviPayActivity)
|
||||
genericErrorCtaHandler.destinationsNavigator = this.destinationsNavigator
|
||||
})
|
||||
}
|
||||
) {
|
||||
naviPayActivity.navController = navController
|
||||
DestinationsNavHost(
|
||||
startRoute = customerStatusRoute ?: NavGraphs.root.startRoute,
|
||||
navGraph = NavGraphs.root,
|
||||
engine = rememberAnimatedNavHostEngine(rootDefaultAnimations = RootNavGraphDefaultAnimations(
|
||||
enterTransition = {
|
||||
slideIntoContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left,
|
||||
animationSpec = tween(400)
|
||||
)
|
||||
},
|
||||
exitTransition = {
|
||||
slideOutOfContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left,
|
||||
animationSpec = tween(400)
|
||||
)
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIntoContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right,
|
||||
animationSpec = tween(400)
|
||||
)
|
||||
},
|
||||
popExitTransition = {
|
||||
slideOutOfContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right,
|
||||
animationSpec = tween(400)
|
||||
)
|
||||
}
|
||||
)),
|
||||
navController = naviPayActivity.navController,
|
||||
dependenciesContainerBuilder = {
|
||||
dependency(naviPayActivity)
|
||||
genericErrorCtaHandler.destinationsNavigator = this.destinationsNavigator
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -103,6 +103,7 @@ import com.navi.pay.management.moneytransfer.scanpay.model.view.QrData
|
||||
import com.navi.pay.management.moneytransfer.scanpay.model.view.QrScanState
|
||||
import com.navi.pay.management.moneytransfer.scanpay.model.view.UriType
|
||||
import com.navi.pay.management.moneytransfer.scanpay.viewmodel.QrScannerViewModel
|
||||
import com.navi.pay.permission.model.view.PermissionState
|
||||
import com.navi.pay.permission.utils.PermissionKeys
|
||||
import com.navi.pay.permission.utils.PermissionUtils
|
||||
import com.navi.pay.utils.DEFAULT_INITIATION_MODE_QR_MANDATE
|
||||
@@ -246,11 +247,14 @@ fun QrScannerScreen(
|
||||
|
||||
resultRecipient.onNavResult { result ->
|
||||
when (result) {
|
||||
is NavResult.Canceled -> navigator.navigateUp()
|
||||
is NavResult.Canceled -> naviPayActivity.finish()
|
||||
is NavResult.Value -> {
|
||||
if (result.value.permissionKey == PermissionKeys.QR_SCAN_PERMISSION_KEY && !result.value.isGranted) {
|
||||
if (result.value.permissionKey == PermissionKeys.QR_SCAN_PERMISSION_KEY && !result.value.isGranted && !result.value.permissionRevoked) {
|
||||
onBackClick()
|
||||
}
|
||||
if (result.value.permissionKey == PermissionKeys.QR_SCAN_PERMISSION_KEY && result.value.permissionRevoked) {
|
||||
qrScannerViewModel.updateShowPopUpPermission(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,6 +262,7 @@ fun QrScannerScreen(
|
||||
val qrScanResult by qrScannerViewModel.qrScanResult.collectAsStateWithLifecycle()
|
||||
val isTorchEnabled by qrScannerViewModel.isTorchEnabled.collectAsStateWithLifecycle()
|
||||
val bottomSheetStateHolder by qrScannerViewModel.bottomSheetStateHolder.collectAsStateWithLifecycle()
|
||||
val showPopUpPermission by qrScannerViewModel.showPopUpPermission.collectAsStateWithLifecycle()
|
||||
|
||||
val cameraPermissionsState = rememberMultiplePermissions(
|
||||
permissions = PermissionUtils.getPermissionListFromPermissionKey(permissionKey = PermissionKeys.QR_SCAN_PERMISSION_KEY)
|
||||
@@ -268,14 +273,32 @@ fun QrScannerScreen(
|
||||
// Camera screen will be shown on recomposition
|
||||
}
|
||||
|
||||
NaviPermissionResult.Denied, NaviPermissionResult.ShowRationale -> {
|
||||
naviPayAnalytics.onCameraPermissionDenied(showRationale = it is NaviPermissionResult.ShowRationale)
|
||||
NaviPermissionResult.HardDenied -> {
|
||||
|
||||
qrScannerViewModel.showPermissionPopup = false
|
||||
if (!qrScannerViewModel.isPermissionScreenLaunched) {
|
||||
qrScannerViewModel.isPermissionScreenLaunched = true
|
||||
navigator.navigate(NaviPayPermissionScreenDestination(permissionKey = PermissionKeys.QR_SCAN_PERMISSION_KEY))
|
||||
}
|
||||
naviPayAnalytics.onCameraPermissionDenied(showRationale = false)
|
||||
qrScannerViewModel.updateShowPopUpPermission(false)
|
||||
navigator.navigate(
|
||||
NaviPayPermissionScreenDestination(
|
||||
permissionKey = PermissionKeys.QR_SCAN_PERMISSION_KEY,
|
||||
ctaText = R.string.go_to_setting,
|
||||
permissionState = PermissionState.HARD_DENIED
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
NaviPermissionResult.ShowRationale -> {
|
||||
naviPayAnalytics.onCameraPermissionDenied(showRationale = true)
|
||||
|
||||
qrScannerViewModel.updateShowPopUpPermission(false)
|
||||
|
||||
navigator.navigate(
|
||||
NaviPayPermissionScreenDestination(
|
||||
permissionKey = PermissionKeys.QR_SCAN_PERMISSION_KEY,
|
||||
titleText = R.string.allow_camera_permission,
|
||||
ctaText = R.string.allow,
|
||||
permissionState = PermissionState.SHOW_RATIONALE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,7 +414,9 @@ fun QrScannerScreen(
|
||||
onScanSuccess = qrScannerViewModel::processQrContent,
|
||||
barcodeScanner = barcodeScanner,
|
||||
)
|
||||
} else if (qrScannerViewModel.showPermissionPopup) {
|
||||
}
|
||||
|
||||
if (showPopUpPermission) {
|
||||
LaunchedEffect(Unit) {
|
||||
cameraPermissionsState.launchMultiplePermissionRequest()
|
||||
}
|
||||
|
||||
@@ -74,11 +74,13 @@ class QrScannerViewModel @Inject constructor(
|
||||
|
||||
var isQrCodeProcessing: AtomicBoolean = AtomicBoolean(false)
|
||||
|
||||
var isPermissionScreenLaunched = false
|
||||
var showPermissionPopup = true
|
||||
var lightSensorValue = 0f
|
||||
|
||||
val isUserOnboarded = naviPayCustomerStatusHandler.getCustomerStatusOnMainThread() == NaviPayCustomerStatus.LINKED_VPA
|
||||
private val _showPopUpPermission = MutableStateFlow(true)
|
||||
val showPopUpPermission = _showPopUpPermission.asStateFlow()
|
||||
|
||||
val isUserOnboarded =
|
||||
naviPayCustomerStatusHandler.getCustomerStatusOnMainThread() == NaviPayCustomerStatus.LINKED_VPA
|
||||
|
||||
fun processQrContent(qrContent: String, sendQrFromGallery: Boolean = false) {
|
||||
if (isQrCodeProcessing.get()) return
|
||||
@@ -132,6 +134,10 @@ class QrScannerViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateShowPopUpPermission(showPopUpPermission: Boolean) {
|
||||
_showPopUpPermission.update { showPopUpPermission }
|
||||
}
|
||||
|
||||
private fun parseUpiQrCode(qrContent: String, sendQrFromGallery: Boolean) {
|
||||
when (val upiResult = upiUriParser.parseForUpiUri(uri = qrContent)) {
|
||||
is UpiUriResult.Success -> {
|
||||
|
||||
@@ -57,6 +57,7 @@ import com.navi.pay.management.paytocontacts.model.view.FrequentTransactionEntit
|
||||
import com.navi.pay.management.paytocontacts.model.view.PhoneContactEntity
|
||||
import com.navi.pay.management.paytocontacts.viewmodel.PayToContactsUIState
|
||||
import com.navi.pay.management.paytocontacts.viewmodel.PayToContactsViewModel
|
||||
import com.navi.pay.permission.model.view.PermissionState
|
||||
import com.navi.pay.permission.utils.PermissionKeys
|
||||
import com.navi.pay.permission.utils.PermissionUtils
|
||||
import com.navi.pay.utils.NAVI_PAY_LOADER
|
||||
@@ -97,6 +98,7 @@ fun PayToContactsScreen(
|
||||
val uiState by payToContactsViewModel.uiState.collectAsStateWithLifecycle()
|
||||
val isNewContactVisible by payToContactsViewModel.isNewContactVisible.collectAsStateWithLifecycle()
|
||||
val isTrailingIconEnabled by payToContactsViewModel.isTrailingIconEnabled.collectAsStateWithLifecycle()
|
||||
val showPopUpPermission by payToContactsViewModel.showPopUpPermission.collectAsStateWithLifecycle()
|
||||
val frequentTransactions by payToContactsViewModel.frequentTransactions.collectAsStateWithLifecycle()
|
||||
|
||||
val onBackClick = {
|
||||
@@ -160,12 +162,12 @@ fun PayToContactsScreen(
|
||||
resultRecipient.onNavResult { result ->
|
||||
when (result) {
|
||||
is NavResult.Canceled -> {
|
||||
navigator.navigateUp()
|
||||
naviPayActivity.finish()
|
||||
}
|
||||
|
||||
is NavResult.Value -> {
|
||||
if (!result.value.isGranted) {
|
||||
navigator.navigateUp()
|
||||
if (result.value.permissionKey == PermissionKeys.QR_SCAN_PERMISSION_KEY && result.value.permissionRevoked) {
|
||||
payToContactsViewModel.updateShowPopUpPermission(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,12 +181,27 @@ fun PayToContactsScreen(
|
||||
// Camera screen will be shown on recomposition
|
||||
}
|
||||
|
||||
NaviPermissionResult.Denied, NaviPermissionResult.ShowRationale -> {
|
||||
payToContactsViewModel.showPermissionPopup = false
|
||||
if (!payToContactsViewModel.isPermissionScreenLaunched) {
|
||||
payToContactsViewModel.isPermissionScreenLaunched = true
|
||||
navigator.navigate(NaviPayPermissionScreenDestination(permissionKey = PermissionKeys.CONTACT_PERMISSION_KEY))
|
||||
}
|
||||
NaviPermissionResult.HardDenied -> {
|
||||
payToContactsViewModel.updateShowPopUpPermission(false)
|
||||
navigator.navigate(
|
||||
NaviPayPermissionScreenDestination(
|
||||
permissionKey = PermissionKeys.CONTACT_PERMISSION_KEY,
|
||||
ctaText = R.string.go_to_setting,
|
||||
permissionState = PermissionState.HARD_DENIED
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
NaviPermissionResult.ShowRationale -> {
|
||||
payToContactsViewModel.updateShowPopUpPermission(false)
|
||||
navigator.navigate(
|
||||
NaviPayPermissionScreenDestination(
|
||||
permissionKey = PermissionKeys.CONTACT_PERMISSION_KEY,
|
||||
ctaText = R.string.allow,
|
||||
permissionState = PermissionState.SHOW_RATIONALE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,12 +210,21 @@ fun PayToContactsScreen(
|
||||
LaunchedEffect(Unit) {
|
||||
payToContactsViewModel.fetchContacts()
|
||||
}
|
||||
when (uiState) {
|
||||
PayToContactsUIState.Loading -> LoadingScreen(
|
||||
lottieFileName = NAVI_PAY_LOADER,
|
||||
titleId = null,
|
||||
showFooter = false
|
||||
)
|
||||
} else {
|
||||
// Update UI state to loaded so that loading screen is not shown for permission not granted case
|
||||
payToContactsViewModel.updateUiState(uiState = PayToContactsUIState.Loaded)
|
||||
if (showPopUpPermission) {
|
||||
LaunchedEffect(Unit) {
|
||||
readContactsPermissionsState.launchMultiplePermissionRequest()
|
||||
}
|
||||
}
|
||||
}
|
||||
when (uiState) {
|
||||
PayToContactsUIState.Loading -> LoadingScreen(
|
||||
lottieFileName = NAVI_PAY_LOADER,
|
||||
titleId = null,
|
||||
showFooter = false
|
||||
)
|
||||
|
||||
PayToContactsUIState.Loaded -> RenderPayToContactsScreen(
|
||||
naviPayActivity = naviPayActivity,
|
||||
@@ -235,15 +261,8 @@ fun PayToContactsScreen(
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (payToContactsViewModel.showPermissionPopup) {
|
||||
LaunchedEffect(Unit) {
|
||||
readContactsPermissionsState.launchMultiplePermissionRequest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
|
||||
@@ -90,6 +90,9 @@ class PayToContactsViewModel @Inject constructor(
|
||||
initialValue = false
|
||||
)
|
||||
|
||||
private val _showPopUpPermission = MutableStateFlow(true)
|
||||
val showPopUpPermission = _showPopUpPermission.asStateFlow()
|
||||
|
||||
init {
|
||||
updateNaviPayDefaultConfig()
|
||||
}
|
||||
@@ -105,7 +108,7 @@ class PayToContactsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
var isPermissionScreenLaunched = false
|
||||
var showPermissionPopup = true
|
||||
|
||||
|
||||
val filteredContactList =
|
||||
combine(_searchQuery, _allContactList) { searchQuery, allContactList ->
|
||||
@@ -142,7 +145,7 @@ class PayToContactsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUiState(uiState: PayToContactsUIState) {
|
||||
fun updateUiState(uiState: PayToContactsUIState) {
|
||||
_uiState.update { uiState }
|
||||
}
|
||||
|
||||
@@ -230,6 +233,10 @@ class PayToContactsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateShowPopUpPermission(showPopUpPermission: Boolean) {
|
||||
_showPopUpPermission.update { showPopUpPermission }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum class PayToContactsUIState {
|
||||
|
||||
@@ -6,15 +6,12 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.NavHostController
|
||||
import com.navi.common.model.ModuleNameV2
|
||||
import com.navi.common.ui.activity.BaseActivity
|
||||
import com.navi.pay.analytics.NaviPayAnalytics
|
||||
import com.navi.pay.common.theme.NaviPayMaterialTheme
|
||||
import com.navi.pay.onboarding.binding.OnboardingNavGraph
|
||||
import com.navi.pay.utils.NAVI_PAY_DEBUG_LOG
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.navigation.dependency
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -24,6 +21,8 @@ class NaviPayOnboardingActivity : BaseActivity() {
|
||||
override val moduleName: ModuleNameV2
|
||||
get() = ModuleNameV2.NAVIPAY
|
||||
|
||||
lateinit var navController: NavHostController
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
|
||||
@@ -32,12 +31,7 @@ class NaviPayOnboardingActivity : BaseActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
NaviPayMaterialTheme {
|
||||
DestinationsNavHost(
|
||||
navGraph = OnboardingNavGraph.root,
|
||||
navController = rememberNavController(),
|
||||
dependenciesContainerBuilder = {
|
||||
dependency(this@NaviPayOnboardingActivity)
|
||||
})
|
||||
NaviPayOnboardingMainScreen(naviPayOnboardingActivity = this)
|
||||
}
|
||||
}
|
||||
Log.d(NAVI_PAY_DEBUG_LOG, "NaviPayOnboardingActivity")
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.navi.pay.onboarding.binding.ui
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.plusAssign
|
||||
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
|
||||
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
|
||||
import com.navi.pay.common.ui.NaviPayModalBottomSheetLayout
|
||||
import com.navi.pay.common.utils.ErrorEventHandler
|
||||
import com.navi.pay.onboarding.binding.OnboardingNavGraph
|
||||
import com.navi.pay.utils.rememberBottomSheetNavigator
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.animations.rememberAnimatedNavHostEngine
|
||||
import com.ramcosta.composedestinations.navigation.dependency
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterialApi::class, ExperimentalMaterialNavigationApi::class,
|
||||
ExperimentalAnimationApi::class
|
||||
)
|
||||
@Composable
|
||||
fun NaviPayOnboardingMainScreen(
|
||||
naviPayOnboardingActivity: NaviPayOnboardingActivity
|
||||
) {
|
||||
|
||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||
val navController = rememberNavController().apply {
|
||||
this.navigatorProvider += bottomSheetNavigator
|
||||
}
|
||||
|
||||
ModalBottomSheetLayout(bottomSheetNavigator = bottomSheetNavigator,
|
||||
content = {
|
||||
NaviPayModalBottomSheetLayout(
|
||||
sheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden),
|
||||
sheetContent = {
|
||||
|
||||
}
|
||||
) {
|
||||
naviPayOnboardingActivity.navController = navController
|
||||
DestinationsNavHost(
|
||||
navGraph = OnboardingNavGraph.root,
|
||||
engine = rememberAnimatedNavHostEngine(),
|
||||
navController = naviPayOnboardingActivity.navController,
|
||||
dependenciesContainerBuilder = {
|
||||
dependency(naviPayOnboardingActivity)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import com.navi.pay.onboarding.binding.model.view.NaviPayOnboardingBottomSheetTy
|
||||
import com.navi.pay.onboarding.binding.model.view.OnboardingDeviceData
|
||||
import com.navi.pay.onboarding.binding.model.view.SmsVerificationState
|
||||
import com.navi.pay.onboarding.binding.viewmodel.NaviPayOnboardingViewModel
|
||||
import com.navi.pay.permission.model.view.PermissionState
|
||||
import com.navi.pay.permission.utils.PermissionKeys
|
||||
import com.navi.pay.permission.utils.PermissionUtils
|
||||
import com.navi.pay.utils.CUSTOMER_STATUS_AFTER_ONBOARDING
|
||||
@@ -87,20 +88,28 @@ fun NaviPayOnboardingScreen(
|
||||
}
|
||||
}
|
||||
|
||||
NaviPermissionResult.Denied, NaviPermissionResult.ShowRationale -> {
|
||||
NaviPermissionResult.HardDenied -> {
|
||||
naviPayAnalytics.onPermissionDenied()
|
||||
naviPayOnboardingViewModel.showPermissionPopup = false
|
||||
if (!naviPayOnboardingViewModel.isPermissionScreenLaunched) {
|
||||
naviPayOnboardingViewModel.isPermissionScreenLaunched = true
|
||||
navigator.navigate(
|
||||
NaviPayPermissionScreenDestination(
|
||||
permissionKey = PermissionKeys.FIRST_TIME_SCREEN_PERMISSION_KEY,
|
||||
titleText = R.string.intro_permission_title,
|
||||
subTitleText = R.string.intro_permission_subtitle,
|
||||
ctaText = R.string.allow
|
||||
ctaText = R.string.go_to_setting,
|
||||
permissionState = PermissionState.HARD_DENIED
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NaviPermissionResult.ShowRationale -> {
|
||||
naviPayAnalytics.onPermissionDenied()
|
||||
navigator.navigate(
|
||||
NaviPayPermissionScreenDestination(
|
||||
permissionKey = PermissionKeys.FIRST_TIME_SCREEN_PERMISSION_KEY,
|
||||
ctaText = R.string.allow,
|
||||
permissionState = PermissionState.SHOW_RATIONALE
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,6 +172,12 @@ fun NaviPayOnboardingScreen(
|
||||
naviPayAnalytics = naviPayAnalytics,
|
||||
isFirstTimeUserExperience = isFirstTimeUserExperience
|
||||
)
|
||||
}
|
||||
if (result.value.permissionKey == PermissionKeys.FIRST_TIME_SCREEN_PERMISSION_KEY && result.value.permissionRevoked) {
|
||||
coroutineScope.launch {
|
||||
naviPayOnboardingViewModel.updateMultiplePermissionScreenLaunched()
|
||||
}
|
||||
|
||||
} else {
|
||||
naviPayAnalytics.onPermissionDenied()
|
||||
finishWithoutResult(naviPayOnboardingActivity)
|
||||
|
||||
@@ -154,6 +154,7 @@ class NaviPayOnboardingViewModel @Inject constructor(
|
||||
private val SMS_SENT_CHECK_TIMEOUT = 45 * 1000L // 45 seconds
|
||||
|
||||
var showPermissionPopup = true
|
||||
|
||||
var isPermissionScreenLaunched = false
|
||||
|
||||
private val naviPayPoller: NaviPayPoller by lazy {
|
||||
@@ -855,4 +856,9 @@ class NaviPayOnboardingViewModel @Inject constructor(
|
||||
updateBottomSheetUIState(showBottomSheet = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun updateMultiplePermissionScreenLaunched() {
|
||||
_onboardingAction.emit(NaviPayOnboardingAction.StartOnboarding)
|
||||
}
|
||||
}
|
||||
@@ -14,3 +14,8 @@ data class PermissionData(
|
||||
val qualifierList: List<String>,
|
||||
var state: Int? = null
|
||||
)
|
||||
|
||||
enum class PermissionState {
|
||||
HARD_DENIED,
|
||||
SHOW_RATIONALE,
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
package com.navi.pay.permission.ui
|
||||
|
||||
import com.navi.common.R as CommonR
|
||||
import com.navi.design.R as DesignR
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@@ -23,18 +21,15 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
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.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.Text
|
||||
@@ -47,16 +42,12 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -68,26 +59,25 @@ import com.navi.pay.R
|
||||
import com.navi.pay.analytics.NaviPayAnalytics
|
||||
import com.navi.pay.common.model.view.PermissionResult
|
||||
import com.navi.pay.common.theme.color.NaviPayColor
|
||||
import com.navi.pay.common.ui.BottomSheetContentWithIconHeaderDescButton
|
||||
import com.navi.pay.common.ui.NaviPayCard
|
||||
import com.navi.pay.common.ui.NaviPayHeader
|
||||
import com.navi.pay.common.ui.NaviPayModalBottomSheetLayout
|
||||
import com.navi.pay.common.ui.ThemeRoundedButton
|
||||
import com.navi.pay.permission.model.view.PermissionData
|
||||
import com.navi.pay.permission.model.view.PermissionState
|
||||
import com.navi.pay.permission.utils.PermissionKeys
|
||||
import com.navi.pay.permission.utils.PermissionUtils.getPermissionListFromPermissionKey
|
||||
import com.navi.pay.permission.viewmodel.PermissionViewModel
|
||||
import com.navi.pay.utils.OnLifecycleEvent
|
||||
import com.navi.pay.utils.clickableDebounce
|
||||
import com.navi.pay.utils.hasPermissions
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
||||
import com.ramcosta.composedestinations.spec.DestinationStyleBottomSheet
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Destination
|
||||
@Destination(style = DestinationStyleBottomSheet::class)
|
||||
@Composable
|
||||
fun NaviPayPermissionScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
naviPayActivity: Activity,
|
||||
resultNavigator: ResultBackNavigator<PermissionResult>,
|
||||
permissionKey: String,
|
||||
@@ -95,6 +85,7 @@ fun NaviPayPermissionScreen(
|
||||
subTitleText: Int? = null,
|
||||
ctaText: Int = R.string.allow,
|
||||
showPermissionDeniedView: Boolean = true,
|
||||
permissionState: PermissionState? = null,
|
||||
permissionViewModel: PermissionViewModel = hiltViewModel(),
|
||||
naviPayAnalytics: NaviPayAnalytics.NaviPayPermission =
|
||||
NaviPayAnalytics.INSTANCE.NaviPayPermission()
|
||||
@@ -111,7 +102,8 @@ fun NaviPayPermissionScreen(
|
||||
permissionKey = permissionKey,
|
||||
titleText = titleText,
|
||||
subTitleText = subTitleText,
|
||||
ctaText = ctaText
|
||||
ctaText = ctaText,
|
||||
permissionState = permissionState
|
||||
)
|
||||
}
|
||||
|
||||
@@ -126,6 +118,7 @@ fun RenderNaviPayPermissionScreen(
|
||||
titleText: Int,
|
||||
subTitleText: Int?,
|
||||
ctaText: Int,
|
||||
permissionState: PermissionState?
|
||||
) {
|
||||
|
||||
val showPermissionDeniedViewState by
|
||||
@@ -143,7 +136,8 @@ fun RenderNaviPayPermissionScreen(
|
||||
resultNavigator.navigateBack(
|
||||
result = PermissionResult(
|
||||
permissionKey = permissionKey,
|
||||
isGranted = false
|
||||
isGranted = false,
|
||||
permissionRevoked = false
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -159,7 +153,12 @@ fun RenderNaviPayPermissionScreen(
|
||||
val permissions = permissionsResultMap.map { it.key }
|
||||
if (allPermissionsGranted) {
|
||||
naviPayAnalytics.onAllPermissionGranted(permissions)
|
||||
resultNavigator.navigateBack(result = PermissionResult(permissionKey, true))
|
||||
resultNavigator.navigateBack(
|
||||
result = PermissionResult(
|
||||
permissionKey, true,
|
||||
permissionRevoked = false
|
||||
)
|
||||
)
|
||||
} else {
|
||||
permissionDetailData.forEach {
|
||||
it.qualifierList.forEach { qualifier ->
|
||||
@@ -206,7 +205,8 @@ fun RenderNaviPayPermissionScreen(
|
||||
resultNavigator.navigateBack(
|
||||
result = PermissionResult(
|
||||
permissionKey,
|
||||
true
|
||||
isGranted = true,
|
||||
permissionRevoked = false
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -254,134 +254,79 @@ fun RenderNaviPayPermissionScreen(
|
||||
}
|
||||
}
|
||||
|
||||
NaviPayModalBottomSheetLayout(
|
||||
sheetState = state,
|
||||
sheetContent = {
|
||||
BottomSheetContentWithIconHeaderDescButton(
|
||||
iconId = DesignR.drawable.ic_info_icon_black,
|
||||
headerTextId = R.string.know_more_navi_pay,
|
||||
descriptionTextId = R.string.permission_settings_know_more,
|
||||
buttonTextId = R.string.np_okay_got_it,
|
||||
onButtonClicked = { permissionViewModel.updateBottomSheetUIState(showBottomSheet = false) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(IntrinsicSize.Min),
|
||||
) {
|
||||
TitleText(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
textResId = titleText,
|
||||
permissionKey = permissionKey
|
||||
)
|
||||
if (subTitleText != null) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = stringResource(id = subTitleText),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
color = NaviPayColor.textTertiary
|
||||
)
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = Color.White),
|
||||
) {
|
||||
NaviPayHeader(
|
||||
title = stringResource(id = R.string.app_permission_header),
|
||||
navigationIcon = R.drawable.ic_np_close_black_16,
|
||||
onNavigationIconClick = {
|
||||
onBackPress()
|
||||
},
|
||||
onActionClick = { permissionViewModel.updateBottomSheetUIState(showBottomSheet = true) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
TitleText(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textResId = titleText,
|
||||
permissionKey = permissionKey
|
||||
)
|
||||
if (subTitleText != null) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = stringResource(id = subTitleText),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
color = NaviPayColor.textTertiary
|
||||
)
|
||||
}
|
||||
|
||||
if (showPermissionDeniedViewState) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
PermissionsDeniedView(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
onClick = {
|
||||
launchPermissionSettingsScreen(
|
||||
naviPayActivity = naviPayActivity,
|
||||
naviPayAnalytics = naviPayAnalytics
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
if (permissionState != null) {
|
||||
PermissionTilesView(
|
||||
permissionViewModel,
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
permissionProvided = permissionState
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
Row(
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
ThemeRoundedButton(
|
||||
onClick = {
|
||||
val permissions: List<String> =
|
||||
permissionDetailData.flatMap { it.qualifierList }
|
||||
naviPayAnalytics.onPermissionNextClicked()
|
||||
if (naviPayActivity.hasPermissions(permissions = permissions)) {
|
||||
resultNavigator.navigateBack(
|
||||
result = PermissionResult(
|
||||
permissionKey,
|
||||
isGranted = true,
|
||||
permissionRevoked = false
|
||||
)
|
||||
)
|
||||
}
|
||||
if (ctaText == R.string.allow) {
|
||||
resultNavigator.navigateBack(
|
||||
result = PermissionResult(
|
||||
permissionKey,
|
||||
isGranted = false,
|
||||
permissionRevoked = true
|
||||
)
|
||||
)
|
||||
} else if (permissionViewModel.shouldGoToSettings()) {
|
||||
launchPermissionSettingsScreen(naviPayActivity, naviPayAnalytics)
|
||||
} else {
|
||||
launcher.launch(permissions.toTypedArray())
|
||||
}
|
||||
},
|
||||
text = stringResource(id = ctaText),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = CommonR.drawable.ic_security_checked_green_svg),
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.your_data_is_secure),
|
||||
fontSize = 8.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
color = NaviPayColor.textTertiary
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = Color.White,
|
||||
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
|
||||
),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(18.dp))
|
||||
ThemeRoundedButton(
|
||||
onClick = {
|
||||
val permissions: List<String> =
|
||||
permissionDetailData.flatMap { it.qualifierList }
|
||||
naviPayAnalytics.onPermissionNextClicked()
|
||||
if (naviPayActivity.hasPermissions(permissions = permissions)) {
|
||||
resultNavigator.navigateBack(
|
||||
result = PermissionResult(
|
||||
permissionKey,
|
||||
true
|
||||
)
|
||||
)
|
||||
} else if (permissionViewModel.shouldGoToSettings()) {
|
||||
launchPermissionSettingsScreen(naviPayActivity, naviPayAnalytics)
|
||||
} else {
|
||||
launcher.launch(permissions.toTypedArray())
|
||||
}
|
||||
},
|
||||
text = stringResource(id = ctaText),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -433,18 +378,24 @@ fun TitleText(modifier: Modifier, textResId: Int, permissionKey: String) {
|
||||
@Composable
|
||||
fun PermissionTilesView(
|
||||
permissionViewModel: PermissionViewModel,
|
||||
modifier: Modifier
|
||||
modifier: Modifier,
|
||||
permissionProvided: PermissionState
|
||||
) {
|
||||
NaviPayCard(modifier = modifier) {
|
||||
val permissionState = permissionViewModel.permissionState
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(vertical = 20.dp, horizontal = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
items(permissionState) { PermissionTileView(permissionData = it) }
|
||||
val permissionState = permissionViewModel.permissionState
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(vertical = 12.dp, horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
permissionState.forEach {
|
||||
PermissionTileView(
|
||||
permissionData = it,
|
||||
permissionState = permissionProvided
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun launchPermissionSettingsScreen(
|
||||
@@ -460,71 +411,11 @@ fun launchPermissionSettingsScreen(
|
||||
naviPayAnalytics.onAppSettingsScreenLaunched()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PermissionsDeniedView(modifier: Modifier, onClick: (() -> Unit)) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.clickableDebounce { onClick.invoke() }
|
||||
.then(
|
||||
Modifier.background(
|
||||
color = NaviPayColor.bgNonEditable, shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
),
|
||||
) {
|
||||
ConstraintLayout(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 16.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
val (title, subtitle, icon) = createRefs()
|
||||
Text(
|
||||
text = stringResource(id = R.string.permission_settings_desc),
|
||||
fontSize = 12.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
color = NaviPayColor.textPrimary,
|
||||
modifier = Modifier
|
||||
.constrainAs(title) {
|
||||
top.linkTo(parent.top, 16.dp)
|
||||
start.linkTo(parent.start, 16.dp)
|
||||
end.linkTo(icon.start, 24.dp)
|
||||
width = Dimension.fillToConstraints
|
||||
}
|
||||
.layoutId(title)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.permission_settings_path),
|
||||
fontSize = 10.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
color = NaviPayColor.textTertiary,
|
||||
modifier = Modifier
|
||||
.constrainAs(subtitle) {
|
||||
top.linkTo(title.bottom, 8.dp)
|
||||
start.linkTo(parent.start, 16.dp)
|
||||
end.linkTo(icon.start, 24.dp)
|
||||
width = Dimension.fillToConstraints
|
||||
}
|
||||
.layoutId(subtitle)
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_green_arrow_right_white_circle_bg),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.constrainAs(icon) {
|
||||
end.linkTo(parent.end, 16.dp)
|
||||
top.linkTo(parent.top, 16.dp)
|
||||
}
|
||||
.layoutId(icon)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PermissionTileView(permissionData: PermissionData) {
|
||||
fun PermissionTileView(permissionData: PermissionData, permissionState: PermissionState) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.Top
|
||||
) {
|
||||
@@ -542,38 +433,56 @@ fun PermissionTileView(permissionData: PermissionData) {
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = permissionData.titleId),
|
||||
fontSize = 14.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
color = NaviPayColor.textPrimary
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = permissionData.descriptionId),
|
||||
fontSize = 12.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
color = NaviPayColor.textTertiary
|
||||
)
|
||||
}
|
||||
if (permissionState == PermissionState.SHOW_RATIONALE) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
when (permissionData.state) {
|
||||
PackageManager.PERMISSION_GRANTED, PackageManager.PERMISSION_DENIED -> {
|
||||
Image(
|
||||
painter = painterResource(id = if (permissionData.state == PackageManager.PERMISSION_GRANTED) R.drawable.ic_success_green else R.drawable.ic_circular_red_exclamation),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = permissionData.titleId),
|
||||
fontSize = 16.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
color = NaviPayColor.textPrimary
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = permissionData.descriptionId),
|
||||
fontSize = 14.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
color = NaviPayColor.textTertiary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
if (permissionState == PermissionState.HARD_DENIED) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = permissionData.titleId),
|
||||
fontSize = 16.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
color = NaviPayColor.textPrimary
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = permissionData.descriptionId),
|
||||
fontSize = 14.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
color = NaviPayColor.textTertiary
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.permission_settings_path),
|
||||
fontSize = 12.sp,
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
color = NaviPayColor.textTertiary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ object PermissionKeyData {
|
||||
|
||||
val CONTACT_PERMISSION_DATA = listOf(
|
||||
PermissionData(
|
||||
iconId = R.drawable.ic_contact_permission,
|
||||
iconId = R.drawable.ic_permission_contact,
|
||||
titleId = R.string.contact,
|
||||
descriptionId = R.string.contact_permission_description,
|
||||
qualifierList = listOf(Manifest.permission.READ_CONTACTS)
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.compose.animation.core.AnimationSpec
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
@@ -31,6 +32,9 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.SwipeableDefaults
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -53,6 +57,8 @@ import androidx.core.content.getSystemService
|
||||
import androidx.paging.LoadState
|
||||
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.zxing.BarcodeFormat
|
||||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
@@ -564,6 +570,23 @@ fun isNaviPayOriginatedIntent(uri: Uri): Boolean {
|
||||
fun Context.getImageRequestBuilder(data: Any?) =
|
||||
ImageRequest.Builder(context = this).data(data = data).allowHardware(enable = false).build()
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialNavigationApi::class,
|
||||
ExperimentalMaterialNavigationApi::class
|
||||
)
|
||||
@Composable
|
||||
fun rememberBottomSheetNavigator(
|
||||
animationSpec: AnimationSpec<Float> = SwipeableDefaults.AnimationSpec
|
||||
): BottomSheetNavigator {
|
||||
val sheetState = rememberModalBottomSheetState(
|
||||
initialValue = ModalBottomSheetValue.Hidden,
|
||||
animationSpec = animationSpec,
|
||||
skipHalfExpanded = false
|
||||
)
|
||||
return remember(sheetState) {
|
||||
BottomSheetNavigator(sheetState = sheetState)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> = runBlocking {
|
||||
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
|
||||
}
|
||||
|
||||
@@ -1,50 +1,68 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M20,20m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
|
||||
android:fillColor="#F5F5F5"/>
|
||||
<path
|
||||
android:pathData="M29.83,27.25C29.83,29.02 28.39,30.46 26.62,30.46H12.01V7.53H26.62C28.39,7.53 29.83,8.97 29.83,10.74V27.25Z"
|
||||
android:pathData="M19.657,20.017C19.657,20.227 19.492,20.392 19.282,20.392H5.67C5.46,20.392 5.295,20.227 5.295,20.017V2.279C5.295,2.069 5.46,1.904 5.67,1.904H19.282C19.492,1.904 19.657,2.069 19.657,2.279V20.009V20.017Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M28.7,8.98C29.07,8.51 29.82,8.77 29.82,9.36V27.25C29.82,29.02 28.38,30.46 26.61,30.46H12.01L28.7,8.98Z"
|
||||
android:fillColor="#E3FFEC"/>
|
||||
android:pathData="M19.657,1.904V20.009C19.657,20.219 19.492,20.384 19.282,20.384H5.295"
|
||||
android:fillColor="#E4FFED"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M10.17,13.77H13.68"
|
||||
android:pathData="M11.58,20.392H5.67C5.46,20.392 5.295,20.227 5.295,20.017V2.279C5.295,2.069 5.46,1.904 5.67,1.904H19.282C19.492,1.904 19.657,2.069 19.657,2.279V16.132"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M10.17,24.23H13.68"
|
||||
android:pathData="M3.817,6.938H6.645"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M21.46,17.94C22.631,17.94 23.58,16.991 23.58,15.82C23.58,14.649 22.631,13.7 21.46,13.7C20.289,13.7 19.34,14.649 19.34,15.82C19.34,16.991 20.289,17.94 21.46,17.94Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58ECA2"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M16.8,24.3C16.8,21.73 18.89,19.64 21.46,19.64C24.03,19.64 26.12,21.73 26.12,24.3H16.8Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58ECA2"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M29.83,27.25C29.83,29.02 28.39,30.46 26.62,30.46H12.01V7.53H26.62C28.39,7.53 29.83,8.97 29.83,10.74V27.25Z"
|
||||
android:pathData="M3.817,15.359H6.645"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M3.817,11.152H6.645"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M12.915,10.297C13.859,10.297 14.625,9.531 14.625,8.587C14.625,7.643 13.859,6.877 12.915,6.877C11.971,6.877 11.205,7.643 11.205,8.587C11.205,9.531 11.971,10.297 12.915,10.297Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58EDA3"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M9.157,15.42C9.157,13.342 10.837,11.662 12.915,11.662C14.993,11.662 16.673,13.342 16.673,15.42H9.157Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58EDA3"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M18.487,18.69L20.182,20.392L18.487,22.094"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M19.635,20.393H14.25"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
||||
@@ -1,65 +1,62 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M20,20m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
|
||||
android:fillColor="#F5F5F5"/>
|
||||
android:pathData="M2.5,6.733L2.528,5.717C2.532,5.454 2.635,5.202 2.815,5.013C2.996,4.824 3.241,4.713 3.5,4.701H4.444C4.714,4.704 4.973,4.81 5.169,4.998C5.364,5.187 5.483,5.444 5.5,5.717V6.733"
|
||||
android:fillColor="#AFECCD"/>
|
||||
<path
|
||||
android:pathData="M11.08,13.75L11.11,12.8C11.11,12.55 11.21,12.32 11.38,12.14C11.55,11.96 11.78,11.86 12.02,11.85H12.91C13.16,11.85 13.41,11.95 13.59,12.13C13.77,12.31 13.89,12.55 13.9,12.81V13.76"
|
||||
android:fillColor="#58ECA2"/>
|
||||
<path
|
||||
android:pathData="M27.64,13.75C27.46,13.75 27.29,13.7 27.14,13.6C26.99,13.5 26.87,13.36 26.79,13.2C26.28,12.09 25.73,10.89 24.7,10.89H20C19.13,10.89 18.69,11.69 17.46,13.36C17.37,13.48 17.26,13.58 17.13,13.65C17,13.72 16.86,13.75 16.71,13.75H11.08C9.67,13.75 9.2,14.4 9.2,15.39V25.3C9.2,26.3 9.67,27.1 11.13,27.1H28.87C30.33,27.1 30.8,26.29 30.8,25.3V15.39C30.8,14.39 30.33,13.75 28.87,13.75H27.64Z"
|
||||
android:pathData="M20.13,6.734C19.94,6.734 19.753,6.679 19.593,6.575C19.433,6.471 19.305,6.323 19.224,6.148C18.682,4.968 18.1,3.686 17,3.686H12C11.069,3.686 10.6,4.533 9.3,6.319C9.208,6.447 9.087,6.551 8.948,6.623C8.809,6.695 8.656,6.733 8.5,6.734H2.5C1,6.734 0.5,7.424 0.5,8.485V19.04C0.5,20.1 1,20.96 2.556,20.96H21.444C23,20.96 23.5,20.1 23.5,19.04V8.485C23.5,7.424 23,6.734 21.444,6.734H20.13Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M28.87,17.57C30.33,17.57 30.8,18.22 30.8,19.21V15.39C30.8,14.39 30.33,13.75 28.87,13.75H27.64C27.46,13.75 27.29,13.7 27.14,13.6C26.99,13.5 26.87,13.36 26.79,13.2C26.28,12.09 25.73,10.89 24.7,10.89H20C19.13,10.89 18.69,11.69 17.46,13.36C17.37,13.48 17.26,13.58 17.13,13.65C17,13.72 16.86,13.75 16.71,13.75H11.08C9.67,13.75 9.2,14.4 9.2,15.39V19.21C9.2,18.21 9.67,17.57 11.08,17.57H28.87Z"
|
||||
android:fillColor="#E3FFEC"/>
|
||||
android:pathData="M21.444,10.798C23,10.798 23.5,11.488 23.5,12.549V8.485C23.5,7.424 23,6.734 21.444,6.734H20.13C19.94,6.734 19.753,6.679 19.593,6.575C19.433,6.471 19.305,6.323 19.224,6.148C18.682,4.968 18.1,3.686 17,3.686H12C11.069,3.686 10.6,4.533 9.3,6.319C9.208,6.447 9.087,6.551 8.948,6.623C8.809,6.695 8.656,6.733 8.5,6.734H2.5C1,6.734 0.5,7.424 0.5,8.485V12.549C0.5,11.488 1,10.798 2.5,10.798H21.444Z"
|
||||
android:fillColor="#E4FFED"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M27.64,13.75C27.46,13.75 27.29,13.7 27.14,13.6C26.99,13.5 26.87,13.36 26.79,13.2C26.28,12.09 25.73,10.89 24.7,10.89H20C19.13,10.89 18.69,11.69 17.46,13.36C17.37,13.48 17.26,13.58 17.13,13.65C17,13.72 16.86,13.75 16.71,13.75H11.08C9.67,13.75 9.2,14.4 9.2,15.39V25.3C9.2,26.3 9.67,27.1 11.13,27.1H28.87C30.33,27.1 30.8,26.29 30.8,25.3V15.39C30.8,14.39 30.33,13.75 28.87,13.75H27.64Z"
|
||||
android:pathData="M20.13,6.734C19.94,6.734 19.753,6.679 19.593,6.575C19.433,6.471 19.305,6.323 19.224,6.148C18.682,4.968 18.1,3.686 17,3.686H12C11.069,3.686 10.6,4.533 9.3,6.319C9.208,6.447 9.087,6.551 8.948,6.623C8.809,6.695 8.656,6.733 8.5,6.734H2.5C1,6.734 0.5,7.424 0.5,8.485V19.04C0.5,20.1 1,20.96 2.556,20.96H21.444C23,20.96 23.5,20.1 23.5,19.04V8.485C23.5,7.424 23,6.734 21.444,6.734H20.13Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M22.35,25.2C25.2,25.2 27.52,22.85 27.52,19.95C27.52,17.05 25.21,14.7 22.35,14.7C19.49,14.7 17.18,17.05 17.18,19.95C17.18,22.85 19.49,25.2 22.35,25.2Z"
|
||||
android:pathData="M14.5,18.927C17.538,18.927 20,16.425 20,13.339C20,10.252 17.538,7.75 14.5,7.75C11.462,7.75 9,10.252 9,13.339C9,16.425 11.462,18.927 14.5,18.927Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M22.35,22.82C23.91,22.82 25.17,21.54 25.17,19.96C25.17,18.38 23.91,17.1 22.35,17.1C20.79,17.1 19.53,18.38 19.53,19.96C19.53,21.54 20.79,22.82 22.35,22.82Z"
|
||||
android:fillColor="#58ECA2"/>
|
||||
<path
|
||||
android:pathData="M12.49,18.52C13.27,18.52 13.9,17.88 13.9,17.09C13.9,16.3 13.27,15.66 12.49,15.66C11.71,15.66 11.08,16.3 11.08,17.09C11.08,17.88 11.71,18.52 12.49,18.52Z"
|
||||
android:pathData="M14.5,16.388C16.157,16.388 17.5,15.023 17.5,13.339C17.5,11.656 16.157,10.291 14.5,10.291C12.843,10.291 11.5,11.656 11.5,13.339C11.5,15.023 12.843,16.388 14.5,16.388Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M20.36,21.98C19.85,21.44 19.56,20.72 19.57,19.97C19.57,19.22 19.87,18.5 20.4,17.97C20.93,17.44 21.63,17.14 22.37,17.13C23.11,17.13 23.82,17.41 24.35,17.93L20.37,21.98H20.36Z"
|
||||
android:pathData="M4,11.814C4.828,11.814 5.5,11.132 5.5,10.29C5.5,9.448 4.828,8.766 4,8.766C3.172,8.766 2.5,9.448 2.5,10.29C2.5,11.132 3.172,11.814 4,11.814Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M12.379,15.494C11.833,14.919 11.53,14.149 11.537,13.35C11.544,12.551 11.859,11.786 12.415,11.221C12.972,10.656 13.724,10.335 14.511,10.328C15.297,10.321 16.055,10.629 16.621,11.184L12.379,15.494Z"
|
||||
android:fillColor="#AFECCD"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M22.35,22.82C23.91,22.82 25.17,21.54 25.17,19.96C25.17,18.38 23.91,17.1 22.35,17.1C20.79,17.1 19.53,18.38 19.53,19.96C19.53,21.54 20.79,22.82 22.35,22.82Z"
|
||||
android:pathData="M14.5,16.388C16.157,16.388 17.5,15.023 17.5,13.339C17.5,11.656 16.157,10.291 14.5,10.291C12.843,10.291 11.5,11.656 11.5,13.339C11.5,15.023 12.843,16.388 14.5,16.388Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M11.08,13.75L11.11,12.8C11.11,12.55 11.21,12.32 11.38,12.14C11.55,11.96 11.78,11.86 12.02,11.85H12.91C13.16,11.85 13.41,11.95 13.59,12.13C13.77,12.31 13.89,12.55 13.9,12.81V13.76"
|
||||
android:pathData="M2.5,6.733L2.528,5.717C2.532,5.454 2.635,5.202 2.815,5.013C2.996,4.824 3.241,4.713 3.5,4.701H4.444C4.714,4.704 4.973,4.81 5.169,4.998C5.364,5.187 5.483,5.444 5.5,5.717V6.733"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M22.35,25.2C25.2,25.2 27.52,22.85 27.52,19.95C27.52,17.05 25.21,14.7 22.35,14.7C19.49,14.7 17.18,17.05 17.18,19.95C17.18,22.85 19.49,25.2 22.35,25.2Z"
|
||||
android:pathData="M14.5,18.927C17.538,18.927 20,16.425 20,13.339C20,10.252 17.538,7.75 14.5,7.75C11.462,7.75 9,10.252 9,13.339C9,16.425 11.462,18.927 14.5,18.927Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M12.49,18.52C13.27,18.52 13.9,17.88 13.9,17.09C13.9,16.3 13.27,15.66 12.49,15.66C11.71,15.66 11.08,16.3 11.08,17.09C11.08,17.88 11.71,18.52 12.49,18.52Z"
|
||||
android:pathData="M4,11.814C4.828,11.814 5.5,11.132 5.5,10.29C5.5,9.448 4.828,8.766 4,8.766C3.172,8.766 2.5,9.448 2.5,10.29C2.5,11.132 3.172,11.814 4,11.814Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58ECA2"
|
||||
android:strokeColor="#21002A"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
||||
68
navi-pay/src/main/res/drawable/ic_permission_contact.xml
Normal file
68
navi-pay/src/main/res/drawable/ic_permission_contact.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19.657,20.017C19.657,20.227 19.492,20.392 19.282,20.392H5.67C5.46,20.392 5.295,20.227 5.295,20.017V2.279C5.295,2.069 5.46,1.904 5.67,1.904H19.282C19.492,1.904 19.657,2.069 19.657,2.279V20.009V20.017Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M19.657,1.904V20.009C19.657,20.219 19.492,20.384 19.282,20.384H5.295"
|
||||
android:fillColor="#E4FFED"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M11.58,20.392H5.67C5.46,20.392 5.295,20.227 5.295,20.017V2.279C5.295,2.069 5.46,1.904 5.67,1.904H19.282C19.492,1.904 19.657,2.069 19.657,2.279V16.132"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M3.817,6.938H6.645"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M3.817,15.359H6.645"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M3.817,11.152H6.645"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M12.915,10.297C13.859,10.297 14.625,9.531 14.625,8.587C14.625,7.643 13.859,6.877 12.915,6.877C11.971,6.877 11.205,7.643 11.205,8.587C11.205,9.531 11.971,10.297 12.915,10.297Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58EDA3"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M9.157,15.42C9.157,13.342 10.837,11.662 12.915,11.662C14.993,11.662 16.673,13.342 16.673,15.42H9.157Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58EDA3"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M18.487,18.69L20.182,20.392L18.487,22.094"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M19.635,20.393H14.25"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
@@ -1,85 +1,43 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M20,20m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
|
||||
android:fillColor="#F5F5F5"/>
|
||||
android:pathData="M15.912,12.393C18.29,12.393 20.217,10.466 20.217,8.088C20.217,5.711 18.29,3.783 15.912,3.783C13.535,3.783 11.607,5.711 11.607,8.088C11.607,10.466 13.535,12.393 15.912,12.393Z"
|
||||
android:fillColor="#FFF3F0"/>
|
||||
<path
|
||||
android:pathData="M26.262,29.109H13.737C13.377,29.109 13.085,28.817 13.085,28.457V14.687C13.085,14.282 13.25,13.899 13.535,13.614L17.899,9.317C18.184,9.039 18.559,8.882 18.957,8.882H26.262C26.622,8.882 26.914,9.174 26.914,9.534V28.457C26.914,28.817 26.622,29.109 26.262,29.109Z"
|
||||
android:pathData="M8.471,10.446C8.616,10.301 8.732,10.128 8.81,9.938C8.889,9.748 8.93,9.544 8.93,9.339C8.93,9.133 8.889,8.93 8.81,8.74C8.732,8.55 8.616,8.377 8.471,8.232L6.81,6.572C6.516,6.278 6.118,6.113 5.703,6.113C5.288,6.113 4.89,6.278 4.596,6.572L3.685,7.483C3.304,7.866 3.066,8.369 3.012,8.907C2.958,9.445 3.091,9.985 3.389,10.436C6.07,14.471 9.53,17.93 13.565,20.611C14.016,20.909 14.556,21.042 15.094,20.988C15.632,20.934 16.135,20.696 16.518,20.314L17.43,19.403C17.723,19.11 17.888,18.712 17.888,18.296C17.888,17.881 17.723,17.483 17.43,17.19L15.769,15.53C15.475,15.236 15.077,15.071 14.662,15.071C14.247,15.071 13.849,15.236 13.555,15.53L13.002,16.083C11.145,14.556 9.441,12.852 7.914,10.995L8.471,10.446Z"
|
||||
android:fillColor="#AFECCD"/>
|
||||
<path
|
||||
android:pathData="M7.917,10.999L8.471,10.446C8.616,10.301 8.732,10.128 8.81,9.938C8.889,9.748 8.93,9.544 8.93,9.339C8.93,9.133 8.889,8.93 8.81,8.74C8.732,8.55 8.616,8.377 8.471,8.232L6.81,6.572C6.516,6.278 6.118,6.113 5.703,6.113C5.288,6.113 4.89,6.278 4.596,6.572L3.685,7.483C3.304,7.866 3.066,8.369 3.012,8.907C2.958,9.445 3.091,9.985 3.389,10.436C4.619,12.285 6.017,14.016 7.565,15.607L9.932,13.24C9.225,12.512 8.543,11.766 7.917,10.999Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M26.914,9.542V28.465C26.914,28.824 26.622,29.117 26.262,29.117H13.737C13.377,29.117 13.085,28.824 13.085,28.465"
|
||||
android:fillColor="#DDFFE6"/>
|
||||
<path
|
||||
android:pathData="M26.262,29.109H13.737C13.377,29.109 13.085,28.817 13.085,28.457V14.687C13.085,14.282 13.25,13.899 13.535,13.614L17.899,9.317C18.184,9.039 18.559,8.882 18.957,8.882H26.262C26.622,8.882 26.914,9.174 26.914,9.534V28.457C26.914,28.817 26.622,29.109 26.262,29.109Z"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M8.471,10.446C8.616,10.301 8.732,10.128 8.81,9.938C8.889,9.748 8.93,9.544 8.93,9.339C8.93,9.133 8.889,8.93 8.81,8.74C8.732,8.55 8.616,8.377 8.471,8.232L6.81,6.572C6.516,6.278 6.118,6.113 5.703,6.113C5.288,6.113 4.89,6.278 4.596,6.572L3.685,7.483C3.304,7.866 3.066,8.369 3.012,8.907C2.958,9.445 3.091,9.985 3.389,10.436C6.07,14.471 9.53,17.93 13.565,20.611C14.016,20.909 14.556,21.042 15.094,20.988C15.632,20.934 16.135,20.696 16.518,20.314L17.43,19.403C17.723,19.11 17.888,18.712 17.888,18.296C17.888,17.881 17.723,17.483 17.43,17.19L15.769,15.53C15.475,15.236 15.077,15.071 14.662,15.071C14.247,15.071 13.849,15.236 13.555,15.53L13.002,16.083C11.145,14.556 9.441,12.852 7.914,10.995L8.471,10.446Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M23.09,17.462H16.827C16.467,17.462 16.175,17.754 16.175,18.114V26.207C16.175,26.619 16.505,26.957 16.932,26.957H23.187C23.547,26.957 23.84,26.664 23.84,26.304V18.212C23.84,17.792 23.502,17.462 23.09,17.462Z"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M12,3C14.387,3 16.677,3.948 18.365,5.636C20.053,7.324 21.001,9.614 21.001,12.001"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#58E69C"
|
||||
android:strokeColor="#21002A"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M17.465,20.896H22.542"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M12,6.131C13.557,6.131 15.05,6.749 16.151,7.85C17.252,8.951 17.87,10.444 17.87,12.001"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M20.007,20.897V18.932"
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M12,9.262C12.726,9.262 13.423,9.55 13.937,10.064C14.451,10.578 14.74,11.275 14.74,12.001"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M17.472,20.897V20.095"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M22.542,20.897V20.095"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M22.542,23.521H17.465"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M20.007,23.521V25.486"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M22.542,23.521V24.324"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M17.472,23.521V24.324"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#21002A"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M20,20m-20,0a20,20 0,1 1,40 0a20,20 0,1 1,-40 0"
|
||||
android:pathData="M12,3C7.03,3 3,6.328 3,10.435C3.021,11.457 3.271,12.462 3.731,13.375C4.192,14.288 4.851,15.086 5.661,15.71L3.783,20.217L8.769,17.368C9.814,17.7 10.904,17.87 12,17.87C16.97,17.87 21,14.541 21,10.435C21,6.328 16.97,3 12,3Z"
|
||||
android:fillColor="#AFECCD"/>
|
||||
<path
|
||||
android:pathData="M12,6.13C13.715,6.085 15.418,6.433 16.978,7.147C18.538,7.861 19.913,8.923 21,10.251C20.879,6.23 16.896,3 12,3C7.104,3 3.121,6.23 3,10.251C4.087,8.923 5.462,7.861 7.022,7.147C8.582,6.433 10.285,6.085 12,6.13Z"
|
||||
android:fillColor="#F5F5F5"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M29.128,11.678H10.902C10.42,11.678 10.03,12.068 10.03,12.549V25.45C10.03,25.931 10.42,26.322 10.902,26.322H29.128C29.61,26.322 30,25.931 30,25.45V12.549C30,12.068 29.61,11.678 29.128,11.678Z"
|
||||
android:pathData="M12,3C7.03,3 3,6.328 3,10.435C3.021,11.457 3.271,12.462 3.731,13.375C4.192,14.288 4.851,15.086 5.661,15.71L3.783,20.217L8.769,17.368C9.814,17.7 10.904,17.87 12,17.87C16.97,17.87 21,14.541 21,10.435C21,6.328 16.97,3 12,3Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#E3FFEC"
|
||||
android:strokeColor="#21002A"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M20.028,17.763L29.337,25.756C29.564,25.948 29.424,26.323 29.128,26.323H10.901C10.605,26.323 10.466,25.948 10.692,25.756L20.028,17.763Z"
|
||||
android:pathData="M7.696,7.695H13.956"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#58EDA3"
|
||||
android:strokeColor="#21002A"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M19.993,20.237L10.692,12.244C10.466,12.053 10.605,11.678 10.901,11.678H29.128C29.424,11.678 29.564,12.053 29.337,12.244L20.002,20.237H19.993Z"
|
||||
android:pathData="M7.696,10.043H16.304"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#21002A"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M7.696,12.391H16.304"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#00303E"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
<string name="navi_pay_moto">Simplifying payments on Navi</string>
|
||||
<string name="powered_by">Powered by</string>
|
||||
<string name="sms_verification">SMS verification</string>
|
||||
<string name="sms_permission_header">SMS permission</string>
|
||||
<string name="sms_permission_rationale_description">To verify your bank accounts</string>
|
||||
<string name="phone_state_permission_header">SIM card permission</string>
|
||||
<string name="phone_state_permission_description">To verify your SIM provider</string>
|
||||
<string name="sms_permission_header">SMS</string>
|
||||
<string name="sms_permission_rationale_description">To verify your phone number for UPI payments.</string>
|
||||
<string name="phone_state_permission_header">Phone</string>
|
||||
<string name="phone_state_permission_description">To verify your SIM card with your registered mobile number.</string>
|
||||
<string name="sms_verification_charges_info">*Standard SMS charges will apply</string>
|
||||
<string name="proceed">Proceed</string>
|
||||
<string name="select_bank">Select bank</string>
|
||||
@@ -172,9 +172,10 @@
|
||||
<string name="money_transfer">Money transfer</string>
|
||||
<string name="manage">Manage</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="app_permission_header">App permission</string>
|
||||
<string name="app_permission_header">Allow app permission</string>
|
||||
<string name="allow_app_permissions">Allow app permissions</string>
|
||||
<string name="allow">Allow </string>
|
||||
<string name="go_to_setting">Go to settings</string>
|
||||
<string name="hassle_free_fast_payments">Hassle-free & fast payments</string>
|
||||
<string name="check_your_balance_anytime_anywhere">Check your bank balance anytime, anywhere</string>
|
||||
<string name="get_started">Get started</string>
|
||||
@@ -208,10 +209,13 @@
|
||||
<string name="no_pending_request">You do not have any pending requests</string>
|
||||
<string name="create_autopay">Create autopay</string>
|
||||
<string name="upi_number_tab_header">UPI number</string>
|
||||
<string name="allow_camera_permission">Allow camera permission</string>
|
||||
<string name="camera">Camera</string>
|
||||
<string name="navi_pay_camera_permission_description">To scan QR code</string>
|
||||
<string name="contact">Contact</string>
|
||||
<string name="contact_permission_description">To make contact finding easier</string>
|
||||
<string name="camera_show_rational">Camera permission is mandatory</string>
|
||||
<string name="navi_pay_camera_permission_description">Grant camera permission to scan QR codes for merchants, family, and friends.</string>
|
||||
<string name="navi_pay_camera_permission_show_rational_description">Required so that you can scan the image of the scanner to pay</string>
|
||||
<string name="contact">Contacts</string>
|
||||
<string name="contact_permission_description">To allow UPI payments to your contacts.</string>
|
||||
<string name="navi_pay_qr_error_title">Invalid QR code</string>
|
||||
<string name="navi_pay_qr_error_description">The QR code you scanned is not a valid UPI QR code</string>
|
||||
<string name="complaint_status_complaint_closed">Complaint closed</string>
|
||||
|
||||
Reference in New Issue
Block a user