NTP-60715 | auto open contact permission pop-up (#16011)
This commit is contained in:
@@ -11,6 +11,7 @@ import androidx.test.core.app.ApplicationProvider
|
||||
import app.cash.turbine.test
|
||||
import com.navi.base.sharedpref.PreferenceManager
|
||||
import com.navi.base.utils.ResourceProvider
|
||||
import com.navi.common.usecase.LitmusExperimentsUseCase
|
||||
import com.navi.pay.analytics.NaviPayAnalytics
|
||||
import com.navi.pay.analytics.NaviPayToContacts
|
||||
import com.navi.pay.common.connectivity.NaviPayNetworkConnectivity
|
||||
@@ -18,7 +19,6 @@ import com.navi.pay.common.model.config.NaviPayDefaultConfig
|
||||
import com.navi.pay.common.model.view.NaviPaySessionHelper
|
||||
import com.navi.pay.common.usecase.NaviPayConfigUseCase
|
||||
import com.navi.pay.common.usecase.ValidateVpaUseCase
|
||||
import com.navi.pay.common.utils.DeviceInfoProvider
|
||||
import com.navi.pay.common.utils.FrequentOrdersHelper
|
||||
import com.navi.pay.entry.NaviPayActivityDataProvider
|
||||
import com.navi.pay.management.paytocontacts.PhoneContactManager
|
||||
@@ -39,7 +39,6 @@ import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class PayToContactsViewModelTest : NaviPayAndroidTest() {
|
||||
private var deviceInfoProvider: DeviceInfoProvider = mockk()
|
||||
private var contactManager: PhoneContactManager = mockk()
|
||||
private var naviPayNetworkConnectivity: NaviPayNetworkConnectivity = mockk()
|
||||
private var naviPayConfigUseCase: NaviPayConfigUseCase = mockk()
|
||||
@@ -48,6 +47,7 @@ class PayToContactsViewModelTest : NaviPayAndroidTest() {
|
||||
private var resourceProvider: ResourceProvider = mockk()
|
||||
private var validateVpaUseCase: ValidateVpaUseCase = mockk()
|
||||
private var naviPayActivityDataProvider: NaviPayActivityDataProvider = mockk()
|
||||
private var litmusExperimentsUseCase: LitmusExperimentsUseCase = mockk()
|
||||
private val naviPayAnalytics: NaviPayToContacts = mockk(relaxed = true)
|
||||
|
||||
private lateinit var viewModel: PayToContactsViewModel
|
||||
@@ -68,10 +68,10 @@ class PayToContactsViewModelTest : NaviPayAndroidTest() {
|
||||
coEvery { resourceProvider.getString(any()) } returns ""
|
||||
coEvery { frequentOrdersHelper.getFrequentOrderList(any<Int>()) } returns emptyList()
|
||||
coEvery { naviPaySessionHelper.getNaviPaySessionAttributes() } returns emptyMap()
|
||||
coEvery { litmusExperimentsUseCase.execute(experimentName = any()) } returns null
|
||||
|
||||
viewModel =
|
||||
PayToContactsViewModel(
|
||||
deviceInfoProvider = deviceInfoProvider,
|
||||
contactManager = contactManager,
|
||||
naviPayNetworkConnectivity = naviPayNetworkConnectivity,
|
||||
naviPayConfigUseCase = naviPayConfigUseCase,
|
||||
@@ -81,6 +81,7 @@ class PayToContactsViewModelTest : NaviPayAndroidTest() {
|
||||
validateVpaUseCase = validateVpaUseCase,
|
||||
naviPayActivityDataProvider = naviPayActivityDataProvider,
|
||||
naviPayAnalytics = naviPayAnalytics,
|
||||
litmusExperimentsUseCase = litmusExperimentsUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ sealed interface 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(
|
||||
|
||||
@@ -329,6 +329,8 @@ fun QrScannerScreenContent(
|
||||
naviPaySessionAttributes = qrScannerViewModel.getNaviPaySessionAttributes(),
|
||||
)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -145,6 +145,11 @@ fun PayToContactsScreen(
|
||||
payToContactsViewModel.frequentOrdersHeading.collectAsStateWithLifecycle()
|
||||
val shouldShowYourContactsTitle by
|
||||
payToContactsViewModel.shouldShowYourContactsTitle.collectAsStateWithLifecycle()
|
||||
val isAutoOpenContactPermissionExperimentEnabled by
|
||||
payToContactsViewModel.isAutoOpenContactPermissionExperimentEnabled
|
||||
.collectAsStateWithLifecycle()
|
||||
val showPermissionWidget by
|
||||
payToContactsViewModel.showPermissionWidget.collectAsStateWithLifecycle()
|
||||
|
||||
val onBackClick = {
|
||||
payToContactsViewModel.cancelPaymentRequest()
|
||||
@@ -226,6 +231,7 @@ fun PayToContactsScreen(
|
||||
naviPaySessionAttributes =
|
||||
payToContactsViewModel.getNaviPaySessionAttributes()
|
||||
)
|
||||
payToContactsViewModel.updatePermissionResult(permissionResult = it)
|
||||
}
|
||||
NaviPermissionResult.HardDenied -> {
|
||||
naviPayAnalytics.onPayToContactsPermissionDenied(
|
||||
@@ -233,11 +239,14 @@ fun PayToContactsScreen(
|
||||
naviPaySessionAttributes =
|
||||
payToContactsViewModel.getNaviPaySessionAttributes(),
|
||||
)
|
||||
naviPayActivity.launchPermissionSettingsScreen()
|
||||
naviPayAnalytics.onAppSettingsScreenLaunched(
|
||||
naviPaySessionAttributes =
|
||||
payToContactsViewModel.getNaviPaySessionAttributes()
|
||||
)
|
||||
payToContactsViewModel.updatePermissionResult(permissionResult = it)
|
||||
if (payToContactsViewModel.isPermissionLaunchedFromAllowClick) {
|
||||
naviPayActivity.launchPermissionSettingsScreen()
|
||||
naviPayAnalytics.onAppSettingsScreenLaunched(
|
||||
naviPaySessionAttributes =
|
||||
payToContactsViewModel.getNaviPaySessionAttributes()
|
||||
)
|
||||
}
|
||||
}
|
||||
NaviPermissionResult.ShowRationale -> {
|
||||
naviPayAnalytics.onPayToContactsPermissionDenied(
|
||||
@@ -245,11 +254,14 @@ fun PayToContactsScreen(
|
||||
naviPaySessionAttributes =
|
||||
payToContactsViewModel.getNaviPaySessionAttributes(),
|
||||
)
|
||||
payToContactsViewModel.updatePermissionResult(permissionResult = it)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
val onAllowPermissionButtonClicked = {
|
||||
payToContactsViewModel.isPermissionLaunchedFromAllowClick = true
|
||||
readContactsPermissionsState.launchMultiplePermissionRequest()
|
||||
}
|
||||
|
||||
@@ -294,7 +306,7 @@ fun PayToContactsScreen(
|
||||
|
||||
LaunchedEffect(readContactsPermissionsState.allPermissionsGranted) {
|
||||
if (readContactsPermissionsState.allPermissionsGranted) {
|
||||
payToContactsViewModel.updateContactPermissionStatus(true)
|
||||
payToContactsViewModel.updateContactPermissionStatus(isContactPermissionGranted = true)
|
||||
payToContactsViewModel.fetchContacts()
|
||||
} else {
|
||||
payToContactsViewModel.updateContactPermissionStatus(false)
|
||||
@@ -304,6 +316,15 @@ fun PayToContactsScreen(
|
||||
isContactListEmpty = contactList.isEmpty(),
|
||||
isFrequentOrderListEmpty = filteredFrequentOrdersList.isEmpty(),
|
||||
)
|
||||
|
||||
if (
|
||||
isAutoOpenContactPermissionExperimentEnabled &&
|
||||
!payToContactsViewModel.isPermissionPopupSeenOnLanded
|
||||
) {
|
||||
payToContactsViewModel.onPermissionPopupSeenOnLanded()
|
||||
payToContactsViewModel.isPermissionLaunchedFromAllowClick = false
|
||||
readContactsPermissionsState.launchMultiplePermissionRequest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +375,7 @@ fun PayToContactsScreen(
|
||||
focusManager.clearFocus()
|
||||
payToContactsViewModel.onHelpCtaClicked()
|
||||
},
|
||||
showPermissionWidget = showPermissionWidget,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -383,6 +405,7 @@ fun RenderPayToContactsScreen(
|
||||
invalidState: WarningErrorInfoState,
|
||||
isEmptyState: Boolean,
|
||||
onHelpCtaClicked: () -> Unit,
|
||||
showPermissionWidget: Boolean,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
@@ -506,6 +529,7 @@ fun RenderPayToContactsScreen(
|
||||
newContact = newContact,
|
||||
shouldShowYourContactsTitle = shouldShowYourContactsTitle,
|
||||
scrollState = scrollState,
|
||||
showPermissionWidget = showPermissionWidget,
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -552,6 +576,7 @@ private fun PayToContactScreenScaffoldContent(
|
||||
newContact: PhoneContactEntity?,
|
||||
shouldShowYourContactsTitle: Boolean,
|
||||
scrollState: LazyListState,
|
||||
showPermissionWidget: Boolean,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxHeight(),
|
||||
@@ -627,7 +652,7 @@ private fun PayToContactScreenScaffoldContent(
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (showPermissionWidget) {
|
||||
item { PayToContactsPermissionView(onPrimaryButtonClicked = onPrimaryButtonClicked) }
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ import com.navi.base.model.CtaData
|
||||
import com.navi.base.utils.ResourceProvider
|
||||
import com.navi.base.utils.isNotNullAndNotEmpty
|
||||
import com.navi.common.constants.EMPTY
|
||||
import com.navi.common.extensions.or
|
||||
import com.navi.common.extensions.removeSpaces
|
||||
import com.navi.common.network.models.RepoResult
|
||||
import com.navi.common.network.models.isSuccessWithData
|
||||
import com.navi.common.usecase.LitmusExperimentsUseCase
|
||||
import com.navi.pay.R
|
||||
import com.navi.pay.analytics.NaviPayAnalytics.Companion.NAVI_PAY_SEND_MONEY_TO_CONTACTS_SCREEN_V2
|
||||
import com.navi.pay.analytics.NaviPayToContacts
|
||||
@@ -25,9 +25,9 @@ import com.navi.pay.common.model.config.NaviPayDefaultConfig
|
||||
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.NaviPermissionResult
|
||||
import com.navi.pay.common.usecase.NaviPayConfigUseCase
|
||||
import com.navi.pay.common.usecase.ValidateVpaUseCase
|
||||
import com.navi.pay.common.utils.DeviceInfoProvider
|
||||
import com.navi.pay.common.utils.FrequentOrdersHelper
|
||||
import com.navi.pay.common.utils.NaviPayCommonUtils.getHelpCtaData
|
||||
import com.navi.pay.common.utils.NaviPayCommonUtils.getNormalisedPhoneNumber
|
||||
@@ -47,9 +47,11 @@ import com.navi.pay.tstore.list.model.view.FrequentOrderEntity
|
||||
import com.navi.pay.utils.DEFAULT_CONFIG
|
||||
import com.navi.pay.utils.INDIA_COUNTRY_CODE_WITH_PLUS
|
||||
import com.navi.pay.utils.INVALID_VPA
|
||||
import com.navi.pay.utils.LITMUS_EXPERIMENT_AUTO_OPEN_CONTACT_PERMISSION
|
||||
import com.navi.pay.utils.NAVI_PAY_SEARCH_QUERY_API_DELAY
|
||||
import com.navi.pay.utils.NAVI_PAY_WIDGET_CLICKED_KEY
|
||||
import com.navi.pay.utils.NOT_LINKED_TO_UPI
|
||||
import com.navi.pay.utils.NaviPayExperimentVariantType
|
||||
import com.navi.pay.utils.PHONE_NUMBER_LENGTH
|
||||
import com.navi.pay.utils.REMOVE_WHITESPACE_REGEX
|
||||
import com.navi.pay.utils.isValidPhoneNumberLength
|
||||
@@ -78,7 +80,6 @@ import kotlinx.coroutines.launch
|
||||
class PayToContactsViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val deviceInfoProvider: DeviceInfoProvider,
|
||||
private val contactManager: PhoneContactManager,
|
||||
private val naviPayNetworkConnectivity: NaviPayNetworkConnectivity,
|
||||
private val naviPayConfigUseCase: NaviPayConfigUseCase,
|
||||
@@ -88,6 +89,7 @@ constructor(
|
||||
private val validateVpaUseCase: ValidateVpaUseCase,
|
||||
private val naviPayActivityDataProvider: NaviPayActivityDataProvider,
|
||||
private val naviPayAnalytics: NaviPayToContacts,
|
||||
private val litmusExperimentsUseCase: LitmusExperimentsUseCase,
|
||||
) : NaviPayBaseVM() {
|
||||
|
||||
private var naviPayDefaultConfig = NaviPayDefaultConfig()
|
||||
@@ -134,6 +136,28 @@ constructor(
|
||||
private val _isNewContactVisible = MutableStateFlow(false)
|
||||
val isNewContactVisible = _isNewContactVisible.asStateFlow()
|
||||
|
||||
private val _permissionResult =
|
||||
MutableStateFlow<NaviPermissionResult>(NaviPermissionResult.None)
|
||||
val permissionResult = _permissionResult.asStateFlow()
|
||||
|
||||
private val _isAutoOpenContactPermissionExperimentEnabled = MutableStateFlow(false)
|
||||
val isAutoOpenContactPermissionExperimentEnabled =
|
||||
_isAutoOpenContactPermissionExperimentEnabled.asStateFlow()
|
||||
|
||||
val showPermissionWidget =
|
||||
combine(permissionResult, isAutoOpenContactPermissionExperimentEnabled) {
|
||||
permissionResult,
|
||||
isAutoOpenContactPermissionExperimentEnabled ->
|
||||
permissionResult != NaviPermissionResult.None ||
|
||||
!isAutoOpenContactPermissionExperimentEnabled
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
initialValue = false,
|
||||
)
|
||||
|
||||
private val _navigateToNextScreenFromHelpCta = MutableSharedFlow<CtaData?>()
|
||||
val navigateToNextScreenFromHelpCta = _navigateToNextScreenFromHelpCta.asSharedFlow()
|
||||
|
||||
@@ -165,6 +189,10 @@ constructor(
|
||||
|
||||
private var lastSearchQuery = ""
|
||||
|
||||
var isPermissionPopupSeenOnLanded = false
|
||||
|
||||
var isPermissionLaunchedFromAllowClick = false
|
||||
|
||||
private var isContactPermissionGranted = false
|
||||
|
||||
private val helpCtaData = getHelpCtaData(screenName = screenName)
|
||||
@@ -243,31 +271,43 @@ constructor(
|
||||
|
||||
init {
|
||||
viewModelScope.launch(context = Dispatchers.IO) {
|
||||
updateNaviPaySessionId()
|
||||
triggerEventForShortcutWidget()
|
||||
updateNaviPayDefaultConfig()
|
||||
initializeSynchronously()
|
||||
initializeAsynchronously()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun initializeSynchronously() {
|
||||
updateNaviPaySessionId()
|
||||
updateNaviPayDefaultConfig()
|
||||
setLitmusExperimentValues()
|
||||
}
|
||||
|
||||
private suspend fun initializeAsynchronously() {
|
||||
coroutineScope {
|
||||
launch { triggerEventForShortcutWidget() }
|
||||
launch { fetchFrequentOrders() }
|
||||
launch { observeAndHandleSearchResults() }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setLitmusExperimentValues() {
|
||||
_isAutoOpenContactPermissionExperimentEnabled.update {
|
||||
val autoOpenContactPermissionExperimentVariant =
|
||||
litmusExperimentsUseCase
|
||||
.execute(experimentName = LITMUS_EXPERIMENT_AUTO_OPEN_CONTACT_PERMISSION)
|
||||
?.variant
|
||||
autoOpenContactPermissionExperimentVariant?.name ==
|
||||
NaviPayExperimentVariantType.TEST.value &&
|
||||
autoOpenContactPermissionExperimentVariant.enabled
|
||||
}
|
||||
}
|
||||
|
||||
private fun triggerEventForShortcutWidget() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
naviPayActivityDataProvider
|
||||
.getIntentData()
|
||||
?.getString(NAVI_PAY_WIDGET_CLICKED_KEY)
|
||||
?.let {
|
||||
naviPayAnalytics.onSendToContactShortcutClicked(
|
||||
eventName = it,
|
||||
naviPaySessionAttributes = getNaviPaySessionAttributes(),
|
||||
)
|
||||
}
|
||||
naviPayActivityDataProvider.getIntentData()?.getString(NAVI_PAY_WIDGET_CLICKED_KEY)?.let {
|
||||
naviPayAnalytics.onSendToContactShortcutClicked(
|
||||
eventName = it,
|
||||
naviPaySessionAttributes = getNaviPaySessionAttributes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +406,14 @@ constructor(
|
||||
updateShimmerState(showShimmer = false)
|
||||
}
|
||||
|
||||
fun onPermissionPopupSeenOnLanded() {
|
||||
isPermissionPopupSeenOnLanded = true
|
||||
}
|
||||
|
||||
fun updatePermissionResult(permissionResult: NaviPermissionResult) {
|
||||
_permissionResult.update { permissionResult }
|
||||
}
|
||||
|
||||
private fun updateIsWarningOrErrorState(
|
||||
isError: Boolean? = null,
|
||||
isWarning: Boolean? = null,
|
||||
|
||||
@@ -312,6 +312,7 @@ fun NaviPayOnboardingScreen(
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,6 +390,7 @@ fun NaviPayOnboardingScreen(
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,6 @@ const val NAVI_PAY_SYNC_TABLE_CUSTOMER_ONBOARDING_DATA_KEY = "customerOnboarding
|
||||
// Litmus experiments
|
||||
const val LITMUS_EXPERIMENT_NAVIPAY_LITE_DEFAULT_ENTERED_AMOUNT =
|
||||
"NaviPay-lite-default-entered-amount"
|
||||
// Transaction ledger Experiment
|
||||
const val LITMUS_EXPERIMENT_NAVIPAY_TRANSACTION_LEDGER = "NaviPay-exp-txn-ledger"
|
||||
const val LITMUS_EXPERIMENT_NAVIPAY_ORDER_TAG_SUMMARY = "NaviPay-order-tag-summary"
|
||||
const val LITMUS_EXPERIMENT_NAVIPAY_SMV_BINDING = "NaviPay-exp-smv-binding"
|
||||
@@ -184,6 +183,8 @@ const val LITMUS_EXPERIMENT_NAVIPAY_UPI_LITE_USP = "NaviPay-exp-lite-usp-exp"
|
||||
const val LITMUS_EXPERIMENT_NAVIPAY_HIGHLIGHT_NEW_PAYEE = "NaviPay-exp-highlight-new-payee"
|
||||
const val LITMUS_EXPERIMENT_ORDER_HISTORY_ERROR_WIDGET = "NaviTStore-exp-tds-error-widget"
|
||||
const val LITMUS_EXPERIMENT_NAVIPAY_PAYMENT_RETRY_EXPERIENCE = "NaviPay-payment-retry-experience"
|
||||
const val LITMUS_EXPERIMENT_AUTO_OPEN_CONTACT_PERMISSION = "NaviPay-auto-open-contact-permission"
|
||||
|
||||
val NAVI_PAY_LITMUS_EXPERIMENTS =
|
||||
listOf(
|
||||
LITMUS_EXPERIMENT_NAVIPAY_TRANSACTION_LEDGER,
|
||||
@@ -200,6 +201,7 @@ val NAVI_PAY_LITMUS_EXPERIMENTS =
|
||||
LITMUS_EXPERIMENT_NAVI_IPL_POWERPLAY,
|
||||
LITMUS_EXPERIMENT_NAVIPAY_PAYMENT_RETRY_EXPERIENCE,
|
||||
LITMUS_EXPERIMENT_ORDER_HISTORY_ERROR_WIDGET,
|
||||
LITMUS_EXPERIMENT_AUTO_OPEN_CONTACT_PERMISSION,
|
||||
)
|
||||
|
||||
// Generic
|
||||
|
||||
@@ -307,6 +307,7 @@ constructor(
|
||||
is NaviPermissionResult.ShowRationale -> {
|
||||
scanCardAnalytics.onCameraPermissionDenied(showRationale = true)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user