Fix push device registration
This commit is contained in:
@@ -22,8 +22,8 @@ android {
|
||||
applicationId = "xyz.kusoft.qmax"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 48
|
||||
versionName = "0.1.47"
|
||||
versionCode = 49
|
||||
versionName = "0.1.48"
|
||||
|
||||
buildConfigField("String", "QMAX_DEFAULT_SERVER_URL", "\"https://qmax.kusoft.xyz\"")
|
||||
buildConfigField("String", "QMAX_DEFAULT_PAIRING_CODE", "\"qmax-MxRq4h2HQBEIFs6k\"")
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.ClipData
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import androidx.core.content.FileProvider
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -268,23 +269,27 @@ class QMaxRepository(
|
||||
return withFreshSession(session) { api.submitMaxCode(it.serverUrl, it.accessToken, code) }
|
||||
}
|
||||
|
||||
suspend fun registerCurrentPushDevice(session: QMaxSession) {
|
||||
suspend fun registerCurrentPushDevice(session: QMaxSession): Boolean {
|
||||
if (!FirebaseBootstrap.ensureInitialized(context)) {
|
||||
return
|
||||
Log.w(PushLogTag, "Firebase is not initialized; push device registration skipped")
|
||||
return false
|
||||
}
|
||||
|
||||
val token = FirebaseMessaging.getInstance().token.await()
|
||||
registerPushDevice(session, token)
|
||||
return registerPushDevice(session, token)
|
||||
}
|
||||
|
||||
suspend fun registerPushDevice(session: QMaxSession, firebaseToken: String) {
|
||||
suspend fun registerPushDevice(session: QMaxSession, firebaseToken: String): Boolean {
|
||||
if (firebaseToken.isBlank()) {
|
||||
return
|
||||
Log.w(PushLogTag, "Firebase returned a blank FCM token")
|
||||
return false
|
||||
}
|
||||
|
||||
withFreshSession(session) {
|
||||
api.registerPushDevice(it.serverUrl, it.accessToken, firebaseToken)
|
||||
}
|
||||
Log.i(PushLogTag, "Push device token registered with QMAX API")
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun sharePayload(session: QMaxSession, text: String?, attachments: List<AttachmentDto>) {
|
||||
@@ -494,6 +499,8 @@ class QMaxRepository(
|
||||
}
|
||||
}
|
||||
|
||||
private const val PushLogTag = "QMaxPush"
|
||||
|
||||
private data class SharedAttachment(
|
||||
val attachment: AttachmentDto,
|
||||
val file: File,
|
||||
|
||||
@@ -70,6 +70,8 @@ class QMaxViewModel(private val repository: QMaxRepository) : ViewModel() {
|
||||
private var chatSearchJob: Job? = null
|
||||
private var autoLoginJob: Job? = null
|
||||
private var pushRegisteredForToken: String? = null
|
||||
private var pushRegistrationJob: Job? = null
|
||||
private var pushRegistrationInFlightForToken: String? = null
|
||||
private var autoLoginAttempted = false
|
||||
private var pendingOpenChatId: String? = null
|
||||
private val locallyReadChatFingerprints = mutableMapOf<String, String>()
|
||||
@@ -97,6 +99,9 @@ class QMaxViewModel(private val repository: QMaxRepository) : ViewModel() {
|
||||
realtimeConnectJob?.cancel()
|
||||
messagePollJob?.cancel()
|
||||
presencePollJob?.cancel()
|
||||
pushRegistrationJob?.cancel()
|
||||
pushRegistrationInFlightForToken = null
|
||||
pushRegisteredForToken = null
|
||||
realtime.disconnect()
|
||||
if (!autoLoginAttempted && state.value.pairingCode.isNotBlank()) {
|
||||
autoLoginAttempted = true
|
||||
@@ -843,11 +848,46 @@ class QMaxViewModel(private val repository: QMaxRepository) : ViewModel() {
|
||||
if (pushRegisteredForToken == registrationKey) {
|
||||
return
|
||||
}
|
||||
if (pushRegistrationJob?.isActive == true && pushRegistrationInFlightForToken == registrationKey) {
|
||||
return
|
||||
}
|
||||
|
||||
pushRegisteredForToken = registrationKey
|
||||
viewModelScope.launch {
|
||||
runCatching {
|
||||
repository.registerCurrentPushDevice(session)
|
||||
pushRegistrationJob?.cancel()
|
||||
pushRegistrationInFlightForToken = registrationKey
|
||||
pushRegistrationJob = viewModelScope.launch {
|
||||
try {
|
||||
var retryDelayMs = 0L
|
||||
for (attempt in 1..PushRegistrationAttempts) {
|
||||
if (retryDelayMs > 0) {
|
||||
delay(retryDelayMs)
|
||||
}
|
||||
|
||||
val registered = try {
|
||||
repository.registerCurrentPushDevice(session)
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (error: Throwable) {
|
||||
Log.w(LogTag, "push registration failed, attempt=$attempt", error)
|
||||
false
|
||||
}
|
||||
|
||||
if (registered) {
|
||||
pushRegisteredForToken = registrationKey
|
||||
Log.i(LogTag, "push registration completed")
|
||||
return@launch
|
||||
}
|
||||
|
||||
Log.w(LogTag, "push registration was not completed, attempt=$attempt")
|
||||
retryDelayMs = if (retryDelayMs == 0L) {
|
||||
InitialPushRegistrationRetryMs
|
||||
} else {
|
||||
(retryDelayMs * 2).coerceAtMost(MaxPushRegistrationRetryMs)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (pushRegistrationInFlightForToken == registrationKey) {
|
||||
pushRegistrationInFlightForToken = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1036,5 +1076,8 @@ class QMaxViewModel(private val repository: QMaxRepository) : ViewModel() {
|
||||
const val ChatListTimeoutMs = 45_000L
|
||||
const val MessageSyncTimeoutMs = 120_000L
|
||||
const val AutoLoginDelayMs = 1_000L
|
||||
const val PushRegistrationAttempts = 6
|
||||
const val InitialPushRegistrationRetryMs = 15_000L
|
||||
const val MaxPushRegistrationRetryMs = 120_000L
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user