Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Biometric Auth & UI improvement #200

Merged
merged 8 commits into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

384 changes: 384 additions & 0 deletions .idea/other.xml

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ dependencies {
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Datastore
implementation("androidx.datastore:datastore-preferences:1.0.0")
implementation("androidx.datastore:datastore-preferences:1.1.2")

// Lottie Animation
implementation("com.airbnb.android:lottie-compose:5.0.3")
Expand Down Expand Up @@ -163,4 +163,6 @@ dependencies {

// BCrypt
implementation("org.mindrot:jbcrypt:0.4")

implementation("androidx.biometric:biometric:1.2.0-alpha05")
}
Binary file added app/release/app-release.aab
Binary file not shown.
37 changes: 36 additions & 1 deletion app/src/main/java/com/aritradas/uncrack/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@ import androidx.annotation.RequiresApi
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.view.WindowCompat
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.aritradas.uncrack.navigation.Navigation
import com.aritradas.uncrack.presentation.settings.SettingsViewModel
import com.aritradas.uncrack.sharedViewModel.SharedViewModel
import com.aritradas.uncrack.ui.theme.UnCrackTheme
import com.aritradas.uncrack.util.AppBioMetricManager
import com.aritradas.uncrack.util.NetworkConnectivityObserver
import com.google.android.gms.tasks.Task
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
Expand All @@ -25,11 +31,18 @@ import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.UpdateAvailability
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
class MainActivity : FragmentActivity() {

private val settingsViewModel: SettingsViewModel by viewModels()
private val viewModel: SharedViewModel by viewModels()

@Inject
lateinit var appBioMetricManager: AppBioMetricManager

private val activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
Expand Down Expand Up @@ -75,6 +88,28 @@ class MainActivity : ComponentActivity() {
Navigation(this, connectivityObserver)
}
}

setObserver()
}

private fun setObserver() {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.initAuth.collect { value ->
if (value && viewModel.loading.value) {
viewModel.showBiometricPrompt(this@MainActivity)
}
}
}
}

lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.finishActivity.collect { value ->
if (value) finish()
}
}
}
}

private fun checkForAppUpdate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.aritradas.uncrack.ui.theme.BackgroundLight
import com.aritradas.uncrack.ui.theme.SurfaceVariantLight

