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

implementing bio-metric pull #59

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
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
15 changes: 3 additions & 12 deletions .idea/codeStyles/Project.xml

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

2 changes: 1 addition & 1 deletion .idea/compiler.xml

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

1 change: 1 addition & 0 deletions .idea/gradle.xml

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

2 changes: 1 addition & 1 deletion .idea/misc.xml

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

10 changes: 0 additions & 10 deletions .idea/runConfigurations.xml

This file was deleted.

3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,7 @@ dependencies {
//implementation "com.google.dagger:hilt-android-testing:$hilt_ver"
implementation "androidx.hilt:hilt-common:1.0.0-alpha03"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"

//Bio-metric
implementation 'androidx.biometric:biometric:1.2.0-alpha03'
}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
package="dev.spikeysanju.expensetracker">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.USE_BIOMETRIC"
android:requiredFeature="true"/>

<application
android:name=".app.ExpenseTracker"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.spikeysanju.expensetracker.data.local.datastore

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.createDataStore


abstract class PrefsDataStore(context: Context, fileName: String) {
internal val dataStore: DataStore<Preferences> = context.createDataStore(fileName)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.spikeysanju.expensetracker.data.local.datastore

import android.content.Context
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Singleton


class SettingsDataStore(context: Context) :
PrefsDataStore(
context,
PREF_FILE_SETTINGS
),
SettingsImpl {

// used to get the data from datastore
override val biometric: Flow<Boolean>
get() = dataStore.data.map { preferences ->
val biometric = preferences[BIOMETRIC_KEY] ?: false
biometric
}

// used to save the bio-metric preference to datastore
override suspend fun saveToDataStore(isBiometricEnabled: Boolean) {
dataStore.edit { preferences ->
preferences[BIOMETRIC_KEY] = isBiometricEnabled
}
}

companion object {
private const val PREF_FILE_SETTINGS = "settings_preference"
private val BIOMETRIC_KEY = booleanPreferencesKey("biometric_mode")
}
}

@Singleton
interface SettingsImpl {
val biometric: Flow<Boolean>
suspend fun saveToDataStore(isBiometricEnabled: Boolean)
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package dev.spikeysanju.expensetracker.data.local.datastore

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.createDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Singleton

abstract class PrefsDataStore(context: Context, fileName: String) {
internal val dataStore: DataStore<Preferences> = context.createDataStore(fileName)
}

class UIModeDataStore(context: Context) :
PrefsDataStore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data class Transaction(
System.currentTimeMillis(),
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
var id: Int = 0,
var id: Int = 0
) : Serializable {
val createdAtDateFormat: String
get() = DateFormat.getDateTimeInstance()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package dev.spikeysanju.expensetracker.view.auth


import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import dev.spikeysanju.expensetracker.R
import dev.spikeysanju.expensetracker.databinding.FragmentAuthBinding
import dev.spikeysanju.expensetracker.view.base.BaseFragment
import kotlinx.coroutines.flow.first
import kotlin.system.exitProcess

@AndroidEntryPoint
class AuthFragment : BaseFragment<FragmentAuthBinding, AuthViewModel>() {
override val viewModel: AuthViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)


lifecycleScope.launchWhenStarted {
if(viewModel.bioMetricPreference.first())
authenticate()
}
}

override fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentAuthBinding.inflate(inflater, container, false)


private fun authenticate(){
val executor = ContextCompat.getMainExecutor(requireContext())
val biometricPrompt = BiometricPrompt(this, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
findNavController().navigate(R.id.action_authFragment_to_dashboardFragment)
}

override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
//todo: show message wait for a second; static err code
exitProcess(-1)
}

override fun onAuthenticationError(errorCode: Int,
errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
//todo: show message wait for a second; static err code
exitProcess(-2)
}
})

val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Please complete bio-metric login to use Expenso")
.setNegativeButtonText("Exit")
.build()
biometricPrompt.authenticate(promptInfo)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.spikeysanju.expensetracker.view.auth


import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.spikeysanju.expensetracker.data.local.datastore.SettingsDataStore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class AuthViewModel @Inject constructor(
application: Application
) :
AndroidViewModel(application) {

// init datastore
private val settingsDataStore = SettingsDataStore(application)

// get bio-metric preference
val bioMetricPreference = settingsDataStore.biometric
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class DashboardFragment :
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return true
}
Expand Down Expand Up @@ -280,6 +280,10 @@ class DashboardFragment :
findNavController().navigate(R.id.action_dashboardFragment_to_aboutFragment)
true
}
R.id.action_settings -> {
findNavController().navigate(R.id.action_dashboardFragment_to_settingsFragment)
true
}
else -> super.onOptionsItemSelected(item)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package dev.spikeysanju.expensetracker.view.main

import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
Expand All @@ -14,6 +18,10 @@ import dev.spikeysanju.expensetracker.databinding.ActivityMainBinding
import dev.spikeysanju.expensetracker.repo.TransactionRepo
import dev.spikeysanju.expensetracker.utils.viewModelFactory
import dev.spikeysanju.expensetracker.view.main.viewmodel.TransactionViewModel
import dev.spikeysanju.expensetracker.view.settings.SettingsViewModel
import kotlinx.coroutines.flow.first
import java.util.concurrent.Executor
import kotlin.system.exitProcess

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
Expand Down Expand Up @@ -79,4 +87,5 @@ class MainActivity : AppCompatActivity() {
navHostFragment.navController.navigateUp()
return super.onSupportNavigateUp()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dev.spikeysanju.expensetracker.view.settings


import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.spikeysanju.expensetracker.databinding.FragmentSettingsBinding
import dev.spikeysanju.expensetracker.view.base.BaseFragment
import kotlinx.coroutines.flow.first

@AndroidEntryPoint
class SettingsFragment : BaseFragment<FragmentSettingsBinding, SettingsViewModel>() {
override val viewModel: SettingsViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Set the item state
lifecycleScope.launchWhenStarted {
binding.biometric.isChecked = viewModel.bioMetricPreference.first()
}

initViews()
}

private fun initViews() = with(binding) {
biometric.setOnCheckedChangeListener { _, biometricEnabled ->
viewModel.setBioMetricLock(biometricEnabled)
}
}

override fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentSettingsBinding.inflate(inflater, container, false)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dev.spikeysanju.expensetracker.view.settings


import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.spikeysanju.expensetracker.data.local.datastore.SettingsDataStore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class SettingsViewModel @Inject constructor(
application: Application
) :
AndroidViewModel(application) {

// init datastore
private val settingsDataStore = SettingsDataStore(application)

// get bio-metric preference
val bioMetricPreference = settingsDataStore.biometric

fun setBioMetricLock(bioMetricLock: Boolean) {
viewModelScope.launch(Dispatchers.IO) {
settingsDataStore.saveToDataStore(bioMetricLock)
}
}

}
Loading