Compare commits

...
2 Commits
Author SHA1 Message Date
shihaam 04af4e1bbd add biometrics to confirm digalogbox
Auto Tag on Version Change / check-version (push) Successful in 2s
2026-05-17 21:39:42 +05:00
shihaam 6197152f6e fix ui glitch, trasnfer button to take you to transfer tab in bottom bar 2026-05-17 21:09:43 +05:00
10 changed files with 149 additions and 56 deletions
@@ -31,7 +31,7 @@ class DashboardFragment : Fragment() {
viewModel.bmlLimits.observe(viewLifecycleOwner) { updateForeignLimits(it) } viewModel.bmlLimits.observe(viewLifecycleOwner) { updateForeignLimits(it) }
binding.btnTransfer.setOnClickListener { binding.btnTransfer.setOnClickListener {
(requireActivity() as HomeActivity).showWithBackStack(TransferFragment()) (requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer)
} }
binding.btnPayMvQr.setOnClickListener { binding.btnPayMvQr.setOnClickListener {
Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show()
@@ -55,6 +55,7 @@ class HomeActivity : AppCompatActivity() {
private lateinit var binding: ActivityHomeBinding private lateinit var binding: ActivityHomeBinding
private val viewModel: HomeViewModel by viewModels() private val viewModel: HomeViewModel by viewModels()
private lateinit var toggle: ActionBarDrawerToggle private lateinit var toggle: ActionBarDrawerToggle
private var suppressBottomNavCallback = false
private val autolockHandler = Handler(Looper.getMainLooper()) private val autolockHandler = Handler(Looper.getMainLooper())
private var warningDialog: AlertDialog? = null private var warningDialog: AlertDialog? = null
@@ -91,6 +92,7 @@ class HomeActivity : AppCompatActivity() {
toggle.syncState() toggle.syncState()
binding.bottomNavigation.setOnItemSelectedListener { item -> binding.bottomNavigation.setOnItemSelectedListener { item ->
if (suppressBottomNavCallback) return@setOnItemSelectedListener true
when (item.itemId) { when (item.itemId) {
R.id.nav_dashboard -> { show(DashboardFragment()); true } R.id.nav_dashboard -> { show(DashboardFragment()); true }
R.id.nav_accounts -> { show(AccountsFragment()); true } R.id.nav_accounts -> { show(AccountsFragment()); true }
@@ -209,6 +211,13 @@ class HomeActivity : AppCompatActivity() {
R.id.nav_settings -> show(SettingsFragment()) R.id.nav_settings -> show(SettingsFragment())
else -> Toast.makeText(this, R.string.work_in_progress, Toast.LENGTH_SHORT).show() else -> Toast.makeText(this, R.string.work_in_progress, Toast.LENGTH_SHORT).show()
} }
binding.navigationView.setCheckedItem(itemId)
val bottomNavIds = setOf(R.id.nav_dashboard, R.id.nav_accounts, R.id.nav_contacts, R.id.nav_transfer, R.id.nav_more)
if (binding.bottomNavigation.visibility == View.VISIBLE && itemId in bottomNavIds) {
suppressBottomNavCallback = true
binding.bottomNavigation.selectedItemId = itemId
suppressBottomNavCallback = false
}
} }
fun setRefreshing(visible: Boolean) { fun setRefreshing(visible: Boolean) {
@@ -17,6 +17,7 @@ class MoreFragment : Fragment() {
private data class NavItem(val id: Int, @DrawableRes val icon: Int, @StringRes val title: Int) private data class NavItem(val id: Int, @DrawableRes val icon: Int, @StringRes val title: Int)
private val items = listOf( private val items = listOf(
NavItem(R.id.nav_pay_mv_qr, R.drawable.ic_qr_scan, R.string.pay_mv_qr),
NavItem(R.id.nav_activities, R.drawable.ic_nav_activities, R.string.nav_activities), NavItem(R.id.nav_activities, R.drawable.ic_nav_activities, R.string.nav_activities),
NavItem(R.id.nav_transfer_history, R.drawable.ic_nav_transfer_history, R.string.nav_transfer_history), NavItem(R.id.nav_transfer_history, R.drawable.ic_nav_transfer_history, R.string.nav_transfer_history),
NavItem(R.id.nav_finances, R.drawable.ic_nav_finances, R.string.nav_finances), NavItem(R.id.nav_finances, R.drawable.ic_nav_finances, R.string.nav_finances),
@@ -36,9 +36,23 @@ class SettingsSecurityFragment : Fragment() {
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS .canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
if (canUseBiometrics) { if (canUseBiometrics) {
binding.rowBiometrics.visibility = View.VISIBLE binding.rowBiometrics.visibility = View.VISIBLE
binding.switchBiometrics.isChecked = prefs.getBoolean("biometrics_enabled", false)
val unlockEnabled = prefs.getBoolean("biometrics_enabled", false)
binding.switchBiometrics.isChecked = unlockEnabled
binding.switchBiometricsTransfer.isChecked = prefs.getBoolean("biometrics_transfer_confirm", false)
binding.switchBiometricsTransfer.isEnabled = unlockEnabled
binding.switchBiometrics.setOnCheckedChangeListener { _, isChecked -> binding.switchBiometrics.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("biometrics_enabled", isChecked).apply() prefs.edit().putBoolean("biometrics_enabled", isChecked).apply()
binding.switchBiometricsTransfer.isEnabled = isChecked
if (!isChecked) {
binding.switchBiometricsTransfer.isChecked = false
prefs.edit().putBoolean("biometrics_transfer_confirm", false).apply()
}
}
binding.switchBiometricsTransfer.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("biometrics_transfer_confirm", isChecked).apply()
} }
} }
@@ -19,6 +19,9 @@ import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@@ -562,29 +565,31 @@ class TransferFragment : Fragment() {
val mainMsg = "Send $currency $amountStr to $destDisplay?\n\nFrom: ${src.accountBriefName} · ${src.accountNumber}" val mainMsg = "Send $currency $amountStr to $destDisplay?\n\nFrom: ${src.accountBriefName} · ${src.accountNumber}"
val dialogBuilder = AlertDialog.Builder(requireContext()) val doTransfer: () -> Unit = {
.setTitle(R.string.transfer) binding.btnTransfer.isEnabled = false
.setPositiveButton(R.string.transfer_confirm) { _, _ -> viewLifecycleOwner.lifecycleScope.launch {
binding.btnTransfer.isEnabled = false val (ok, msg, receipt) = withContext(Dispatchers.IO) {
viewLifecycleOwner.lifecycleScope.launch { if (!isSrcBml) {
val (ok, msg, receipt) = withContext(Dispatchers.IO) { doMibTransfer(src, resolvedAccountNumber, resolvedRecipientName, destDisplay, amountStr, remarks, bankNameCapture)
if (!isSrcBml) { } else {
doMibTransfer(src, resolvedAccountNumber, resolvedRecipientName, destDisplay, amountStr, remarks, bankNameCapture) doBmlTransfer(src, resolvedAccountNumber, destDisplay, amount, amountStr, remarks, isSrcCard, isDestMib, currency, allAccounts, allContacts)
} else {
doBmlTransfer(src, resolvedAccountNumber, destDisplay, amount, amountStr, remarks, isSrcCard, isDestMib, currency, allAccounts, allContacts)
}
}
binding.btnTransfer.isEnabled = true
if (ok && receipt != null) {
clearForm()
val activity = requireActivity() as HomeActivity
activity.refreshBalances(src)
activity.showWithBackStack(TransferReceiptFragment.newInstance(receipt, capturedToAvatar))
} else if (!ok) {
Toast.makeText(requireContext(), msg, Toast.LENGTH_LONG).show()
} }
} }
binding.btnTransfer.isEnabled = true
if (ok && receipt != null) {
clearForm()
val activity = requireActivity() as HomeActivity
activity.refreshBalances(src)
activity.showWithBackStack(TransferReceiptFragment.newInstance(receipt, capturedToAvatar))
} else if (!ok) {
Toast.makeText(requireContext(), msg, Toast.LENGTH_LONG).show()
}
} }
}
val dialogBuilder = AlertDialog.Builder(requireContext())
.setTitle(R.string.transfer)
.setPositiveButton(R.string.transfer_confirm) { _, _ -> doTransfer() }
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
if (isUsdToMvr) { if (isUsdToMvr) {
@@ -606,7 +611,39 @@ class TransferFragment : Fragment() {
} else { } else {
dialogBuilder.setMessage(mainMsg) dialogBuilder.setMessage(mainMsg)
} }
dialogBuilder.show()
val dialog = dialogBuilder.show()
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
val biometricTransferConfirm = prefs.getBoolean("biometrics_transfer_confirm", false)
val canAuth = BiometricManager.from(requireContext())
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
if (biometricTransferConfirm && canAuth) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val prompt = BiometricPrompt(this, ContextCompat.getMainExecutor(requireContext()),
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
dialog.dismiss()
doTransfer()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
if (errorCode != BiometricPrompt.ERROR_CANCELED &&
errorCode != BiometricPrompt.ERROR_USER_CANCELED &&
errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
Toast.makeText(requireContext(), errString, Toast.LENGTH_SHORT).show()
}
}
override fun onAuthenticationFailed() { /* keep dialog open */ }
})
prompt.authenticate(
BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.biometric_transfer_title))
.setSubtitle("$currency $amountStr$destDisplay")
.setNegativeButtonText(getString(android.R.string.cancel))
.build()
)
}
}
} }
private fun doMibTransfer( private fun doMibTransfer(
@@ -30,23 +30,60 @@
android:id="@+id/rowBiometrics" android:id="@+id/rowBiometrics"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="vertical"
android:gravity="center_vertical"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:visibility="gone"> android:visibility="gone">
<TextView <TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_biometrics"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchBiometrics"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:text="@string/settings_biometrics"
android:textAppearance="?attr/textAppearanceTitleSmall"
android:textColor="?attr/colorOnSurfaceVariant"
android:layout_marginBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_biometrics_unlock"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchBiometrics"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_biometrics_transfer"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchBiometricsTransfer"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout> </LinearLayout>
@@ -12,19 +12,6 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:strokeWidth="1dp"
app:strokeColor="?attr/colorOutlineVariant">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<!-- From label + account dropdown --> <!-- From label + account dropdown -->
<TextView <TextView
@@ -355,10 +342,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/transfer" /> android:text="@string/transfer" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
+9 -3
View File
@@ -5,12 +5,18 @@
<item android:id="@+id/nav_dashboard" <item android:id="@+id/nav_dashboard"
android:icon="@drawable/ic_nav_dashboard" android:icon="@drawable/ic_nav_dashboard"
android:title="@string/nav_dashboard" /> android:title="@string/nav_dashboard" />
<item android:id="@+id/nav_accounts"
android:icon="@drawable/ic_nav_accounts"
android:title="@string/nav_accounts" />
</group> </group>
<group android:id="@+id/group_finance" android:checkableBehavior="single"> <group android:id="@+id/group_finance" android:checkableBehavior="single">
<item android:id="@+id/nav_accounts"
android:icon="@drawable/ic_nav_accounts"
android:title="@string/nav_accounts" />
<item android:id="@+id/nav_transfer"
android:icon="@drawable/ic_send"
android:title="@string/transfer" />
<item android:id="@+id/nav_pay_mv_qr"
android:icon="@drawable/ic_qr_scan"
android:title="@string/pay_mv_qr" />
<item android:id="@+id/nav_contacts" <item android:id="@+id/nav_contacts"
android:icon="@drawable/ic_contacts" android:icon="@drawable/ic_contacts"
android:title="@string/nav_contacts" /> android:title="@string/nav_contacts" />
+3
View File
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/nav_pay_mv_qr"
android:icon="@drawable/ic_qr_scan"
android:title="@string/pay_mv_qr" />
<item android:id="@+id/nav_activities" <item android:id="@+id/nav_activities"
android:icon="@drawable/ic_nav_activities" android:icon="@drawable/ic_nav_activities"
android:title="@string/nav_activities" /> android:title="@string/nav_activities" />
+4 -1
View File
@@ -103,7 +103,10 @@
<!-- Settings --> <!-- Settings -->
<string name="settings_security">Security</string> <string name="settings_security">Security</string>
<string name="settings_change_lock">Change PIN / Pattern</string> <string name="settings_change_lock">Change PIN / Pattern</string>
<string name="settings_biometrics">Use Biometrics</string> <string name="settings_biometrics">Use biometrics</string>
<string name="settings_biometrics_unlock">To unlock app</string>
<string name="settings_biometrics_transfer">Confirm transfer</string>
<string name="biometric_transfer_title">Confirm Transfer</string>
<string name="settings_autolock">Auto-lock</string> <string name="settings_autolock">Auto-lock</string>
<string name="autolock_off">Off</string> <string name="autolock_off">Off</string>
<string name="autolock_30s">30s</string> <string name="autolock_30s">30s</string>