Skip to content

Commit 383546e

Browse files
authored
Merge pull request #4598 from nextcloud/bugfix/4503/fixTooManyRequestsException
fix TooManyRequestsException
2 parents 35d18cc + 69ddeaa commit 383546e

File tree

9 files changed

+76
-68
lines changed

9 files changed

+76
-68
lines changed

app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import android.util.Log
1414
import android.util.TypedValue
1515
import android.view.View
1616
import androidx.core.content.res.ResourcesCompat
17+
import androidx.lifecycle.lifecycleScope
1718
import autodagger.AutoInjector
1819
import coil.load
1920
import com.google.android.flexbox.FlexboxLayout
@@ -133,8 +134,10 @@ class OutcomingTextMessageViewHolder(itemView: View) :
133134
updateStatus(R.drawable.ic_check, context.resources?.getString(R.string.nc_message_sent))
134135
}
135136

136-
CoroutineScope(Dispatchers.Main).launch {
137-
if (message.isTemporary && !networkMonitor.isOnline.first()) {
137+
val chatActivity = commonMessageInterface as ChatActivity
138+
139+
chatActivity.lifecycleScope.launch {
140+
if (message.isTemporary && !networkMonitor.isOnline.value) {
138141
updateStatus(
139142
R.drawable.ic_signal_wifi_off_white_24dp,
140143
context.resources?.getString(R.string.nc_message_offline)

app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,6 @@ import kotlinx.coroutines.CoroutineScope
201201
import kotlinx.coroutines.Dispatchers
202202
import kotlinx.coroutines.delay
203203
import kotlinx.coroutines.flow.collect
204-
import kotlinx.coroutines.flow.first
205204
import kotlinx.coroutines.flow.onEach
206205
import kotlinx.coroutines.launch
207206
import kotlinx.coroutines.withContext
@@ -452,7 +451,7 @@ class ChatActivity :
452451

453452
this.lifecycleScope.launch {
454453
delay(DELAY_TO_SHOW_PROGRESS_BAR)
455-
if (adapter?.isEmpty == true && networkMonitor.isOnline.first()) {
454+
if (adapter?.isEmpty == true && networkMonitor.isOnline.value) {
456455
binding.progressBar.visibility = View.VISIBLE
457456
}
458457
}
@@ -927,7 +926,7 @@ class ChatActivity :
927926
chatViewModel.getGeneralUIFlow.onEach { key ->
928927
when (key) {
929928
NO_OFFLINE_MESSAGES_FOUND -> {
930-
if (networkMonitor.isOnline.first().not()) {
929+
if (networkMonitor.isOnline.value.not()) {
931930
binding.offline.root.visibility = View.VISIBLE
932931
}
933932
}

app/src/main/java/com/nextcloud/talk/chat/data/network/OfflineFirstChatRepository.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class OfflineFirstChatRepository @Inject constructor(
5454
private val chatDao: ChatMessagesDao,
5555
private val chatBlocksDao: ChatBlocksDao,
5656
private val network: ChatNetworkDataSource,
57-
private val monitor: NetworkMonitor,
57+
private val networkMonitor: NetworkMonitor,
5858
userProvider: CurrentUserProviderNew
5959
) : ChatMessageRepository {
6060

@@ -303,7 +303,7 @@ class OfflineFirstChatRepository @Inject constructor(
303303
var showUnreadMessagesMarker = true
304304

305305
while (true) {
306-
if (!monitor.isOnline.first() || itIsPaused) {
306+
if (!networkMonitor.isOnline.value || itIsPaused) {
307307
Thread.sleep(HALF_SECOND)
308308
} else {
309309
// sync database with server
@@ -530,7 +530,7 @@ class OfflineFirstChatRepository @Inject constructor(
530530
}
531531

532532
private suspend fun sync(bundle: Bundle): List<ChatMessageEntity>? {
533-
if (!monitor.isOnline.first()) {
533+
if (!networkMonitor.isOnline.value) {
534534
Log.d(TAG, "Device is offline, can't load chat messages from server")
535535
return null
536536
}
@@ -810,7 +810,7 @@ class OfflineFirstChatRepository @Inject constructor(
810810
sendWithoutNotification: Boolean,
811811
referenceId: String
812812
): Flow<Result<ChatMessage?>> {
813-
if (!monitor.isOnline.first()) {
813+
if (!networkMonitor.isOnline.value) {
814814
return flow {
815815
emit(Result.failure(IOException("Skipped to send message as device is offline")))
816816
}

app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ import io.reactivex.disposables.Disposable
138138
import io.reactivex.schedulers.Schedulers
139139
import io.reactivex.subjects.BehaviorSubject
140140
import kotlinx.coroutines.flow.collect
141-
import kotlinx.coroutines.flow.first
142141
import kotlinx.coroutines.flow.onEach
143142
import kotlinx.coroutines.launch
144143
import org.apache.commons.lang3.builder.CompareToBuilder
@@ -1359,7 +1358,7 @@ class ConversationsListActivity :
13591358

13601359
override fun onItemLongClick(position: Int) {
13611360
this.lifecycleScope.launch {
1362-
if (showShareToScreen || !networkMonitor.isOnline.first()) {
1361+
if (showShareToScreen || !networkMonitor.isOnline.value) {
13631362
Log.d(TAG, "sharing to multiple rooms not yet implemented. onItemLongClick is ignored.")
13641363
} else {
13651364
val clickedItem: Any? = adapter!!.getItem(position)

app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class OfflineFirstConversationsRepository @Inject constructor(
3939
private val dao: ConversationsDao,
4040
private val network: ConversationsNetworkDataSource,
4141
private val chatNetworkDataSource: ChatNetworkDataSource,
42-
private val monitor: NetworkMonitor,
42+
private val networkMonitor: NetworkMonitor,
4343
private val currentUserProviderNew: CurrentUserProviderNew
4444
) : OfflineConversationsRepository {
4545
override val roomListFlow: Flow<List<ConversationModel>>
@@ -58,7 +58,7 @@ class OfflineFirstConversationsRepository @Inject constructor(
5858
val initialConversationModels = getListOfConversations(user.id!!)
5959
_roomListFlow.emit(initialConversationModels)
6060

61-
if (monitor.isOnline.first()) {
61+
if (networkMonitor.isOnline.value) {
6262
val conversationEntitiesFromSync = getRoomsFromServer()
6363
if (!conversationEntitiesFromSync.isNullOrEmpty()) {
6464
val conversationModelsFromSync = conversationEntitiesFromSync.map(ConversationEntity::asModel)
@@ -108,7 +108,7 @@ class OfflineFirstConversationsRepository @Inject constructor(
108108
private suspend fun getRoomsFromServer(): List<ConversationEntity>? {
109109
var conversationsFromSync: List<ConversationEntity>? = null
110110

111-
if (!monitor.isOnline.first()) {
111+
if (!networkMonitor.isOnline.value) {
112112
Log.d(TAG, "Device is offline, can't load conversations from server")
113113
return null
114114
}

app/src/main/java/com/nextcloud/talk/data/network/NetworkMonitor.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
* Nextcloud Talk - Android Client
33
*
44
* SPDX-FileCopyrightText: 2024 Julius Linus <[email protected]>
5+
* SPDX-FileCopyrightText: 2024 Marcel Hibbe <[email protected]>
56
* SPDX-License-Identifier: GPL-3.0-or-later
67
*/
78

89
package com.nextcloud.talk.data.network
910

1011
import androidx.lifecycle.LiveData
11-
import kotlinx.coroutines.flow.Flow
12+
import kotlinx.coroutines.flow.StateFlow
1213

1314
/**
1415
* Utility for reporting app connectivity status.
@@ -17,7 +18,7 @@ interface NetworkMonitor {
1718
/**
1819
* Returns the device's current connectivity status.
1920
*/
20-
val isOnline: Flow<Boolean>
21+
val isOnline: StateFlow<Boolean>
2122

2223
/**
2324
* Returns the device's current connectivity status as LiveData for better interop with Java code.

app/src/main/java/com/nextcloud/talk/data/network/NetworkMonitorImpl.kt

+44-35
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Nextcloud Talk - Android Client
33
*
44
* SPDX-FileCopyrightText: 2024 Julius Linus <[email protected]>
5+
* SPDX-FileCopyrightText: 2024 Marcel Hibbe <[email protected]>
56
* SPDX-License-Identifier: GPL-3.0-or-later
67
*/
78

@@ -11,67 +12,75 @@ import android.content.Context
1112
import android.net.ConnectivityManager
1213
import android.net.Network
1314
import android.net.NetworkCapabilities
14-
import android.net.NetworkRequest.Builder
15+
import android.util.Log
1516
import androidx.core.content.getSystemService
1617
import androidx.lifecycle.LiveData
1718
import androidx.lifecycle.asLiveData
19+
import kotlinx.coroutines.CoroutineScope
1820
import kotlinx.coroutines.Dispatchers
1921
import kotlinx.coroutines.channels.awaitClose
20-
import kotlinx.coroutines.flow.Flow
22+
import kotlinx.coroutines.flow.SharingStarted
23+
import kotlinx.coroutines.flow.StateFlow
2124
import kotlinx.coroutines.flow.callbackFlow
22-
import kotlinx.coroutines.flow.conflate
23-
import kotlinx.coroutines.flow.distinctUntilChanged
24-
import kotlinx.coroutines.flow.flowOn
25+
import kotlinx.coroutines.flow.stateIn
2526
import javax.inject.Inject
2627
import javax.inject.Singleton
2728

2829
@Singleton
2930
class NetworkMonitorImpl @Inject constructor(
3031
private val context: Context
3132
) : NetworkMonitor {
33+
34+
private val connectivityManager = context.getSystemService<ConnectivityManager>()!!
35+
3236
override val isOnlineLiveData: LiveData<Boolean>
3337
get() = isOnline.asLiveData()
3438

35-
override val isOnline: Flow<Boolean> = callbackFlow {
36-
val connectivityManager = context.getSystemService<ConnectivityManager>()
37-
if (connectivityManager == null) {
38-
channel.trySend(false)
39-
channel.close()
40-
return@callbackFlow
41-
}
42-
43-
val networkRequest = Builder()
44-
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
45-
.build()
39+
override val isOnline: StateFlow<Boolean> get() = _isOnline
4640

47-
val networkCallback = object : ConnectivityManager.NetworkCallback() {
48-
private val networks = mutableSetOf<Network>()
41+
private val _isOnline: StateFlow<Boolean> = callbackFlow {
42+
val callback = object : ConnectivityManager.NetworkCallback() {
43+
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
44+
super.onCapabilitiesChanged(network, networkCapabilities)
45+
val connected = networkCapabilities.hasCapability(
46+
NetworkCapabilities.NET_CAPABILITY_VALIDATED
47+
)
48+
trySend(connected)
49+
Log.d(TAG, "Network status changed: $connected")
50+
}
4951

50-
override fun onAvailable(network: Network) {
51-
networks += network
52-
channel.trySend(true)
52+
override fun onUnavailable() {
53+
super.onUnavailable()
54+
trySend(false)
55+
Log.d(TAG, "Network status: onUnavailable")
5356
}
5457

5558
override fun onLost(network: Network) {
56-
networks -= network
57-
channel.trySend(networks.isNotEmpty())
59+
super.onLost(network)
60+
trySend(false)
61+
Log.d(TAG, "Network status: onLost")
5862
}
59-
}
6063

61-
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
64+
override fun onAvailable(network: Network) {
65+
super.onAvailable(network)
66+
trySend(true)
67+
Log.d(TAG, "Network status: onAvailable")
68+
}
69+
}
6270

63-
channel.trySend(connectivityManager.isCurrentlyConnected())
71+
connectivityManager.registerDefaultNetworkCallback(callback)
6472

6573
awaitClose {
66-
connectivityManager.unregisterNetworkCallback(networkCallback)
74+
connectivityManager.unregisterNetworkCallback(callback)
6775
}
68-
}
69-
.distinctUntilChanged()
70-
.flowOn(Dispatchers.IO)
71-
.conflate()
76+
}.stateIn(
77+
CoroutineScope(Dispatchers.IO),
78+
SharingStarted.WhileSubscribed(COROUTINE_TIMEOUT),
79+
false
80+
)
7281

73-
private fun ConnectivityManager.isCurrentlyConnected() =
74-
activeNetwork
75-
?.let(::getNetworkCapabilities)
76-
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
82+
companion object {
83+
private val TAG = NetworkMonitorImpl::class.java.simpleName
84+
private const val COROUTINE_TIMEOUT = 5000L
85+
}
7786
}

app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt

+11-12
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import io.reactivex.Observer
5151
import io.reactivex.android.schedulers.AndroidSchedulers
5252
import io.reactivex.disposables.Disposable
5353
import io.reactivex.schedulers.Schedulers
54-
import kotlinx.coroutines.flow.first
5554
import kotlinx.coroutines.launch
5655
import java.util.Date
5756
import javax.inject.Inject
@@ -134,7 +133,7 @@ class MessageActionsDialog(
134133
initMenuAddToNote(
135134
!message.isDeleted &&
136135
!ConversationUtils.isNoteToSelfConversation(currentConversation) &&
137-
networkMonitor.isOnline.first(),
136+
networkMonitor.isOnline.value,
138137
state.roomToken
139138
)
140139
}
@@ -147,16 +146,16 @@ class MessageActionsDialog(
147146
}
148147
}
149148

150-
initMenuItems()
149+
initMenuItems(networkMonitor.isOnline.value)
151150
}
152151

153-
private fun initMenuItems() {
152+
private fun initMenuItems(isOnline: Boolean) {
154153
this.lifecycleScope.launch {
155154
initMenuItemTranslate(
156155
!message.isDeleted &&
157156
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() &&
158157
CapabilitiesUtil.isTranslationsSupported(spreedCapabilities) &&
159-
networkMonitor.isOnline.first()
158+
isOnline
160159
)
161160
initMenuEditorDetails(message.lastEditTimestamp!! != 0L && !message.isDeleted)
162161
initMenuReplyToMessage(message.replyable && hasChatPermission)
@@ -165,29 +164,29 @@ class MessageActionsDialog(
165164
hasUserId(user) &&
166165
hasUserActorId(message) &&
167166
currentConversation?.type != ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL &&
168-
networkMonitor.isOnline.first()
167+
isOnline
169168
)
170169
initMenuEditMessage(isMessageEditable)
171-
initMenuDeleteMessage(showMessageDeletionButton && networkMonitor.isOnline.first())
170+
initMenuDeleteMessage(showMessageDeletionButton && isOnline)
172171
initMenuForwardMessage(
173172
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() &&
174173
!(message.isDeletedCommentMessage || message.isDeleted) &&
175-
networkMonitor.isOnline.first()
174+
isOnline
176175
)
177176
initMenuRemindMessage(
178177
!message.isDeleted &&
179178
hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.REMIND_ME_LATER) &&
180-
networkMonitor.isOnline.first()
179+
isOnline
181180
)
182181
initMenuMarkAsUnread(
183182
message.previousMessageId > NO_PREVIOUS_MESSAGE_ID &&
184183
ChatMessage.MessageType.SYSTEM_MESSAGE != message.getCalculateMessageType() &&
185-
networkMonitor.isOnline.first()
184+
isOnline
186185
)
187-
initMenuShare(messageHasFileAttachment || messageHasRegularText && networkMonitor.isOnline.first())
186+
initMenuShare(messageHasFileAttachment || messageHasRegularText && isOnline)
188187
initMenuItemOpenNcApp(
189188
ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE == message.getCalculateMessageType() &&
190-
networkMonitor.isOnline.first()
189+
isOnline
191190
)
192191
initMenuItemSave(message.getCalculateMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE)
193192
}

app/src/main/java/com/nextcloud/talk/ui/dialog/TempMessageActionsDialog.kt

+3-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import com.nextcloud.talk.databinding.DialogTempMessageActionsBinding
2323
import com.nextcloud.talk.ui.theme.ViewThemeUtils
2424
import com.nextcloud.talk.utils.ApiUtils
2525
import com.nextcloud.talk.utils.DateUtils
26-
import kotlinx.coroutines.flow.first
2726
import kotlinx.coroutines.launch
2827
import javax.inject.Inject
2928

@@ -59,10 +58,9 @@ class TempMessageActionsDialog(
5958

6059
private fun initMenuItems() {
6160
this.lifecycleScope.launch {
62-
val isOnline = networkMonitor.isOnline.first()
63-
initResendMessage(message.sendingFailed && isOnline)
64-
initMenuEditMessage(message.sendingFailed || !isOnline)
65-
initMenuDeleteMessage(message.sendingFailed || !isOnline)
61+
initResendMessage(message.sendingFailed && networkMonitor.isOnline.value)
62+
initMenuEditMessage(message.sendingFailed || !networkMonitor.isOnline.value)
63+
initMenuDeleteMessage(message.sendingFailed || !networkMonitor.isOnline.value)
6664
initMenuItemCopy()
6765
}
6866
}

0 commit comments

Comments
 (0)