Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
# WrapperWorkerFactory resolves inner workers by class name from input data.
# Keep names stable so existing enqueued work remains resolvable after minification.
# See docs/minification-workmanager-compat.md
-keepnames class com.wire.kalium.logic.sync.PendingMessagesSenderWorker
-keepnames class com.wire.kalium.logic.sync.periodic.UserConfigSyncWorker
-keepnames class com.wire.kalium.logic.sync.periodic.UpdateApiVersionsWorker
-keepnames class com.wire.kalium.logic.sync.receiver.asset.AudioNormalizedLoudnessWorker
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Expand Down Expand Up @@ -349,6 +350,11 @@
android:exported="false"
android:foregroundServiceType="specialUse" />

<service
android:name=".services.PendingMessagesForegroundService"
android:exported="false"
android:foregroundServiceType="dataSync" />

<service
android:name=".services.CallService"
android:exported="false"
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/baseline-prof.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30668,7 +30668,6 @@ Lcom/wire/kalium/logic/sync/GlobalWorkScheduler;
Lcom/wire/kalium/logic/sync/GlobalWorkSchedulerImpl;
SPLcom/wire/kalium/logic/sync/GlobalWorkSchedulerImpl;-><init>(Landroid/content/Context;Lcom/wire/kalium/logic/GlobalKaliumScope;)V
SPLcom/wire/kalium/logic/sync/GlobalWorkSchedulerImpl;->schedulePeriodicApiVersionUpdate()V
Lcom/wire/kalium/logic/sync/PendingMessagesSenderWorker;
Lcom/wire/kalium/logic/sync/WorkSchedulerImplKt;
SPLcom/wire/kalium/logic/sync/WorkSchedulerImplKt;-><clinit>()V
SPLcom/wire/kalium/logic/sync/WorkSchedulerImplKt;->buildConnectedPeriodicWorkRequest$default(Lkotlin/reflect/KClass;Lcom/wire/kalium/logic/data/id/QualifiedID;ZILjava/lang/Object;)Landroidx/work/PeriodicWorkRequest;
Expand Down Expand Up @@ -40417,4 +40416,4 @@ SPLorg/slf4j/helpers/Util;-><clinit>()V
SPLorg/slf4j/helpers/Util;->safeGetBooleanSystemProperty(Ljava/lang/String;)Z
SPLorg/slf4j/helpers/Util;->safeGetSystemProperty(Ljava/lang/String;)Ljava/lang/String;
Lorg/slf4j/spi/MDCAdapter;
Lorg/slf4j/spi/SLF4JServiceProvider;
Lorg/slf4j/spi/SLF4JServiceProvider;
38 changes: 38 additions & 0 deletions app/src/main/kotlin/com/wire/android/GlobalObserversManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.wire.android.datastore.UserDataStoreProvider
import com.wire.android.di.KaliumCoreLogic
import com.wire.android.notification.NotificationChannelsManager
import com.wire.android.notification.WireNotificationManager
import com.wire.android.services.SendPendingMessagesAfterForegroundSyncUseCase
import com.wire.android.util.CurrentScreenManager
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.kalium.logic.CoreLogic
Expand All @@ -30,6 +31,8 @@ import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.auth.LogoutCallback
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.feature.user.webSocketStatus.ObservePersistentWebSocketConnectionStatusUseCase
import com.wire.kalium.network.NetworkState
import com.wire.kalium.network.NetworkStateObserver
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.awaitClose
Expand Down Expand Up @@ -61,6 +64,8 @@ class GlobalObserversManager @Inject constructor(
private val notificationChannelsManager: NotificationChannelsManager,
private val userDataStoreProvider: UserDataStoreProvider,
private val currentScreenManager: CurrentScreenManager,
private val sendPendingMessagesAfterForegroundSync: SendPendingMessagesAfterForegroundSyncUseCase,
private val networkStateObserver: NetworkStateObserver,
) {
// TODO(tests): refactor so scope/dispatcher can be injected and properly stopped
private val scope = CoroutineScope(SupervisorJob() + dispatcherProvider.io())
Expand All @@ -78,6 +83,7 @@ class GlobalObserversManager @Inject constructor(
}
scope.handleLogouts()
scope.handleDeleteEphemeralMessageEndDate()
scope.retryPendingMessagesOnAppOpen()
}

private suspend fun setUpNotifications() {
Expand Down Expand Up @@ -162,4 +168,36 @@ class GlobalObserversManager @Inject constructor(
.collect { userId -> coreLogic.getSessionScope(userId).messages.deleteEphemeralMessageEndDate() }
}
}

private fun CoroutineScope.retryPendingMessagesOnAppOpen() {
launch {
currentScreenManager.isAppVisibleFlow()
.filter { isAppVisible -> isAppVisible }
.collectLatest {
val networkState = networkStateObserver.observeNetworkState().value
if (networkState !is NetworkState.ConnectedWithInternet) {
appLogger.i("$TAG: no internet connection, skipping pending messages retry on app open")
return@collectLatest
}

when (val result = coreLogic.getGlobalScope().session.currentSession()) {
is CurrentSessionResult.Success -> {
val accountInfo = result.accountInfo
if (accountInfo.isValid()) {
sendPendingMessagesAfterForegroundSync(accountInfo.userId)
} else {
appLogger.w("$TAG: current session is invalid, skipping pending messages retry on app open")
}
}

is CurrentSessionResult.Failure ->
appLogger.w("$TAG: unable to get current valid session, skipping pending messages retry on app open: $result")
}
}
}
}

