NTP-66799 | Android App lock - Biometric (#16437)
This commit is contained in:
@@ -8,14 +8,11 @@
|
||||
package com.naviapp.app.initializers
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import com.navi.analytics.utils.NaviTrackEvent
|
||||
import com.navi.base.sharedpref.PreferenceManager
|
||||
import com.navi.base.utils.AppLaunchUtils
|
||||
import com.navi.base.utils.coroutine.CoroutineManager
|
||||
import com.navi.base.utils.isNull
|
||||
import com.navi.chat.base.ChatBaseActivity
|
||||
import com.navi.common.NaviActivityLifecycleCallbacks
|
||||
import com.navi.common.checkmate.core.CheckMateManager
|
||||
@@ -28,8 +25,6 @@ import com.navi.common.resourcemanager.manager.ResourceManager
|
||||
import com.navi.common.ui.activity.BaseActivity
|
||||
import com.navi.common.utils.BiometricPromptUtils
|
||||
import com.navi.common.utils.CommonUtils.isQaRelease
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.ENABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.IS_SCREEN_LOCK_ENABLED
|
||||
import com.navi.insurance.health.activity.BaseActivity as InsuranceBaseActivity
|
||||
import com.navi.pay.common.setup.NaviPayManager
|
||||
import com.naviapp.analytics.utils.NaviAnalytics.Companion.EXTERNAL
|
||||
@@ -66,16 +61,6 @@ constructor(
|
||||
if (activity is HomePageActivity) {
|
||||
AppLaunchUtils.resetAppOpenOnLaunch()
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
|
||||
PreferenceManager.getStringPreference(IS_SCREEN_LOCK_ENABLED)
|
||||
.isNull() &&
|
||||
BiometricPromptUtils().isDeviceSecure(application)
|
||||
) {
|
||||
PreferenceManager.setStringPreference(IS_SCREEN_LOCK_ENABLED, ENABLED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
@@ -163,7 +148,6 @@ constructor(
|
||||
application.decrementAppInForeground()
|
||||
|
||||
if (application.getAppInForegroundCounter() == 0) {
|
||||
BiometricPromptUtils.appInBackgroundTimeStamp = System.currentTimeMillis()
|
||||
startSessionDetails?.let { initSessionDetails ->
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val endSessionDetails = getCurrentSessionMetrics(application)
|
||||
@@ -200,6 +184,7 @@ constructor(
|
||||
eventName = NAVIAPP_BACKGROUND_PUSH,
|
||||
mapOf("screenName" to screenName),
|
||||
)
|
||||
BiometricPromptUtils.appInBackgroundTimeStamp = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ import com.navi.common.model.common.NetworkConnectivityNudgeData
|
||||
import com.navi.common.network.models.GenericErrorResponse
|
||||
import com.navi.common.resourcemanager.manager.ResourceManager
|
||||
import com.navi.common.utils.ApiPollScheduler
|
||||
import com.navi.common.utils.BiometricPromptUtils
|
||||
import com.navi.common.utils.BiometricPromptUtils.Companion.getScreenLockEventName
|
||||
import com.navi.common.utils.CommonNaviAnalytics
|
||||
import com.navi.common.utils.Constants.IN_APP_UPDATE_REQUEST_CODE
|
||||
@@ -71,7 +72,6 @@ import com.navi.common.utils.Constants.NAVI_PAY_SECTION_GLANCE_WIDGET_SCREEN
|
||||
import com.navi.common.utils.Constants.SUB_REDIRECT
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.DISABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.ENABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.HPC_SCREEN_LOCK_EXPERIMENT
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.IS_SCREEN_LOCK_ENABLED
|
||||
import com.navi.common.utils.TemporaryStorageHelper
|
||||
import com.navi.common.utils.getDeviceSignature
|
||||
@@ -535,9 +535,10 @@ class HomePageActivity :
|
||||
observeGIPaymentFailed()
|
||||
observeNetworkConnectivity()
|
||||
observeLoansTabPaymentData()
|
||||
observeScreenLockEnabledData()
|
||||
observeScreenLockToggleData()
|
||||
observeCollectRequestFromPn()
|
||||
observeScreenOverlayEffect()
|
||||
observeScreenLockEnableStatus()
|
||||
}
|
||||
|
||||
private fun observeScreenOverlayEffect() {
|
||||
@@ -588,6 +589,28 @@ class HomePageActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeScreenLockEnableStatus() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
homeVM.handle.getStateFlow<String?>(IS_SCREEN_LOCK_ENABLED, null).collect { state ->
|
||||
when (state) {
|
||||
ENABLED -> {
|
||||
if (
|
||||
PreferenceManager.getStringPreference(IS_SCREEN_LOCK_ENABLED) == null &&
|
||||
biometricPromptUtils.isDeviceSecure(this@HomePageActivity)
|
||||
) {
|
||||
BiometricPromptUtils().reset()
|
||||
PreferenceManager.setStringPreference(IS_SCREEN_LOCK_ENABLED, ENABLED)
|
||||
}
|
||||
}
|
||||
DISABLED -> {
|
||||
BiometricPromptUtils().reset()
|
||||
PreferenceManager.setStringPreference(IS_SCREEN_LOCK_ENABLED, DISABLED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeCollectRequestFromPn() {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
@@ -603,17 +626,20 @@ class HomePageActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeScreenLockEnabledData() {
|
||||
private fun observeScreenLockToggleData() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
repeatOnLifecycle(state = Lifecycle.State.STARTED) {
|
||||
profileVM.isScreenLockToggled.collect { isScreenLockToggled ->
|
||||
profileVM.handle.getStateFlow<String?>(IS_SCREEN_LOCK_ENABLED, null).collect {
|
||||
isScreenLockToggled ->
|
||||
NaviTrackEvent.trackEventOnClickStream(
|
||||
eventName =
|
||||
getScreenLockEventName(eventType = ::observeScreenLockEnabledData.name)
|
||||
getScreenLockEventName(eventType = ::observeScreenLockToggleData.name)
|
||||
)
|
||||
isScreenLockToggled?.let {
|
||||
profileVM.canCachedDataBeUsed = false
|
||||
PreferenceManager.setStringPreference(IS_SCREEN_LOCK_ENABLED, it)
|
||||
if (PreferenceManager.getStringPreference(IS_SCREEN_LOCK_ENABLED) != it)
|
||||
PreferenceManager.setStringPreference(IS_SCREEN_LOCK_ENABLED, it)
|
||||
else return@collect
|
||||
biometricPromptUtils.reset()
|
||||
}
|
||||
if (
|
||||
@@ -907,13 +933,11 @@ class HomePageActivity :
|
||||
if (fetchDataDisabled.not()) {
|
||||
profileVM.fetchProfileItems(
|
||||
isScreenLockEnabled =
|
||||
FirebaseRemoteConfigHelper.getBoolean(IS_SCREEN_LOCK_ENABLED) &&
|
||||
PreferenceManager.getStringPreference(HPC_SCREEN_LOCK_EXPERIMENT) ==
|
||||
ENABLED,
|
||||
FirebaseRemoteConfigHelper.getBoolean(IS_SCREEN_LOCK_ENABLED),
|
||||
isMobileScreenLockSet =
|
||||
biometricPromptUtils.isDeviceSecure(this@HomePageActivity) &&
|
||||
PreferenceManager.getStringPreference(IS_SCREEN_LOCK_ENABLED) !=
|
||||
DISABLED,
|
||||
PreferenceManager.getStringPreference(IS_SCREEN_LOCK_ENABLED) ==
|
||||
ENABLED,
|
||||
naeScreenName = screenName,
|
||||
isProfileDrawerOpen = isProfileDrawerOpen,
|
||||
)
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.navi.common.network.models.RepoResult
|
||||
import com.navi.common.utils.BiometricPromptUtils
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.DISABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.ENABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.HPC_SCREEN_LOCK_EXPERIMENT
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.IS_SCREEN_LOCK_ENABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.TOGGLE_SWITCH_CHECKED_STATE
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.TOGGLE_SWITCH_LAYOUT_ID
|
||||
@@ -44,9 +43,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
@@ -67,9 +64,6 @@ constructor(
|
||||
MutableStateFlow<ProfileScreenState>(ProfileScreenState.Loading)
|
||||
val profileScreenDataState = _profileScreenDataState.asStateFlow()
|
||||
|
||||
private val _isScreenLockToggled = MutableSharedFlow<String?>()
|
||||
val isScreenLockToggled = _isScreenLockToggled.asSharedFlow()
|
||||
|
||||
private lateinit var cacheResponse: ScreenDefinition
|
||||
|
||||
var canCachedDataBeUsed: Boolean = true
|
||||
@@ -93,13 +87,11 @@ constructor(
|
||||
} else {
|
||||
getProfileResponseFromApi(
|
||||
isScreenLockEnabled =
|
||||
FirebaseRemoteConfigHelper.getBoolean(IS_SCREEN_LOCK_ENABLED) &&
|
||||
PreferenceManager.getStringPreference(HPC_SCREEN_LOCK_EXPERIMENT) ==
|
||||
ENABLED,
|
||||
FirebaseRemoteConfigHelper.getBoolean(IS_SCREEN_LOCK_ENABLED),
|
||||
isMobileScreenLockSet =
|
||||
biometricPromptUtils.isDeviceSecure(context) &&
|
||||
PreferenceManager.getStringPreference(IS_SCREEN_LOCK_ENABLED) !=
|
||||
DISABLED,
|
||||
PreferenceManager.getStringPreference(IS_SCREEN_LOCK_ENABLED) ==
|
||||
ENABLED,
|
||||
naeScreenName = NaviAnalytics.NEW_HOME_ACTIVITY,
|
||||
isNaeRequired = false,
|
||||
)
|
||||
@@ -108,7 +100,6 @@ constructor(
|
||||
}
|
||||
|
||||
private fun initListeners() {
|
||||
observeScreenToggleDataFromHandle()
|
||||
observeBiometricAuthenticationEnabledStatus()
|
||||
observeEmailSuccess()
|
||||
}
|
||||
@@ -128,14 +119,6 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeScreenToggleDataFromHandle() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
handle.getStateFlow<String?>(IS_SCREEN_LOCK_ENABLED, null).collect { state ->
|
||||
_isScreenLockToggled.emit(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeBiometricAuthenticationEnabledStatus() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
BiometricPromptUtils.isBiometricAuthenticationEnabled.collect { state ->
|
||||
@@ -162,6 +145,7 @@ constructor(
|
||||
}
|
||||
|
||||
fun enabledToggle() {
|
||||
PreferenceManager.setStringPreference(IS_SCREEN_LOCK_ENABLED, ENABLED)
|
||||
handle[IS_SCREEN_LOCK_ENABLED] = ENABLED
|
||||
handleActions(
|
||||
UiTronActionData(
|
||||
|
||||
@@ -12,15 +12,12 @@ import android.os.Bundle
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.navi.analytics.utils.NaviTrackEvent
|
||||
import com.navi.analytics.utils.NaviTrackEvent.trackEventOnClickStream
|
||||
import com.navi.base.sharedpref.PreferenceManager
|
||||
import com.navi.base.utils.BaseUtils
|
||||
import com.navi.base.utils.isNull
|
||||
import com.navi.base.utils.orTrue
|
||||
import com.navi.common.constants.APP_UPGRADE_DATA
|
||||
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
|
||||
import com.navi.common.model.AppUpgradeResponse
|
||||
import com.navi.common.ui.activity.BaseActivity
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.HPC_SCREEN_LOCK_EXPERIMENT
|
||||
import com.navi.common.utils.observeNonNull
|
||||
import com.navi.common.utils.observeNullable
|
||||
import com.navi.insurance.util.Constants.CLICKED
|
||||
@@ -72,7 +69,6 @@ abstract class BaseLauncherActivity : BaseActivity() {
|
||||
uploadAnalyticsData()
|
||||
updateFcmToken()
|
||||
updateMarketingDataToSaphyra()
|
||||
fetchScreenLockExperiment()
|
||||
}
|
||||
|
||||
fun initJuspaySDK() {
|
||||
@@ -103,15 +99,6 @@ abstract class BaseLauncherActivity : BaseActivity() {
|
||||
launcherVM.fetchLoginSettings(naeScreenName = screenName)
|
||||
}
|
||||
|
||||
private fun fetchScreenLockExperiment() {
|
||||
if (
|
||||
BaseUtils.isUserLoggedIn() &&
|
||||
PreferenceManager.getStringPreference(HPC_SCREEN_LOCK_EXPERIMENT).isNull()
|
||||
) {
|
||||
launcherVM.fetchABExperiment(HPC_SCREEN_LOCK_EXPERIMENT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setFirebaseAppInstanceId() {
|
||||
launcherVM.setFirebaseAppInstanceId()
|
||||
}
|
||||
|
||||
@@ -24,15 +24,11 @@ import com.navi.base.sharedpref.CommonPrefConstants.PREVIOUS_APPSFLYER_ID
|
||||
import com.navi.base.sharedpref.CommonPrefConstants.PREVIOUS_GOOGLE_ADVERTISEMENT_ID
|
||||
import com.navi.base.sharedpref.PreferenceManager
|
||||
import com.navi.base.utils.BaseUtils
|
||||
import com.navi.base.utils.isNotNull
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
|
||||
import com.navi.common.managers.NaviLocationManager
|
||||
import com.navi.common.model.CommunicationAppLaunchData
|
||||
import com.navi.common.utils.Constants.SAPHYRA_APP_LOGIN_EVENT
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.DISABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.ENABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.HPC_SCREEN_LOCK_EXPERIMENT
|
||||
import com.navi.common.utils.deviceId
|
||||
import com.navi.common.utils.getAppsflyerUid
|
||||
import com.navi.common.utils.isValidResponse
|
||||
@@ -250,23 +246,4 @@ constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchABExperiment(experimentName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val response = configRepository.fetchABExperiment(experimentName)
|
||||
|
||||
if (response.isSuccessful && response.body()?.result.isNotNull()) {
|
||||
val experimentResult =
|
||||
response.body()?.result?.let {
|
||||
when (it) {
|
||||
true -> ENABLED
|
||||
false -> DISABLED
|
||||
}
|
||||
}
|
||||
experimentResult?.let { result ->
|
||||
PreferenceManager.setStringPreference(HPC_SCREEN_LOCK_EXPERIMENT, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package com.navi.common.ui.dialog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewStub
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -16,12 +17,14 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -36,6 +39,8 @@ import androidx.databinding.DataBindingUtil
|
||||
import com.navi.common.R
|
||||
import com.navi.common.databinding.NaviLockScreenDialogBinding
|
||||
import com.navi.common.ui.fragment.BaseBottomSheet
|
||||
import com.navi.common.utils.BiometricAuthenticationStatus
|
||||
import com.navi.common.utils.BiometricPromptUtils
|
||||
import com.navi.design.font.FontWeightEnum
|
||||
import com.navi.design.font.getFontWeight
|
||||
import com.navi.design.font.naviFontFamily
|
||||
@@ -60,12 +65,6 @@ class NaviLockScreenDialog : BaseBottomSheet() {
|
||||
}
|
||||
|
||||
override fun setContainerView(viewStub: ViewStub) {
|
||||
setBackgroundTint(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
com.navi.design.R.color.translucent_blue_midnight,
|
||||
)
|
||||
)
|
||||
viewStub.layoutResource = R.layout.navi_lock_screen_dialog
|
||||
binding = DataBindingUtil.getBinding(viewStub.inflate())!!
|
||||
initUI()
|
||||
@@ -77,62 +76,89 @@ class NaviLockScreenDialog : BaseBottomSheet() {
|
||||
|
||||
@Composable
|
||||
fun LockScreen() {
|
||||
Box(modifier = Modifier.fillMaxWidth().wrapContentSize()) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(
|
||||
color = Color.White,
|
||||
shape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp),
|
||||
)
|
||||
.padding(all = 16.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_purple_lock),
|
||||
contentDescription = null,
|
||||
val showDialog =
|
||||
remember(BiometricPromptUtils.biometricAuthenticationStatus.value) {
|
||||
mutableStateOf(
|
||||
BiometricPromptUtils.biometricAuthenticationStatus.value ==
|
||||
BiometricAuthenticationStatus.FAILED
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = resources.getString(R.string.navi_app_is_locked),
|
||||
style = TextStyle(fontFamily = naviFontFamily),
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
|
||||
fontSize = 16.sp,
|
||||
color = FF191919,
|
||||
textAlign = TextAlign.Start,
|
||||
}
|
||||
if (showDialog.value) {
|
||||
requireActivity().window?.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
||||
setBackgroundTint(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
com.navi.design.R.color.translucent_blue_midnight,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = resources.getString(R.string.your_sensitive_data_is_protected),
|
||||
style = TextStyle(fontFamily = naviFontFamily),
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
fontSize = 14.sp,
|
||||
color = FF6B6B6B,
|
||||
textAlign = TextAlign.Start,
|
||||
)
|
||||
Row(
|
||||
)
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(top = 32.dp, bottom = 16.dp)
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(
|
||||
color = Color(0xFF1F002A),
|
||||
shape = RoundedCornerShape(size = 4.dp),
|
||||
color = Color.White,
|
||||
shape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp),
|
||||
)
|
||||
.padding(all = 16.dp)
|
||||
.clickable { unlock?.invoke() },
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_purple_lock),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = resources.getString(R.string.log_in_securely),
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = resources.getString(R.string.navi_app_is_locked),
|
||||
style = TextStyle(fontFamily = naviFontFamily),
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
|
||||
fontSize = 14.sp,
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 16.sp,
|
||||
color = FF191919,
|
||||
textAlign = TextAlign.Start,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = resources.getString(R.string.your_sensitive_data_is_protected),
|
||||
style = TextStyle(fontFamily = naviFontFamily),
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
fontSize = 14.sp,
|
||||
color = FF6B6B6B,
|
||||
textAlign = TextAlign.Start,
|
||||
)
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(top = 32.dp, bottom = 16.dp)
|
||||
.background(
|
||||
color = Color(0xFF1F002A),
|
||||
shape = RoundedCornerShape(size = 4.dp),
|
||||
)
|
||||
.padding(all = 16.dp)
|
||||
.clickable { unlock?.invoke() },
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = resources.getString(R.string.log_in_securely),
|
||||
style = TextStyle(fontFamily = naviFontFamily),
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
|
||||
fontSize = 14.sp,
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setBackgroundTint(
|
||||
ContextCompat.getColor(requireContext(), com.navi.design.R.color.black_opacity_0)
|
||||
)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.fillMaxSize().background(color = Color.Transparent).clickable(
|
||||
enabled = false
|
||||
) {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,15 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
|
||||
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.navi.analytics.utils.NaviTrackEvent
|
||||
@@ -28,7 +31,6 @@ import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.AUTHENTICATION_SESSION_TIME_OUT_THRESHOLD
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.DISABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.ENABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.HPC_SCREEN_LOCK_EXPERIMENT
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.IS_SCREEN_LOCK_ENABLED
|
||||
import com.navi.common.utils.Constants.ScreenLockConstants.LOGIN_SESSION_ID
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -55,7 +57,6 @@ class BiometricPromptUtils {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
|
||||
FirebaseRemoteConfigHelper.getBoolean(IS_SCREEN_LOCK_ENABLED) &&
|
||||
BaseUtils.isUserLoggedIn() &&
|
||||
PreferenceManager.getStringPreference(HPC_SCREEN_LOCK_EXPERIMENT) == ENABLED &&
|
||||
isScreenLockToggled == ENABLED
|
||||
}
|
||||
|
||||
@@ -78,7 +79,7 @@ class BiometricPromptUtils {
|
||||
NaviTrackEvent.trackEventOnClickStream(
|
||||
eventName = getScreenLockEventName(eventType = ::onAuthenticationError.name)
|
||||
)
|
||||
biometricAuthenticationStatus = BiometricAuthenticationStatus.FAILED
|
||||
biometricAuthenticationStatus.value = BiometricAuthenticationStatus.FAILED
|
||||
onFailed()
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ class BiometricPromptUtils {
|
||||
eventName =
|
||||
getScreenLockEventName(eventType = ::onAuthenticationFailed.name)
|
||||
)
|
||||
biometricAuthenticationStatus = BiometricAuthenticationStatus.FAILED
|
||||
biometricAuthenticationStatus.value = BiometricAuthenticationStatus.FAILED
|
||||
onFailed()
|
||||
}
|
||||
|
||||
@@ -101,8 +102,9 @@ class BiometricPromptUtils {
|
||||
getScreenLockEventName(eventType = ::onAuthenticationSucceeded.name)
|
||||
)
|
||||
reset()
|
||||
biometricAuthenticationStatus = BiometricAuthenticationStatus.SUCCESS
|
||||
biometricAuthenticationStatus.value = BiometricAuthenticationStatus.SUCCESS
|
||||
onSuccess()
|
||||
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
||||
}
|
||||
}
|
||||
return BiometricPrompt(activity, executor, callback)
|
||||
@@ -164,11 +166,10 @@ class BiometricPromptUtils {
|
||||
) && PreferenceManager.getStringPreference(LOGIN_SESSION_ID) != getSessionId()
|
||||
) {
|
||||
if (isDeviceSecure(activity)) {
|
||||
if (
|
||||
isAuthenticationSessionExpired() ||
|
||||
biometricAuthenticationStatus == BiometricAuthenticationStatus.FAILED
|
||||
) {
|
||||
if (isAuthenticationSessionExpired() || hasBiometricAuthenticationFailed()) {
|
||||
withContext(Dispatchers.Main) {
|
||||
activity.window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
||||
showLockScreenDialog()
|
||||
showBiometricPrompt(
|
||||
activity = activity,
|
||||
onSuccess = hideLockScreenDialog,
|
||||
@@ -193,10 +194,15 @@ class BiometricPromptUtils {
|
||||
return result
|
||||
}
|
||||
|
||||
private fun hasBiometricAuthenticationFailed(): Boolean {
|
||||
return biometricAuthenticationStatus.value != BiometricAuthenticationStatus.SUCCESS &&
|
||||
biometricAuthenticationStatus.value != BiometricAuthenticationStatus.RESET
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
appInForegroundTimeStamp = null
|
||||
appInBackgroundTimeStamp = null
|
||||
biometricAuthenticationStatus = null
|
||||
biometricAuthenticationStatus.value = BiometricAuthenticationStatus.RESET
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -204,16 +210,19 @@ class BiometricPromptUtils {
|
||||
|
||||
var appInBackgroundTimeStamp: Long? = null
|
||||
|
||||
private var biometricAuthenticationStatus: BiometricAuthenticationStatus? = null
|
||||
var biometricAuthenticationStatus: MutableState<BiometricAuthenticationStatus?> =
|
||||
mutableStateOf(null)
|
||||
private set
|
||||
|
||||
private val _isBiometricAuthenticationEnabled = MutableSharedFlow<String?>()
|
||||
val isBiometricAuthenticationEnabled = _isBiometricAuthenticationEnabled.asSharedFlow()
|
||||
|
||||
fun getScreenLockEventName(eventType: String) = "screen_lock_${eventType}"
|
||||
fun getScreenLockEventName(eventType: String) = "naviapp_screen_lock_${eventType}"
|
||||
}
|
||||
}
|
||||
|
||||
enum class BiometricAuthenticationStatus {
|
||||
SUCCESS,
|
||||
FAILED,
|
||||
RESET,
|
||||
}
|
||||
|
||||
@@ -375,7 +375,6 @@ object Constants {
|
||||
const val ENABLED = "ENABLED"
|
||||
const val DISABLED = "DISABLED"
|
||||
const val IS_SCREEN_LOCK_ENABLED = "IS_SCREEN_LOCK_ENABLED"
|
||||
const val HPC_SCREEN_LOCK_EXPERIMENT = "hpc-screen-lock-experiment"
|
||||
const val TOGGLE_SWITCH_LAYOUT_ID = "lock_screen_toggle_switch"
|
||||
const val TOGGLE_SWITCH_UNCHECKED_STATE = "unchecked"
|
||||
const val TOGGLE_SWITCH_CHECKED_STATE = "checked"
|
||||
|
||||
@@ -113,7 +113,12 @@
|
||||
<style name="FullScreenDialogFragmentWithPurpleBackground" parent="AppTheme.NoActionBar.FullScreen">
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowAnimationStyle">@style/Animation.Design.BottomSheetDialog</item>
|
||||
<item name="android:windowBackground">@color/translucent_blue_midnight</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">false</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="FullScreenDialogFragmentWithNoAnimation" parent="AppTheme.NoActionBar.FullScreen">
|
||||
|
||||
Reference in New Issue
Block a user