forked from thijooree/android
optimize bml refresh flow
This commit is contained in:
@@ -39,7 +39,6 @@ import sh.sar.basedbank.BasedBankApp
|
||||
import sh.sar.basedbank.R
|
||||
import sh.sar.basedbank.api.bml.AuthExpiredException
|
||||
import sh.sar.basedbank.api.bml.BmlAccountClient
|
||||
import sh.sar.basedbank.api.bml.BmlActivationResult
|
||||
import sh.sar.basedbank.api.bml.BmlContactsClient
|
||||
import sh.sar.basedbank.api.bml.BmlForeignLimitsClient
|
||||
import sh.sar.basedbank.api.bml.BmlLoanDetail
|
||||
@@ -569,34 +568,63 @@ fun applyNavLabelVisibility() {
|
||||
}
|
||||
|
||||
// One async job per BML login, all run in parallel
|
||||
val bmlJobs = bmlLoginIds.mapNotNull { loginId ->
|
||||
val creds = store.loadBmlCredentials(loginId) ?: return@mapNotNull null
|
||||
val bmlJobs = bmlLoginIds.map { loginId ->
|
||||
loginId to async(Dispatchers.IO) {
|
||||
val loginTag = "bml_$loginId"
|
||||
val app = application as BasedBankApp
|
||||
val savedProfiles = store.loadBmlProfiles(loginId)
|
||||
val allAccounts = mutableListOf<BankAccount>()
|
||||
var anyExpired = savedProfiles.isEmpty()
|
||||
|
||||
// Try each saved profile's cached session
|
||||
for (profile in savedProfiles) {
|
||||
val saved = store.loadBmlProfileSession(profile.profileId)
|
||||
if (saved != null) {
|
||||
try {
|
||||
val session = BmlSession(saved.first, saved.second)
|
||||
val accounts = BmlAccountClient().fetchAccounts(session, loginTag, profile.name, profile.profileId)
|
||||
app.bmlSessions[profile.profileId] = session
|
||||
allAccounts += accounts
|
||||
} catch (_: AuthExpiredException) { anyExpired = true
|
||||
} catch (_: Exception) { anyExpired = true }
|
||||
} else {
|
||||
anyExpired = true
|
||||
}
|
||||
}
|
||||
|
||||
if (savedProfiles.isNotEmpty()) app.bmlProfilesMap[loginId] = savedProfiles
|
||||
|
||||
// Also try legacy single-profile session token (pre-multi-profile installs)
|
||||
val bmlClient = BmlAccountClient()
|
||||
for (profile in savedProfiles) {
|
||||
val saved = store.loadBmlProfileSession(profile.profileId)
|
||||
val refreshToken = store.loadBmlProfileRefreshToken(profile.profileId)
|
||||
if (saved == null) {
|
||||
allAccounts += AccountCache.loadBml(this@HomeActivity, loginId)
|
||||
.filter { it.profileId == profile.profileId }
|
||||
continue
|
||||
}
|
||||
val expiresAt = store.loadBmlProfileExpiresAt(profile.profileId)
|
||||
val tokenKnownExpired = expiresAt > 0L && System.currentTimeMillis() >= expiresAt
|
||||
|
||||
suspend fun fetchWithSession(session: BmlSession) {
|
||||
bmlClient.checkProfile(session)
|
||||
val accounts = bmlClient.fetchAccounts(session, loginTag, profile.name, profile.profileId)
|
||||
app.bmlSessions[profile.profileId] = session
|
||||
allAccounts += accounts
|
||||
}
|
||||
|
||||
suspend fun tryRefresh() {
|
||||
if (refreshToken == null) throw Exception("No refresh token")
|
||||
val oldSession = BmlSession(saved.first, saved.second, refreshToken)
|
||||
val newSession = app.bmlFlowFor(loginId).refreshSession(oldSession)
|
||||
store.saveBmlProfileSession(profile.profileId, newSession.accessToken, newSession.deviceId)
|
||||
if (newSession.refreshToken.isNotBlank())
|
||||
store.saveBmlProfileRefreshToken(profile.profileId, newSession.refreshToken)
|
||||
if (newSession.expiresAt > 0)
|
||||
store.saveBmlProfileExpiresAt(profile.profileId, newSession.expiresAt)
|
||||
fetchWithSession(newSession)
|
||||
}
|
||||
|
||||
try {
|
||||
if (tokenKnownExpired) {
|
||||
tryRefresh()
|
||||
} else {
|
||||
try {
|
||||
fetchWithSession(BmlSession(saved.first, saved.second))
|
||||
} catch (_: AuthExpiredException) {
|
||||
tryRefresh()
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
allAccounts += AccountCache.loadBml(this@HomeActivity, loginId)
|
||||
.filter { it.profileId == profile.profileId }
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy single-profile session (pre-multi-profile installs)
|
||||
if (savedProfiles.isEmpty()) {
|
||||
val legacyToken = store.loadBmlSession(loginId)
|
||||
if (legacyToken != null) {
|
||||
@@ -605,47 +633,11 @@ fun applyNavLabelVisibility() {
|
||||
val accounts = BmlAccountClient().fetchAccounts(session, loginTag)
|
||||
app.bmlSessions[loginId] = session
|
||||
allAccounts += accounts
|
||||
anyExpired = false
|
||||
} catch (_: AuthExpiredException) { anyExpired = true
|
||||
} catch (_: Exception) { anyExpired = true }
|
||||
}
|
||||
}
|
||||
|
||||
if (anyExpired || allAccounts.isEmpty()) {
|
||||
// Re-authenticate to refresh personal profile sessions
|
||||
try {
|
||||
val flow = app.bmlFlowFor(loginId)
|
||||
val profiles = flow.login(creds.username, creds.password, creds.otpSeed)
|
||||
store.saveBmlProfiles(loginId, profiles)
|
||||
app.bmlProfilesMap[loginId] = profiles
|
||||
|
||||
for (profile in profiles) {
|
||||
if (profile.profileType == "business") {
|
||||
// Can't activate business profiles without user OTP — use cached
|
||||
val cached = AccountCache.loadBml(this@HomeActivity, loginId)
|
||||
.filter { it.profileId == profile.profileId }
|
||||
if (allAccounts.none { it.profileId == profile.profileId })
|
||||
allAccounts += cached
|
||||
continue
|
||||
}
|
||||
try {
|
||||
val result = flow.activateProfile(profile, loginTag)
|
||||
if (result is BmlActivationResult.Success) {
|
||||
store.saveBmlProfileSession(profile.profileId, result.session.accessToken, result.session.deviceId)
|
||||
app.bmlSessions[profile.profileId] = result.session
|
||||
allAccounts.removeAll { it.profileId == profile.profileId }
|
||||
allAccounts += result.accounts
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
if (allAccounts.none { it.profileId == profile.profileId }) {
|
||||
allAccounts += AccountCache.loadBml(this@HomeActivity, loginId)
|
||||
.filter { it.profileId == profile.profileId }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
if (allAccounts.isEmpty())
|
||||
} catch (_: Exception) {
|
||||
allAccounts += AccountCache.loadBml(this@HomeActivity, loginId)
|
||||
}
|
||||
} else {
|
||||
allAccounts += AccountCache.loadBml(this@HomeActivity, loginId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -433,6 +433,10 @@ class SettingsLoginsFragment : Fragment() {
|
||||
return when (activationResult) {
|
||||
is BmlActivationResult.Success -> {
|
||||
store.saveBmlProfileSession(profile.profileId, activationResult.session.accessToken, activationResult.session.deviceId)
|
||||
if (activationResult.session.refreshToken.isNotBlank())
|
||||
store.saveBmlProfileRefreshToken(profile.profileId, activationResult.session.refreshToken)
|
||||
if (activationResult.session.expiresAt > 0)
|
||||
store.saveBmlProfileExpiresAt(profile.profileId, activationResult.session.expiresAt)
|
||||
true
|
||||
}
|
||||
is BmlActivationResult.NeedsBusinessOtp ->
|
||||
@@ -475,6 +479,10 @@ class SettingsLoginsFragment : Fragment() {
|
||||
}
|
||||
verifyProgress.dismiss()
|
||||
store.saveBmlProfileSession(profile.profileId, session.accessToken, session.deviceId)
|
||||
if (session.refreshToken.isNotBlank())
|
||||
store.saveBmlProfileRefreshToken(profile.profileId, session.refreshToken)
|
||||
if (session.expiresAt > 0)
|
||||
store.saveBmlProfileExpiresAt(profile.profileId, session.expiresAt)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
verifyProgress.dismiss()
|
||||
|
||||
@@ -267,6 +267,10 @@ class CredentialsFragment : Fragment() {
|
||||
bmlAccumulatedAccounts += result.accounts
|
||||
val store = CredentialStore(requireContext())
|
||||
store.saveBmlProfileSession(profile.profileId, result.session.accessToken, result.session.deviceId)
|
||||
if (result.session.refreshToken.isNotBlank())
|
||||
store.saveBmlProfileRefreshToken(profile.profileId, result.session.refreshToken)
|
||||
if (result.session.expiresAt > 0)
|
||||
store.saveBmlProfileExpiresAt(profile.profileId, result.session.expiresAt)
|
||||
val app = requireActivity().application as BasedBankApp
|
||||
app.bmlSessions[profile.profileId] = result.session
|
||||
}
|
||||
@@ -326,8 +330,16 @@ class CredentialsFragment : Fragment() {
|
||||
val session = app.bmlSessions.remove(oldId)
|
||||
if (session != null) {
|
||||
app.bmlSessions[customerId] = session
|
||||
val savedRefresh = store.loadBmlProfileRefreshToken(oldId)
|
||||
val savedExpiry = store.loadBmlProfileExpiresAt(oldId)
|
||||
store.clearBmlProfileSession(oldId)
|
||||
store.saveBmlProfileSession(customerId, session.accessToken, session.deviceId)
|
||||
if (session.refreshToken.isNotBlank())
|
||||
store.saveBmlProfileRefreshToken(customerId, session.refreshToken)
|
||||
else if (savedRefresh != null)
|
||||
store.saveBmlProfileRefreshToken(customerId, savedRefresh)
|
||||
val expiryToSave = if (session.expiresAt > 0) session.expiresAt else savedExpiry
|
||||
if (expiryToSave > 0) store.saveBmlProfileExpiresAt(customerId, expiryToSave)
|
||||
}
|
||||
// Update stored profile list with the real ID
|
||||
val updatedProfiles = profiles.map {
|
||||
|
||||
Reference in New Issue
Block a user