customize quick actions and bottom bar
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 2s

This commit is contained in:
2026-05-19 18:21:36 +05:00
parent 9c9729e268
commit 3a5b9459a9
15 changed files with 434 additions and 63 deletions

View File

@@ -8,7 +8,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.widget.addTextChangedListener
@@ -177,11 +177,11 @@ class ContactsFragment : Fragment() {
}
private fun confirmDelete(contact: ContactDisplay) {
AlertDialog.Builder(requireContext())
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.contact_delete_title)
.setMessage(getString(R.string.contact_delete_message, contact.name))
.setPositiveButton(R.string.contact_delete) { _, _ -> deleteContact(contact) }
.setNegativeButton(android.R.string.cancel, null)
.setNegativeButton(R.string.cancel, null)
.show()
}

View File

@@ -1,10 +1,11 @@
package sh.sar.basedbank.ui.home
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
@@ -41,17 +42,25 @@ class DashboardFragment : Fragment() {
insets
}
binding.btnTransfer.setOnClickListener {
(requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer)
}
binding.btnPayMvQr.setOnClickListener {
Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show()
}
}
override fun onResume() {
super.onResume()
requireActivity().title = getString(R.string.nav_dashboard)
refreshQuickActions()
}
private fun refreshQuickActions() {
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
val ids = NavCustomization.getQuickActions(prefs)
listOf(binding.btnQuickAction1, binding.btnQuickAction2).forEachIndexed { i, btn ->
val def = NavCustomization.ALL_SWAPPABLE.find { it.id == ids[i] }
if (def != null) {
btn.setText(def.titleRes)
btn.icon = ContextCompat.getDrawable(requireContext(), def.iconRes)
}
btn.setOnClickListener { (requireActivity() as HomeActivity).navigateTo(ids[i]) }
}
}
private fun updateBalances(accounts: List<MibAccount>) {

View File

@@ -9,7 +9,9 @@ import android.os.Looper
import android.view.Menu
import android.view.MenuItem
import android.view.View
import com.google.android.material.navigation.NavigationBarView
import android.widget.Toast
import sh.sar.basedbank.ui.home.NavCustomization
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import androidx.activity.viewModels
@@ -108,14 +110,21 @@ class HomeActivity : AppCompatActivity() {
binding.bottomNavigation.setOnItemSelectedListener { item ->
if (suppressBottomNavCallback) return@setOnItemSelectedListener true
when (item.itemId) {
R.id.nav_dashboard -> { show(DashboardFragment()); true }
R.id.nav_accounts -> { show(AccountsFragment()); true }
R.id.nav_contacts -> { show(ContactsFragment()); true }
R.id.nav_transfer -> { show(TransferFragment()); true }
R.id.nav_more -> { show(MoreFragment()); true }
else -> false
val frag = when (item.itemId) {
R.id.nav_dashboard -> DashboardFragment()
R.id.nav_accounts -> AccountsFragment()
R.id.nav_contacts -> ContactsFragment()
R.id.nav_transfer -> TransferFragment()
R.id.nav_more -> MoreFragment()
R.id.nav_transfer_history -> TransferHistoryFragment()
R.id.nav_finances -> FinancingFragment()
R.id.nav_otp -> OtpFragment()
R.id.nav_settings -> SettingsFragment()
else -> null
}
if (frag != null) show(frag)
else Toast.makeText(this, R.string.work_in_progress, Toast.LENGTH_SHORT).show()
true
}
applyNavMode()
@@ -210,12 +219,15 @@ class HomeActivity : AppCompatActivity() {
}
fun applyNavMode() {
val isBottom = getSharedPreferences("prefs", MODE_PRIVATE).getBoolean("bottom_nav", false)
val prefs = getSharedPreferences("prefs", MODE_PRIVATE)
val isBottom = prefs.getBoolean("bottom_nav", false)
if (isBottom) {
binding.drawerLayout.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
toggle.isDrawerIndicatorEnabled = false
supportActionBar?.setDisplayHomeAsUpEnabled(false)
binding.bottomNavigation.visibility = View.VISIBLE
rebuildBottomNav(prefs)
applyNavLabelVisibility()
} else {
binding.drawerLayout.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED)
toggle.isDrawerIndicatorEnabled = true
@@ -224,6 +236,27 @@ class HomeActivity : AppCompatActivity() {
}
}
fun rebuildBottomNav(prefs: android.content.SharedPreferences = getSharedPreferences("prefs", MODE_PRIVATE)) {
val slots = NavCustomization.getSlots(prefs)
val menu = binding.bottomNavigation.menu
menu.clear()
menu.add(Menu.NONE, R.id.nav_dashboard, 0, R.string.nav_dashboard)
.setIcon(R.drawable.ic_nav_dashboard)
slots.forEachIndexed { i, id ->
val item = NavCustomization.ALL_SWAPPABLE.find { it.id == id } ?: return@forEachIndexed
menu.add(Menu.NONE, item.id, i + 1, item.titleRes).setIcon(item.iconRes)
}
menu.add(Menu.NONE, R.id.nav_more, 4, R.string.nav_more)
.setIcon(R.drawable.ic_nav_more)
}
fun applyNavLabelVisibility() {
val showLabels = getSharedPreferences("prefs", MODE_PRIVATE).getBoolean("bottom_nav_show_labels", true)
binding.bottomNavigation.labelVisibilityMode =
if (showLabels) NavigationBarView.LABEL_VISIBILITY_LABELED
else NavigationBarView.LABEL_VISIBILITY_AUTO
}
fun navigateTo(itemId: Int, fragment: Fragment? = null) {
val dest = fragment ?: when (itemId) {
R.id.nav_dashboard -> DashboardFragment()
@@ -238,11 +271,15 @@ class HomeActivity : AppCompatActivity() {
}
show(dest)
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
if (binding.bottomNavigation.visibility == View.VISIBLE) {
val bottomNavIds = (0 until binding.bottomNavigation.menu.size())
.map { binding.bottomNavigation.menu.getItem(it).itemId }.toSet()
val selectId = if (itemId in bottomNavIds) itemId else if (R.id.nav_more in bottomNavIds) R.id.nav_more else null
if (selectId != null) {
suppressBottomNavCallback = true
binding.bottomNavigation.selectedItemId = selectId
suppressBottomNavCallback = false
}
}
}

View File

@@ -1,5 +1,6 @@
package sh.sar.basedbank.ui.home
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -7,35 +8,23 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import sh.sar.basedbank.R
class MoreFragment : Fragment() {
private data class NavItem(val id: Int, @DrawableRes val icon: Int, @StringRes val title: Int)
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_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_card_settings, R.drawable.ic_nav_card, R.string.nav_card_settings),
NavItem(R.id.nav_otp, R.drawable.ic_nav_otp, R.string.nav_otp),
NavItem(R.id.nav_settings, R.drawable.ic_nav_settings, R.string.nav_settings),
)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
inflater.inflate(R.layout.fragment_more, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
val items = NavCustomization.getMoreItems(prefs)
val list = view.findViewById<LinearLayout>(R.id.moreList)
val inflater = LayoutInflater.from(requireContext())
for (item in items) {
val row = inflater.inflate(R.layout.item_more_nav, list, false)
row.findViewById<ImageView>(R.id.ivIcon).setImageResource(item.icon)
row.findViewById<TextView>(R.id.tvLabel).setText(item.title)
row.findViewById<ImageView>(R.id.ivIcon).setImageResource(item.iconRes)
row.findViewById<TextView>(R.id.tvLabel).setText(item.titleRes)
row.setOnClickListener { (requireActivity() as HomeActivity).navigateTo(item.id) }
list.addView(row)
}

View File

@@ -0,0 +1,61 @@
package sh.sar.basedbank.ui.home
import android.content.SharedPreferences
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import sh.sar.basedbank.R
object NavCustomization {
data class NavItemDef(
val id: Int,
@DrawableRes val iconRes: Int,
@StringRes val titleRes: Int
)
/** All items that can occupy either a bottom nav slot or the "More" screen. */
val ALL_SWAPPABLE = listOf(
NavItemDef(R.id.nav_accounts, R.drawable.ic_nav_accounts, R.string.nav_accounts),
NavItemDef(R.id.nav_contacts, R.drawable.ic_contacts, R.string.nav_contacts),
NavItemDef(R.id.nav_transfer, R.drawable.ic_send, R.string.transfer),
NavItemDef(R.id.nav_pay_mv_qr, R.drawable.ic_qr_scan, R.string.pay_mv_qr),
NavItemDef(R.id.nav_activities, R.drawable.ic_nav_activities, R.string.nav_activities),
NavItemDef(R.id.nav_transfer_history, R.drawable.ic_nav_transfer_history, R.string.nav_transfer_history),
NavItemDef(R.id.nav_finances, R.drawable.ic_nav_finances, R.string.nav_finances),
NavItemDef(R.id.nav_card_settings, R.drawable.ic_nav_card, R.string.nav_card_settings),
NavItemDef(R.id.nav_otp, R.drawable.ic_nav_otp, R.string.nav_otp),
NavItemDef(R.id.nav_settings, R.drawable.ic_nav_settings, R.string.nav_settings),
)
fun getSlots(prefs: SharedPreferences): List<Int> = listOf(
prefs.getInt("bottom_nav_slot_1", R.id.nav_accounts),
prefs.getInt("bottom_nav_slot_2", R.id.nav_contacts),
prefs.getInt("bottom_nav_slot_3", R.id.nav_transfer),
)
fun saveSlots(prefs: SharedPreferences, slots: List<Int>) {
prefs.edit()
.putInt("bottom_nav_slot_1", slots[0])
.putInt("bottom_nav_slot_2", slots[1])
.putInt("bottom_nav_slot_3", slots[2])
.apply()
}
fun getQuickActions(prefs: SharedPreferences): List<Int> = listOf(
prefs.getInt("quick_action_1", R.id.nav_transfer),
prefs.getInt("quick_action_2", R.id.nav_pay_mv_qr),
)
fun saveQuickActions(prefs: SharedPreferences, ids: List<Int>) {
prefs.edit()
.putInt("quick_action_1", ids[0])
.putInt("quick_action_2", ids[1])
.apply()
}
/** Items that belong in the "More" screen — those not occupying a bottom nav slot. */
fun getMoreItems(prefs: SharedPreferences): List<NavItemDef> {
val slots = getSlots(prefs).toSet()
return ALL_SWAPPABLE.filter { it.id !in slots }
}
}

View File

@@ -1,7 +1,9 @@
package sh.sar.basedbank.ui.home
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
@@ -16,7 +18,14 @@ class NavMoreSheetFragment : BottomSheetDialogFragment() {
inflater.inflate(R.layout.sheet_nav_more, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<NavigationView>(R.id.navMoreView).setNavigationItemSelectedListener { item ->
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
val items = NavCustomization.getMoreItems(prefs)
val navView = view.findViewById<NavigationView>(R.id.navMoreView)
navView.menu.clear()
items.forEachIndexed { i, item ->
navView.menu.add(Menu.NONE, item.id, i, item.titleRes).setIcon(item.iconRes)
}
navView.setNavigationItemSelectedListener { item ->
dismiss()
onNavigate?.invoke(item.itemId)
true

View File

@@ -1,29 +1,45 @@
package sh.sar.basedbank.ui.home
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setApplicationLocales
import androidx.core.os.LocaleListCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import sh.sar.basedbank.R
import sh.sar.basedbank.databinding.FragmentSettingsAppearanceBinding
import java.util.Collections
class SettingsAppearanceFragment : Fragment() {
private var _binding: FragmentSettingsAppearanceBinding? = null
private val binding get() = _binding!!
private lateinit var prefs: SharedPreferences
private val slots = mutableListOf<Int>()
private val quickActions = mutableListOf<Int>()
private lateinit var slotAdapter: NavItemAdapter
private lateinit var quickActionAdapter: NavItemAdapter
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentSettingsAppearanceBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
// Navigation mode
val isBottom = prefs.getBoolean("bottom_nav", false)
@@ -32,8 +48,35 @@ class SettingsAppearanceFragment : Fragment() {
if (!isChecked) return@addOnButtonCheckedListener
prefs.edit().putBoolean("bottom_nav", checkedId == R.id.btnNavBottom).apply()
(activity as? HomeActivity)?.applyNavMode()
updateShortcutsVisibility()
}
// Quick actions
quickActions.clear()
quickActions.addAll(NavCustomization.getQuickActions(prefs))
quickActionAdapter = NavItemAdapter(quickActions) {
NavCustomization.saveQuickActions(prefs, quickActions)
}
setupNavItemRecyclerView(binding.rvQuickActions, quickActionAdapter, quickActions)
// Bottom bar shortcuts
slots.clear()
slots.addAll(NavCustomization.getSlots(prefs))
slotAdapter = NavItemAdapter(slots) {
NavCustomization.saveSlots(prefs, slots)
(activity as? HomeActivity)?.rebuildBottomNav(prefs)
}
setupNavItemRecyclerView(binding.rvNavSlots, slotAdapter, slots)
// Show labels toggle
val showLabels = prefs.getBoolean("bottom_nav_show_labels", true)
binding.switchShowLabels.isChecked = showLabels
binding.switchShowLabels.setOnCheckedChangeListener { _, checked ->
prefs.edit().putBoolean("bottom_nav_show_labels", checked).apply()
(activity as? HomeActivity)?.applyNavLabelVisibility()
}
updateShortcutsVisibility()
// Theme
val saved = prefs.getString("theme", "system")
binding.themeToggle.check(when (saved) {
@@ -63,6 +106,95 @@ class SettingsAppearanceFragment : Fragment() {
}
}
private fun setupNavItemRecyclerView(
rv: RecyclerView,
adapter: NavItemAdapter,
items: MutableList<Int>
) {
rv.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
rv.adapter = adapter
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.START or ItemTouchHelper.END, 0
) {
override fun onMove(
rv: RecyclerView,
from: RecyclerView.ViewHolder,
to: RecyclerView.ViewHolder
): Boolean {
val fromPos = from.adapterPosition
val toPos = to.adapterPosition
Collections.swap(items, fromPos, toPos)
adapter.notifyItemMoved(fromPos, toPos)
adapter.onSave()
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
}).attachToRecyclerView(rv)
}
private fun updateShortcutsVisibility() {
val isBottom = prefs.getBoolean("bottom_nav", false)
binding.sectionBottomBarShortcuts.alpha = if (isBottom) 1f else 0.38f
}
private fun showItemPicker(items: MutableList<Int>, slotIndex: Int, adapter: NavItemAdapter) {
if (items === slots && !prefs.getBoolean("bottom_nav", false)) return
val ctx = requireContext()
val otherIds = items.filterIndexed { i, _ -> i != slotIndex }.toSet()
val available = NavCustomization.ALL_SWAPPABLE.filter { it.id !in otherIds }
val listLayout = LinearLayout(ctx).apply { orientation = LinearLayout.VERTICAL }
val rows = available.map { item ->
LayoutInflater.from(ctx).inflate(R.layout.item_more_nav, listLayout, false).also { row ->
row.findViewById<ImageView>(R.id.ivIcon).setImageResource(item.iconRes)
row.findViewById<TextView>(R.id.tvLabel).setText(item.titleRes)
listLayout.addView(row)
}
}
val scroll = ScrollView(ctx).apply { addView(listLayout) }
var dialog: androidx.appcompat.app.AlertDialog? = null
dialog = MaterialAlertDialogBuilder(ctx)
.setTitle(R.string.settings_bottom_bar_select)
.setView(scroll)
.setNegativeButton(R.string.cancel, null)
.show()
available.forEachIndexed { i, item ->
rows[i].setOnClickListener {
items[slotIndex] = item.id
adapter.onSave()
adapter.notifyItemChanged(slotIndex)
dialog?.dismiss()
}
}
}
private inner class NavItemAdapter(
val items: MutableList<Int>,
val onSave: () -> Unit
) : RecyclerView.Adapter<NavItemAdapter.VH>() {
inner class VH(view: View) : RecyclerView.ViewHolder(view) {
val ivNavIcon: ImageView = view.findViewById(R.id.ivNavIcon)
val tvNavLabel: TextView = view.findViewById(R.id.tvNavLabel)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_nav_slot, parent, false)
val itemWidth = if (parent.measuredWidth > 0) parent.measuredWidth / items.size
else RecyclerView.LayoutParams.WRAP_CONTENT
view.layoutParams = RecyclerView.LayoutParams(itemWidth, RecyclerView.LayoutParams.WRAP_CONTENT)
return VH(view)
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: VH, position: Int) {
val def = NavCustomization.ALL_SWAPPABLE.find { it.id == items[position] } ?: return
holder.ivNavIcon.setImageResource(def.iconRes)
holder.tvNavLabel.setText(def.titleRes)
holder.itemView.setOnClickListener { showItemPicker(items, holder.adapterPosition, this) }
}
}
override fun onResume() {
super.onResume()
requireActivity().title = getString(R.string.settings_appearance)

View File

@@ -19,6 +19,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
@@ -590,10 +591,10 @@ class TransferFragment : Fragment() {
if (isSrcBml && isDestMib && currency == "USD") {
val hasBmlContact = allContacts.any { it.benefCategoryId == "BML" && it.benefAccount == resolvedAccountNumber }
if (!hasBmlContact) {
AlertDialog.Builder(requireContext())
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.transfer_bml_contact_required_title)
.setMessage(R.string.transfer_bml_contact_required_msg)
.setPositiveButton("OK", null)
.setPositiveButton(R.string.close, null)
.show()
return
}
@@ -637,7 +638,7 @@ class TransferFragment : Fragment() {
}
}
val dialogBuilder = AlertDialog.Builder(requireContext())
val dialogBuilder = MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.transfer)
.setPositiveButton(R.string.transfer_confirm) { _, _ -> doTransfer() }
.setNegativeButton(android.R.string.cancel, null)

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorOnSurfaceVariant"
android:pathData="M9,3h2v2H9V3zm4,0h2v2h-2V3zM9,7h2v2H9V7zm4,0h2v2h-2V7zM9,11h2v2H9v-2zm4,0h2v2h-2v-2zM9,15h2v2H9v-2zm4,0h2v2h-2v-2zM9,19h2v2H9v-2zm4,0h2v2h-2v-2z"/>
</vector>

View File

@@ -172,33 +172,52 @@
</androidx.core.widget.NestedScrollView>
<!-- Action buttons fixed at bottom -->
<!-- Quick actions fixed at bottom -->
<LinearLayout
android:id="@+id/buttonBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingTop="8dp"
android:paddingBottom="16dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnTransfer"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:text="@string/transfer" />
android:text="@string/dashboard_quick_actions"
android:textAppearance="?attr/textAppearanceLabelMedium"
android:textColor="?attr/colorOnSurfaceVariant"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnPayMvQr"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:text="@string/pay_mv_qr" />
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnQuickAction1"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
app:iconGravity="textStart"
app:iconSize="18dp"
app:iconPadding="8dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnQuickAction2"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
app:iconGravity="textStart"
app:iconSize="18dp"
app:iconPadding="8dp" />
</LinearLayout>
</LinearLayout>

View File

@@ -45,6 +45,69 @@
</com.google.android.material.button.MaterialButtonToggleGroup>
<!-- Quick actions (always active) -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dashboard_quick_actions"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="8dp"
android:layout_marginBottom="12dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvQuickActions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
android:layout_marginBottom="16dp" />
<!-- Bottom bar shortcuts — shown only when bottom nav is active -->
<LinearLayout
android:id="@+id/sectionBottomBarShortcuts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_bottom_bar_shortcuts"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="8dp"
android:layout_marginBottom="12dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvNavSlots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:overScrollMode="never" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_bottom_bar_show_labels"
android:textAppearance="?attr/textAppearanceTitleMedium" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchShowLabels"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:paddingVertical="12dp"
android:paddingHorizontal="8dp"
android:background="?attr/selectableItemBackground">
<ImageView
android:id="@+id/ivNavIcon"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginBottom="6dp" />
<TextView
android:id="@+id/tvNavLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceLabelSmall"
android:textColor="?attr/colorOnSurface"
android:gravity="center"
android:maxLines="1"
android:ellipsize="end" />
</LinearLayout>

View File

@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.navigation.NavigationView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navMoreView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/more_nav_menu" />
android:layout_height="wrap_content" />

View File

@@ -79,6 +79,7 @@
<string name="work_in_progress">ތައްޔާރުވަމުން ދަނީ</string>
<!-- Dashboard -->
<string name="dashboard_quick_actions">ހަލުވި ހަރަކާތްތައް</string>
<string name="balance_mvr">ޖުމްލަ MVR</string>
<string name="balance_usd">ޖުމްލަ USD</string>
<string name="card_support_wip">ކާޑް ސަޕޯޓް</string>
@@ -86,6 +87,11 @@
<string name="pay_mv_qr">PayMV QR</string>
<!-- Settings -->
<string name="settings_bottom_bar_shortcuts">ތިރި ބާ ޝޯޓްކަޓްތައް</string>
<string name="settings_bottom_bar_select">ބަޓަން ހިޔާރު ކުރޭ</string>
<string name="settings_bottom_bar_slot_1">ތަން 1</string>
<string name="settings_bottom_bar_slot_2">ތަން 2</string>
<string name="settings_bottom_bar_slot_3">ތަން 3</string>
<string name="theme">ތީމް</string>
<string name="theme_system">ސިސްޓަމް</string>
<string name="theme_light">ލައިޓް</string>
@@ -113,6 +119,8 @@
<string name="close">ބަންދު</string>
<string name="cancel">ކެންސަލް</string>
<string name="settings_bottom_bar_show_labels">ބޮޓަމް ބާ ލޭބަލް އަބަދުވެސް ދެއްކުން</string>
<!-- Home -->
<string name="accounts">އެކައުންޓްތައް</string>
<string name="available_balance">ލިބެން ހުރި ބެލެންސް</string>

View File

@@ -92,6 +92,7 @@
<!-- Dashboard -->
<string name="dashboard_pending_finances">Pending Finances</string>
<string name="dashboard_quick_actions">Quick Actions</string>
<string name="balance_mvr">MVR Total</string>
<string name="balance_usd">USD Total</string>
<string name="card_support_wip">Card Support</string>
@@ -136,6 +137,12 @@
<string name="settings_nav_drawer">Drawer</string>
<string name="settings_nav_bottom">Bottom Bar</string>
<string name="settings_appearance">Appearance</string>
<string name="settings_bottom_bar_shortcuts">Bottom Bar Shortcuts</string>
<string name="settings_bottom_bar_show_labels">Always show bottom bar labels</string>
<string name="settings_bottom_bar_select">Choose button</string>
<string name="settings_bottom_bar_slot_1">Slot 1</string>
<string name="settings_bottom_bar_slot_2">Slot 2</string>
<string name="settings_bottom_bar_slot_3">Slot 3</string>
<string name="settings_privacy_security">Privacy &amp; Security</string>
<string name="settings_storage">Storage</string>
<string name="settings_logins">Logins</string>