diff --git a/app/build.gradle b/app/build.gradle
index 0b82abe7..d797dc5c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,7 +15,7 @@ android {
defaultConfig {
applicationId "com.steve_md.smartmkulima"
- minSdk 21
+ minSdk 23
targetSdk 34
versionCode 1
versionName "1.0"
@@ -54,24 +54,22 @@ android {
dependencies {
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
- implementation 'com.google.firebase:firebase-auth-ktx:22.3.1'
- implementation 'com.google.firebase:firebase-database-ktx:20.3.0'
- implementation 'com.google.firebase:firebase-storage-ktx:20.3.0'
- implementation 'com.google.firebase:firebase-firestore-ktx:24.10.2'
- def nav_version = "2.7.7"
- def lifecycle_version = "2.7.0"
+ implementation 'com.google.firebase:firebase-auth-ktx:23.0.0'
+ implementation 'com.google.firebase:firebase-database-ktx:21.0.0'
+ implementation 'com.google.firebase:firebase-storage-ktx:21.0.0'
+ implementation 'com.google.firebase:firebase-firestore-ktx:25.0.0'
def timber_version = "5.0.1"
def room_version = "2.6.1"
// Ktx
- implementation 'androidx.core:core-ktx:1.12.0'
+ implementation 'androidx.core:core-ktx:1.13.1'
// AppCompat
- implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'androidx.appcompat:appcompat:1.7.0'
// Material
- implementation 'com.google.android.material:material:1.11.0'
+ implementation 'com.google.android.material:material:1.12.0'
// Constraint Layout
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
@@ -82,15 +80,15 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
// Navigation Components
- implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
- implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
+ implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
+ implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
// Alternatively - just LiveData
- implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
+ implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.2")
// Alternatively - just ViewModel
- implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2")
// Dagger - Hilt
implementation 'com.google.dagger:hilt-android:2.50'
@@ -126,7 +124,7 @@ dependencies {
// Maps - Dependency
implementation 'com.google.android.gms:play-services-maps:18.2.0'
- implementation 'com.google.android.gms:play-services-location:21.1.0'
+ implementation 'com.google.android.gms:play-services-location:21.3.0'
// Lottie Animation
implementation 'com.airbnb.android:lottie:6.0.0'
@@ -147,4 +145,8 @@ dependencies {
// Step View
implementation 'com.github.shuhart:stepview:1.5.1'
+
+ // Biometric authentication
+ implementation "androidx.biometric:biometric:1.1.0"
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/auth/SignInDetailsWithEmailFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/auth/SignInDetailsWithEmailFragment.kt
index b17db3f1..67028eae 100644
--- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/auth/SignInDetailsWithEmailFragment.kt
+++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/auth/SignInDetailsWithEmailFragment.kt
@@ -1,10 +1,20 @@
package com.steve_md.smartmkulima.ui.fragments.auth
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.os.Build
import android.os.Bundle
+import android.provider.Settings
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
@@ -16,8 +26,10 @@ import com.steve_md.smartmkulima.utils.EventObserver
import com.steve_md.smartmkulima.utils.displaySnackBar
import com.steve_md.smartmkulima.utils.hideKeyboard
import com.steve_md.smartmkulima.utils.snackBar
+import com.steve_md.smartmkulima.utils.toast
import com.steve_md.smartmkulima.viewmodel.AuthViewModel
import dagger.hilt.android.AndroidEntryPoint
+import timber.log.Timber
@AndroidEntryPoint
@@ -31,12 +43,155 @@ class SignInDetailsWithEmailFragment : Fragment() {
private val authViewModel: AuthViewModel by viewModels()
+ private lateinit var biometricPrompt: BiometricPrompt
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+
+ }
+
+ private fun authenticate() {
+
+ /**
+ * Check whether the Authentication is available
+ * @param biometricManager is present, is enrolled
+ */
+ val biometricManager = BiometricManager.from(requireContext())
+ when(biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
+ BiometricManager.BIOMETRIC_SUCCESS -> {
+ Timber.tag("Biometric").e("Authenticated using biometrics")
+ val prompt = createBiometricPrompt()
+ prompt.authenticate(createPromptInfo())
+ }
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
+ Timber.tag(requireActivity().toString())
+ .e("onCreate: Biometric features are currently unavailable.")
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
+ // The user didn't enroll in biometrics that your app accepts, prompt them to enroll in it
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ val enrollIntent =
+ Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
+ putExtra(
+ Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
+ BIOMETRIC_STRONG or DEVICE_CREDENTIAL
+ )
+ }
+ startActivityForResult(enrollIntent, REQUEST_CODE)
+ }
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
+ Timber.d("Biometric features hardware is missing")
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> {
+
+ }
+
+ BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> {
+
+ }
+
+ BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> {
+
+ }
+ }
+ }
+
+ private fun setUpBinding() {
+ binding.fingerprintToPress.setOnClickListener {
+ authenticate()
+ }
+ }
+
+ private fun createPromptInfo(): BiometricPrompt.PromptInfo =
+ BiometricPrompt.PromptInfo.Builder()
+ .setTitle("Login")
+ .setSubtitle("Log in to your Agri-Sasa account")
+ .setDescription("Please authenticate using biometrics")
+ .setNegativeButtonText("CANCEL")
+ .setAllowedAuthenticators(BIOMETRIC_STRONG)
+ .setConfirmationRequired(false)
+ .build()
+
+
+ /**
+ *
+ * Display biometric prompt
+ * @param biometricPrompt
+ */
+ private fun createBiometricPrompt(): BiometricPrompt {
+ val executor = ContextCompat.getMainExecutor(requireContext())
+
+ val callBack = object : BiometricPrompt.AuthenticationCallback() {
+ @SuppressLint("TimberArgCount")
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
+ super.onAuthenticationError(errorCode, errString)
+ Timber.tag(this@SignInDetailsWithEmailFragment.toString())
+ .d("$errorCode :: $errString")
+
+ if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
+ loginWithEmailPasswordOtherwise()
+ } else if (errorCode == BiometricPrompt.ERROR_NO_BIOMETRICS) {
+ snackBar("Device does not support Fingerprint biometrics")
+ loginWithEmailPasswordOtherwise()
+ } else if (errorCode == BiometricPrompt.ERROR_CANCELED) {
+ loginWithEmailPasswordOtherwise()
+ } else if (errorCode == BiometricPrompt.ERROR_SECURITY_UPDATE_REQUIRED) {
+ snackBar("Security violation")
+ loginWithEmailPasswordOtherwise()
+ } else if (errorCode == BiometricPrompt.ERROR_TIMEOUT) {
+ snackBar("Fingerprint failed. try again or login using email instead")
+ } else {
+ Timber.tag(this.toString())
+ .e("%s%s", "%s || ", "onAuthenticationError: %s", errorCode, errString)
+ }
+
+ }
+
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
+ super.onAuthenticationSucceeded(result)
+ Timber.tag(this.toString()).d("Authentication was successful")
+ toast("Authenticated with biometrics successfully")
+
+ navigateHome()
+ // showEncryptedMessage(result.cryptoObject)
+ }
+
+ override fun onAuthenticationFailed() {
+ super.onAuthenticationFailed()
+ Timber.tag(this.toString()).d("Authentication failed for an unknown reason")
+ toast("Unknown authentication error")
+ }
+
+ }
+
+ return BiometricPrompt(this.requireActivity(), executor, callBack)
+ }
+
+ private fun showEncryptedMessage(cryptoObject: BiometricPrompt.CryptoObject?) {
+
+ }
+
+ private fun loginWithEmailPasswordOtherwise() {
+
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSignInDetailsWithEmailBinding.inflate(layoutInflater, container, false)
+ setUpBinding()
+
+ biometricPrompt = createBiometricPrompt()
+
+ authenticate()
+
return binding.root
}
@@ -98,4 +253,8 @@ class SignInDetailsWithEmailFragment : Fragment() {
//findNavController().navigate(R.id.action_signInDetailsWithEmailFragment_to_signInDetailsFragment2)
displaySnackBar("Feature is coming soon!")
}
+
+ companion object {
+ const val REQUEST_CODE = 1333
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/HomeDashboardFragment.kt b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/HomeDashboardFragment.kt
index 41442507..df62ceea 100644
--- a/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/HomeDashboardFragment.kt
+++ b/app/src/main/java/com/steve_md/smartmkulima/ui/fragments/main/HomeDashboardFragment.kt
@@ -55,6 +55,11 @@ class HomeDashboardFragment : Fragment() {
firebaseAuth = FirebaseAuth.getInstance()
val userId = firebaseAuth!!.uid
+
+ if (userId == null) {
+ Timber.e("Firebase User ID is null")
+ return binding.root
+ }
val currentUserLogged = firebaseAuth!!.currentUser
databaseReference = FirebaseDatabase.getInstance().getReference("users").child(userId!!)
diff --git a/app/src/main/res/drawable/baseline_fingerprint_24.xml b/app/src/main/res/drawable/baseline_fingerprint_24.xml
new file mode 100644
index 00000000..00e63e22
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_fingerprint_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_sign_in_details_with_email.xml b/app/src/main/res/layout/fragment_sign_in_details_with_email.xml
index 066e71fa..e40d9ee5 100644
--- a/app/src/main/res/layout/fragment_sign_in_details_with_email.xml
+++ b/app/src/main/res/layout/fragment_sign_in_details_with_email.xml
@@ -82,12 +82,14 @@
app:endIconMode="clear_text"
android:textColor="@color/textColor"
app:endIconTint="@color/SilverGray"
+
app:layout_constraintEnd_toEndOf="@id/textView2"
app:layout_constraintStart_toStartOf="@id/textView2"
app:layout_constraintTop_toBottomOf="@id/textView2">
@@ -134,7 +137,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="45dp"
- android:layout_marginTop="150dp"
+ android:layout_marginTop="100dp"
android:layout_marginEnd="45dp"
android:backgroundTint="@color/main1"
android:fontFamily="@font/montserrat_bold"
@@ -162,7 +165,7 @@
+
+
+
+