diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt index 0a6c4a8..65b6c11 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt @@ -80,6 +80,8 @@ class TransferFragment : Fragment() { private var resolvedAccountNumber = "" private var resolvedRecipientName = "" private var resolvedBankName = "" + /** Last real profile/contact photo loaded into the "To" card (not an initials placeholder). */ + private var loadedToPhoto: Bitmap? = null private var resolvedDestCurrency = "" // "MVR" / "USD" / "" if unknown private var resolvedToOwnAccount: BankAccount? = null @@ -1168,6 +1170,8 @@ class TransferFragment : Fragment() { val destDisplay = binding.tvToAccountName.text?.toString() ?: resolvedAccountNumber val bankNameCapture = resolvedBankName val capturedToAvatar = (binding.ivToPhoto.drawable as? android.graphics.drawable.BitmapDrawable)?.bitmap + // The MIB receipt only takes a real photo; it draws its own initials placeholder otherwise + val capturedToPhoto = capturedToAvatar?.takeIf { it === loadedToPhoto } val destCurrency = resolvedDestCurrency.ifBlank { allAccounts.firstOrNull { it.accountNumber == resolvedAccountNumber } @@ -1205,7 +1209,7 @@ class TransferFragment : Fragment() { val activity = requireActivity() as HomeActivity activity.triggerRefresh() dialog.dismiss() - activity.showWithBackStack(TransferReceiptFragment.newInstance(receipt, capturedToAvatar)) + activity.showWithBackStack(TransferReceiptFragment.newInstance(receipt, capturedToPhoto)) } else if (!ok) { dialog.dismiss() if (msg == "CONNECTIVITY") { @@ -1581,6 +1585,7 @@ class TransferFragment : Fragment() { if (_binding != null) { binding.ivToPhoto.scaleType = android.widget.ImageView.ScaleType.CENTER_CROP binding.ivToPhoto.setImageBitmap(bitmap) + loadedToPhoto = bitmap } } } diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptData.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptData.kt index f497a3c..73620f0 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptData.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptData.kt @@ -14,6 +14,8 @@ data class TransferReceiptData( // MIB receipt fields val mibReferenceNo: String = "", val mibTransactionDate: String = "", + val mibFromProfileName: String = "", + val mibTransactionType: String = "", // "Own Transfer", "MIB Transfer", "Quick Transfer" // BML receipt fields val bmlFromName: String = "", val bmlReference: String = "", diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptFragment.kt index 7841c2d..a071425 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferReceiptFragment.kt @@ -63,6 +63,8 @@ class TransferReceiptFragment : Fragment() { private const val ARG_REMARKS = "remarks" private const val ARG_MIB_REF = "mib_ref" private const val ARG_MIB_DATE = "mib_date" + private const val ARG_MIB_FROM_PROFILE = "mib_from_profile" + private const val ARG_MIB_TXN_TYPE = "mib_txn_type" private const val ARG_BML_FROM_NAME = "bml_from_name" private const val ARG_BML_REFERENCE = "bml_reference" private const val ARG_BML_TIMESTAMP = "bml_timestamp" @@ -91,6 +93,8 @@ class TransferReceiptFragment : Fragment() { putString(ARG_REMARKS, data.remarks) putString(ARG_MIB_REF, data.mibReferenceNo) putString(ARG_MIB_DATE, data.mibTransactionDate) + putString(ARG_MIB_FROM_PROFILE, data.mibFromProfileName) + putString(ARG_MIB_TXN_TYPE, data.mibTransactionType) putString(ARG_BML_FROM_NAME, data.bmlFromName) putString(ARG_BML_REFERENCE, data.bmlReference) putString(ARG_BML_TIMESTAMP, data.bmlTimestamp) @@ -170,43 +174,64 @@ class TransferReceiptFragment : Fragment() { private fun bindMib(binding: FragmentReceiptMibBinding) { val args = requireArguments() val fromLabel = args.getString(ARG_FROM_LABEL, "") - val fromColor = args.getString(ARG_FROM_COLOR, "#FE860E") val fromProfileHash = args.getString(ARG_FROM_PROFILE_HASH) val toLabel = args.getString(ARG_TO_LABEL, "") val currency = args.getString(ARG_CURRENCY, "MVR") val amount = args.getString(ARG_AMOUNT, "") // From avatar: initials first, then load profile image if hash available - binding.ivFromAvatar.setImageBitmap(makeInitialsBitmap(fromLabel, fromColor)) + binding.ivFromAvatar.setImageBitmap(makeMibInitialsBitmap(fromLabel)) binding.tvFromLabel.text = fromLabel if (fromProfileHash != null) { - loadProfileImage(fromProfileHash, isProfile = true) { binding.ivFromAvatar.setImageBitmap(it) } + loadProfileImage(fromProfileHash, isProfile = true) { binding.ivFromAvatar.setImageBitmap(circleCrop(it)) } } // To avatar: use already-rendered bitmap from TransferFragment if available val toAvatar = pendingToAvatarBitmap if (toAvatar != null) { - binding.ivToAvatar.setImageBitmap(toAvatar) + binding.ivToAvatar.setImageBitmap(circleCrop(toAvatar)) } else { - binding.ivToAvatar.setImageBitmap(makeInitialsBitmap(toLabel, "#607D8B")) + binding.ivToAvatar.setImageBitmap(makeMibInitialsBitmap(toLabel)) } binding.tvToLabel.text = toLabel binding.tvAmount.text = "$currency $amount" + + val toBank = args.getString(ARG_TO_BANK, "") + val rawDate = args.getString(ARG_MIB_DATE, "") binding.tvReferenceNo.text = args.getString(ARG_MIB_REF, "") - binding.tvToAccount.text = args.getString(ARG_TO_ACCOUNT, "") - binding.tvToBank.text = args.getString(ARG_TO_BANK, "") - binding.tvTransactionDate.text = args.getString(ARG_MIB_DATE, "") - binding.tvValueDate.text = args.getString(ARG_MIB_DATE, "") + binding.tvFromName.text = args.getString(ARG_MIB_FROM_PROFILE, "").ifBlank { fromLabel } + binding.tvToAccount.text = listOf(toLabel, args.getString(ARG_TO_ACCOUNT, "")) + .filter { it.isNotBlank() }.joinToString("\n") + binding.tvToBank.text = toBank + // Receipts saved before the type was recorded fall back to a guess from the bank + binding.tvTransactionType.text = args.getString(ARG_MIB_TXN_TYPE, "").ifBlank { + if (toBank == "MIB") "MIB Transfer" else "Quick Transfer" + } + binding.tvTransactionDate.text = formatMibDate(rawDate, "dd MMM yyyy HH:mm") + binding.tvValueDate.text = formatMibDate(rawDate, "dd MMM yyyy") binding.tvPurpose.text = args.getString(ARG_REMARKS, "") + .takeUnless { it.isNullOrBlank() || it.trim() == "-" } ?: "N/A" copyOnLongClick( - binding.tvFromLabel, binding.tvToLabel, binding.tvAmount, - binding.tvReferenceNo, binding.tvToAccount, binding.tvToBank, - binding.tvTransactionDate, binding.tvValueDate, binding.tvPurpose + binding.tvFromLabel, binding.tvToLabel, binding.tvAmount, binding.tvStatus, + binding.tvReferenceNo, binding.tvFromName, binding.tvToAccount, binding.tvToBank, + binding.tvTransactionType, binding.tvTransactionDate, binding.tvValueDate, binding.tvPurpose ) } + /** Reformats the MIB transfer date ("2026-05-16 15:10:25") to [pattern]; raw text if unparseable. */ + private fun formatMibDate(raw: String, pattern: String): String { + if (raw.isBlank()) return "" + val out = DateTimeFormatter.ofPattern(pattern, Locale.US) + for (inPattern in listOf("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "dd MMM yyyy HH:mm")) { + try { + return java.time.LocalDateTime.parse(raw.trim(), DateTimeFormatter.ofPattern(inPattern, Locale.US)).format(out) + } catch (_: Exception) { } + } + return raw + } + private fun loadProfileImage(hash: String, isProfile: Boolean, onLoaded: (Bitmap) -> Unit) { val app = requireActivity().application as BasedBankApp val sess = app.anyMibSession() ?: return @@ -422,20 +447,38 @@ class TransferReceiptFragment : Fragment() { } } - private fun makeInitialsBitmap(name: String, colorHex: String): Bitmap { + /** Center-crops [src] to a square and masks it to a circle. */ + private fun circleCrop(src: Bitmap): Bitmap { + val size = minOf(src.width, src.height) + val out = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG).apply { + shader = android.graphics.BitmapShader(src, android.graphics.Shader.TileMode.CLAMP, android.graphics.Shader.TileMode.CLAMP).apply { + setLocalMatrix(android.graphics.Matrix().apply { + setTranslate(-(src.width - size) / 2f, -(src.height - size) / 2f) + }) + } + } + Canvas(out).drawCircle(size / 2f, size / 2f, size / 2f, paint) + return out + } + + /** MIB receipt placeholder: up to two initials in #1168F3 on a #C6E1FD circle. */ + private fun makeMibInitialsBitmap(name: String): Bitmap { val sizePx = (resources.displayMetrics.density * 52).toInt() - val bgColor = try { Color.parseColor(colorHex) } catch (_: 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 + paint.color = Color.parseColor("#C6E1FD") canvas.drawCircle(sizePx / 2f, sizePx / 2f, sizePx / 2f, paint) - paint.color = Color.WHITE - paint.textSize = sizePx * 0.42f + paint.color = Color.parseColor("#1168F3") + paint.textSize = sizePx * 0.36f paint.textAlign = Paint.Align.CENTER - val letter = name.firstOrNull()?.uppercaseChar()?.toString() ?: "?" + paint.typeface = android.graphics.Typeface.DEFAULT_BOLD + val initials = name.split(Regex("\\s+")) + .mapNotNull { word -> word.firstOrNull { it.isLetterOrDigit() }?.uppercaseChar() } + .take(2).joinToString("").ifEmpty { "?" } val metrics = paint.fontMetrics - canvas.drawText(letter, sizePx / 2f, sizePx / 2f - (metrics.ascent + metrics.descent) / 2f, paint) + canvas.drawText(initials, sizePx / 2f, sizePx / 2f - (metrics.ascent + metrics.descent) / 2f, paint) return bm } diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/transfer/MibTransferHandler.kt b/app/src/main/java/sh/sar/basedbank/ui/home/transfer/MibTransferHandler.kt index a5c6cb4..8c670aa 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/transfer/MibTransferHandler.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/transfer/MibTransferHandler.kt @@ -129,6 +129,15 @@ class MibTransferHandler( else -> bankName.ifBlank { "LOCAL" } } } + val isOwnAccount = isDestMib && app.mibAccounts.any { + it.accountNumber == destAccount && it.loginTag == src.loginTag && + (src.profileId.isBlank() || it.profileId == src.profileId) + } + val transactionType = when { + isOwnAccount -> "Own Transfer" + isDestMib -> "MIB Transfer" + else -> "Quick Transfer" + } return try { // Switch to the profile that owns the source account if (src.profileId.isNotBlank()) { @@ -160,7 +169,9 @@ class MibTransferHandler( toBank = toBank, remarks = remarks, mibReferenceNo = result.trxId, - mibTransactionDate = result.date + mibTransactionDate = result.date, + mibFromProfileName = src.profileName, + mibTransactionType = transactionType ) Triple(true, "BankTransaction ID: ${result.trxId}\n${result.date}", receipt) } else { diff --git a/app/src/main/java/sh/sar/basedbank/util/ReceiptStore.kt b/app/src/main/java/sh/sar/basedbank/util/ReceiptStore.kt index fbcc986..e1bac74 100644 --- a/app/src/main/java/sh/sar/basedbank/util/ReceiptStore.kt +++ b/app/src/main/java/sh/sar/basedbank/util/ReceiptStore.kt @@ -40,6 +40,8 @@ object ReceiptStore { remarks = o.optString("remarks"), mibReferenceNo = o.optString("mibReferenceNo"), mibTransactionDate = o.optString("mibTransactionDate"), + mibFromProfileName = o.optString("mibFromProfileName"), + mibTransactionType = o.optString("mibTransactionType"), bmlFromName = o.optString("bmlFromName"), bmlReference = o.optString("bmlReference"), bmlTimestamp = o.optString("bmlTimestamp"), @@ -76,6 +78,8 @@ object ReceiptStore { put("remarks", d.remarks) put("mibReferenceNo", d.mibReferenceNo) put("mibTransactionDate", d.mibTransactionDate) + put("mibFromProfileName", d.mibFromProfileName) + put("mibTransactionType", d.mibTransactionType) put("bmlFromName", d.bmlFromName) put("bmlReference", d.bmlReference) put("bmlTimestamp", d.bmlTimestamp) diff --git a/app/src/main/res/drawable/bg_mib_receipt_header.xml b/app/src/main/res/drawable/bg_mib_receipt_header.xml deleted file mode 100644 index 261ae7b..0000000 --- a/app/src/main/res/drawable/bg_mib_receipt_header.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/mib_logo_full.xml b/app/src/main/res/drawable/mib_logo_full.xml new file mode 100644 index 0000000..a5b0eb7 --- /dev/null +++ b/app/src/main/res/drawable/mib_logo_full.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/mib_receipt_header_bg.xml b/app/src/main/res/drawable/mib_receipt_header_bg.xml new file mode 100644 index 0000000..8980193 --- /dev/null +++ b/app/src/main/res/drawable/mib_receipt_header_bg.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/mib_receipt_texture.png b/app/src/main/res/drawable/mib_receipt_texture.png new file mode 100644 index 0000000..522e1c0 Binary files /dev/null and b/app/src/main/res/drawable/mib_receipt_texture.png differ diff --git a/app/src/main/res/drawable/trx_success_bg.png b/app/src/main/res/drawable/trx_success_bg.png deleted file mode 100644 index 35db358..0000000 Binary files a/app/src/main/res/drawable/trx_success_bg.png and /dev/null differ diff --git a/app/src/main/res/layout/fragment_receipt_mib.xml b/app/src/main/res/layout/fragment_receipt_mib.xml index a641cfb..6e6635b 100644 --- a/app/src/main/res/layout/fragment_receipt_mib.xml +++ b/app/src/main/res/layout/fragment_receipt_mib.xml @@ -15,7 +15,7 @@ android:overScrollMode="never" android:scrollbars="none"> - + - + + android:layout_height="160dp" + android:background="@drawable/mib_receipt_header_bg"> - - - - - - - - - - - - - - - - + android:paddingHorizontal="24dp"> - + + android:scaleType="fitCenter" + android:contentDescription="@null" /> @@ -109,22 +69,25 @@ - + + android:scaleType="fitCenter" + android:contentDescription="@null" /> @@ -140,81 +105,92 @@ - + - - + + + android:paddingTop="14dp" /> - + - + - - - + + - + - + - - - + + - + - + - - - + + - + + + + + + + + + + + + + + + - - - + + - + - + - - - + + - + - + - - - + + - - - - + android:contentDescription="Maldives Islamic Bank" /> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index ccea614..7eac7fa 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -8,4 +8,11 @@ #000000 #3D3E40 #1E1F21 + + + #1A1A3C + #FFFFFF + #FFFFFF + #33335C + #FFFFFF diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 167d557..c5277b9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,4 +15,12 @@ #E9E9E9 #EBEBEB #E21B23 + + + #FFFFFF + #000000 + #000000 + #CAC4D0 + #000000 + #1EA833