delete and transfer contacts
This commit is contained in:
@@ -311,6 +311,22 @@ class BmlLoginFlow {
|
||||
return parseContacts(json)
|
||||
}
|
||||
|
||||
fun deleteContact(session: BmlSession, contactId: String): Boolean {
|
||||
val body = """{"_method":"delete"}""".toRequestBody("application/json".toMediaType())
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_URL/api/mobile/contacts/$contactId")
|
||||
.post(body)
|
||||
.header("Authorization", "Bearer ${session.accessToken}")
|
||||
.header("User-Agent", APP_USER_AGENT)
|
||||
.header("x-app-version", APP_VERSION)
|
||||
.header("accept", "application/json")
|
||||
.build()
|
||||
return apiClient.newCall(request).execute().use { response ->
|
||||
val bodyStr = response.body?.string() ?: return@use false
|
||||
try { JSONObject(bodyStr).optBoolean("success") } catch (_: Exception) { false }
|
||||
}
|
||||
}
|
||||
|
||||
private fun apiRequest(session: BmlSession, url: String) =
|
||||
Request.Builder().url(url)
|
||||
.header("Authorization", "Bearer ${session.accessToken}")
|
||||
|
||||
@@ -182,6 +182,19 @@ class MibContactsClient {
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteContact(session: MibSession, benefNo: String): Boolean {
|
||||
val body = FormBody.Builder().add("benefNo", benefNo).build()
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_WV_URL/ajaxBeneficiary/deleteBeneficiary")
|
||||
.post(body)
|
||||
.withSessionHeaders(session)
|
||||
.build()
|
||||
return client.newCall(request).execute().use { response ->
|
||||
val bodyStr = response.body?.string() ?: return@use false
|
||||
try { JSONObject(bodyStr).optBoolean("success") } catch (_: Exception) { false }
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchProfileImageBase64(session: MibSession, imageHash: String): String? {
|
||||
val body = FormBody.Builder()
|
||||
.add("imageHash", imageHash)
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
package sh.sar.basedbank.ui.home
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import sh.sar.basedbank.R
|
||||
import sh.sar.basedbank.api.mib.MibBeneficiary
|
||||
import sh.sar.basedbank.databinding.ItemContactBinding
|
||||
|
||||
class ContactsAdapter(
|
||||
private val onImageNeeded: (hash: String) -> Unit
|
||||
private val onImageNeeded: (hash: String) -> Unit,
|
||||
private val onDeleteClick: (MibBeneficiary) -> Unit,
|
||||
private val onTransferClick: (MibBeneficiary) -> Unit
|
||||
) : RecyclerView.Adapter<ContactsAdapter.ViewHolder>() {
|
||||
|
||||
private var allContacts: List<MibBeneficiary> = emptyList()
|
||||
private var displayed: List<MibBeneficiary> = emptyList()
|
||||
private val imageCache = mutableMapOf<String, Bitmap>()
|
||||
private val expandedPositions = mutableSetOf<Int>()
|
||||
|
||||
private var activeCategoryId: String? = null
|
||||
private var searchQuery: String = ""
|
||||
|
||||
fun updateContacts(contacts: List<MibBeneficiary>) {
|
||||
allContacts = contacts
|
||||
expandedPositions.clear()
|
||||
applyFilter()
|
||||
}
|
||||
|
||||
@@ -36,6 +46,7 @@ class ContactsAdapter(
|
||||
fun setFilter(categoryId: String?, query: String) {
|
||||
activeCategoryId = categoryId
|
||||
searchQuery = query
|
||||
expandedPositions.clear()
|
||||
applyFilter()
|
||||
}
|
||||
|
||||
@@ -61,15 +72,42 @@ class ContactsAdapter(
|
||||
val cachedImage = contact.customerImgHash?.let { hash ->
|
||||
imageCache[hash] ?: run { onImageNeeded(hash); null }
|
||||
}
|
||||
holder.bind(contact, cachedImage)
|
||||
holder.bind(contact, cachedImage, position in expandedPositions)
|
||||
|
||||
holder.binding.root.setOnClickListener {
|
||||
val pos = holder.bindingAdapterPosition
|
||||
if (pos == RecyclerView.NO_POSITION) return@setOnClickListener
|
||||
if (pos in expandedPositions) expandedPositions.remove(pos) else expandedPositions.add(pos)
|
||||
notifyItemChanged(pos)
|
||||
}
|
||||
|
||||
holder.binding.root.setOnLongClickListener {
|
||||
val ctx = it.context
|
||||
val clipboard = ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("account", contact.benefAccount))
|
||||
Toast.makeText(ctx, contact.benefAccount, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
|
||||
holder.binding.btnTransferContact.setOnClickListener {
|
||||
onTransferClick(contact)
|
||||
}
|
||||
|
||||
holder.binding.btnEditContact.setOnClickListener {
|
||||
Toast.makeText(it.context, R.string.work_in_progress, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
holder.binding.btnDeleteContact.setOnClickListener {
|
||||
onDeleteClick(contact)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = displayed.size
|
||||
|
||||
inner class ViewHolder(private val binding: ItemContactBinding) :
|
||||
inner class ViewHolder(val binding: ItemContactBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(contact: MibBeneficiary, photo: Bitmap?) {
|
||||
fun bind(contact: MibBeneficiary, photo: Bitmap?, expanded: Boolean) {
|
||||
binding.tvContactName.text = contact.benefNickName
|
||||
binding.tvContactBank.text = contact.benefBankName
|
||||
binding.tvContactAccount.text = "${contact.benefAccount} · ${contact.transferCyDesc}"
|
||||
@@ -81,6 +119,10 @@ class ContactsAdapter(
|
||||
makeInitialsBitmap(contact.benefNickName, contact.bankColor)
|
||||
)
|
||||
}
|
||||
|
||||
val vis = if (expanded) View.VISIBLE else View.GONE
|
||||
binding.dividerExpand.visibility = vis
|
||||
binding.expandedSection.visibility = vis
|
||||
}
|
||||
|
||||
private fun makeInitialsBitmap(name: String, colorHex: String): Bitmap {
|
||||
@@ -88,22 +130,17 @@ class ContactsAdapter(
|
||||
.getDimensionPixelSize(android.R.dimen.app_icon_size)
|
||||
.coerceAtLeast(96)
|
||||
val bgColor = try { Color.parseColor(colorHex) } catch (e: Exception) { Color.GRAY }
|
||||
|
||||
val bm = Bitmap.createBitmap(sizePx, sizePx, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bm)
|
||||
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
|
||||
paint.color = bgColor
|
||||
canvas.drawCircle(sizePx / 2f, sizePx / 2f, sizePx / 2f, paint)
|
||||
|
||||
paint.color = Color.WHITE
|
||||
paint.textSize = sizePx * 0.42f
|
||||
paint.textAlign = Paint.Align.CENTER
|
||||
val letter = name.firstOrNull()?.uppercaseChar()?.toString() ?: "?"
|
||||
val metrics = paint.fontMetrics
|
||||
val textY = sizePx / 2f - (metrics.ascent + metrics.descent) / 2f
|
||||
canvas.drawText(letter, sizePx / 2f, textY, paint)
|
||||
|
||||
canvas.drawText(letter, sizePx / 2f, sizePx / 2f - (metrics.ascent + metrics.descent) / 2f, paint)
|
||||
return bm
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import android.util.Base64
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
@@ -17,9 +19,12 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import sh.sar.basedbank.BasedBankApp
|
||||
import sh.sar.basedbank.R
|
||||
import sh.sar.basedbank.api.bml.BmlLoginFlow
|
||||
import sh.sar.basedbank.api.mib.MibBeneficiary
|
||||
import sh.sar.basedbank.api.mib.MibBeneficiaryCategory
|
||||
import sh.sar.basedbank.api.mib.MibContactsClient
|
||||
import sh.sar.basedbank.databinding.FragmentContactsBinding
|
||||
import sh.sar.basedbank.util.ContactsCache
|
||||
|
||||
class ContactsFragment : Fragment() {
|
||||
|
||||
@@ -29,10 +34,11 @@ class ContactsFragment : Fragment() {
|
||||
private lateinit var adapter: ContactsAdapter
|
||||
|
||||
private val pendingHashes = mutableSetOf<String>()
|
||||
private val session get() = (requireActivity().application as BasedBankApp).mibSession
|
||||
private val app get() = requireActivity().application as BasedBankApp
|
||||
private val session get() = app.mibSession
|
||||
|
||||
private var categories: List<MibBeneficiaryCategory> = emptyList()
|
||||
private var activeCategoryId: String? = null // null = All
|
||||
private var activeCategoryId: String? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FragmentContactsBinding.inflate(inflater, container, false)
|
||||
@@ -40,7 +46,20 @@ class ContactsFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
adapter = ContactsAdapter { hash -> fetchImage(hash) }
|
||||
adapter = ContactsAdapter(
|
||||
onImageNeeded = { hash -> fetchImage(hash) },
|
||||
onDeleteClick = { contact -> confirmDelete(contact) },
|
||||
onTransferClick = { contact ->
|
||||
val fragment = TransferFragment.newInstance(
|
||||
accountNumber = contact.benefAccount,
|
||||
displayName = contact.benefNickName,
|
||||
subtitle = "${contact.benefBankName} · ${contact.benefAccount}",
|
||||
colorHex = contact.bankColor,
|
||||
imageHash = contact.customerImgHash
|
||||
)
|
||||
(requireActivity() as HomeActivity).showWithBackStack(fragment)
|
||||
}
|
||||
)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
@@ -97,6 +116,60 @@ class ContactsFragment : Fragment() {
|
||||
})
|
||||
}
|
||||
|
||||
private fun confirmDelete(contact: MibBeneficiary) {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.contact_delete_title)
|
||||
.setMessage(getString(R.string.contact_delete_message, contact.benefNickName))
|
||||
.setPositiveButton(R.string.contact_delete) { _, _ -> deleteContact(contact) }
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun deleteContact(contact: MibBeneficiary) {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val success = withContext(Dispatchers.IO) {
|
||||
if (contact.benefCategoryId == "BML") deleteBml(contact) else deleteMib(contact)
|
||||
}
|
||||
if (success) {
|
||||
Toast.makeText(requireContext(), R.string.contact_deleted, Toast.LENGTH_SHORT).show()
|
||||
removeFromViewModel(contact)
|
||||
} else {
|
||||
Toast.makeText(requireContext(), R.string.contact_delete_failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteBml(contact: MibBeneficiary): Boolean {
|
||||
val sess = app.bmlSession ?: return false
|
||||
val contactId = contact.benefNo.removePrefix("bml_")
|
||||
return try { BmlLoginFlow().deleteContact(sess, contactId) } catch (_: Exception) { false }
|
||||
}
|
||||
|
||||
private fun deleteMib(contact: MibBeneficiary): Boolean {
|
||||
val sess = session ?: return false
|
||||
return try {
|
||||
if (contact.profileId.isNotBlank()) {
|
||||
val profile = app.mibProfiles.firstOrNull { it.profileId == contact.profileId }
|
||||
if (profile != null) app.mibLoginFlow.switchProfile(sess, profile)
|
||||
}
|
||||
MibContactsClient().deleteContact(sess, contact.benefNo)
|
||||
} catch (_: Exception) { false }
|
||||
}
|
||||
|
||||
private fun removeFromViewModel(contact: MibBeneficiary) {
|
||||
val updated = viewModel.contacts.value?.filter { it.benefNo != contact.benefNo } ?: return
|
||||
viewModel.contacts.value = updated
|
||||
if (contact.benefCategoryId == "BML") {
|
||||
ContactsCache.saveBml(requireContext(), updated.filter { it.benefCategoryId == "BML" })
|
||||
} else {
|
||||
ContactsCache.save(
|
||||
requireContext(),
|
||||
updated.filter { it.benefCategoryId != "BML" },
|
||||
viewModel.contactCategories.value ?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchImage(hash: String) {
|
||||
if (!pendingHashes.add(hash)) return
|
||||
val sess = session ?: return
|
||||
@@ -106,11 +179,9 @@ class ContactsFragment : Fragment() {
|
||||
val base64 = client.fetchProfileImageBase64(sess, hash) ?: return@launch
|
||||
val bytes = Base64.decode(base64, Base64.DEFAULT)
|
||||
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) ?: return@launch
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.updateImage(hash, bitmap)
|
||||
}
|
||||
withContext(Dispatchers.Main) { adapter.updateImage(hash, bitmap) }
|
||||
} catch (_: Exception) {
|
||||
pendingHashes.remove(hash) // allow retry
|
||||
pendingHashes.remove(hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,30 @@ class TransferFragment : Fragment() {
|
||||
private val session get() = (requireActivity().application as BasedBankApp).mibSession
|
||||
private val bmlSession get() = (requireActivity().application as BasedBankApp).bmlSession
|
||||
|
||||
companion object {
|
||||
private const val ARG_ACCOUNT = "contact_account"
|
||||
private const val ARG_NAME = "contact_name"
|
||||
private const val ARG_SUBTITLE = "contact_subtitle"
|
||||
private const val ARG_COLOR = "contact_color"
|
||||
private const val ARG_IMAGE_HASH = "contact_image_hash"
|
||||
|
||||
fun newInstance(
|
||||
accountNumber: String,
|
||||
displayName: String,
|
||||
subtitle: String,
|
||||
colorHex: String,
|
||||
imageHash: String?
|
||||
) = TransferFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(ARG_ACCOUNT, accountNumber)
|
||||
putString(ARG_NAME, displayName)
|
||||
putString(ARG_SUBTITLE, subtitle)
|
||||
putString(ARG_COLOR, colorHex)
|
||||
if (imageHash != null) putString(ARG_IMAGE_HASH, imageHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
|
||||
val raw = result.data?.getStringExtra(QrScannerActivity.EXTRA_QR_CONTENT) ?: return@registerForActivityResult
|
||||
@@ -94,6 +118,17 @@ class TransferFragment : Fragment() {
|
||||
binding.btnTransfer.setOnClickListener {
|
||||
Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
// Pre-select contact if navigated from contacts page
|
||||
arguments?.getString(ARG_ACCOUNT)?.let { account ->
|
||||
prefillToDirectly(
|
||||
accountNumber = account,
|
||||
displayName = arguments?.getString(ARG_NAME) ?: account,
|
||||
subtitle = arguments?.getString(ARG_SUBTITLE) ?: account,
|
||||
colorHex = arguments?.getString(ARG_COLOR) ?: "#607D8B",
|
||||
imageHash = arguments?.getString(ARG_IMAGE_HASH)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFromDropdown() {
|
||||
|
||||
10
app/src/main/res/drawable/ic_copy.xml
Normal file
10
app/src/main/res/drawable/ic_copy.xml
Normal 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="M16,1H4C2.9,1 2,1.9 2,3v14h2V3h12V1zM19,5H8C6.9,5 6,5.9 6,7v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 19,5zM19,21H8V7h11V21z" />
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_delete.xml
Normal file
10
app/src/main/res/drawable/ic_delete.xml
Normal 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="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_edit.xml
Normal file
10
app/src/main/res/drawable/ic_edit.xml
Normal 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="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
|
||||
</vector>
|
||||
@@ -1,63 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<LinearLayout
|
||||
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"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="10dp">
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/ivContactPhoto"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearance.Circle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvContactName"
|
||||
android:layout_width="0dp"
|
||||
<!-- Header row — same look as before -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintStart_toEndOf="@id/ivContactPhoto"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/ivContactPhoto" />
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvContactBank"
|
||||
android:layout_width="0dp"
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/ivContactPhoto"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearance.Circle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvContactName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintStart_toEndOf="@id/ivContactPhoto"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/ivContactPhoto" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvContactBank"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintStart_toEndOf="@id/ivContactPhoto"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvContactName" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvContactAccount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintStart_toEndOf="@id/ivContactPhoto"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvContactBank"
|
||||
app:layout_constraintBottom_toBottomOf="@id/ivContactPhoto" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- Divider + expanded section, hidden by default -->
|
||||
<View
|
||||
android:id="@+id/dividerExpand"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:background="?attr/colorOutlineVariant"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/expandedSection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintStart_toEndOf="@id/ivContactPhoto"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvContactName" />
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvContactAccount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintStart_toEndOf="@id/ivContactPhoto"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvContactBank"
|
||||
app:layout_constraintBottom_toBottomOf="@id/ivContactPhoto" />
|
||||
<!-- Action buttons -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnTransferContact"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/transfer"
|
||||
app:icon="@drawable/ic_nav_generic" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEditContact"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="6dp"
|
||||
android:text="@string/contact_edit"
|
||||
app:icon="@drawable/ic_edit" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDeleteContact"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="6dp"
|
||||
android:text="@string/contact_delete"
|
||||
android:textColor="?attr/colorError"
|
||||
app:strokeColor="?attr/colorError"
|
||||
app:icon="@drawable/ic_delete"
|
||||
app:iconTint="?attr/colorError" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -140,6 +140,14 @@
|
||||
<string name="contact_already_exists">Contact already exists: %s</string>
|
||||
<string name="contact_own_account">Cannot save your own account as a contact</string>
|
||||
|
||||
<!-- Contact expand/delete -->
|
||||
<string name="contact_edit">Edit</string>
|
||||
<string name="contact_delete">Delete</string>
|
||||
<string name="contact_delete_title">Delete Contact</string>
|
||||
<string name="contact_delete_message">Remove %s from your contacts?</string>
|
||||
<string name="contact_deleted">Contact deleted</string>
|
||||
<string name="contact_delete_failed">Could not delete contact</string>
|
||||
|
||||
<!-- Financing -->
|
||||
<string name="financing_empty">No financing deals found</string>
|
||||
<string name="financing_total">Total</string>
|
||||
|
||||
Reference in New Issue
Block a user