redegsigned contacts page: removed delete and edit buttons from contacts list, made a new contacts sheet and moved buttons there
Auto Tag on Version Change / check-version (push) Successful in 4s

This commit is contained in:
2026-09-25 06:35:39 +05:00
parent 369c6e4a37
commit 5210e6207f
12 changed files with 640 additions and 55 deletions
@@ -0,0 +1,171 @@
package sh.sar.basedbank.ui.home
import android.content.ClipData
import android.content.ClipboardManager
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.os.bundleOf
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import sh.sar.basedbank.R
import sh.sar.basedbank.databinding.SheetContactDetailsBinding
import sh.sar.basedbank.util.ContactDisplay
import sh.sar.basedbank.util.ContactImageCache
import sh.sar.basedbank.util.ContactListParser
import sh.sar.basedbank.util.CredentialStore
import sh.sar.basedbank.util.TransferNetwork
/** Contact details drawer shown when a row in [ContactsFragment] is tapped. */
class ContactDetailsSheetFragment : BottomSheetDialogFragment() {
private var _binding: SheetContactDetailsBinding? = null
private val binding get() = _binding!!
private val viewModel: HomeViewModel by activityViewModels()
private val contactsFragment get() = parentFragment as? ContactsFragment
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = SheetContactDetailsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val args = requireArguments()
val contactId = args.getString(ARG_CONTACT_ID)
val profileId = args.getString(ARG_PROFILE_ID)
viewModel.contacts.observe(viewLifecycleOwner) { contacts ->
val contact = contacts.firstOrNull { it.benefNo == contactId && it.profileId == profileId }
?.let { ContactListParser.from(it) }
if (contact == null) dismissAllowingStateLoss() else bind(contact)
}
}
private fun bind(contact: ContactDisplay) {
val sizePx = resources.getDimensionPixelSize(android.R.dimen.app_icon_size).coerceAtLeast(96) * 2
val photo = contact.imageHash?.let { ContactImageCache.load(requireContext(), it) }
binding.ivPhoto.setImageBitmap(photo ?: contactInitialsBitmap(contact.name, contact.bankColor, sizePx))
binding.tvName.text = contact.name
binding.actionTransfer.visibility = if (contact.canTransfer) View.VISIBLE else View.GONE
binding.actionEdit.visibility = if (contact.canEdit) View.VISIBLE else View.GONE
binding.actionDelete.visibility = if (contact.canDelete) View.VISIBLE else View.GONE
binding.actionRow.visibility =
if (contact.canTransfer || contact.canEdit || contact.canDelete) View.VISIBLE else View.GONE
binding.btnTransfer.setOnClickListener {
val parent = contactsFragment
dismiss()
parent?.openTransfer(contact)
}
binding.btnEdit.setOnClickListener {
Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show()
}
binding.btnDelete.setOnClickListener {
val parent = contactsFragment
dismiss()
parent?.confirmDelete(contact)
}
binding.tvAccount.text = contact.accountNumber
val copy = View.OnClickListener { copyAccount(contact.accountNumber) }
binding.rowAccount.setOnClickListener(copy)
binding.btnCopyAccount.setOnClickListener(copy)
val showRealName = contact.realName.isNotBlank() && contact.realName != contact.name
binding.tvRealName.text = contact.realName
binding.rowRealName.visibility = if (showRealName) View.VISIBLE else View.GONE
binding.tvCurrency.text = contact.currency ?: ""
binding.rowCurrency.visibility = if (contact.currency != null) View.VISIBLE else View.GONE
binding.tvBank.text = contact.bankName ?: ""
binding.rowBank.visibility = if (contact.bankName != null) View.VISIBLE else View.GONE
val bankLogo = bankLogoRes(contact)
if (bankLogo != null) {
binding.ivBankIcon.setImageResource(bankLogo)
binding.ivBankIcon.imageTintList = null
}
binding.sourceSection.visibility = View.GONE
viewLifecycleOwner.lifecycleScope.launch {
val appContext = requireContext().applicationContext
val source = withContext(Dispatchers.IO) { sourceProfile(appContext, contact) }
val b = _binding ?: return@launch
if (source == null) return@launch
b.ivSourceIcon.setImageResource(source.logoRes)
b.tvSource.text = source.name
b.tvSourceBank.setText(source.bankNameRes)
b.sourceSection.visibility = View.VISIBLE
}
}
/** Logo of the contact's bank; null when we have no logo for it (only MIB and BML for now). */
private fun bankLogoRes(contact: ContactDisplay): Int? = when {
contact.network == TransferNetwork.MIB -> R.drawable.mib_logo
contact.accountNumber.matches(Regex("^7\\d{12}$")) -> R.drawable.bml_logo_vector
contact.accountNumber.matches(Regex("^9\\d{16}$")) -> R.drawable.mib_logo
else -> null
}
private data class SourceProfile(val logoRes: Int, val bankNameRes: Int, val name: String)
/** Which of the user's own bank profiles this contact is saved under. */
private fun sourceProfile(context: Context, contact: ContactDisplay): SourceProfile? {
val store = CredentialStore(context)
return when (contact.network) {
TransferNetwork.BML -> {
// BML contacts carry either the loginId or a BML profileId
val loginId = store.getBmlLoginIds().firstOrNull { loginId ->
loginId == contact.profileId ||
store.loadBmlProfiles(loginId).any { it.profileId == contact.profileId }
} ?: return null
val profile = store.loadBmlProfiles(loginId).firstOrNull { it.profileId == contact.profileId }
val fullName = store.loadBmlUserProfile(loginId)?.fullName?.takeIf { it.isNotBlank() }
val name = (if (profile != null && profile.profileType == "business") profile.name else fullName ?: profile?.name)
?: return null
SourceProfile(R.drawable.bml_logo_vector, R.string.bml_name, name)
}
TransferNetwork.FAHIPAY -> {
val name = store.getFahipayLoginIds().firstNotNullOfOrNull { loginId ->
store.loadFahipayUserProfile(loginId)?.fullName?.takeIf { it.isNotBlank() }
} ?: return null
SourceProfile(R.drawable.fahipay_logo, R.string.fahipay_name, name)
}
else -> {
val profile = store.getMibLoginIds().firstNotNullOfOrNull { loginId ->
store.loadMibProfiles(loginId).firstOrNull { it.profileId == contact.profileId }
} ?: return null
SourceProfile(R.drawable.mib_logo, R.string.mib_name, profile.name)
}
}
}
private fun copyAccount(account: String) {
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText("account", account))
Toast.makeText(requireContext(), R.string.contact_account_copied, Toast.LENGTH_SHORT).show()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
private const val ARG_CONTACT_ID = "contact_id"
private const val ARG_PROFILE_ID = "profile_id"
fun newInstance(contact: ContactDisplay) = ContactDetailsSheetFragment().apply {
arguments = bundleOf(ARG_CONTACT_ID to contact.id, ARG_PROFILE_ID to contact.profileId)
}
}
}
@@ -11,14 +11,13 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import sh.sar.basedbank.R
import sh.sar.basedbank.databinding.ItemContactBinding import sh.sar.basedbank.databinding.ItemContactBinding
import sh.sar.basedbank.util.ContactDisplay import sh.sar.basedbank.util.ContactDisplay
class ContactsAdapter( class ContactsAdapter(
private val imageCache: MutableMap<String, Bitmap>, private val imageCache: MutableMap<String, Bitmap>,
private val onImageNeeded: (hash: String) -> Unit, private val onImageNeeded: (hash: String) -> Unit,
private val onDeleteClick: (ContactDisplay) -> Unit, private val onContactClick: (ContactDisplay) -> Unit,
private val onTransferClick: (ContactDisplay) -> Unit private val onTransferClick: (ContactDisplay) -> Unit
) : RecyclerView.Adapter<ContactsAdapter.ViewHolder>() { ) : RecyclerView.Adapter<ContactsAdapter.ViewHolder>() {
@@ -66,12 +65,9 @@ class ContactsAdapter(
val pos = holder.bindingAdapterPosition val pos = holder.bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION) onTransferClick(displayed[pos]) if (pos != RecyclerView.NO_POSITION) onTransferClick(displayed[pos])
} }
binding.btnEditContact.setOnClickListener { binding.root.setOnClickListener {
Toast.makeText(it.context, R.string.work_in_progress, Toast.LENGTH_SHORT).show()
}
binding.btnDeleteContact.setOnClickListener {
val pos = holder.bindingAdapterPosition val pos = holder.bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION) onDeleteClick(displayed[pos]) if (pos != RecyclerView.NO_POSITION) onContactClick(displayed[pos])
} }
binding.root.setOnLongClickListener { binding.root.setOnLongClickListener {
val pos = holder.bindingAdapterPosition val pos = holder.bindingAdapterPosition
@@ -107,24 +103,22 @@ class ContactsAdapter(
if (contact.detail != null) android.view.View.VISIBLE else android.view.View.GONE if (contact.detail != null) android.view.View.VISIBLE else android.view.View.GONE
binding.btnTransferContact.visibility = binding.btnTransferContact.visibility =
if (contact.canTransfer) android.view.View.VISIBLE else android.view.View.GONE if (contact.canTransfer) android.view.View.VISIBLE else android.view.View.GONE
binding.btnEditContact.visibility =
if (contact.canEdit) android.view.View.VISIBLE else android.view.View.GONE
binding.btnDeleteContact.visibility =
if (contact.canDelete) android.view.View.VISIBLE else android.view.View.GONE
if (photo != null) { if (photo != null) {
binding.ivContactPhoto.setImageBitmap(photo) binding.ivContactPhoto.setImageBitmap(photo)
} else { } else {
binding.ivContactPhoto.setImageBitmap(
makeInitialsBitmap(contact.name, contact.bankColor)
)
}
}
private fun makeInitialsBitmap(name: String, colorHex: String): Bitmap {
val sizePx = binding.ivContactPhoto.context.resources val sizePx = binding.ivContactPhoto.context.resources
.getDimensionPixelSize(android.R.dimen.app_icon_size) .getDimensionPixelSize(android.R.dimen.app_icon_size)
.coerceAtLeast(96) .coerceAtLeast(96)
binding.ivContactPhoto.setImageBitmap(
contactInitialsBitmap(contact.name, contact.bankColor, sizePx)
)
}
}
}
}
internal fun contactInitialsBitmap(name: String, colorHex: String, sizePx: Int): Bitmap {
val bgColor = try { Color.parseColor(colorHex) } catch (e: Exception) { Color.GRAY } val bgColor = try { Color.parseColor(colorHex) } catch (e: Exception) { Color.GRAY }
val bm = Bitmap.createBitmap(sizePx, sizePx, Bitmap.Config.ARGB_8888) val bm = Bitmap.createBitmap(sizePx, sizePx, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bm) val canvas = Canvas(bm)
@@ -139,5 +133,3 @@ class ContactsAdapter(
canvas.drawText(letter, sizePx / 2f, sizePx / 2f - (metrics.ascent + metrics.descent) / 2f, paint) canvas.drawText(letter, sizePx / 2f, sizePx / 2f - (metrics.ascent + metrics.descent) / 2f, paint)
return bm return bm
} }
}
}
@@ -2,13 +2,20 @@ package sh.sar.basedbank.ui.home
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Typeface
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan
import android.os.Bundle import android.os.Bundle
import android.util.Base64 import android.util.Base64
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
@@ -59,7 +66,7 @@ class ContactsFragment : Fragment() {
ContactsAdapter( ContactsAdapter(
imageCache = sharedImageCache, imageCache = sharedImageCache,
onImageNeeded = { hash -> fetchImage(hash) }, onImageNeeded = { hash -> fetchImage(hash) },
onDeleteClick = { contact -> confirmDelete(contact) }, onContactClick = { contact -> showDetails(contact) },
onTransferClick = { contact -> openTransfer(contact) } onTransferClick = { contact -> openTransfer(contact) }
).also { a -> ).also { a ->
a.setFilter(page.categoryId, currentSearch) a.setFilter(page.categoryId, currentSearch)
@@ -170,7 +177,12 @@ class ContactsFragment : Fragment() {
binding.viewPager.setCurrentItem(savedPosition.coerceIn(0, pages.size - 1), false) binding.viewPager.setCurrentItem(savedPosition.coerceIn(0, pages.size - 1), false)
} }
private fun openTransfer(contact: ContactDisplay) { private fun showDetails(contact: ContactDisplay) {
if (childFragmentManager.findFragmentByTag("contact_details") != null) return
ContactDetailsSheetFragment.newInstance(contact).show(childFragmentManager, "contact_details")
}
internal fun openTransfer(contact: ContactDisplay) {
val fragment = TransferFragment.newInstance( val fragment = TransferFragment.newInstance(
accountNumber = contact.accountNumber, accountNumber = contact.accountNumber,
displayName = contact.name, displayName = contact.name,
@@ -181,10 +193,26 @@ class ContactsFragment : Fragment() {
(requireActivity() as HomeActivity).showWithBackStack(fragment) (requireActivity() as HomeActivity).showWithBackStack(fragment)
} }
private fun confirmDelete(contact: ContactDisplay) { internal fun confirmDelete(contact: ContactDisplay) {
MaterialAlertDialogBuilder(requireContext()) val ctx = requireContext()
val errorColor = MaterialColors.getColor(binding.root, com.google.android.material.R.attr.colorError)
val icon = ContextCompat.getDrawable(ctx, R.drawable.ic_delete)?.mutate()?.apply { setTint(errorColor) }
val prompt = getString(R.string.contact_delete_message, contact.name)
val message = SpannableStringBuilder(prompt).apply {
val start = prompt.indexOf(contact.name)
if (start >= 0) setSpan(StyleSpan(Typeface.BOLD), start, start + contact.name.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append("\n").append(contact.accountNumber)
append("\n\n")
val warnStart = length
append(getString(R.string.contact_delete_warning))
setSpan(ForegroundColorSpan(errorColor), warnStart, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
MaterialAlertDialogBuilder(ctx, R.style.ThemeOverlay_BasedBank_DestructiveDialog)
.setIcon(icon)
.setTitle(R.string.contact_delete_title) .setTitle(R.string.contact_delete_title)
.setMessage(getString(R.string.contact_delete_message, contact.name)) .setMessage(message)
.setPositiveButton(R.string.contact_delete) { _, _ -> deleteContact(contact) } .setPositiveButton(R.string.contact_delete) { _, _ -> deleteContact(contact) }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()
@@ -13,6 +13,8 @@ data class ContactDisplay(
val network: TransferNetwork, val network: TransferNetwork,
val bankColor: String, val bankColor: String,
val detail: String?, // pre-formatted "Name · CCY · Bank" line; null = hide row val detail: String?, // pre-formatted "Name · CCY · Bank" line; null = hide row
val bankName: String?, // shown in the contact details sheet; null = hide row
val currency: String?, // shown in the contact details sheet; null = hide row
val imageHash: String?, val imageHash: String?,
val profileId: String, // MIB profile ID or BML loginTag (needed by ContactManager) val profileId: String, // MIB profile ID or BML loginTag (needed by ContactManager)
val transferSubtitle: String, // "Bank · accountNumber" shown in transfer screen val transferSubtitle: String, // "Bank · accountNumber" shown in transfer screen
@@ -15,6 +15,8 @@ object BmlContactParser {
network = TransferNetwork.BML, network = TransferNetwork.BML,
bankColor = contact.bankColor, bankColor = contact.bankColor,
detail = "${contact.benefName} · ${contact.transferCyDesc} · ${contact.benefBankName}", detail = "${contact.benefName} · ${contact.transferCyDesc} · ${contact.benefBankName}",
bankName = contact.benefBankName.takeIf { it.isNotBlank() },
currency = contact.transferCyDesc.takeIf { it.isNotBlank() },
imageHash = contact.customerImgHash, imageHash = contact.customerImgHash,
profileId = contact.profileId, profileId = contact.profileId,
transferSubtitle = "${contact.benefBankName} · ${contact.benefAccount}", transferSubtitle = "${contact.benefBankName} · ${contact.benefAccount}",
@@ -15,6 +15,8 @@ object FahipayContactParser {
network = TransferNetwork.FAHIPAY, network = TransferNetwork.FAHIPAY,
bankColor = contact.bankColor, bankColor = contact.bankColor,
detail = null, // Fahipay contacts show no detail line detail = null, // Fahipay contacts show no detail line
bankName = contact.benefBankName.takeIf { it.isNotBlank() },
currency = contact.transferCyDesc.takeIf { it.isNotBlank() },
imageHash = contact.customerImgHash, imageHash = contact.customerImgHash,
profileId = contact.profileId, profileId = contact.profileId,
transferSubtitle = contact.benefAccount, transferSubtitle = contact.benefAccount,
@@ -21,6 +21,8 @@ object MibContactParser {
network = network, network = network,
bankColor = contact.bankColor, bankColor = contact.bankColor,
detail = "${contact.benefName} · ${contact.transferCyDesc} · ${contact.benefBankName}", detail = "${contact.benefName} · ${contact.transferCyDesc} · ${contact.benefBankName}",
bankName = contact.benefBankName.takeIf { it.isNotBlank() },
currency = contact.transferCyDesc.takeIf { it.isNotBlank() },
imageHash = contact.customerImgHash, imageHash = contact.customerImgHash,
profileId = contact.profileId, profileId = contact.profileId,
transferSubtitle = "${contact.benefBankName} · ${contact.benefAccount}", transferSubtitle = "${contact.benefBankName} · ${contact.benefAccount}",
+10
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="@android:color/black"
android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.61 -4.7,-4.2z" />
</vector>
+1 -19
View File
@@ -4,6 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
android:paddingVertical="10dp"> android:paddingVertical="10dp">
@@ -26,25 +27,6 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"> app:layout_constraintBottom_toBottomOf="parent">
<ImageButton
android:id="@+id/btnEditContact"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_edit"
android:padding="6dp"
android:contentDescription="@string/contact_edit" />
<ImageButton
android:id="@+id/btnDeleteContact"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_delete"
android:padding="6dp"
android:tint="?attr/colorError"
android:contentDescription="@string/contact_delete" />
<ImageButton <ImageButton
android:id="@+id/btnTransferContact" android:id="@+id/btnTransferContact"
android:layout_width="36dp" android:layout_width="36dp"
@@ -0,0 +1,378 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="24dp">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/ivPhoto"
android:layout_width="112dp"
android:layout_height="112dp"
android:layout_gravity="center_horizontal"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/ShapeAppearance.Circle" />
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_horizontal"
android:paddingHorizontal="24dp"
android:textAppearance="?attr/textAppearanceHeadlineSmall"
android:textColor="?attr/colorOnSurface" />
<LinearLayout
android:id="@+id/actionRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
<LinearLayout
android:id="@+id/actionTransfer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnTransfer"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="56dp"
android:layout_height="56dp"
android:contentDescription="@string/transfer"
app:icon="@drawable/ic_send"
app:iconSize="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/transfer"
android:textAppearance="?attr/textAppearanceLabelMedium"
android:textColor="?attr/colorOnSurface" />
</LinearLayout>
<LinearLayout
android:id="@+id/actionEdit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnEdit"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="56dp"
android:layout_height="56dp"
android:contentDescription="@string/contact_edit"
app:icon="@drawable/ic_edit"
app:iconSize="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/contact_edit"
android:textAppearance="?attr/textAppearanceLabelMedium"
android:textColor="?attr/colorOnSurface" />
</LinearLayout>
<LinearLayout
android:id="@+id/actionDelete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnDelete"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="56dp"
android:layout_height="56dp"
android:contentDescription="@string/contact_delete"
app:backgroundTint="?attr/colorErrorContainer"
app:icon="@drawable/ic_delete"
app:iconSize="24dp"
app:iconTint="?attr/colorOnErrorContainer" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/contact_delete"
android:textAppearance="?attr/textAppearanceLabelMedium"
android:textColor="?attr/colorError" />
</LinearLayout>
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="8dp" />
<LinearLayout
android:id="@+id/rowAccount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="64dp"
android:orientation="horizontal"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_manage_card"
app:tint="?attr/colorOnSurfaceVariant" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvAccount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface"
android:textIsSelectable="false" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/contact_account_number"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>
<ImageButton
android:id="@+id/btnCopyAccount"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/contact_copy_account"
android:padding="8dp"
android:src="@drawable/ic_copy"
app:tint="?attr/colorOnSurfaceVariant" />
</LinearLayout>
<LinearLayout
android:id="@+id/rowRealName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="64dp"
android:orientation="horizontal"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_contacts"
app:tint="?attr/colorOnSurfaceVariant" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvRealName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface"
android:textIsSelectable="false" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/contact_account_name"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/rowCurrency"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="64dp"
android:orientation="horizontal"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_currency"
app:tint="?attr/colorOnSurfaceVariant" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvCurrency"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface"
android:textIsSelectable="false" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/contact_currency"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/rowBank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="64dp"
android:orientation="horizontal"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp">
<ImageView
android:id="@+id/ivBankIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_nav_finances"
app:tint="?attr/colorOnSurfaceVariant" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvBank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface"
android:textIsSelectable="false" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/contact_bank"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/sourceSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="56dp"
android:orientation="horizontal"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp">
<ImageView
android:id="@+id/ivSourceIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:importantForAccessibility="no" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvSource"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textColor="?attr/colorOnSurfaceVariant" />
<TextView
android:id="@+id/tvSourceBank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
+6
View File
@@ -332,6 +332,12 @@
<string name="contact_delete_message">Remove %s from your contacts?</string> <string name="contact_delete_message">Remove %s from your contacts?</string>
<string name="contact_deleted">Contact deleted</string> <string name="contact_deleted">Contact deleted</string>
<string name="contact_delete_failed">Could not delete contact</string> <string name="contact_delete_failed">Could not delete contact</string>
<string name="contact_account_number">Account number</string>
<string name="contact_account_name">Account name</string>
<string name="contact_bank">Bank</string>
<string name="contact_delete_warning">This can\'t be undone.</string>
<string name="contact_copy_account">Copy account number</string>
<string name="contact_account_copied">Account number copied</string>
<!-- Financing --> <!-- Financing -->
<string name="financing_empty">No financing deals found</string> <string name="financing_empty">No financing deals found</string>
+10
View File
@@ -1,6 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<style name="ShapeAppearance.Circle" parent="ShapeAppearance.Material3.Corner.Full" /> <style name="ShapeAppearance.Circle" parent="ShapeAppearance.Material3.Corner.Full" />
<!-- Destructive confirmation dialog: centered icon/title, filled red confirm button -->
<style name="ThemeOverlay.BasedBank.DestructiveDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog.Centered">
<item name="buttonBarPositiveButtonStyle">@style/Widget.BasedBank.DestructiveDialogButton</item>
</style>
<style name="Widget.BasedBank.DestructiveDialogButton" parent="Widget.Material3.Button">
<item name="backgroundTint">?attr/colorError</item>
<item name="android:textColor">?attr/colorOnError</item>
</style>
<style name="Theme.BasedBank" parent="Theme.Material3.DayNight.NoActionBar"> <style name="Theme.BasedBank" parent="Theme.Material3.DayNight.NoActionBar">
<item name="colorPrimary">@color/seed_primary</item> <item name="colorPrimary">@color/seed_primary</item>
<item name="colorSecondary">@color/seed_secondary</item> <item name="colorSecondary">@color/seed_secondary</item>