Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@
### ⬆️ Improved

### ✅ Added
- Add `StatePluginConfig.isAutomaticSyncOnReconnectEnabled` for configuring the automatic sync process on connect/reconnect. [#6017](https://github.com/GetStream/stream-chat-android/pull/6017)

### ⚠️ Changed
- Deprecate `StatePluginConfig.backgroundSyncEnabled`. [#6017](https://github.com/GetStream/stream-chat-android/pull/6017)

### ❌ Removed

Expand Down
1 change: 1 addition & 0 deletions DEPRECATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This document lists deprecated constructs in the SDK, with their expected time

| API / Feature | Deprecated (warning) | Deprecated (error) | Removed | Notes |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------|-----------------------|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `StatePluginConfig.backgroundSyncEnabled` property | 2025.11.24 ⌛ | | | This property has been deprecated and will be removed in the future. We recommend disabling it to avoid unnecessary background work. |
| `PollConfig(String, List<String>, String, VotingVisibility, Boolean, Int, Boolean, Boolean)` constructor | 2025.11.07 ⌛ | | | This constructor has been deprecated. Please use `PollConfig(String, List<PollOption>, String, VotingVisibility, Boolean, Int, Boolean, Boolean, Map<String, Any>)` instead. |
| `ChatClient.suggestPollOption(pollId: String, option: String)` method | 2025.11.07 ⌛ | | | This method has been deprecated. Please use `ChatClient.createPollOption(pollId: String, option: CreatePollOptionRequest)` instead. |
| `ChatClient.removePollVote(messageId: String, pollId: String, vote: Vote)` method | 2025.11.07 ⌛ | | | This method has been deprecated. Please use `ChatClient.removePollVote(messageId: String, pollId: String, voteId: String)` instead. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ object ChatHelper {

val statePluginFactory = StreamStatePluginFactory(
config = StatePluginConfig(
backgroundSyncEnabled = true,
backgroundSyncEnabled = false,
userPresence = true,
),
appContext = context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class AndroidIntroduction {

val statePluginFactory = StreamStatePluginFactory(
config = StatePluginConfig(
backgroundSyncEnabled = true,
backgroundSyncEnabled = false,
userPresence = true,
),
appContext = applicationContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ class GettingStarted {
// Create a state plugin factory
val statePluginFactory = StreamStatePluginFactory(
config = StatePluginConfig(
// Enables background sync which syncs user actions performed while offline
backgroundSyncEnabled = true,
// Enables/disables background sync which syncs user actions performed while offline
backgroundSyncEnabled = false,
// Enables tracking online states for users
userPresence = true
),
Expand All @@ -65,8 +65,8 @@ class GettingStarted {
// Create a state plugin factory
val statePluginFactory = StreamStatePluginFactory(
config = StatePluginConfig(
// Enables background sync which syncs user actions performed while offline.
backgroundSyncEnabled = true,
// Enables/disables background synchronization when push notifications are received
backgroundSyncEnabled = false,
// Enables tracking online states for users
userPresence = true,
),
Expand Down
21 changes: 12 additions & 9 deletions stream-chat-android-state/api/stream-chat-android-state.api
Original file line number Diff line number Diff line change
Expand Up @@ -126,24 +126,27 @@ public final class io/getstream/chat/android/state/plugin/config/StatePluginConf
public fun <init> ()V
public fun <init> (Z)V
public fun <init> (ZZ)V
public fun <init> (ZZLio/getstream/chat/android/models/TimeDuration;)V
public fun <init> (ZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;)V
public fun <init> (ZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;)V
public synthetic fun <init> (ZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (ZZZ)V
public fun <init> (ZZZLio/getstream/chat/android/models/TimeDuration;)V
public fun <init> (ZZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;)V
public fun <init> (ZZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;)V
public synthetic fun <init> (ZZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Z
public final fun component2 ()Z
public final fun component3 ()Lio/getstream/chat/android/models/TimeDuration;
public final fun component4 ()Lkotlin/jvm/functions/Function0;
public final fun component5 ()Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;
public final fun copy (ZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;)Lio/getstream/chat/android/state/plugin/config/StatePluginConfig;
public static synthetic fun copy$default (Lio/getstream/chat/android/state/plugin/config/StatePluginConfig;ZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;ILjava/lang/Object;)Lio/getstream/chat/android/state/plugin/config/StatePluginConfig;
public final fun component3 ()Z
public final fun component4 ()Lio/getstream/chat/android/models/TimeDuration;
public final fun component5 ()Lkotlin/jvm/functions/Function0;
public final fun component6 ()Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;
public final fun copy (ZZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;)Lio/getstream/chat/android/state/plugin/config/StatePluginConfig;
public static synthetic fun copy$default (Lio/getstream/chat/android/state/plugin/config/StatePluginConfig;ZZZLio/getstream/chat/android/models/TimeDuration;Lkotlin/jvm/functions/Function0;Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;ILjava/lang/Object;)Lio/getstream/chat/android/state/plugin/config/StatePluginConfig;
public fun equals (Ljava/lang/Object;)Z
public final fun getBackgroundSyncEnabled ()Z
public final fun getMessageLimitConfig ()Lio/getstream/chat/android/state/plugin/config/MessageLimitConfig;
public final fun getNow ()Lkotlin/jvm/functions/Function0;
public final fun getSyncMaxThreshold ()Lio/getstream/chat/android/models/TimeDuration;
public final fun getUserPresence ()Z
public fun hashCode ()I
public final fun isAutomaticSyncOnReconnectEnabled ()Z
public fun toString ()Ljava/lang/String;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,79 @@

package io.getstream.chat.android.state.plugin.config

import io.getstream.chat.android.client.ChatClient
import io.getstream.chat.android.client.setup.state.ClientState
import io.getstream.chat.android.models.TimeDuration
import io.getstream.chat.android.state.extensions.queryChannelsAsState
import io.getstream.chat.android.state.extensions.watchChannelAsState
import io.getstream.chat.android.state.plugin.internal.StatePlugin

/**
* Provides a configuration for [io.getstream.chat.android.state.plugin.internal.StatePlugin].
*
* @param backgroundSyncEnabled Whether the SDK should perform background sync if some queries fail.
* @param userPresence Whether the SDK should receive user presence changes.
* @param syncMaxThreshold The maximum time allowed for data to synchronize. If not synced within this limit, the SDK deletes it.
* @param now A function that provides the current time in milliseconds.
* @param messageLimitConfig Configuration for message limits in channels.
* @param backgroundSyncEnabled Controls whether the SDK performs background synchronization when push notifications
* are received. When enabled (default: `true`), the SDK automatically syncs messages in the background when a push
* notification arrives, ensuring the local state/database stays up-to-date even when the app is in the background.
* This is particularly useful for displaying accurate notification content and maintaining
* offline state consistency. Disable this if you want to reduce background processing.
*
* @param userPresence Controls whether the SDK subscribes to and processes user presence events (online/offline status,
* last active time). When enabled (default: `true`), the SDK receives real-time updates about user presence changes
* and updates the user objects in channels, members, and watchers accordingly. This affects both WebSocket event
* subscriptions and the `presence` parameter in API requests. Disabling this can reduce network traffic and processing
* overhead if your application doesn't need to display user online/offline status.
*
* @param isAutomaticSyncOnReconnectEnabled Specifies if local data is updated with subscribing to web-socket events
* after reconnection.
* When turning this off, it is up for SDK user to observe web-socket connection state for
* reconnection and syncing data by calling, for example [ChatClient.queryChannels], or the [StatePlugin] methods such
* as [ChatClient.queryChannelsAsState] / [ChatClient.watchChannelAsState]. Note that these calls fetch the latest
* state and also subscribe to web-socket events if the query's `watch` is set to true.
*
* Web-socket connection status can be monitored by observing the [ClientState.connectionState] flow, exposed from the
* [ChatClient.clientState]:
* ```kotlin
* client.clientState.connectionState.collectLatest { state ->
* when (state) {
* is ConnectionState.Connected -> {
* // Data can be updated and watching can be resumed
* }
* else -> {}
* }
* }
* ```
*
* @param syncMaxThreshold The maximum age threshold for pending local operations (channels, messages, reactions)
* before they are considered too old to retry and are discarded. Default is 12 hours. When the SDK attempts to
* retry failed operations (e.g., sending a message, creating a channel, adding a reaction) upon reconnection, it
* checks if the operation's timestamp (createdLocallyAt, updatedLocallyAt, deletedAt, or createdAt) exceeds this
* threshold. If it does, the operation is removed from the local database instead of being retried, preventing
* the SDK from attempting to sync stale operations that are no longer relevant.
*
* @param now A function that provides the current time in milliseconds since epoch (Unix timestamp). Defaults to
* [System.currentTimeMillis]. This is used throughout the state plugin for time-based operations such as:
* - Determining if pinned messages have expired (based on `pinExpires` timestamp)
* - Calculating sync thresholds and time differences
* - Timestamping state updates
* This parameter primarily exists for testing purposes, allowing tests to inject a controlled time source, but can
* also be used in production if you need custom time handling (e.g., using a synchronized server time).
*
* @param messageLimitConfig Configuration that controls the maximum number of messages kept in memory for different
* channel types. This helps manage memory usage in channels with large message histories. When the number of messages
* exceeds the configured limit (plus a buffer), older messages are automatically trimmed from the in-memory state.
* By default, no limits are applied, meaning all messages are kept in memory. See [MessageLimitConfig] and
* [ChannelMessageLimit] for configuration details.
*/
public data class StatePluginConfig @JvmOverloads constructor(
@Deprecated(
"The background sync on push notification is no longer needed to keep the state in sync and " +
"will be removed in the future. If you are using the default UI components, or building your own UI " +
"using [ChatClient.queryChannelsAsState] / [ChatClient.watchChannelAsState], the state will always be " +
"up-to-date. We recommend disabling it to avoid unnecessary background work.",
)
public val backgroundSyncEnabled: Boolean = true,

Check warning on line 89 in stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/config/StatePluginConfig.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=GetStream_stream-chat-android&issues=AZq2qqsugJWAU1vBDnzq&open=AZq2qqsugJWAU1vBDnzq&pullRequest=6017
public val userPresence: Boolean = true,
public val isAutomaticSyncOnReconnectEnabled: Boolean = true,
public val syncMaxThreshold: TimeDuration = TimeDuration.hours(12),
public val now: () -> Long = { System.currentTimeMillis() },
public val messageLimitConfig: MessageLimitConfig = MessageLimitConfig(),
Expand All @@ -38,21 +97,65 @@
/**
* Configuration for message limits in channels.
*
* This configuration allows you to control memory usage by limiting the number of messages kept in the in-memory
* state for different channel types. When a channel exceeds its configured message limit (plus a 30-message buffer
* to avoid frequent trimming), the SDK automatically removes the oldest messages from memory while keeping the most
* recent ones.
*
* Message trimming behavior:
* - Only applies when not loading older messages (to avoid interfering with pagination)
* - Sorts messages by creation time and keeps the most recent ones
* - Sets `endOfOlderMessages` to `false` when trimming occurs (indicating more messages exist)
* - Messages are only removed from in-memory state, not from the local database
*
* Use cases:
* - Limit memory usage in channels with extensive message history
* - Optimize performance in resource-constrained environments
* - Configure different limits for different channel types (e.g., smaller limits for group channels, larger for DMs)
*
* Example configuration:
* ```kotlin
* StatePluginConfig(
* messageLimitConfig = MessageLimitConfig(
* channelMessageLimits = setOf(
* ChannelMessageLimit(channelType = "messaging", baseLimit = 1000),
* ChannelMessageLimit(channelType = "livestream", baseLimit = 500)
* )
* )
* )
* ```
*
* @param channelMessageLimits A set of [ChannelMessageLimit] defining the maximum number of messages to keep in
* memory for different channel types.
* This configuration allows you to specify the maximum number of messages to keep in memory for different
* channel types.
* By default, no limits per channel type are applied, meaning all messages will be kept in memory.
* memory for different channel types. By default, this is an empty set, meaning no limits are applied and all
* messages are kept in memory. Each channel type can have its own limit configured independently.
*/
public data class MessageLimitConfig(
public val channelMessageLimits: Set<ChannelMessageLimit> = setOf(),
)

/**
* Configuration for message limits in channels, specifying the channel type and limit.
* Defines a message limit for a specific channel type.
*
* This class specifies the maximum number of messages to keep in memory for channels of a particular type.
* When the number of messages in a channel exceeds this limit (plus a 30-message buffer), the SDK automatically
* trims older messages from the in-memory state, keeping only the most recent messages up to the limit.
*
* Example usage:
* ```kotlin
* // Limit messaging channels to 100 messages in memory
* ChannelMessageLimit(channelType = "messaging", baseLimit = 1000)
*
* // Limit livestream channels to 50 messages in memory
* ChannelMessageLimit(channelType = "livestream", baseLimit = 500)
* ```
*
* @param channelType The type of the channel for which this limit applies (e.g., "messaging", "livestream",
* "commerce"). This should match the channel type used when creating channels. Channel types not specified in the
* configuration will have no message limits applied.
*
* @param channelType The type of the channel for which the limit applies.
* @param baseLimit The initial maximum number of messages to keep in memory for the channel.
* @param baseLimit The maximum number of messages to keep in memory for channels of this type. When the message
* count exceeds `baseLimit + 30` (the buffer), the SDK trims messages down to this limit. Must be a positive integer.
* The 30-message buffer is included to avoid trimming too frequently during normal message flow.
*/
public data class ChannelMessageLimit(
public val channelType: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
logicRegistry = logic,
stateRegistry = stateRegistry,
userPresence = config.userPresence,
isAutomaticSyncOnReconnectEnabled = config.isAutomaticSyncOnReconnectEnabled,
syncMaxThreshold = config.syncMaxThreshold,
now = { System.currentTimeMillis() },
)
Expand All @@ -151,7 +152,7 @@
sideEffect = syncManager::awaitSyncing,
)

if (config.backgroundSyncEnabled) {

Check warning on line 155 in stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/factory/StreamStatePluginFactory.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Deprecated code should not be used.

See more on https://sonarcloud.io/project/issues?id=GetStream_stream-chat-android&issues=AZq2qqvygJWAU1vBDnzr&open=AZq2qqvygJWAU1vBDnzr&pullRequest=6017
chatClient.setPushNotificationReceivedListener { channelType, channelId ->
OfflineSyncFirebaseMessagingHandler().syncMessages(appContext, "$channelType:$channelId")
}
Expand Down
Loading
Loading