@Composable
fun SettingsItemGroup(
Expand All @@ -21,9 +22,9 @@ fun SettingsItemGroup(
Column(
modifier = modifier
.widthIn(max = 500.dp)
.padding(start = 12.dp, end = 12.dp)
.clip(RoundedCornerShape(8.dp))
.background(BackgroundLight),
.padding(horizontal = 12.dp)
.clip(RoundedCornerShape(10.dp))
.background(SurfaceVariantLight),
horizontalAlignment = Alignment.CenterHorizontally
) {
columnScope()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ class DataStoreUtil @Inject constructor(context: Context) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
val IS_DARK_MODE_KEY = booleanPreferencesKey("dark_mode")
val IS_SS_BLOCK_KEY = booleanPreferencesKey("ss_block")
val IS_BIOMETRIC_AUTH_SET_KEY = booleanPreferencesKey("biometric_auth")
}
}
5 changes: 5 additions & 0 deletions app/src/main/java/com/aritradas/uncrack/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object AppModule {

@Provides
fun provideContext(@ApplicationContext context: Context): Context {
return context
}

@Provides
fun provideDataStoreUtil(@ApplicationContext context: Context): DataStoreUtil =
DataStoreUtil(context)
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/com/aritradas/uncrack/di/BioMetricUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.aritradas.uncrack.di

import android.content.Context
import com.aritradas.uncrack.util.AppBioMetricManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent

@Module
@InstallIn(ViewModelComponent::class)
class BioMetricUtil {

@Provides
fun provideAppBioMetricManager(context: Context): AppBioMetricManager {
return AppBioMetricManager(context)
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/com/aritradas/uncrack/di/ViewModelModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.aritradas.uncrack.di

import com.aritradas.uncrack.data.datastore.DataStoreUtil
import com.aritradas.uncrack.sharedViewModel.SharedViewModel
import com.aritradas.uncrack.util.AppBioMetricManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent

@Module
@InstallIn(ViewModelComponent::class)
class ViewModelModule {

@Provides
fun provideMainViewModel(
bioMetricManager: AppBioMetricManager,
dataStoreUtil: DataStoreUtil,
): SharedViewModel {
return SharedViewModel(bioMetricManager, dataStoreUtil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import com.aritradas.uncrack.ui.theme.FadeOut
import com.aritradas.uncrack.ui.theme.OnPrimaryContainerLight
import com.aritradas.uncrack.ui.theme.OnSurfaceVariantLight
import com.aritradas.uncrack.ui.theme.PrimaryDark
import com.aritradas.uncrack.ui.theme.SurfaceVariantLight
import com.aritradas.uncrack.util.BackPressHandler
import com.aritradas.uncrack.util.ConnectivityObserver
import kotlinx.collections.immutable.ImmutableList
Expand Down Expand Up @@ -156,6 +157,7 @@ fun Navigation(

composable(Screen.SignUpScreen.name) {
SignupScreen(
navController,
authViewModel,
connectivityObserver,
onSignUp = {
Expand Down Expand Up @@ -271,7 +273,6 @@ fun Navigation(

composable(route = Screen.SettingsScreen.name) {
SettingsScreen(
activity,
navController,
settingsViewModel
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.aritradas.uncrack.presentation.auth.login

import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -47,6 +48,7 @@ import com.aritradas.uncrack.components.UCButton
import com.aritradas.uncrack.components.UCTextField
import com.aritradas.uncrack.navigation.Screen
import com.aritradas.uncrack.presentation.auth.AuthViewModel
import com.aritradas.uncrack.ui.theme.BackgroundLight
import com.aritradas.uncrack.ui.theme.DMSansFontFamily
import com.aritradas.uncrack.ui.theme.OnPrimaryContainerLight
import com.aritradas.uncrack.ui.theme.PrimaryLight
Expand Down Expand Up @@ -105,6 +107,7 @@ fun LoginScreen(
Column(
modifier = Modifier
.fillMaxSize()
.background(BackgroundLight)
.padding(paddingValues)
.padding(16.dp)
) {
Expand Down Expand Up @@ -202,10 +205,6 @@ fun LoginScreen(
}
}
}

if (isLoading) {
ProgressDialog {}
}
}
else -> {
NoInternetScreen()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.aritradas.uncrack.presentation.auth.signup

import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -45,6 +46,7 @@ import com.aritradas.uncrack.components.UCButton
import com.aritradas.uncrack.components.UCTextField
import com.aritradas.uncrack.navigation.Screen
import com.aritradas.uncrack.presentation.auth.AuthViewModel
import com.aritradas.uncrack.ui.theme.BackgroundLight
import com.aritradas.uncrack.ui.theme.DMSansFontFamily
import com.aritradas.uncrack.ui.theme.OnPrimaryContainerLight
import com.aritradas.uncrack.ui.theme.PrimaryLight
Expand All @@ -60,6 +62,7 @@ import kotlinx.coroutines.flow.collectLatest

@Composable
fun SignupScreen(
navController: NavController,
authViewModel: AuthViewModel,
connectivityObserver: ConnectivityObserver,
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -106,9 +109,6 @@ fun SignupScreen(
}
}

if (isLoading) {
ProgressDialog {}
}

when(networkStatus) {
ConnectivityObserver.Status.Available -> {
Expand All @@ -118,6 +118,7 @@ fun SignupScreen(
Column(
modifier = Modifier
.fillMaxSize()
.background(BackgroundLight)
.padding(paddingValues)
.padding(16.dp)
) {
Expand Down Expand Up @@ -229,7 +230,7 @@ fun SignupScreen(

Text(
modifier = Modifier.clickable {

navController.navigate(Screen.LoginScreen.name)
},
text = stringResource(id = R.string.login),
style = medium16.copy(color = PrimaryLight)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,34 @@ import com.aritradas.uncrack.components.UCButton
import com.aritradas.uncrack.components.UCTextField
import com.aritradas.uncrack.navigation.Screen
import com.aritradas.uncrack.presentation.masterKey.KeyViewModel
import com.aritradas.uncrack.ui.theme.BackgroundLight
import com.aritradas.uncrack.ui.theme.SurfaceVariantLight
import com.aritradas.uncrack.ui.theme.bold30
import kotlinx.coroutines.delay

@Composable
fun ConfirmMasterKeyScreen(
navController: NavController,
modifier: Modifier = Modifier,
masterKeyViewModel: KeyViewModel = hiltViewModel()
) {

val context = LocalContext.current
var confirmMasterKey by remember { mutableStateOf("") }
var passwordVisibility by remember { mutableStateOf(false) }
var isLoading by remember { mutableStateOf(false) }
val savedMasterKey = masterKeyViewModel.keyModel.password

LaunchedEffect(Unit) {
masterKeyViewModel.getMasterKey()
}

LaunchedEffect(isLoading) {
if (isLoading) {
delay(2000)
isLoading = false
navController.navigate(Screen.VaultScreen.name)
}
}

Scaffold(
modifier.fillMaxSize()
) { paddingValues ->
Expand All @@ -63,7 +72,7 @@ fun ConfirmMasterKeyScreen(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.background(SurfaceVariantLight)
.background(BackgroundLight)
.padding(16.dp)
) {

Expand Down Expand Up @@ -98,10 +107,7 @@ fun ConfirmMasterKeyScreen(
R.string.hide_password
)


IconButton(onClick =
{ passwordVisibility = passwordVisibility.not() }
) {
IconButton(onClick = { passwordVisibility = passwordVisibility.not() }) {
Icon(
modifier = Modifier.size(24.dp),
painter = image,
Expand All @@ -117,8 +123,12 @@ fun ConfirmMasterKeyScreen(
modifier = Modifier
.fillMaxWidth(),
text = stringResource(R.string.unlock_uncrack),
isLoading = isLoading,
loadingText = "Unlocking...",
onClick = {
navController.navigate(Screen.VaultScreen.name)
if (savedMasterKey == confirmMasterKey) {
isLoading = true
}
},
enabled = savedMasterKey == confirmMasterKey
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.aritradas.uncrack.components.UCTextField
import com.aritradas.uncrack.domain.model.Key
import com.aritradas.uncrack.navigation.Screen
import com.aritradas.uncrack.presentation.masterKey.KeyViewModel
import com.aritradas.uncrack.ui.theme.BackgroundLight
import com.aritradas.uncrack.ui.theme.OnPrimaryContainerLight
import com.aritradas.uncrack.ui.theme.SurfaceTintLight
import com.aritradas.uncrack.ui.theme.SurfaceVariantLight
Expand Down Expand Up @@ -66,7 +67,7 @@ fun CreateMasterKeyScreen(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.background(SurfaceVariantLight)
.background(BackgroundLight)
.padding(16.dp)
) {
Text(
Expand Down
Loading