Skip to content

Commit

Permalink
Merge pull request #316 from PermanentOrg/feature/VSP-1473
Browse files Browse the repository at this point in the history
Biometrics implementation for phone.
  • Loading branch information
flaviahandrea-vsp authored Nov 18, 2024
2 parents f2538fa + d86f63a commit 3df2015
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import org.permanent.permanent.ui.PREFS_NAME
import org.permanent.permanent.ui.PreferencesHelper
import org.permanent.permanent.ui.activities.PermanentBaseActivity
import org.permanent.permanent.ui.computeWindowSizeClasses
import org.permanent.permanent.ui.login.compose.AuthPage

class AuthenticationActivity : PermanentBaseActivity() {

Expand Down Expand Up @@ -41,16 +40,7 @@ class AuthenticationActivity : PermanentBaseActivity() {
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.authenticationNavHostFragment) as NavHostFragment
navController = navHostFragment.navController

val intentExtras = intent.extras
val startDestPageVal = intentExtras?.getInt(START_DESTINATION_PAGE_VALUE_KEY)
if (startDestPageVal != null && startDestPageVal != 0 && startDestPageVal == AuthPage.BIOMETRICS.value) {
val navGraph = navController.graph
navGraph.startDestination = R.id.biometricsFragment
navController.setGraph(navGraph, intent.extras)
} else {
navController.setGraph(R.navigation.authentication_navigation_graph, intent.extras)
}
navController.setGraph(R.navigation.authentication_navigation_graph, intent.extras)
}

override fun connectViewModelEvents() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class AuthenticationFragment : PermanentBaseFragment() {
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(this)[AuthenticationViewModel::class.java]
viewModel.buildPromptParams(this)

prefsHelper = PreferencesHelper(
requireContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
Expand All @@ -53,16 +54,36 @@ class AuthenticationFragment : PermanentBaseFragment() {
startDestPageValue?.let {
val targetPage = when (it) {
AuthPage.SIGN_UP.value -> AuthPage.SIGN_UP
AuthPage.BIOMETRICS.value -> {
if (!prefsHelper.isBiometricsLogIn() || viewModel.skipLogin()) {
navigateToMainActivity()
return
} else if (!prefsHelper.isUserLoggedIn()) {
onLoggedOut()
AuthPage.SIGN_IN
} else {
viewModel.authenticateUser()
AuthPage.BIOMETRICS
}
}
else -> AuthPage.SIGN_IN
}
viewModel.setNavigateToPage(targetPage)
}
}

private val onLoggedIn = Observer<Void?> {
private fun onLoggedOut() {
EventsManager(requireContext()).resetUser()
// Navigate to Sign in after this
}

private val onSignedIn = Observer<Void?> {
logSignInEvents()
startActivity(Intent(context, MainActivity::class.java))
activity?.finish()
navigateToMainActivity()
}

private val onAuthenticated = Observer<Void?> {
navigateToMainActivity()
}

private val onAccountCreated = Observer<Void?> {
Expand Down Expand Up @@ -91,15 +112,22 @@ class AuthenticationFragment : PermanentBaseFragment() {
EventsManager(requireContext()).sendToMixpanel(EventType.SignUp)
}

private fun navigateToMainActivity() {
startActivity(Intent(context, MainActivity::class.java))
activity?.finish()
}

override fun connectViewModelEvents() {
viewModel.getOnAccountCreated().observe(this, onAccountCreated)
viewModel.getOnLoggedIn().observe(this, onLoggedIn)
viewModel.getOnSignedIn().observe(this, onSignedIn)
viewModel.getOnAuthenticated().observe(this, onAuthenticated)
viewModel.getOnUserMissingDefaultArchive().observe(this, userMissingDefaultArchiveObserver)
}

override fun disconnectViewModelEvents() {
viewModel.getOnAccountCreated().removeObserver(onAccountCreated)
viewModel.getOnLoggedIn().removeObserver(onLoggedIn)
viewModel.getOnSignedIn().removeObserver(onSignedIn)
viewModel.getOnAuthenticated().removeObserver(onAuthenticated)
viewModel.getOnUserMissingDefaultArchive().removeObserver(userMissingDefaultArchiveObserver)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ fun AuthenticationContainer(
AuthPage.FORGOT_PASSWORD_DONE.value -> {
ForgotPasswordDonePage(pagerState = pagerState)
}

AuthPage.BIOMETRICS.value -> {
BiometricsPage(viewModel = viewModel)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
@file:OptIn(ExperimentalFoundationApi::class)

package org.permanent.permanent.ui.login.compose

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.permanent.permanent.R
import org.permanent.permanent.ui.composeComponents.ButtonColor
import org.permanent.permanent.ui.composeComponents.CenteredTextAndIconButton
import org.permanent.permanent.ui.composeComponents.CustomSnackbar
import org.permanent.permanent.viewmodels.AuthenticationViewModel


@Composable
fun BiometricsPage(
viewModel: AuthenticationViewModel
) {
val regularFont = FontFamily(Font(R.font.open_sans_regular_ttf))

val snackbarMessage by viewModel.snackbarMessage.collectAsState()
val snackbarType by viewModel.snackbarType.collectAsState()

val keyboardController = LocalSoftwareKeyboardController.current
val scrollState = rememberScrollState()

Box(
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.img_logo),
contentDescription = "Logo",
modifier = Modifier.size(64.dp)
)

Spacer(modifier = Modifier.height(32.dp))

Text(
text = stringResource(id = R.string.welcome_back),
fontSize = 32.sp,
lineHeight = 48.sp,
color = Color.White,
fontFamily = regularFont
)

Spacer(modifier = Modifier.height(64.dp))

Column(
modifier = Modifier.fillMaxWidth()
) {
CenteredTextAndIconButton(
buttonColor = ButtonColor.LIGHT,
text = stringResource(id = R.string.unlock_with_fingerprint),
icon = painterResource(id = R.drawable.ic_fingerprint_primary),
) {
keyboardController?.hide()
viewModel.clearSnackbar()
viewModel.authenticateUser()
}

Spacer(modifier = Modifier.height(16.dp))

CenteredTextAndIconButton(
buttonColor = ButtonColor.TRANSPARENT,
text = stringResource(id = R.string.unlock_with_credentials),
icon = null
) {
viewModel.deleteDeviceToken()
}
}
}

CustomSnackbar(modifier = Modifier.align(Alignment.BottomCenter),
isForError = snackbarType == AuthenticationViewModel.SnackbarType.ERROR,
message = snackbarMessage,
buttonText = stringResource(id = R.string.ok),
onButtonClick = {
viewModel.clearSnackbar()
})
}
}
Loading

0 comments on commit 3df2015

Please sign in to comment.