diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt index 650ea5f..71eb0f4 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt @@ -7,12 +7,15 @@ import android.os.Build import android.os.Bundle import android.os.Environment import android.provider.MediaStore +import android.text.TextPaint +import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.* import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.FileProvider +import androidx.core.content.res.ResourcesCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding @@ -65,7 +68,18 @@ class PayMvQrFragment : Fragment() { private data class QrTarget(val accountNumber: String, val name: String, val bank: String) private fun currentTarget(): QrTarget? = contactTarget - ?: selectedAccount?.let { QrTarget(it.accountNumber, it.accountBriefName, it.bank) } + ?: selectedAccount?.let { QrTarget(it.accountNumber, qrHolderName(it), it.bank) } + + /** Name printed on the card and put in the payload (tag 59). */ + private fun qrHolderName(account: BankAccount): String = when { + // Fahipay's brief name is the generic "Fahipay Wallet"; the holder's name is on the profile + account.bank == "FAHIPAY" -> account.profileName.takeIf { it.isNotBlank() && it != "Fahipay" } + ?: CredentialStore(requireContext()) + .loadFahipayUserProfile(sh.sar.basedbank.util.ProfileImageStore.loginIdFromTag(account.loginTag)) + ?.fullName?.takeIf { it.isNotBlank() } + ?: account.accountBriefName + else -> account.accountBriefName + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { contactTarget = arguments?.let { args -> @@ -146,11 +160,13 @@ class PayMvQrFragment : Fragment() { "FAHIPAY" -> "FAHIMVMV" else -> "MADVMVMV" } - val amountFormatted = binding.etAmount.text?.toString()?.trim() - ?.replace(",", "") + val amountRaw = binding.etAmount.text?.toString()?.trim()?.replace(",", "") + val amountFormatted = amountRaw ?.toDoubleOrNull() ?.takeIf { it > 0 } ?.let { "%.2f".format(it) } + // BML shows the amount on the card as typed (no forced decimals) + val amountDisplay = amountRaw?.takeIf { amountFormatted != null } val ctx = requireContext() val account = selectedAccount @@ -165,17 +181,28 @@ class PayMvQrFragment : Fragment() { when { m.startsWith("+") -> m m.length == 7 -> "+960$m" + m.length == 10 && m.startsWith("960") -> "+$m" // Fahipay stores 960XXXXXXX else -> m } } } else null val purpose = binding.etReference.text?.toString()?.trim() - ?.takeIf { it.isNotBlank() } ?: getString(R.string.paymvqr_reference_default) + ?.takeIf { it.isNotBlank() } + + // The reference (62/05) is also printed vertically beside the QR, as each bank does + val reference = when (target.bank) { + // BML: base-32 account number followed by the amount as typed + "BML" -> ((target.accountNumber.toBigIntegerOrNull()?.toString(32)?.uppercase() ?: "") + + (amountDisplay ?: "")).take(25).ifEmpty { generateReference(9) } + "FAHIPAY" -> "P" + generateReference(9) // Fahipay's own references are P + 9 chars + else -> generateReference(9) + } val bmp = withContext(Dispatchers.Default) { - val payload = buildQrPayload(target.accountNumber, target.name, acquirer, amountFormatted, mobile, purpose) - renderQrCard(ctx, target, payload, amountFormatted) + val payload = buildQrPayload(target.accountNumber, target.name, acquirer, amountFormatted, mobile, purpose, reference, target.bank) + if (target.bank == "FAHIPAY") renderFahipayQrCard(ctx, target, payload, reference) + else renderQrCard(ctx, target, payload, reference) } if (_binding == null) return generatedBitmap = bmp @@ -194,14 +221,19 @@ class PayMvQrFragment : Fragment() { acquirer: String, amountStr: String?, mobile: String?, - purpose: String + purpose: String?, + ref: String, + bank: String ): String { fun tlv(tag: String, value: String): String { val len = value.length return tag + (if (len < 10) "0$len" else "$len") + value } val format = tlv("00", "01") - val poi = tlv("01", "11") + // Fahipay's own QRs are dynamic (12) when they carry an amount and mask the amount + // as "***" when they don't; its scanner may reject QRs that differ + val fahipay = bank == "FAHIPAY" + val poi = tlv("01", if (fahipay && !amountStr.isNullOrBlank()) "12" else "11") val sub00 = tlv("00", "mv.favara.mpqr") val sub01 = tlv("01", acquirer) val sub02 = tlv("02", acquirer) // repeated acquirer, as per official PayMV app @@ -211,21 +243,28 @@ class PayMvQrFragment : Fragment() { val merchantAcct = tlv("26", sub00 + sub01 + sub02 + sub03 + sub05 + sub10) val mcc = tlv("52", "0000") val currency = tlv("53", "462") - val amountTLV = if (!amountStr.isNullOrBlank()) tlv("54", amountStr) else "" + val amountTLV = when { + !amountStr.isNullOrBlank() -> tlv("54", amountStr) + fahipay -> tlv("54", "***") + else -> "" + } val country = tlv("58", "MV") - val name = tlv("59", accountName.take(25)) - val ref = generateReference() - val addlData = tlv("62", tlv("05", ref) + tlv("08", purpose)) + val name = tlv("59", accountName.uppercase().take(25)) + // Fahipay's QRs always carry a city ("LD" + 4 digits) and default the purpose to PAYMENT + val city = if (fahipay) tlv("60", "LD" + (0..9999).random().toString().padStart(4, '0')) else "" + val purposeText = purpose?.takeIf { it.isNotBlank() } ?: if (fahipay) "PAYMENT" else null + val purposeTLV = if (purposeText != null) tlv("08", purposeText) else "" + val addlData = tlv("62", tlv("05", ref) + purposeTLV) val timestamp = java.time.LocalDateTime.now() .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.00000")) val tag80 = tlv("80", tlv("00", "mv.favara.mpqr") + tlv("01", timestamp)) - val prefix = format + poi + merchantAcct + mcc + currency + amountTLV + country + name + addlData + tag80 + "6304" + val prefix = format + poi + merchantAcct + mcc + currency + amountTLV + country + name + city + addlData + tag80 + "6304" return prefix + crc16(prefix) } - private fun generateReference(): String { + private fun generateReference(length: Int): String { val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - return (1..9).map { chars.random() }.joinToString("") + return (1..length).map { chars.random() }.joinToString("") } private fun crc16(data: String): String { @@ -242,88 +281,127 @@ class PayMvQrFragment : Fragment() { // ── QR card rendering ──────────────────────────────────────────────────── + /** + * Replicates the BML app's ReceiveCard (React Native, v2.1.47) 1:1. All measurements are in + * dp, as in BML's StyleSheet, laid out for BML's reference screen width and drawn at + * [PX_PER_DP] pixels per dp. + */ private fun renderQrCard( ctx: Context, target: QrTarget, qrPayload: String, - amountStr: String? + qrId: String ): Bitmap { - val W = 900 - val H = 1080 - val outerCorner = 48f - val boxBlue = Color.parseColor("#2272B7") - val footerBlue = Color.parseColor("#1A5799") - val boxL = 24f; val boxT = 110f; val boxR = 876f; val boxB = 962f + val sw = SCREEN_WIDTH_DP + fun px(dp: Float) = dp * PX_PER_DP + val mmaBlue = Color.parseColor("#0E5CA4") - val bm = Bitmap.createBitmap(W, H, Bitmap.Config.ARGB_8888) + val cardW = sw - 48f // screen's horizontal margins, spacing[5] each side + val qrSize = sw * 0.5f + val railW = sw / 1.85f + + val namePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.WHITE + textSize = px(14f) + typeface = Typeface.DEFAULT + textAlign = Paint.Align.CENTER + } + val footerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.WHITE + textSize = px(sw * 0.046f) + typeface = ResourcesCompat.getFont(ctx, R.font.sofia_pro_bold) ?: Typeface.DEFAULT_BOLD + letterSpacing = 1.2f / (sw * 0.046f) // RN letterSpacing is in dp, Paint's is in em + textAlign = Paint.Align.CENTER + } + // Android RN Text lines include font padding: line height = bottom - top + fun lineHeight(p: Paint) = p.fontMetrics.let { it.bottom - it.top } / PX_PER_DP + + // --- Vertical layout (dp, card-local) --- + val topCardTop = 2f + val brandTop = topCardTop + 32f // brandRow marginTop spacing[6] + val logoBoxW = sw * 0.38f // bml-logo-paymv box: 0.38·sw wide + val logoBoxH = logoBoxW * 0.1116751269035533f + val payMvBoxW = sw * 0.2f // paymv-logo box: 0.2·sw wide + val payMvBoxH = payMvBoxW * 0.17333333333333334f + val brandH = maxOf(logoBoxH, payMvBoxH) + val nameText = target.name.uppercase() + val hasName = nameText.isNotBlank() + val qrCardTop = brandTop + brandH + if (hasName) 24f else 32f + val nameTop = qrCardTop + 8f + 12f // qrCard paddingTop spacing[2], name marginTop spacing[3] + val nameH = if (hasName) lineHeight(namePaint) else 0f + val qrTop = if (hasName) nameTop + nameH + 16f else qrCardTop + 37f + val qrCardBottom = qrTop + qrSize + 37f // paddingBottom spacing[6] + 5 + val topCardBottom = qrCardBottom + 24f + 8f // qrCard marginBottom, topCard paddingBottom + val footerLineH = lineHeight(footerPaint) + val cardH = topCardBottom + 12f + footerLineH + 12f + 2f + + val bm = Bitmap.createBitmap(px(cardW).toInt(), px(cardH).toInt(), Bitmap.Config.ARGB_8888) val canvas = Canvas(bm) val paint = Paint(Paint.ANTI_ALIAS_FLAG) - // Clip to outer rounded card shape - val outerPath = Path() - outerPath.addRoundRect(RectF(0f, 0f, W.toFloat(), H.toFloat()), outerCorner, outerCorner, Path.Direction.CW) - canvas.clipPath(outerPath) - canvas.drawColor(Color.WHITE) - - // --- Bank logo top-left --- - val logoRes = when (target.bank) { - "BML" -> R.drawable.bml_logo_vector - "MIB" -> R.drawable.mib_faisanet_logo - else -> R.drawable.fahipay_logo_long + // captureWrapper: mmaBlue, radius 20 — shows as a 2dp border around the white top card + val outerPath = Path().apply { + addRoundRect(RectF(0f, 0f, px(cardW), px(cardH)), px(20f), px(20f), Path.Direction.CW) } + canvas.clipPath(outerPath) + canvas.drawColor(mmaBlue) + + // topCard: white, 2dp inset, top corners 18 + paint.color = Color.WHITE + val r = px(18f) + canvas.drawPath(Path().apply { + addRoundRect( + RectF(px(2f), px(topCardTop), px(cardW - 2f), px(topCardBottom)), + floatArrayOf(r, r, r, r, 0f, 0f, 0f, 0f), Path.Direction.CW + ) + }, paint) + + // --- brandRow: "BANK OF MALDIVES" wordmark left, "PayMV QR" right, 40dp side margins --- + val rowL = 2f + 40f + val rowR = cardW - 2f - 40f + val rowCenterY = brandTop + brandH / 2 + val logoRes = if (target.bank == "BML") R.drawable.bml_logo_paymv else R.drawable.mib_faisanet_logo AppCompatResources.getDrawable(ctx, logoRes)?.let { d -> val nW = d.intrinsicWidth.coerceAtLeast(1) val nH = d.intrinsicHeight.coerceAtLeast(1) - val maxW = 180f; val maxH = 76f - val scale = minOf(maxW / nW, maxH / nH) + // resizeMode "contain" inside the logo box, left-aligned in the row + val scale = minOf(px(logoBoxW) / nW, px(logoBoxH) / nH) val lW = (nW * scale).toInt() val lH = (nH * scale).toInt() - val lTop = ((boxT - lH) / 2).toInt().coerceAtLeast(10) - d.setBounds(24, lTop, 24 + lW, lTop + lH) + val lLeft = (px(rowL) + (px(logoBoxW) - lW) / 2f).toInt() + val lTop = (px(rowCenterY) - lH / 2f).toInt() + d.setBounds(lLeft, lTop, lLeft + lW, lTop + lH) d.draw(canvas) } - - // --- "PayMV QR" top-right --- - paint.color = Color.parseColor("#1A1A2E") - paint.textSize = 36f - paint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) + // BML draws the paymv-logo image (fills its box exactly), nudged down 1dp + paint.color = mmaBlue + paint.typeface = footerPaint.typeface paint.textAlign = Paint.Align.RIGHT - canvas.drawText("PayMV QR", W - 28f, 66f, paint) + paint.textSize = px(payMvBoxH) + paint.textSize *= px(payMvBoxW) / paint.measureText("PayMV QR") + val payMvBounds = Rect().also { paint.getTextBounds("PayMV QR", 0, 8, it) } + canvas.drawText( + "PayMV QR", px(rowR), + px(rowCenterY + 1f) - payMvBounds.exactCenterY(), paint + ) - // --- Blue rounded box --- - paint.color = boxBlue - paint.textAlign = Paint.Align.LEFT - canvas.drawRoundRect(RectF(boxL, boxT, boxR, boxB), 36f, 36f, paint) + // --- qrCard: mmaBlue, radius 16, 40dp side margins --- + val qrCardL = 2f + 40f + val qrCardR = cardW - 2f - 40f + paint.color = mmaBlue + canvas.drawRoundRect(RectF(px(qrCardL), px(qrCardTop), px(qrCardR), px(qrCardBottom)), px(16f), px(16f), paint) - // Account name (white, bold, uppercase, auto-scaled to fit) - paint.color = Color.WHITE - paint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) - paint.textAlign = Paint.Align.CENTER - val nameText = target.name.uppercase() - paint.textSize = 36f - val maxNameW = boxR - boxL - 48f - if (paint.measureText(nameText) > maxNameW) { - paint.textSize = 36f * maxNameW / paint.measureText(nameText) - } - val nameBaseline = boxT + 68f - canvas.drawText(nameText, W / 2f, nameBaseline, paint) - - // Optional amount below name - val qrTopY: Float - if (!amountStr.isNullOrBlank()) { - paint.textSize = 28f - paint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) - val amtBaseline = nameBaseline + 42f - canvas.drawText("MVR $amountStr", W / 2f, amtBaseline, paint) - qrTopY = amtBaseline + 20f - } else { - qrTopY = nameBaseline + 26f + if (hasName) { + val maxNameW = px(qrCardR - qrCardL) + if (namePaint.measureText(nameText) > maxNameW) { + namePaint.textSize *= maxNameW / namePaint.measureText(nameText) + } + canvas.drawText(nameText, px(cardW / 2), px(nameTop) - namePaint.fontMetrics.top, namePaint) } - // QR code — white modules on the same blue as the box background - val availH = boxB - qrTopY - 24f - val qrPx = minOf(availH, boxR - boxL - 48f).toInt().coerceAtMost(700).coerceAtLeast(200) - val qrLeft = ((W - qrPx) / 2).toFloat() + // QR — white modules on mmaBlue, ECL M, no quiet zone (react-native-qrcode-svg defaults) + val qrPx = px(qrSize).toInt() + val qrLeft = px(cardW / 2) - qrPx / 2f try { val hints = mapOf( EncodeHintType.MARGIN to 0, @@ -333,23 +411,150 @@ class PayMvQrFragment : Fragment() { val pixels = IntArray(qrPx * qrPx) for (y in 0 until qrPx) { for (x in 0 until qrPx) { - pixels[y * qrPx + x] = if (matrix[x, y]) Color.WHITE else boxBlue + pixels[y * qrPx + x] = if (matrix[x, y]) Color.WHITE else mmaBlue } } val qrBm = Bitmap.createBitmap(pixels, qrPx, qrPx, Bitmap.Config.ARGB_8888) - canvas.drawBitmap(qrBm, qrLeft, qrTopY, null) + canvas.drawBitmap(qrBm, qrLeft, px(qrTop), null) qrBm.recycle() } catch (_: Exception) { /* skip if encoding fails */ } - // --- Dark blue footer --- - paint.color = footerBlue - paint.textAlign = Paint.Align.LEFT - canvas.drawRect(RectF(0f, 970f, W.toFloat(), H.toFloat()), paint) + // qrIdRail: the reference, rotated -90°, beside the QR's right edge + if (qrId.isNotEmpty()) { + val idPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.BLACK + alpha = (255 * 0.8f).toInt() + textSize = px(10f) + typeface = Typeface.DEFAULT + textAlign = Paint.Align.CENTER + } + val qrRight = cardW / 2 + qrSize / 2 + val cx = px(qrRight + railW / 1.37f - railW / 2) + val cy = px(qrTop + (qrSize + railW / 1.5f) / 2) + val idText = TextUtils.ellipsize(qrId, TextPaint(idPaint), px(railW), TextUtils.TruncateAt.END).toString() + canvas.save() + canvas.rotate(-90f, cx, cy) + val fm = idPaint.fontMetrics + canvas.drawText(idText, cx, cy - (fm.bottom + fm.top) / 2, idPaint) + canvas.restore() + } + + // --- footer: SofiaPro-Bold, letterSpacing 1.2 --- + canvas.drawText( + "MALDIVES NATIONAL QR", px(cardW / 2), + px(topCardBottom + 12f) - footerPaint.fontMetrics.top, footerPaint + ) + + return bm + } + + /** + * Replicates the card Fahipay's server renders for PayMV QR (api/app/qr/), measured + * in pixels on its 1240×1322 image. Fonts follow the BML card (Sofia Pro Bold, Roboto), + * except the vertical reference, which is Montserrat as on Fahipay's. + */ + private fun renderFahipayQrCard( + ctx: Context, + target: QrTarget, + qrPayload: String, + reference: String + ): Bitmap { + val w = 1240f + val h = 1322f + val blue = Color.parseColor("#005DA3") + val sofiaBold = ResourcesCompat.getFont(ctx, R.font.sofia_pro_bold) ?: Typeface.DEFAULT_BOLD + val montserrat = ResourcesCompat.getFont(ctx, R.font.montserrat_regular) ?: Typeface.DEFAULT + + val bm = Bitmap.createBitmap(w.toInt(), h.toInt(), Bitmap.Config.ARGB_8888) + val canvas = Canvas(bm) + val paint = Paint(Paint.ANTI_ALIAS_FLAG) + + // Blue card with a 6px border around the white area; footer is the blue below it + canvas.clipPath(Path().apply { + addRoundRect(RectF(0f, 0f, w, h), 50f, 50f, Path.Direction.CW) + }) + canvas.drawColor(blue) paint.color = Color.WHITE - paint.textSize = 32f - paint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) - paint.textAlign = Paint.Align.CENTER - canvas.drawText("MALDIVES NATIONAL QR", W / 2f, 1038f, paint) + canvas.drawPath(Path().apply { + addRoundRect( + RectF(6f, 6f, w - 6f, 1174f), + floatArrayOf(44f, 44f, 44f, 44f, 0f, 0f, 0f, 0f), Path.Direction.CW + ) + }, paint) + + // Square app icon, then the "FahiPay" wordmark, in one row + AppCompatResources.getDrawable(ctx, R.drawable.fahipay_logo)?.let { d -> + d.setBounds(94, 94, 163, 163) + d.draw(canvas) + } + AppCompatResources.getDrawable(ctx, R.drawable.fahipay_logo_long)?.let { d -> + val lH = 48 + val lW = (lH * d.intrinsicWidth.toFloat() / d.intrinsicHeight.coerceAtLeast(1)).toInt() + d.setBounds(178, 104, 178 + lW, 104 + lH) + d.draw(canvas) + } + + // Sized so capitals match the reference's cap heights; positions below are cap tops + fun textPaint(tf: Typeface, capH: Float, spacingEm: Float, align: Paint.Align) = + Paint(Paint.ANTI_ALIAS_FLAG).apply { + typeface = tf + letterSpacing = spacingEm + textAlign = align + textSize = 100f + val h = Rect().also { getTextBounds("H", 0, 1, it) }.height().coerceAtLeast(1) + textSize = 100f * capH / h + } + fun Paint.capHeight() = Rect().also { getTextBounds("H", 0, 1, it) }.height() + + // "PayMV QR" top-right + val payMvPaint = textPaint(sofiaBold, 30f, 0f, Paint.Align.RIGHT).apply { color = blue } + canvas.drawText("PayMV QR", 1147f, 101f + 30f, payMvPaint) + + // Blue QR panel + paint.color = blue + canvas.drawRoundRect(RectF(166f, 218f, 1074f, 1126f), 55f, 55f, paint) + + // Account name + val nameText = target.name.uppercase() + if (nameText.isNotBlank()) { + val namePaint = textPaint(Typeface.DEFAULT, 30f, 0f, Paint.Align.CENTER).apply { color = Color.WHITE } + val maxNameW = 1074f - 166f - 80f + if (namePaint.measureText(nameText) > maxNameW) { + namePaint.textSize *= maxNameW / namePaint.measureText(nameText) + } + canvas.drawText(nameText, 619f, 314f + namePaint.capHeight(), namePaint) + } + + // QR — white modules on blue, no quiet zone + val qrPx = 562 + try { + val hints = mapOf( + EncodeHintType.MARGIN to 0, + EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.M + ) + val matrix = QRCodeWriter().encode(qrPayload, BarcodeFormat.QR_CODE, qrPx, qrPx, hints) + val pixels = IntArray(qrPx * qrPx) + for (y in 0 until qrPx) { + for (x in 0 until qrPx) { + pixels[y * qrPx + x] = if (matrix[x, y]) Color.WHITE else blue + } + } + val qrBm = Bitmap.createBitmap(pixels, qrPx, qrPx, Bitmap.Config.ARGB_8888) + canvas.drawBitmap(qrBm, 338f, 417f, null) + qrBm.recycle() + } catch (_: Exception) { /* skip if encoding fails */ } + + // Reference, blue, reading bottom-to-top in the white margin right of the panel, + // starting level with y=1087 and with its baseline at x=1171 + val refPaint = textPaint(montserrat, 27f, 0.005f, Paint.Align.LEFT).apply { color = blue } + canvas.save() + canvas.rotate(-90f, 1171f, 1087f) + canvas.drawText(reference, 1171f, 1087f, refPaint) + canvas.restore() + + // Footer + val footerPaint = textPaint(sofiaBold, 55.5f, 1.2f / 25.76f, Paint.Align.CENTER).apply { color = Color.WHITE } + canvas.drawText("MALDIVES NATIONAL QR", w / 2, 1220f + 55.5f, footerPaint) return bm } @@ -437,6 +642,10 @@ class PayMvQrFragment : Fragment() { private const val ARG_ACCOUNT_NAME = "account_name" private const val ARG_BANK = "bank" + /** BML's layout reference: the screen width (dp) its ReceiveCard sizes were captured at. */ + private const val SCREEN_WIDTH_DP = 560f + private const val PX_PER_DP = 2f + /** QR for a contact's account. [bank] is "BML" / "MIB" / "FAHIPAY", as on [BankAccount.bank]. */ fun forContact(accountNumber: String, name: String, bank: String) = PayMvQrFragment().apply { arguments = Bundle().apply { diff --git a/app/src/main/res/drawable-nodpi/bml_logo_paymv.png b/app/src/main/res/drawable-nodpi/bml_logo_paymv.png new file mode 100644 index 0000000..a9abad5 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/bml_logo_paymv.png differ diff --git a/app/src/main/res/font/montserrat_regular.ttf b/app/src/main/res/font/montserrat_regular.ttf new file mode 100644 index 0000000..2a2b2aa Binary files /dev/null and b/app/src/main/res/font/montserrat_regular.ttf differ diff --git a/app/src/main/res/font/sofia_pro_bold.ttf b/app/src/main/res/font/sofia_pro_bold.ttf new file mode 100644 index 0000000..cd156bf Binary files /dev/null and b/app/src/main/res/font/sofia_pro_bold.ttf differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee06fdc..6f3cec4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,7 +127,6 @@ Failed to save image Include phone number Reference (optional) - PayMV QR Transfer Lock app