This commit is contained in:
@@ -39,14 +39,13 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import sh.sar.basedbank.BasedBankApp
|
import sh.sar.basedbank.BasedBankApp
|
||||||
import sh.sar.basedbank.R
|
import sh.sar.basedbank.R
|
||||||
import sh.sar.basedbank.api.dhiraagu.DhiraaguClient
|
|
||||||
import sh.sar.basedbank.api.fahipay.OoredooClient
|
|
||||||
import sh.sar.basedbank.api.models.BankAccount
|
import sh.sar.basedbank.api.models.BankAccount
|
||||||
import sh.sar.basedbank.api.mib.MibIpsAccountInfo
|
import sh.sar.basedbank.api.mib.MibIpsAccountInfo
|
||||||
import sh.sar.basedbank.databinding.FragmentTransferBinding
|
import sh.sar.basedbank.databinding.FragmentTransferBinding
|
||||||
import sh.sar.basedbank.databinding.ItemAccountDropdownBinding
|
import sh.sar.basedbank.databinding.ItemAccountDropdownBinding
|
||||||
import sh.sar.basedbank.databinding.ItemPickerSectionHeaderBinding
|
import sh.sar.basedbank.databinding.ItemPickerSectionHeaderBinding
|
||||||
import sh.sar.basedbank.ui.home.transfer.BmlTransferHandler
|
import sh.sar.basedbank.ui.home.transfer.BmlTransferHandler
|
||||||
|
import sh.sar.basedbank.ui.home.transfer.FahipayTransferHandler
|
||||||
import sh.sar.basedbank.ui.home.transfer.MfaisaTransferHandler
|
import sh.sar.basedbank.ui.home.transfer.MfaisaTransferHandler
|
||||||
import sh.sar.basedbank.ui.home.transfer.MibTransferHandler
|
import sh.sar.basedbank.ui.home.transfer.MibTransferHandler
|
||||||
import sh.sar.basedbank.util.AccountListParser
|
import sh.sar.basedbank.util.AccountListParser
|
||||||
@@ -82,10 +81,6 @@ class TransferFragment : Fragment() {
|
|||||||
private var resolvedDestCurrency = "" // "MVR" / "USD" / "" if unknown
|
private var resolvedDestCurrency = "" // "MVR" / "USD" / "" if unknown
|
||||||
private var resolvedToOwnAccount: BankAccount? = null
|
private var resolvedToOwnAccount: BankAccount? = null
|
||||||
|
|
||||||
// Selected Fahipay service when source is Fahipay and destination is a phone number
|
|
||||||
// Values: "FAHIPAY_TRANSFER", "RAASTAS", "OOREDOO_BILL"
|
|
||||||
private var selectedFahipayService: String? = null
|
|
||||||
|
|
||||||
// Form state preserved across view destroy/create when the fragment instance is cached
|
// Form state preserved across view destroy/create when the fragment instance is cached
|
||||||
private var savedAmount = ""
|
private var savedAmount = ""
|
||||||
private var savedRemarks = ""
|
private var savedRemarks = ""
|
||||||
@@ -129,6 +124,11 @@ class TransferFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
).also { bmlHandler = it }
|
).also { bmlHandler = it }
|
||||||
|
|
||||||
|
/** Lazy: created the first time a Fahipay source needs a recipient lookup. */
|
||||||
|
private var fahipayHandler: FahipayTransferHandler? = null
|
||||||
|
private fun fahipayHandler(): FahipayTransferHandler =
|
||||||
|
fahipayHandler ?: FahipayTransferHandler(this, binding, viewModel).also { fahipayHandler = it }
|
||||||
|
|
||||||
/** Lazy: created the first time the user selects an MFAISA source account. */
|
/** Lazy: created the first time the user selects an MFAISA source account. */
|
||||||
private var mfaisaHandler: MfaisaTransferHandler? = null
|
private var mfaisaHandler: MfaisaTransferHandler? = null
|
||||||
private fun mfaisaHandler(): MfaisaTransferHandler =
|
private fun mfaisaHandler(): MfaisaTransferHandler =
|
||||||
@@ -509,7 +509,7 @@ class TransferFragment : Fragment() {
|
|||||||
binding.btnScanQr.visibility = View.VISIBLE
|
binding.btnScanQr.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startLookupLoading() {
|
internal fun startLookupLoading() {
|
||||||
val spinner = CircularProgressDrawable(requireContext()).apply {
|
val spinner = CircularProgressDrawable(requireContext()).apply {
|
||||||
setStyle(CircularProgressDrawable.DEFAULT)
|
setStyle(CircularProgressDrawable.DEFAULT)
|
||||||
setColorSchemeColors(com.google.android.material.color.MaterialColors.getColor(
|
setColorSchemeColors(com.google.android.material.color.MaterialColors.getColor(
|
||||||
@@ -520,7 +520,7 @@ class TransferFragment : Fragment() {
|
|||||||
binding.tilTo.isEnabled = false
|
binding.tilTo.isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopLookupLoading() {
|
internal fun stopLookupLoading() {
|
||||||
binding.tilTo.isEnabled = true
|
binding.tilTo.isEnabled = true
|
||||||
binding.tilTo.endIconDrawable = ContextCompat.getDrawable(requireContext(), android.R.drawable.ic_menu_search)
|
binding.tilTo.endIconDrawable = ContextCompat.getDrawable(requireContext(), android.R.drawable.ic_menu_search)
|
||||||
}
|
}
|
||||||
@@ -816,10 +816,9 @@ class TransferFragment : Fragment() {
|
|||||||
resolvedRecipientName = ""
|
resolvedRecipientName = ""
|
||||||
resolvedDestCurrency = ""
|
resolvedDestCurrency = ""
|
||||||
resolvedToOwnAccount = null
|
resolvedToOwnAccount = null
|
||||||
selectedFahipayService = null
|
fahipayHandler().clearState()
|
||||||
mfaisaHandler?.clearState()
|
mfaisaHandler?.clearState()
|
||||||
binding.cardToInfo.visibility = View.GONE
|
binding.cardToInfo.visibility = View.GONE
|
||||||
binding.layoutServiceSelector.visibility = View.INVISIBLE
|
|
||||||
binding.tilTo.visibility = View.VISIBLE
|
binding.tilTo.visibility = View.VISIBLE
|
||||||
binding.btnPickContact.visibility = View.VISIBLE
|
binding.btnPickContact.visibility = View.VISIBLE
|
||||||
binding.btnScanQr.visibility = View.VISIBLE
|
binding.btnScanQr.visibility = View.VISIBLE
|
||||||
@@ -887,11 +886,7 @@ class TransferFragment : Fragment() {
|
|||||||
|
|
||||||
// Fahipay source: only phone numbers are supported
|
// Fahipay source: only phone numbers are supported
|
||||||
if (selectedAccount?.bank == "FAHIPAY") {
|
if (selectedAccount?.bank == "FAHIPAY") {
|
||||||
if (AccountInputParser.detect(accountNumber) == AccountInputParser.InputType.PHONE) {
|
fahipayHandler().lookupRecipient(accountNumber)
|
||||||
lookupFahipayTarget(accountNumber)
|
|
||||||
} else {
|
|
||||||
binding.tilTo.error = getString(R.string.transfer_fahipay_phone_only)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -990,100 +985,7 @@ class TransferFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lookupFahipayTarget(number: String) {
|
internal fun prefillToDirectly(
|
||||||
startLookupLoading()
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
data class LookupResult(
|
|
||||||
val dhiraagu: DhiraaguClient.Result,
|
|
||||||
val ooredoo: OoredooClient.CustType
|
|
||||||
)
|
|
||||||
val result = withContext(Dispatchers.IO) {
|
|
||||||
if (number.startsWith("7")) {
|
|
||||||
// Dhiraagu first, fall back to Ooredoo
|
|
||||||
val d = try { DhiraaguClient().validateNumber(number) }
|
|
||||||
catch (_: Exception) { DhiraaguClient.Result(DhiraaguClient.CustType.UNSUPPORTED) }
|
|
||||||
val o = if (d.type == DhiraaguClient.CustType.UNSUPPORTED)
|
|
||||||
try { OoredooClient().validateNumber(number) }
|
|
||||||
catch (_: Exception) { OoredooClient.CustType.UNSUPPORTED }
|
|
||||||
else OoredooClient.CustType.UNSUPPORTED
|
|
||||||
LookupResult(d, o)
|
|
||||||
} else {
|
|
||||||
// Ooredoo first, fall back to Dhiraagu
|
|
||||||
val o = try { OoredooClient().validateNumber(number) }
|
|
||||||
catch (_: Exception) { OoredooClient.CustType.UNSUPPORTED }
|
|
||||||
val d = if (o == OoredooClient.CustType.UNSUPPORTED)
|
|
||||||
try { DhiraaguClient().validateNumber(number) }
|
|
||||||
catch (_: Exception) { DhiraaguClient.Result(DhiraaguClient.CustType.UNSUPPORTED) }
|
|
||||||
else DhiraaguClient.Result(DhiraaguClient.CustType.UNSUPPORTED)
|
|
||||||
LookupResult(d, o)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stopLookupLoading()
|
|
||||||
|
|
||||||
val dhiraaguName = result.dhiraagu.ownerName.takeIf { it.isNotBlank() }
|
|
||||||
|
|
||||||
// Collect all applicable services
|
|
||||||
val services = buildList {
|
|
||||||
if (result.dhiraagu.type == DhiraaguClient.CustType.RELOAD) add("DHIRAAGU_RELOAD")
|
|
||||||
if (result.dhiraagu.type == DhiraaguClient.CustType.BILL_PAY) add("DHIRAAGU_BILL")
|
|
||||||
if (result.ooredoo == OoredooClient.CustType.PRE || result.ooredoo == OoredooClient.CustType.HYBRID) add("RAASTAS")
|
|
||||||
if (result.ooredoo == OoredooClient.CustType.POST || result.ooredoo == OoredooClient.CustType.HYBRID) add("OOREDOO_BILL")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (services.isEmpty()) return@launch
|
|
||||||
|
|
||||||
// Only one option — auto-select, no chip UI needed
|
|
||||||
if (services.size == 1) {
|
|
||||||
selectFahipayService(services[0], number, dhiraaguName)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple options (Ooredoo HYBRID) — show chips
|
|
||||||
binding.chipDhiraaguReload.visibility = if ("DHIRAAGU_RELOAD" in services) View.VISIBLE else View.GONE
|
|
||||||
binding.chipDhiraaguBill.visibility = if ("DHIRAAGU_BILL" in services) View.VISIBLE else View.GONE
|
|
||||||
binding.chipRaastas.visibility = if ("RAASTAS" in services) View.VISIBLE else View.GONE
|
|
||||||
binding.chipOoredooBill.visibility = if ("OOREDOO_BILL" in services) View.VISIBLE else View.GONE
|
|
||||||
binding.layoutServiceSelector.visibility = View.VISIBLE
|
|
||||||
binding.chipGroupService.clearCheck()
|
|
||||||
|
|
||||||
binding.chipDhiraaguReload.setOnCheckedChangeListener { _, checked ->
|
|
||||||
if (checked) { selectFahipayService("DHIRAAGU_RELOAD", number, dhiraaguName); binding.layoutServiceSelector.visibility = View.INVISIBLE }
|
|
||||||
}
|
|
||||||
binding.chipDhiraaguBill.setOnCheckedChangeListener { _, checked ->
|
|
||||||
if (checked) { selectFahipayService("DHIRAAGU_BILL", number, dhiraaguName); binding.layoutServiceSelector.visibility = View.INVISIBLE }
|
|
||||||
}
|
|
||||||
binding.chipRaastas.setOnCheckedChangeListener { _, checked ->
|
|
||||||
if (checked) { selectFahipayService("RAASTAS", number); binding.layoutServiceSelector.visibility = View.INVISIBLE }
|
|
||||||
}
|
|
||||||
binding.chipOoredooBill.setOnCheckedChangeListener { _, checked ->
|
|
||||||
if (checked) { selectFahipayService("OOREDOO_BILL", number); binding.layoutServiceSelector.visibility = View.INVISIBLE }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun selectFahipayService(service: String, number: String, ownerName: String? = null) {
|
|
||||||
selectedFahipayService = service
|
|
||||||
val contacts = viewModel.contacts.value ?: emptyList()
|
|
||||||
val displayName = ownerName
|
|
||||||
?: contacts.firstOrNull { it.benefAccount == number }?.benefNickName
|
|
||||||
?: number
|
|
||||||
val serviceLabel = when (service) {
|
|
||||||
"RAASTAS" -> "Raastas"
|
|
||||||
"OOREDOO_BILL" -> "Ooredoo Bill Pay"
|
|
||||||
"DHIRAAGU_RELOAD" -> "Dhiraagu Reload"
|
|
||||||
"DHIRAAGU_BILL" -> "Dhiraagu Bill Pay"
|
|
||||||
else -> service
|
|
||||||
}
|
|
||||||
prefillToDirectly(
|
|
||||||
accountNumber = number,
|
|
||||||
displayName = displayName,
|
|
||||||
subtitle = "$serviceLabel · $number",
|
|
||||||
colorHex = "#FF6B00",
|
|
||||||
imageHash = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun prefillToDirectly(
|
|
||||||
accountNumber: String,
|
accountNumber: String,
|
||||||
displayName: String,
|
displayName: String,
|
||||||
subtitle: String,
|
subtitle: String,
|
||||||
@@ -1403,13 +1305,7 @@ class TransferFragment : Fragment() {
|
|||||||
bankNameCapture.equals("MADVMVMV", ignoreCase = true) -> "MIB"
|
bankNameCapture.equals("MADVMVMV", ignoreCase = true) -> "MIB"
|
||||||
bankNameCapture.isNotBlank() -> bankNameCapture
|
bankNameCapture.isNotBlank() -> bankNameCapture
|
||||||
isDestMib -> "MIB"
|
isDestMib -> "MIB"
|
||||||
else -> when (selectedFahipayService) {
|
else -> fahipayHandler().destinationLabel
|
||||||
"RAASTAS" -> "Ooredoo · Raastas"
|
|
||||||
"OOREDOO_BILL" -> "Ooredoo · Bill Pay"
|
|
||||||
"DHIRAAGU_RELOAD" -> "Dhiraagu · Reload"
|
|
||||||
"DHIRAAGU_BILL" -> "Dhiraagu · Bill Pay"
|
|
||||||
else -> ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val toDetail = listOfNotNull(toBankLabel.ifBlank { null }, toTypeLabel?.ifBlank { null }).joinToString(" · ")
|
val toDetail = listOfNotNull(toBankLabel.ifBlank { null }, toTypeLabel?.ifBlank { null }).joinToString(" · ")
|
||||||
|
|
||||||
@@ -1810,9 +1706,8 @@ class TransferFragment : Fragment() {
|
|||||||
resolvedBankName = ""
|
resolvedBankName = ""
|
||||||
resolvedDestCurrency = ""
|
resolvedDestCurrency = ""
|
||||||
resolvedToOwnAccount = null
|
resolvedToOwnAccount = null
|
||||||
selectedFahipayService = null
|
fahipayHandler().clearState()
|
||||||
binding.cardToInfo.visibility = View.GONE
|
binding.cardToInfo.visibility = View.GONE
|
||||||
binding.chipGroupService.visibility = View.GONE
|
|
||||||
binding.tilTo.visibility = View.VISIBLE
|
binding.tilTo.visibility = View.VISIBLE
|
||||||
binding.btnPickContact.visibility = View.VISIBLE
|
binding.btnPickContact.visibility = View.VISIBLE
|
||||||
binding.btnScanQr.visibility = View.VISIBLE
|
binding.btnScanQr.visibility = View.VISIBLE
|
||||||
@@ -1909,6 +1804,7 @@ class TransferFragment : Fragment() {
|
|||||||
// Clearing also resets any in-progress OTP flow, which cannot sensibly resume.
|
// Clearing also resets any in-progress OTP flow, which cannot sensibly resume.
|
||||||
bmlHandler?.clearState()
|
bmlHandler?.clearState()
|
||||||
bmlHandler = null
|
bmlHandler = null
|
||||||
|
fahipayHandler = null
|
||||||
mfaisaHandler?.clearState()
|
mfaisaHandler?.clearState()
|
||||||
mfaisaHandler = null
|
mfaisaHandler = null
|
||||||
// Unregistered automatically with viewLifecycleOwner; drop the stale handle.
|
// Unregistered automatically with viewLifecycleOwner; drop the stale handle.
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
package sh.sar.basedbank.ui.home.transfer
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import sh.sar.basedbank.R
|
||||||
|
import sh.sar.basedbank.api.dhiraagu.DhiraaguClient
|
||||||
|
import sh.sar.basedbank.api.fahipay.OoredooClient
|
||||||
|
import sh.sar.basedbank.databinding.FragmentTransferBinding
|
||||||
|
import sh.sar.basedbank.ui.home.HomeViewModel
|
||||||
|
import sh.sar.basedbank.ui.home.TransferFragment
|
||||||
|
import sh.sar.basedbank.util.AccountInputParser
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service a Fahipay wallet can pay out to. The carrier lookup decides which of these apply to
|
||||||
|
* a given number; [label] names it in the recipient card, [destinationLabel] in the confirm
|
||||||
|
* dialog's "To" block.
|
||||||
|
*
|
||||||
|
* Wallet-to-wallet Fahipay transfer is not here yet — there is no send path for it (see the
|
||||||
|
* class KDoc on [FahipayTransferHandler]). Add it as a constant once that lands, and the
|
||||||
|
* exhaustive `when`s over this enum will point at every site that needs updating.
|
||||||
|
*/
|
||||||
|
enum class FahipayService(val label: String, val destinationLabel: String) {
|
||||||
|
RAASTAS("Raastas", "Ooredoo · Raastas"),
|
||||||
|
OOREDOO_BILL("Ooredoo Bill Pay", "Ooredoo · Bill Pay"),
|
||||||
|
DHIRAAGU_RELOAD("Dhiraagu Reload", "Dhiraagu · Reload"),
|
||||||
|
DHIRAAGU_BILL("Dhiraagu Bill Pay", "Dhiraagu · Bill Pay"),
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Owns the Fahipay-only parts of the Transfer screen: the carrier lookup that turns a phone
|
||||||
|
* number into a set of payable services, the chip picker shown when more than one applies, and
|
||||||
|
* the selected service that the confirm dialog labels the destination with.
|
||||||
|
*
|
||||||
|
* Mirrors [BmlTransferHandler] / [MfaisaTransferHandler]: the fragment keeps the shared confirm
|
||||||
|
* dialog, the recipient card and the form state; the handler keeps everything Fahipay-specific.
|
||||||
|
*
|
||||||
|
* **There is no send path yet.** A Fahipay source currently falls through to the MIB branch of
|
||||||
|
* `initiateTransfer`, which signs the request with a MIB session. When the real payout API is
|
||||||
|
* wired up it belongs here, as a `doTransfer(...)` alongside the lookup — same shape as the
|
||||||
|
* other handlers.
|
||||||
|
*
|
||||||
|
* Lifetime is bound to the fragment's view: it captures [binding] + [viewModel] + [fragment]
|
||||||
|
* (for `viewLifecycleOwner` and Context) — and must be re-created when the view is recreated.
|
||||||
|
*/
|
||||||
|
class FahipayTransferHandler(
|
||||||
|
private val fragment: TransferFragment,
|
||||||
|
private val binding: FragmentTransferBinding,
|
||||||
|
private val viewModel: HomeViewModel,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val ctx get() = fragment.requireContext()
|
||||||
|
|
||||||
|
/** The service picked for the current recipient; null until a lookup resolves one. */
|
||||||
|
var service: FahipayService? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
/** How the confirm dialog names the destination, or "" when nothing is selected. */
|
||||||
|
val destinationLabel: String get() = service?.destinationLabel.orEmpty()
|
||||||
|
|
||||||
|
// ─── Public API the fragment calls ───────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a destination for a Fahipay source. Only phone numbers are payable, so anything
|
||||||
|
* else is rejected inline on the "To" field.
|
||||||
|
*/
|
||||||
|
fun lookupRecipient(rawInput: String) {
|
||||||
|
if (AccountInputParser.detect(rawInput) != AccountInputParser.InputType.PHONE) {
|
||||||
|
binding.tilTo.error = ctx.getString(R.string.transfer_fahipay_phone_only)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lookupCarrier(rawInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears the selected service and hides the chip picker. */
|
||||||
|
fun clearState() {
|
||||||
|
service = null
|
||||||
|
binding.layoutServiceSelector.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Carrier lookup ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private data class CarrierResult(
|
||||||
|
val dhiraagu: DhiraaguClient.Result,
|
||||||
|
val ooredoo: OoredooClient.CustType
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun lookupCarrier(number: String) {
|
||||||
|
fragment.startLookupLoading()
|
||||||
|
fragment.viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
val result = withContext(Dispatchers.IO) { queryCarriers(number) }
|
||||||
|
fragment.stopLookupLoading()
|
||||||
|
|
||||||
|
val dhiraaguName = result.dhiraagu.ownerName.takeIf { it.isNotBlank() }
|
||||||
|
val services = servicesFor(result)
|
||||||
|
|
||||||
|
if (services.isEmpty()) return@launch
|
||||||
|
|
||||||
|
// Only one option — auto-select, no chip UI needed
|
||||||
|
if (services.size == 1) {
|
||||||
|
selectService(services[0], number, dhiraaguName)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple options (Ooredoo HYBRID) — show chips
|
||||||
|
showServiceChips(services, number, dhiraaguName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the likelier carrier first based on the leading digit and only falls back to the
|
||||||
|
* other when the first says it doesn't know the number. Blocking — call from IO.
|
||||||
|
*/
|
||||||
|
private fun queryCarriers(number: String): CarrierResult =
|
||||||
|
if (number.startsWith("7")) {
|
||||||
|
// Dhiraagu first, fall back to Ooredoo
|
||||||
|
val d = dhiraagu(number)
|
||||||
|
val o = if (d.type == DhiraaguClient.CustType.UNSUPPORTED) ooredoo(number)
|
||||||
|
else OoredooClient.CustType.UNSUPPORTED
|
||||||
|
CarrierResult(d, o)
|
||||||
|
} else {
|
||||||
|
// Ooredoo first, fall back to Dhiraagu
|
||||||
|
val o = ooredoo(number)
|
||||||
|
val d = if (o == OoredooClient.CustType.UNSUPPORTED) dhiraagu(number)
|
||||||
|
else DhiraaguClient.Result(DhiraaguClient.CustType.UNSUPPORTED)
|
||||||
|
CarrierResult(d, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dhiraagu(number: String) =
|
||||||
|
try { DhiraaguClient().validateNumber(number) }
|
||||||
|
catch (_: Exception) { DhiraaguClient.Result(DhiraaguClient.CustType.UNSUPPORTED) }
|
||||||
|
|
||||||
|
private fun ooredoo(number: String) =
|
||||||
|
try { OoredooClient().validateNumber(number) }
|
||||||
|
catch (_: Exception) { OoredooClient.CustType.UNSUPPORTED }
|
||||||
|
|
||||||
|
private fun servicesFor(result: CarrierResult): List<FahipayService> = buildList {
|
||||||
|
if (result.dhiraagu.type == DhiraaguClient.CustType.RELOAD) add(FahipayService.DHIRAAGU_RELOAD)
|
||||||
|
if (result.dhiraagu.type == DhiraaguClient.CustType.BILL_PAY) add(FahipayService.DHIRAAGU_BILL)
|
||||||
|
if (result.ooredoo == OoredooClient.CustType.PRE || result.ooredoo == OoredooClient.CustType.HYBRID) add(FahipayService.RAASTAS)
|
||||||
|
if (result.ooredoo == OoredooClient.CustType.POST || result.ooredoo == OoredooClient.CustType.HYBRID) add(FahipayService.OOREDOO_BILL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Service picker ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private fun showServiceChips(
|
||||||
|
services: List<FahipayService>,
|
||||||
|
number: String,
|
||||||
|
dhiraaguName: String?
|
||||||
|
) {
|
||||||
|
binding.chipDhiraaguReload.visibility = visibilityFor(FahipayService.DHIRAAGU_RELOAD in services)
|
||||||
|
binding.chipDhiraaguBill.visibility = visibilityFor(FahipayService.DHIRAAGU_BILL in services)
|
||||||
|
binding.chipRaastas.visibility = visibilityFor(FahipayService.RAASTAS in services)
|
||||||
|
binding.chipOoredooBill.visibility = visibilityFor(FahipayService.OOREDOO_BILL in services)
|
||||||
|
binding.layoutServiceSelector.visibility = View.VISIBLE
|
||||||
|
binding.chipGroupService.clearCheck()
|
||||||
|
|
||||||
|
// Dhiraagu is the only carrier that hands back an owner name, so the Ooredoo chips
|
||||||
|
// resolve their display name from saved contacts instead.
|
||||||
|
bindChip(binding.chipDhiraaguReload, FahipayService.DHIRAAGU_RELOAD, number, dhiraaguName)
|
||||||
|
bindChip(binding.chipDhiraaguBill, FahipayService.DHIRAAGU_BILL, number, dhiraaguName)
|
||||||
|
bindChip(binding.chipRaastas, FahipayService.RAASTAS, number, null)
|
||||||
|
bindChip(binding.chipOoredooBill, FahipayService.OOREDOO_BILL, number, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindChip(
|
||||||
|
chip: com.google.android.material.chip.Chip,
|
||||||
|
picked: FahipayService,
|
||||||
|
number: String,
|
||||||
|
ownerName: String?
|
||||||
|
) {
|
||||||
|
chip.setOnCheckedChangeListener { _, checked ->
|
||||||
|
if (checked) {
|
||||||
|
selectService(picked, number, ownerName)
|
||||||
|
binding.layoutServiceSelector.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun visibilityFor(shown: Boolean) = if (shown) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
private fun selectService(picked: FahipayService, number: String, ownerName: String?) {
|
||||||
|
service = picked
|
||||||
|
val contacts = viewModel.contacts.value ?: emptyList()
|
||||||
|
val displayName = ownerName
|
||||||
|
?: contacts.firstOrNull { it.benefAccount == number }?.benefNickName
|
||||||
|
?: number
|
||||||
|
fragment.prefillToDirectly(
|
||||||
|
accountNumber = number,
|
||||||
|
displayName = displayName,
|
||||||
|
subtitle = "${picked.label} · $number",
|
||||||
|
colorHex = "#FF6B00",
|
||||||
|
imageHash = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user