Merge pull request #4598 from nextcloud/bugfix/4503/fixTooManyRequestsException

fix TooManyRequestsException
This commit is contained in:
Marcel Hibbe 2025-01-16 14:18:02 +00:00 committed by GitHub
commit 383546eb97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 76 additions and 68 deletions

View File

@ -14,6 +14,7 @@ import android.util.Log
import android.util.TypedValue
import android.view.View
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.lifecycleScope
import autodagger.AutoInjector
import coil.load
import com.google.android.flexbox.FlexboxLayout
@ -133,8 +134,10 @@ class OutcomingTextMessageViewHolder(itemView: View) :
updateStatus(R.drawable.ic_check, context.resources?.getString(R.string.nc_message_sent))
}
CoroutineScope(Dispatchers.Main).launch {
if (message.isTemporary && !networkMonitor.isOnline.first()) {
val chatActivity = commonMessageInterface as ChatActivity
chatActivity.lifecycleScope.launch {
if (message.isTemporary && !networkMonitor.isOnline.value) {
updateStatus(
R.drawable.ic_signal_wifi_off_white_24dp,
context.resources?.getString(R.string.nc_message_offline)

View File

@ -201,7 +201,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -452,7 +451,7 @@ class ChatActivity :
this.lifecycleScope.launch {
delay(DELAY_TO_SHOW_PROGRESS_BAR)
if (adapter?.isEmpty == true && networkMonitor.isOnline.first()) {
if (adapter?.isEmpty == true && networkMonitor.isOnline.value) {
binding.progressBar.visibility = View.VISIBLE
}
}
@ -927,7 +926,7 @@ class ChatActivity :
chatViewModel.getGeneralUIFlow.onEach { key ->
when (key) {
NO_OFFLINE_MESSAGES_FOUND -> {
if (networkMonitor.isOnline.first().not()) {
if (networkMonitor.isOnline.value.not()) {
binding.offline.root.visibility = View.VISIBLE
}
}

View File

@ -54,7 +54,7 @@ class OfflineFirstChatRepository @Inject constructor(
private val chatDao: ChatMessagesDao,
private val chatBlocksDao: ChatBlocksDao,
private val network: ChatNetworkDataSource,
private val monitor: NetworkMonitor,
private val networkMonitor: NetworkMonitor,
userProvider: CurrentUserProviderNew
) : ChatMessageRepository {
@ -303,7 +303,7 @@ class OfflineFirstChatRepository @Inject constructor(
var showUnreadMessagesMarker = true
while (true) {
if (!monitor.isOnline.first() || itIsPaused) {
if (!networkMonitor.isOnline.value || itIsPaused) {
Thread.sleep(HALF_SECOND)
} else {
// sync database with server
@ -530,7 +530,7 @@ class OfflineFirstChatRepository @Inject constructor(
}
private suspend fun sync(bundle: Bundle): List<ChatMessageEntity>? {
if (!monitor.isOnline.first()) {
if (!networkMonitor.isOnline.value) {
Log.d(TAG, "Device is offline, can't load chat messages from server")
return null
}
@ -810,7 +810,7 @@ class OfflineFirstChatRepository @Inject constructor(
sendWithoutNotification: Boolean,
referenceId: String
): Flow<Result<ChatMessage?>> {
if (!monitor.isOnline.first()) {
if (!networkMonitor.isOnline.value) {
return flow {
emit(Result.failure(IOException("Skipped to send message as device is offline")))
}

View File

@ -138,7 +138,6 @@ import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.BehaviorSubject
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.apache.commons.lang3.builder.CompareToBuilder
@ -1359,7 +1358,7 @@ class ConversationsListActivity :
override fun onItemLongClick(position: Int) {
this.lifecycleScope.launch {
if (showShareToScreen || !networkMonitor.isOnline.first()) {
if (showShareToScreen || !networkMonitor.isOnline.value) {
Log.d(TAG, "sharing to multiple rooms not yet implemented. onItemLongClick is ignored.")
} else {
val clickedItem: Any? = adapter!!.getItem(position)

View File

@ -39,7 +39,7 @@ class OfflineFirstConversationsRepository @Inject constructor(
private val dao: ConversationsDao,
private val network: ConversationsNetworkDataSource,
private val chatNetworkDataSource: ChatNetworkDataSource,
private val monitor: NetworkMonitor,
private val networkMonitor: NetworkMonitor,
private val currentUserProviderNew: CurrentUserProviderNew
) : OfflineConversationsRepository {
override val roomListFlow: Flow<List<ConversationModel>>
@ -58,7 +58,7 @@ class OfflineFirstConversationsRepository @Inject constructor(
val initialConversationModels = getListOfConversations(user.id!!)
_roomListFlow.emit(initialConversationModels)
if (monitor.isOnline.first()) {
if (networkMonitor.isOnline.value) {
val conversationEntitiesFromSync = getRoomsFromServer()
if (!conversationEntitiesFromSync.isNullOrEmpty()) {
val conversationModelsFromSync = conversationEntitiesFromSync.map(ConversationEntity::asModel)
@ -108,7 +108,7 @@ class OfflineFirstConversationsRepository @Inject constructor(
private suspend fun getRoomsFromServer(): List<ConversationEntity>? {
var conversationsFromSync: List<ConversationEntity>? = null
if (!monitor.isOnline.first()) {
if (!networkMonitor.isOnline.value) {
Log.d(TAG, "Device is offline, can't load conversations from server")
return null
}

View File

@ -2,13 +2,14 @@
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
* SPDX-FileCopyrightText: 2024 Marcel Hibbe <dev@mhibbe.de>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.data.network
import androidx.lifecycle.LiveData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/**
* Utility for reporting app connectivity status.
@ -17,7 +18,7 @@ interface NetworkMonitor {
/**
* Returns the device's current connectivity status.
*/
val isOnline: Flow<Boolean>
val isOnline: StateFlow<Boolean>
/**
* Returns the device's current connectivity status as LiveData for better interop with Java code.

View File

@ -2,6 +2,7 @@
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
* SPDX-FileCopyrightText: 2024 Marcel Hibbe <dev@mhibbe.de>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
@ -11,17 +12,17 @@ import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest.Builder
import android.util.Log
import androidx.core.content.getSystemService
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
import javax.inject.Singleton
@ -29,49 +30,57 @@ import javax.inject.Singleton
class NetworkMonitorImpl @Inject constructor(
private val context: Context
) : NetworkMonitor {
private val connectivityManager = context.getSystemService<ConnectivityManager>()!!
override val isOnlineLiveData: LiveData<Boolean>
get() = isOnline.asLiveData()
override val isOnline: Flow<Boolean> = callbackFlow {
val connectivityManager = context.getSystemService<ConnectivityManager>()
if (connectivityManager == null) {
channel.trySend(false)
channel.close()
return@callbackFlow
}
override val isOnline: StateFlow<Boolean> get() = _isOnline
val networkRequest = Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
private val _isOnline: StateFlow<Boolean> = callbackFlow {
val callback = object : ConnectivityManager.NetworkCallback() {
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities)
val connected = networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_VALIDATED
)
trySend(connected)
Log.d(TAG, "Network status changed: $connected")
}
val networkCallback = object : ConnectivityManager.NetworkCallback() {
private val networks = mutableSetOf<Network>()
override fun onAvailable(network: Network) {
networks += network
channel.trySend(true)
override fun onUnavailable() {
super.onUnavailable()
trySend(false)
Log.d(TAG, "Network status: onUnavailable")
}
override fun onLost(network: Network) {
networks -= network
channel.trySend(networks.isNotEmpty())
super.onLost(network)
trySend(false)
Log.d(TAG, "Network status: onLost")
}
override fun onAvailable(network: Network) {
super.onAvailable(network)
trySend(true)
Log.d(TAG, "Network status: onAvailable")
}
}
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
channel.trySend(connectivityManager.isCurrentlyConnected())
connectivityManager.registerDefaultNetworkCallback(callback)
awaitClose {
connectivityManager.unregisterNetworkCallback(networkCallback)
connectivityManager.unregisterNetworkCallback(callback)
}
}
.distinctUntilChanged()
.flowOn(Dispatchers.IO)
.conflate()
}.stateIn(
CoroutineScope(Dispatchers.IO),
SharingStarted.WhileSubscribed(COROUTINE_TIMEOUT),
false
)
private fun ConnectivityManager.isCurrentlyConnected() =
activeNetwork
?.let(::getNetworkCapabilities)
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
companion object {
private val TAG = NetworkMonitorImpl::class.java.simpleName
private const val COROUTINE_TIMEOUT = 5000L
}
}

View File

@ -51,7 +51,6 @@ import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import java.util.Date
import javax.inject.Inject
@ -134,7 +133,7 @@ class MessageActionsDialog(
initMenuAddToNote(
!message.isDeleted &&
!ConversationUtils.isNoteToSelfConversation(currentConversation) &&
networkMonitor.isOnline.first(),
networkMonitor.isOnline.value,
state.roomToken
)
}
@ -147,16 +146,16 @@ class MessageActionsDialog(
}
}
initMenuItems()
initMenuItems(networkMonitor.isOnline.value)
}
private fun initMenuItems() {
private fun initMenuItems(isOnline: Boolean) {
this.lifecycleScope.launch {
initMenuItemTranslate(
!message.isDeleted &&
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() &&
CapabilitiesUtil.isTranslationsSupported(spreedCapabilities) &&
networkMonitor.isOnline.first()
isOnline
)
initMenuEditorDetails(message.lastEditTimestamp!! != 0L && !message.isDeleted)
initMenuReplyToMessage(message.replyable && hasChatPermission)
@ -165,29 +164,29 @@ class MessageActionsDialog(
hasUserId(user) &&
hasUserActorId(message) &&
currentConversation?.type != ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL &&
networkMonitor.isOnline.first()
isOnline
)
initMenuEditMessage(isMessageEditable)
initMenuDeleteMessage(showMessageDeletionButton && networkMonitor.isOnline.first())
initMenuDeleteMessage(showMessageDeletionButton && isOnline)
initMenuForwardMessage(
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() &&
!(message.isDeletedCommentMessage || message.isDeleted) &&
networkMonitor.isOnline.first()
isOnline
)
initMenuRemindMessage(
!message.isDeleted &&
hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.REMIND_ME_LATER) &&
networkMonitor.isOnline.first()
isOnline
)
initMenuMarkAsUnread(
message.previousMessageId > NO_PREVIOUS_MESSAGE_ID &&
ChatMessage.MessageType.SYSTEM_MESSAGE != message.getCalculateMessageType() &&
networkMonitor.isOnline.first()
isOnline
)
initMenuShare(messageHasFileAttachment || messageHasRegularText && networkMonitor.isOnline.first())
initMenuShare(messageHasFileAttachment || messageHasRegularText && isOnline)
initMenuItemOpenNcApp(
ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE == message.getCalculateMessageType() &&
networkMonitor.isOnline.first()
isOnline
)
initMenuItemSave(message.getCalculateMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE)
}

View File

@ -23,7 +23,6 @@ import com.nextcloud.talk.databinding.DialogTempMessageActionsBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -59,10 +58,9 @@ class TempMessageActionsDialog(
private fun initMenuItems() {
this.lifecycleScope.launch {
val isOnline = networkMonitor.isOnline.first()
initResendMessage(message.sendingFailed && isOnline)
initMenuEditMessage(message.sendingFailed || !isOnline)
initMenuDeleteMessage(message.sendingFailed || !isOnline)
initResendMessage(message.sendingFailed && networkMonitor.isOnline.value)
initMenuEditMessage(message.sendingFailed || !networkMonitor.isOnline.value)
initMenuDeleteMessage(message.sendingFailed || !networkMonitor.isOnline.value)
initMenuItemCopy()
}
}