private companion object {
private const val TAG = "GlobalObserversManager"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import com.wire.android.notification.broadcastreceivers.PlayPauseAudioMessageRec
import com.wire.android.notification.broadcastreceivers.StopAudioMessageReceiver
import com.wire.android.search.SearchMetroViewModelBindings
import com.wire.android.services.CallService
import com.wire.android.services.PendingMessagesForegroundService
import com.wire.android.services.PersistentWebSocketService
import com.wire.android.services.PlayingAudioMessageService
import com.wire.android.ui.AppLockActivity
Expand Down Expand Up @@ -125,6 +126,7 @@ interface WireApplicationGraph : ViewModelGraph {
fun inject(activity: CallActivity)
fun inject(activity: OngoingCallActivity)
fun inject(service: PersistentWebSocketService)
fun inject(service: PendingMessagesForegroundService)
fun inject(service: CallService)
fun inject(service: PlayingAudioMessageService)
fun inject(receiver: StartServiceReceiver)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ package com.wire.android.notification.broadcastreceivers
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.core.content.ContextCompat
import com.wire.android.BuildConfig.EMM_SUPPORT_ENABLED
import com.wire.android.appLogger
import com.wire.android.emm.ManagedConfigurationsReceiver
import com.wire.android.di.ApplicationContext
import com.wire.kalium.logic.sync.PendingMessagesForegroundSync
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.SingleIn
Expand All @@ -35,35 +37,51 @@ import dev.zacsweers.metro.SingleIn
@SingleIn(AppScope::class)
class DynamicReceiversManager @Inject constructor(
@ApplicationContext val context: Context,
private val managedConfigurationsReceiver: ManagedConfigurationsReceiver
private val managedConfigurationsReceiver: ManagedConfigurationsReceiver,
private val pendingMessagesScheduledReceiver: PendingMessagesScheduledReceiver,
) {
@Volatile
private var isRegistered = false

fun registerAll() {
if (EMM_SUPPORT_ENABLED) {
synchronized(this) {
if (!isRegistered) {
synchronized(this) {
if (!isRegistered) {
if (EMM_SUPPORT_ENABLED) {
appLogger.i("$TAG Registering Runtime ManagedConfigurations Broadcast receiver")
context.registerReceiver(managedConfigurationsReceiver, IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED))
isRegistered = true
} else {
appLogger.w("$TAG Receiver already registered, skipping")
}

appLogger.i("$TAG Registering PendingMessagesScheduledReceiver")
val pendingMessagesIntentFilter = IntentFilter(PendingMessagesForegroundSync.ACTION_SENDING_OF_PENDING_MESSAGES_SCHEDULED)
.apply { addAction(PendingMessagesForegroundSync.ACTION_SENDING_OF_PENDING_MESSAGES_CANCELLED) }
ContextCompat.registerReceiver(
context,
pendingMessagesScheduledReceiver,
pendingMessagesIntentFilter,
ContextCompat.RECEIVER_NOT_EXPORTED
)

isRegistered = true
} else {
appLogger.w("$TAG Receiver already registered, skipping")
}
}
}

fun unregisterAll() {
if (EMM_SUPPORT_ENABLED) {
synchronized(this) {
if (isRegistered) {
synchronized(this) {
if (isRegistered) {
if (EMM_SUPPORT_ENABLED) {
appLogger.i("$TAG Unregistering Runtime ManagedConfigurations Broadcast receiver")
context.unregisterReceiver(managedConfigurationsReceiver)
isRegistered = false
} else {
appLogger.w("$TAG Receiver not registered, skipping unregister")
}

appLogger.i("$TAG Unregistering PendingMessagesScheduledReceiver")
context.unregisterReceiver(pendingMessagesScheduledReceiver)

isRegistered = false
} else {
appLogger.w("$TAG Receiver not registered, skipping unregister")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Wire
* Copyright (C) 2026 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/

package com.wire.android.notification.broadcastreceivers

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.wire.android.appLogger
import com.wire.android.services.ServicesManager
import com.wire.kalium.logic.sync.PendingMessagesForegroundSync
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn

@SingleIn(AppScope::class)
class PendingMessagesScheduledReceiver @Inject constructor(
private val servicesManager: ServicesManager,
) : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
PendingMessagesForegroundSync.ACTION_SENDING_OF_PENDING_MESSAGES_SCHEDULED ->
servicesManager.startPendingMessagesForegroundService()

PendingMessagesForegroundSync.ACTION_SENDING_OF_PENDING_MESSAGES_CANCELLED ->
servicesManager.stopPendingMessagesForegroundService()

else -> {
appLogger.w("$TAG: unexpected action ${intent.action}")
return
}
}
}

companion object {
private const val TAG = "PendingMessagesScheduledReceiver"
}
}
Loading