From 6d2d24c171760d647f0415f21e6b02779f7549b7 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Thu, 3 Oct 2024 17:59:47 +0530 Subject: [PATCH 01/22] Added checks for current room state before room ATTACH operation --- .../src/main/java/com/ably/chat/Room.kt | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index 84cecaf..3782a22 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -2,6 +2,9 @@ package com.ably.chat +import io.ably.lib.types.AblyException +import io.ably.lib.types.ErrorInfo + /** * Represents a chat room. */ @@ -115,13 +118,22 @@ internal class DefaultRoom( override val occupancy: Occupancy = DefaultOccupancy( messages = messages, ) - override val status: RoomStatus - get() { - TODO("Not yet implemented") - } + get() = TODO("Not yet implemented") override suspend fun attach() { + when(status.current) { + RoomLifecycle.Attached -> { + return + } + RoomLifecycle.Releasing -> { + throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASING state", ErrorCodes.RoomIsReleasing)) + } + RoomLifecycle.Released -> { + throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASED state", ErrorCodes.RoomIsReleased)) + } + else -> {} + } messages.channel.attachCoroutine() typing.channel.attachCoroutine() reactions.channel.attachCoroutine() From d018b0d67f773eca9d0b85a6b82c8dd047ce0b98 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Tue, 8 Oct 2024 17:46:28 +0530 Subject: [PATCH 02/22] Implemented RoomStatus interface, added code to handle exceptions when messages, typing and reactions channel attach fails --- .../src/main/java/com/ably/chat/Room.kt | 19 +++++++++++++------ .../src/main/java/com/ably/chat/RoomStatus.kt | 14 +++++++++++++- .../src/main/java/com/ably/chat/Rooms.kt | 1 + 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index 3782a22..c876b49 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -4,6 +4,8 @@ package com.ably.chat import io.ably.lib.types.AblyException import io.ably.lib.types.ErrorInfo +import io.ably.lib.util.Log +import io.ably.lib.util.Log.LogHandler /** * Represents a chat room. @@ -59,7 +61,7 @@ interface Room { * * @returns The status observable. */ - val status: RoomStatus + val status: IRoomStatus /** * Returns the room options. @@ -91,6 +93,8 @@ internal class DefaultRoom( override val options: RoomOptions, realtimeClient: RealtimeClient, chatApi: ChatApi, + override val status: IRoomStatus = RoomStatus(RoomLifecycle.Initialized, null), + val logger: LogHandler?, ) : Room { private val _messages = DefaultMessages( @@ -118,8 +122,6 @@ internal class DefaultRoom( override val occupancy: Occupancy = DefaultOccupancy( messages = messages, ) - override val status: RoomStatus - get() = TODO("Not yet implemented") override suspend fun attach() { when(status.current) { @@ -134,9 +136,14 @@ internal class DefaultRoom( } else -> {} } - messages.channel.attachCoroutine() - typing.channel.attachCoroutine() - reactions.channel.attachCoroutine() + try { + messages.channel.attachCoroutine() + typing.channel.attachCoroutine() + reactions.channel.attachCoroutine() + } catch (e: Exception) { + logger?.println(3, "", "", e) + // TODO - revert channel attach + } } override suspend fun detach() { diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 05e54e4..bce7ed4 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -5,7 +5,7 @@ import io.ably.lib.types.ErrorInfo /** * Represents the status of a Room. */ -interface RoomStatus { +interface IRoomStatus { /** * The current status of the room. */ @@ -35,6 +35,18 @@ interface RoomStatus { } } +class RoomStatus(override val current: RoomLifecycle, override val error: ErrorInfo?) : IRoomStatus { + + private var listeners = mutableListOf() + + override fun on(listener: IRoomStatus.Listener): Subscription { + listeners.add(listener) + return Subscription { + listeners.remove(listener) + } + } +} + /** * The different states that a room can be in throughout its lifecycle. */ diff --git a/chat-android/src/main/java/com/ably/chat/Rooms.kt b/chat-android/src/main/java/com/ably/chat/Rooms.kt index 31c0c49..442f11b 100644 --- a/chat-android/src/main/java/com/ably/chat/Rooms.kt +++ b/chat-android/src/main/java/com/ably/chat/Rooms.kt @@ -57,6 +57,7 @@ internal class DefaultRooms( options = options, realtimeClient = realtimeClient, chatApi = chatApi, + logger = clientOptions.logHandler, ) } From 99c7363fddf3613adad6fcb9dfd74cedb1afc927 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Fri, 11 Oct 2024 14:20:18 +0530 Subject: [PATCH 03/22] Marked RoomOptions as optional as per spec --- chat-android/src/main/java/com/ably/chat/RoomOptions.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomOptions.kt b/chat-android/src/main/java/com/ably/chat/RoomOptions.kt index b6d9d96..8f7d4ef 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomOptions.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomOptions.kt @@ -9,25 +9,25 @@ data class RoomOptions( * use {@link RoomOptionsDefaults.presence} to enable presence with default options. * @defaultValue undefined */ - val presence: PresenceOptions = PresenceOptions(), + val presence: PresenceOptions? = PresenceOptions(), /** * The typing options for the room. To enable typing in the room, set this property. You may use * {@link RoomOptionsDefaults.typing} to enable typing with default options. */ - val typing: TypingOptions = TypingOptions(), + val typing: TypingOptions? = TypingOptions(), /** * The reactions options for the room. To enable reactions in the room, set this property. You may use * {@link RoomOptionsDefaults.reactions} to enable reactions with default options. */ - val reactions: RoomReactionsOptions = RoomReactionsOptions, + val reactions: RoomReactionsOptions? = RoomReactionsOptions, /** * The occupancy options for the room. To enable occupancy in the room, set this property. You may use * {@link RoomOptionsDefaults.occupancy} to enable occupancy with default options. */ - val occupancy: OccupancyOptions = OccupancyOptions, + val occupancy: OccupancyOptions? = OccupancyOptions, ) /** From 30677748affa7a38fa54366b6f020f4a5f6762c3 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Fri, 11 Oct 2024 14:36:10 +0530 Subject: [PATCH 04/22] Added offAll method interface method to remove all listeners --- chat-android/src/main/java/com/ably/chat/Room.kt | 1 - chat-android/src/main/java/com/ably/chat/RoomStatus.kt | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index c876b49..fa9a832 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -4,7 +4,6 @@ package com.ably.chat import io.ably.lib.types.AblyException import io.ably.lib.types.ErrorInfo -import io.ably.lib.util.Log import io.ably.lib.util.Log.LogHandler /** diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index bce7ed4..5a33cd3 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -33,6 +33,11 @@ interface IRoomStatus { */ fun roomStatusChanged(change: RoomStatusChange) } + + /** + * Removes all listeners that were added by the `onChange` method. + */ + fun offAll(); } class RoomStatus(override val current: RoomLifecycle, override val error: ErrorInfo?) : IRoomStatus { @@ -45,6 +50,10 @@ class RoomStatus(override val current: RoomLifecycle, override val error: ErrorI listeners.remove(listener) } } + + override fun offAll() { + listeners.clear(); + } } /** From 8ffabb1406d73f0dfe6fa17a99f8ca5523a10d7b Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Fri, 11 Oct 2024 15:52:14 +0530 Subject: [PATCH 05/22] Refactored room interfaces, implemented DefaultRoomStatus class that extends internal eventemitter --- .../src/main/java/com/ably/chat/Room.kt | 4 +- .../src/main/java/com/ably/chat/RoomStatus.kt | 80 +++++++++++++++++-- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index fa9a832..22bd019 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -60,7 +60,7 @@ interface Room { * * @returns The status observable. */ - val status: IRoomStatus + val status: RoomStatus /** * Returns the room options. @@ -92,7 +92,7 @@ internal class DefaultRoom( override val options: RoomOptions, realtimeClient: RealtimeClient, chatApi: ChatApi, - override val status: IRoomStatus = RoomStatus(RoomLifecycle.Initialized, null), + override val status: RoomStatus = DefaultRoomStatus(RoomLifecycle.Initialized, null), val logger: LogHandler?, ) : Room { diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 5a33cd3..6ba74e8 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -1,11 +1,12 @@ package com.ably.chat import io.ably.lib.types.ErrorInfo +import io.ably.lib.util.EventEmitter /** * Represents the status of a Room. */ -interface IRoomStatus { +interface RoomStatus { /** * The current status of the room. */ @@ -21,7 +22,7 @@ interface IRoomStatus { * @param listener The function to call when the status changes. * @returns An object that can be used to unregister the listener. */ - fun on(listener: Listener): Subscription + fun onChange(listener: Listener): Subscription /** * An interface for listening to changes for the room status @@ -40,26 +41,89 @@ interface IRoomStatus { fun offAll(); } -class RoomStatus(override val current: RoomLifecycle, override val error: ErrorInfo?) : IRoomStatus { +/** + * A new room status that can be set. + */ +interface NewRoomStatus { + /** + * The new status of the room. + */ + val status: RoomLifecycle; - private var listeners = mutableListOf() + /** + * An error that provides a reason why the room has + * entered the new status, if applicable. + */ + val error: ErrorInfo? +} + +interface InternalRoomStatus: RoomStatus { + /** + * Registers a listener that will be called once when the room status changes. + * @param listener The function to call when the status changes. + */ + fun onChangeOnce(listener: RoomStatus.Listener) - override fun on(listener: IRoomStatus.Listener): Subscription { - listeners.add(listener) + /** + * Sets the status of the room. + * + * @param params The new status of the room. + */ + fun setStatus(params: NewRoomStatus) +} + +open class ChatEventEmitter: EventEmitter() { + override fun apply(listener: Listener, event: Event, vararg args: Any?) { + TODO("Not yet implemented") + } +} + +class DefaultRoomStatus() : InternalRoomStatus, + ChatEventEmitter() { + + private var _state = RoomLifecycle.Initializing + private var _error: ErrorInfo? = null + + private val internalEmitter = ChatEventEmitter() + + override fun onChange(listener: RoomStatus.Listener): Subscription { + this.on(listener); return Subscription { - listeners.remove(listener) + this.off(listener) } } override fun offAll() { - listeners.clear(); + this.offAll() } + + override fun onChangeOnce(listener: RoomStatus.Listener) { + internalEmitter.once(listener) + } + + override fun setStatus(params: NewRoomStatus) { + val change = RoomStatusChange(params.status, current, params.error); + this._state = change.current + this._error = change.error + this.internalEmitter.emit(change.current, change) + this.emit(change.current, change) + } + + override val current: RoomLifecycle + get() = _state + override val error: ErrorInfo? + get() = _error } /** * The different states that a room can be in throughout its lifecycle. */ enum class RoomLifecycle(val stateName: String) { + /** + * The library is currently initializing the room. + */ + Initializing("initializing"), + /** * A temporary state for when the library is first initialized. */ From 5b321c6c17bc9fe8aae37690ba8535879c0d174d Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Mon, 14 Oct 2024 17:53:34 +0530 Subject: [PATCH 06/22] Replaced ChatEventEmitter with RoomStatusEventEmitter, implemented missing impl. for EventEmitter apply --- .../src/main/java/com/ably/chat/RoomStatus.kt | 91 ++++++++++--------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 6ba74e8..ec4817d 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -2,6 +2,7 @@ package com.ably.chat import io.ably.lib.types.ErrorInfo import io.ably.lib.util.EventEmitter +import io.ably.lib.util.Log /** * Represents the status of a Room. @@ -72,49 +73,6 @@ interface InternalRoomStatus: RoomStatus { fun setStatus(params: NewRoomStatus) } -open class ChatEventEmitter: EventEmitter() { - override fun apply(listener: Listener, event: Event, vararg args: Any?) { - TODO("Not yet implemented") - } -} - -class DefaultRoomStatus() : InternalRoomStatus, - ChatEventEmitter() { - - private var _state = RoomLifecycle.Initializing - private var _error: ErrorInfo? = null - - private val internalEmitter = ChatEventEmitter() - - override fun onChange(listener: RoomStatus.Listener): Subscription { - this.on(listener); - return Subscription { - this.off(listener) - } - } - - override fun offAll() { - this.offAll() - } - - override fun onChangeOnce(listener: RoomStatus.Listener) { - internalEmitter.once(listener) - } - - override fun setStatus(params: NewRoomStatus) { - val change = RoomStatusChange(params.status, current, params.error); - this._state = change.current - this._error = change.error - this.internalEmitter.emit(change.current, change) - this.emit(change.current, change) - } - - override val current: RoomLifecycle - get() = _state - override val error: ErrorInfo? - get() = _error -} - /** * The different states that a room can be in throughout its lifecycle. */ @@ -190,3 +148,50 @@ data class RoomStatusChange( */ val error: ErrorInfo? = null, ) + +open class RoomStatusEvenEmitter : EventEmitter() { + + override fun apply(listener: RoomStatus.Listener?, event: RoomLifecycle?, vararg args: Any?) { + try { + listener?.roomStatusChanged(args[0] as RoomStatusChange) + } catch (t: Throwable) { + Log.e("RoomEventEmitter", "Unexpected exception calling Room Status Listener", t) + } + } +} + +class DefaultRoomStatusStatus : InternalRoomStatus, RoomStatusEvenEmitter() { + + private var _state = RoomLifecycle.Initializing + override val current: RoomLifecycle + get() = _state + + private var _error: ErrorInfo? = null + override val error: ErrorInfo? + get() = _error + + private val internalEmitter = RoomStatusEvenEmitter() + + override fun onChange(listener: RoomStatus.Listener): Subscription { + this.on(listener); + return Subscription { + this.off(listener) + } + } + + override fun offAll() { + this.offAll() + } + + override fun onChangeOnce(listener: RoomStatus.Listener) { + internalEmitter.once(listener) + } + + override fun setStatus(params: NewRoomStatus) { + val change = RoomStatusChange(params.status, current, params.error); + this._state = change.current + this._error = change.error + this.internalEmitter.emit(change.current, change) + this.emit(change.current, change) + } +} From 7943aba2e2ff0512233bcfc159d6d78c978099e0 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Tue, 15 Oct 2024 17:33:48 +0530 Subject: [PATCH 07/22] Added proper logging for DefaultRoom room attach operation failure --- chat-android/src/main/java/com/ably/chat/Room.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index 22bd019..b4caa10 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -4,6 +4,7 @@ package com.ably.chat import io.ably.lib.types.AblyException import io.ably.lib.types.ErrorInfo +import io.ably.lib.util.Log import io.ably.lib.util.Log.LogHandler /** @@ -92,10 +93,14 @@ internal class DefaultRoom( override val options: RoomOptions, realtimeClient: RealtimeClient, chatApi: ChatApi, - override val status: RoomStatus = DefaultRoomStatus(RoomLifecycle.Initialized, null), + override val status: RoomStatus = DefaultRoomStatusStatus(), val logger: LogHandler?, ) : Room { + companion object { + const val TAG = "DefaultRoom" + } + private val _messages = DefaultMessages( roomId = roomId, realtimeChannels = realtimeClient.channels, @@ -140,8 +145,7 @@ internal class DefaultRoom( typing.channel.attachCoroutine() reactions.channel.attachCoroutine() } catch (e: Exception) { - logger?.println(3, "", "", e) - // TODO - revert channel attach + logger?.println(Log.ERROR, TAG, "Error handling room ATTACH ", e) } } From 21ac68ae25fb04ecbf31fb02d339f15bdaf4383b Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Tue, 15 Oct 2024 18:00:44 +0530 Subject: [PATCH 08/22] Added RoomLifecycleManager class, defined basic interfaces needed for RoomLifecYCLE management --- .../com/ably/chat/RoomLifecycleManager.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt new file mode 100644 index 0000000..8e3fdf2 --- /dev/null +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -0,0 +1,40 @@ +package com.ably.chat + +import io.ably.lib.realtime.ChannelBase + +/** + * An interface for features that contribute to the room status. + */ +interface ContributesToRoomLifecycle: Subscription { + /** + * Gets the channel on which the feature operates. This promise is never + * rejected except in the case where room initialization is canceled. + */ + val channel: ChannelBase + + /** + * Gets the ErrorInfo code that should be used when the feature fails to attach. + * @returns The error that should be used when the feature fails to attach. + */ + val attachmentErrorCode: ErrorCodes; + + /** + * Gets the ErrorInfo code that should be used when the feature fails to detach. + * @returns The error that should be used when the feature fails to detach. + */ + val detachmentErrorCode: ErrorCodes; +} + +/** + * The order of precedence for lifecycle operations, passed to PriorityQueueExecutor which allows + * us to ensure that internal operations take precedence over user-driven operations. + */ +enum class LifecycleOperationPrecedence(val operationPriority: Int) { + Internal(1), + Release(2), + AttachOrDetach(3), +} + +class RoomLifecycleManager { + +} From 0d7f9a9c2bc2e7ae4fa74c918736ba568db7cb2a Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Wed, 16 Oct 2024 16:24:04 +0530 Subject: [PATCH 09/22] Updated coroutine core as a direct dependency to chat-android package --- chat-android/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/chat-android/build.gradle.kts b/chat-android/build.gradle.kts index 9f116f4..8890d60 100644 --- a/chat-android/build.gradle.kts +++ b/chat-android/build.gradle.kts @@ -45,6 +45,7 @@ buildConfig { dependencies { api(libs.ably.android) implementation(libs.gson) + implementation(libs.coroutine.core) testImplementation(libs.junit) testImplementation(libs.mockk) From ba0f81e7f5aa42d507e1e527b57010b31f42e6e1 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Wed, 16 Oct 2024 17:08:46 +0530 Subject: [PATCH 10/22] Implemented missing interfaces as a part of RoomLifeCycleManager --- .../com/ably/chat/RoomLifecycleManager.kt | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index 8e3fdf2..7d8098f 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -1,16 +1,18 @@ package com.ably.chat import io.ably.lib.realtime.ChannelBase +import io.ably.lib.types.ErrorInfo +import kotlinx.coroutines.Deferred /** * An interface for features that contribute to the room status. */ -interface ContributesToRoomLifecycle: Subscription { +interface ContributesToRoomLifecycle: EmitsDiscontinuities { /** * Gets the channel on which the feature operates. This promise is never * rejected except in the case where room initialization is canceled. */ - val channel: ChannelBase + val channel: Deferred /** * Gets the ErrorInfo code that should be used when the feature fails to attach. @@ -25,6 +27,18 @@ interface ContributesToRoomLifecycle: Subscription { val detachmentErrorCode: ErrorCodes; } +/** + * This interface represents a feature that contributes to the room lifecycle and + * exposes its channel directly. Objects of this type are created by awaiting the + * channel promises of all the {@link ContributesToRoomLifecycle} objects. + * + * @internal + */ +interface ResolvedContributor { + val channel: ChannelBase + val contributor: ContributesToRoomLifecycle +} + /** * The order of precedence for lifecycle operations, passed to PriorityQueueExecutor which allows * us to ensure that internal operations take precedence over user-driven operations. @@ -35,6 +49,22 @@ enum class LifecycleOperationPrecedence(val operationPriority: Int) { AttachOrDetach(3), } +/** + * A map of contributors to pending discontinuity events. + */ +typealias DiscontinuityEventMap = Map + +/** + * An internal interface that represents the result of a room attachment operation. + */ +interface RoomAttachmentResult : NewRoomStatus { + val failedFeature: ResolvedContributor? +} + +/** + * An implementation of the `Status` interface. + * @internal + */ class RoomLifecycleManager { } From 899363bfdbd52a86090b48be8981080bf5dd0873 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Fri, 18 Oct 2024 17:15:14 +0530 Subject: [PATCH 11/22] Fixed linting issues recommended by detekt --- .../src/main/java/com/ably/chat/Room.kt | 20 ++++++++----------- .../com/ably/chat/RoomLifecycleManager.kt | 10 ++++------ .../src/main/java/com/ably/chat/RoomStatus.kt | 10 +++++----- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index b9f1293..e31590c 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -98,10 +98,6 @@ internal class DefaultRoom( val logger: LogHandler?, ) : Room { - companion object { - const val TAG = "DefaultRoom" - } - private val _messages = DefaultMessages( roomId = roomId, realtimeChannels = realtimeClient.channels, @@ -129,16 +125,12 @@ internal class DefaultRoom( ) override suspend fun attach() { - when(status.current) { - RoomLifecycle.Attached -> { - return - } - RoomLifecycle.Releasing -> { + when (status.current) { + RoomLifecycle.Attached -> return + RoomLifecycle.Releasing -> throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASING state", ErrorCodes.RoomIsReleasing)) - } - RoomLifecycle.Released -> { + RoomLifecycle.Released -> throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASED state", ErrorCodes.RoomIsReleased)) - } else -> {} } try { @@ -159,4 +151,8 @@ internal class DefaultRoom( fun release() { _messages.release() } + + companion object { + const val TAG = "DefaultRoom" + } } diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index 7d8098f..3f477aa 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.Deferred /** * An interface for features that contribute to the room status. */ -interface ContributesToRoomLifecycle: EmitsDiscontinuities { +interface ContributesToRoomLifecycle : EmitsDiscontinuities { /** * Gets the channel on which the feature operates. This promise is never * rejected except in the case where room initialization is canceled. @@ -18,13 +18,13 @@ interface ContributesToRoomLifecycle: EmitsDiscontinuities { * Gets the ErrorInfo code that should be used when the feature fails to attach. * @returns The error that should be used when the feature fails to attach. */ - val attachmentErrorCode: ErrorCodes; + val attachmentErrorCode: ErrorCodes /** * Gets the ErrorInfo code that should be used when the feature fails to detach. * @returns The error that should be used when the feature fails to detach. */ - val detachmentErrorCode: ErrorCodes; + val detachmentErrorCode: ErrorCodes } /** @@ -65,6 +65,4 @@ interface RoomAttachmentResult : NewRoomStatus { * An implementation of the `Status` interface. * @internal */ -class RoomLifecycleManager { - -} +class RoomLifecycleManager diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 9d16299..003cfe7 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -41,7 +41,7 @@ interface RoomStatus { /** * Removes all listeners that were added by the `onChange` method. */ - fun offAll(); + fun offAll() } /** @@ -51,7 +51,7 @@ interface NewRoomStatus { /** * The new status of the room. */ - val status: RoomLifecycle; + val status: RoomLifecycle /** * An error that provides a reason why the room has @@ -60,7 +60,7 @@ interface NewRoomStatus { val error: ErrorInfo? } -interface InternalRoomStatus: RoomStatus { +interface InternalRoomStatus : RoomStatus { /** * Registers a listener that will be called once when the room status changes. * @param listener The function to call when the status changes. @@ -186,7 +186,7 @@ class DefaultRoomStatusStatus : InternalRoomStatus, RoomStatusEvenEmitter() { private val internalEmitter = RoomStatusEvenEmitter() override fun onChange(listener: RoomStatus.Listener): Subscription { - this.on(listener); + this.on(listener) return Subscription { this.off(listener) } @@ -201,7 +201,7 @@ class DefaultRoomStatusStatus : InternalRoomStatus, RoomStatusEvenEmitter() { } override fun setStatus(params: NewRoomStatus) { - val change = RoomStatusChange(params.status, current, params.error); + val change = RoomStatusChange(params.status, current, params.error) this._state = change.current this._error = change.error this.internalEmitter.emit(change.current, change) From 102513c600660d878d36c5499e2d85435e073aa3 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Mon, 21 Oct 2024 14:13:06 +0530 Subject: [PATCH 12/22] Updated ably-java dependency to latest version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3817db2..88c9b33 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ [versions] ably-chat = "0.0.1" -ably = "1.2.43" +ably = "1.2.44" junit = "4.13.2" agp = "8.5.2" detekt = "1.23.6" From 572fd8d7005788b108b6e942756cffb328904c9d Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Mon, 21 Oct 2024 14:15:17 +0530 Subject: [PATCH 13/22] Updated ContributesToRoomLifecycle channel property to CompletableDeferred --- .../src/main/java/com/ably/chat/RoomLifecycleManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index 3f477aa..5b414c9 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -2,7 +2,7 @@ package com.ably.chat import io.ably.lib.realtime.ChannelBase import io.ably.lib.types.ErrorInfo -import kotlinx.coroutines.Deferred +import kotlinx.coroutines.CompletableDeferred /** * An interface for features that contribute to the room status. @@ -12,7 +12,7 @@ interface ContributesToRoomLifecycle : EmitsDiscontinuities { * Gets the channel on which the feature operates. This promise is never * rejected except in the case where room initialization is canceled. */ - val channel: Deferred + val channel: CompletableDeferred /** * Gets the ErrorInfo code that should be used when the feature fails to attach. From da00860a78a30fdd2fe109efc0bf6a2251bb2eb8 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Mon, 21 Oct 2024 18:50:09 +0530 Subject: [PATCH 14/22] Upgraded kotlinx.coroutines dependency to latest version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 88c9b33..9f7c915 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ activity-compose = "1.9.1" compose-bom = "2024.06.00" gson = "2.11.0" mockk = "1.13.12" -coroutine = "1.8.1" +coroutine = "1.9.0" build-config = "5.4.0" [libraries] From be1142840f99c5cc3af5a7e4f995a4c69e3b5de9 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Mon, 21 Oct 2024 18:52:44 +0530 Subject: [PATCH 15/22] Renamed DefaultRoomStatusStatus class to DefaultStatus, same as chat-js --- chat-android/src/main/java/com/ably/chat/Room.kt | 2 +- chat-android/src/main/java/com/ably/chat/RoomStatus.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index e31590c..f2458bb 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -94,7 +94,7 @@ internal class DefaultRoom( override val options: RoomOptions, realtimeClient: RealtimeClient, chatApi: ChatApi, - override val status: RoomStatus = DefaultRoomStatusStatus(), + override val status: RoomStatus = DefaultStatus(), val logger: LogHandler?, ) : Room { diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 003cfe7..82fab66 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -173,7 +173,7 @@ open class RoomStatusEvenEmitter : EventEmitter Date: Mon, 21 Oct 2024 18:53:49 +0530 Subject: [PATCH 16/22] Implemented basic definition for RoomLifeCycleManager --- .../com/ably/chat/RoomLifecycleManager.kt | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index 5b414c9..09c9c3c 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -1,8 +1,11 @@ package com.ably.chat -import io.ably.lib.realtime.ChannelBase +import io.ably.lib.realtime.Channel as AblyRealtimeChannel import io.ably.lib.types.ErrorInfo +import io.ably.lib.util.Log.LogHandler import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers /** * An interface for features that contribute to the room status. @@ -12,7 +15,7 @@ interface ContributesToRoomLifecycle : EmitsDiscontinuities { * Gets the channel on which the feature operates. This promise is never * rejected except in the case where room initialization is canceled. */ - val channel: CompletableDeferred + val channel: CompletableDeferred /** * Gets the ErrorInfo code that should be used when the feature fails to attach. @@ -35,7 +38,7 @@ interface ContributesToRoomLifecycle : EmitsDiscontinuities { * @internal */ interface ResolvedContributor { - val channel: ChannelBase + val channel: AblyRealtimeChannel val contributor: ContributesToRoomLifecycle } @@ -66,3 +69,51 @@ interface RoomAttachmentResult : NewRoomStatus { * @internal */ class RoomLifecycleManager + (status: DefaultStatus, contributors: List, logger: LogHandler) { + + /** + * The status of the room. + */ + private var _status: DefaultStatus = status + + /** + * The features that contribute to the room status. + */ + private var _contributors: List = contributors + + /** + * Logger for RoomLifeCycleManager + */ + private val _logger: LogHandler = logger + + /** + * sequentialCoroutineScope is to ensure the integrity and atomicity of operations that affect the room status, such as + * attaching, detaching, and releasing the room. It makes sure that we don't have multiple operations happening + * at once which could leave us in an inconsistent state. + * It is used as a CoroutineContext for with [kotlinx.coroutines.selects.select] statement. + * See [Kotlin Dispatchers](https://kt.academy/article/cc-dispatchers) for more information. + */ + private val sequentialCoroutineScope = CoroutineScope(Dispatchers.Default.limitedParallelism(1)) + + /** + * This flag indicates whether some sort of controlled operation is in progress (e.g. attaching, detaching, releasing). + * + * It is used to prevent the room status from being changed by individual channel state changes and ignore + * underlying channel events until we reach a consistent state. + */ + private var _operationInProgress = false; + + /** + * A map of contributors to whether their first attach has completed. + * + * Used to control whether we should trigger discontinuity events. + */ + private val _firstAttachesCompleted = mutableMapOf() + + init { + if (_status.current != RoomLifecycle.Attached) { + _operationInProgress = true + } + // TODO - [CHA-RL4] set up room monitoring here + } +} From a5e837c7bcc629b3185a61cf48e477866376821a Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Mon, 21 Oct 2024 20:24:15 +0530 Subject: [PATCH 17/22] Added interface impl. for ContributesToRoomLifecycle and ResolvedContributor across all contributors and initialized RoomLifeCycle inside Room --- .../src/main/java/com/ably/chat/ChatApi.kt | 4 +- .../src/main/java/com/ably/chat/ErrorCodes.kt | 51 ++++++++++--------- .../src/main/java/com/ably/chat/Messages.kt | 18 ++++--- .../src/main/java/com/ably/chat/Occupancy.kt | 12 +++-- .../src/main/java/com/ably/chat/Presence.kt | 11 ++-- .../src/main/java/com/ably/chat/Room.kt | 30 +++++++---- .../com/ably/chat/RoomLifecycleManager.kt | 8 +-- .../main/java/com/ably/chat/RoomReactions.kt | 12 +++-- .../src/main/java/com/ably/chat/RoomStatus.kt | 5 +- .../src/main/java/com/ably/chat/Rooms.kt | 2 +- .../src/main/java/com/ably/chat/Timeserial.kt | 2 +- .../src/main/java/com/ably/chat/Typing.kt | 12 +++-- 12 files changed, 104 insertions(+), 63 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/ChatApi.kt b/chat-android/src/main/java/com/ably/chat/ChatApi.kt index 2bc74d9..db7af8a 100644 --- a/chat-android/src/main/java/com/ably/chat/ChatApi.kt +++ b/chat-android/src/main/java/com/ably/chat/ChatApi.kt @@ -87,7 +87,7 @@ internal class ChatApi(private val realtimeClient: RealtimeClient, private val c ErrorInfo( "Metadata contains reserved 'ably-chat' key", HttpStatusCodes.BadRequest, - ErrorCodes.InvalidRequestBody, + ErrorCodes.InvalidRequestBody.errorCode, ), ) } @@ -98,7 +98,7 @@ internal class ChatApi(private val realtimeClient: RealtimeClient, private val c ErrorInfo( "Headers contains reserved key with reserved 'ably-chat' prefix", HttpStatusCodes.BadRequest, - ErrorCodes.InvalidRequestBody, + ErrorCodes.InvalidRequestBody.errorCode, ), ) } diff --git a/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt b/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt index b683aed..d604ff4 100644 --- a/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt +++ b/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt @@ -3,105 +3,106 @@ package com.ably.chat /** * Error codes for the Chat SDK. */ -object ErrorCodes { +enum class ErrorCodes (val errorCode: Int) { + /** * The messages feature failed to attach. */ - const val MessagesAttachmentFailed = 102_001 + MessagesAttachmentFailed(102001), /** * The presence feature failed to attach. */ - const val PresenceAttachmentFailed = 102_002 + PresenceAttachmentFailed(102002), /** * The reactions feature failed to attach. */ - const val ReactionsAttachmentFailed = 102_003 + ReactionsAttachmentFailed(102003), /** * The occupancy feature failed to attach. */ - const val OccupancyAttachmentFailed = 102_004 + OccupancyAttachmentFailed(102004), /** * The typing feature failed to attach. */ - const val TypingAttachmentFailed = 102_005 + TypingAttachmentFailed(102005), // 102006 - 102049 reserved for future use for attachment errors /** * The messages feature failed to detach. */ - const val MessagesDetachmentFailed = 102_050 + MessagesDetachmentFailed(102050), /** * The presence feature failed to detach. */ - const val PresenceDetachmentFailed = 102_051 + PresenceDetachmentFailed(102051), /** * The reactions feature failed to detach. */ - const val ReactionsDetachmentFailed = 102_052 + ReactionsDetachmentFailed(102052), /** * The occupancy feature failed to detach. */ - const val OccupancyDetachmentFailed = 102_053 + OccupancyDetachmentFailed(102053), /** * The typing feature failed to detach. */ - const val TypingDetachmentFailed = 102_054 + TypingDetachmentFailed(102054), // 102055 - 102099 reserved for future use for detachment errors /** * The room has experienced a discontinuity. */ - const val RoomDiscontinuity = 102_100 + RoomDiscontinuity(102100), // Unable to perform operation; /** * Cannot perform operation because the room is in a failed state. */ - const val RoomInFailedState = 102_101 + RoomInFailedState(102101), /** * Cannot perform operation because the room is in a releasing state. */ - const val RoomIsReleasing = 102_102 + RoomIsReleasing(102102), /** * Cannot perform operation because the room is in a released state. */ - const val RoomIsReleased = 102_103 + RoomIsReleased(102103), /** * Cannot perform operation because the previous operation failed. */ - const val PreviousOperationFailed = 102_104 + PreviousOperationFailed(102104), /** * An unknown error has happened in the room lifecycle. */ - const val RoomLifecycleError = 102_105 + RoomLifecycleError(102105), /** * The request cannot be understood - */ - const val BadRequest = 40_000 + */ + BadRequest(40_000), /** * Invalid request body - */ - const val InvalidRequestBody = 40_001 + */ + InvalidRequestBody(40_001), - /** - * Internal error - */ - const val InternalError = 50_000 + /** + * Internal error + */ + InternalError(50_000) } /** diff --git a/chat-android/src/main/java/com/ably/chat/Messages.kt b/chat-android/src/main/java/com/ably/chat/Messages.kt index e0c8ca8..67b1afc 100644 --- a/chat-android/src/main/java/com/ably/chat/Messages.kt +++ b/chat-android/src/main/java/com/ably/chat/Messages.kt @@ -207,7 +207,7 @@ internal class DefaultMessagesSubscription( ErrorInfo( "The `end` parameter is specified and is more recent than the subscription point timeserial", HttpStatusCodes.BadRequest, - ErrorCodes.BadRequest, + ErrorCodes.BadRequest.errorCode, ), ) } @@ -225,7 +225,7 @@ internal class DefaultMessages( private val roomId: String, realtimeChannels: AblyRealtime.Channels, private val chatApi: ChatApi, -) : Messages { +) : Messages, ContributesToRoomLifecycle, ResolvedContributor { private var listeners: Map> = emptyMap() @@ -241,6 +241,12 @@ internal class DefaultMessages( override val channel: Channel = realtimeChannels.get(messagesChannelName, ChatChannelOptions()) + override val contributor: ContributesToRoomLifecycle = this + + override val attachmentErrorCode: ErrorCodes = ErrorCodes.MessagesAttachmentFailed + + override val detachmentErrorCode: ErrorCodes = ErrorCodes.MessagesDetachmentFailed + init { channelStateListener = ChannelStateListener { if (!it.resumed) updateChannelSerialsAfterDiscontinuity() @@ -253,7 +259,7 @@ internal class DefaultMessages( addListener(listener, deferredChannelSerial) val messageListener = PubSubMessageListener { val pubSubMessage = it ?: throw AblyException.fromErrorInfo( - ErrorInfo("Got empty pubsub channel message", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest), + ErrorInfo("Got empty pubsub channel message", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest.errorCode), ) val data = parsePubSubMessageData(pubSubMessage.data) val chatMessage = Message( @@ -284,7 +290,7 @@ internal class DefaultMessages( ErrorInfo( "This messages subscription instance was already unsubscribed", HttpStatusCodes.BadRequest, - ErrorCodes.BadRequest, + ErrorCodes.BadRequest.errorCode, ), ) }, @@ -323,14 +329,14 @@ internal class DefaultMessages( private fun requireChannelSerial(): String { return channel.properties.channelSerial ?: throw AblyException.fromErrorInfo( - ErrorInfo("Channel has been attached, but channelSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest), + ErrorInfo("Channel has been attached, but channelSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest.errorCode), ) } private fun requireAttachSerial(): String { return channel.properties.attachSerial ?: throw AblyException.fromErrorInfo( - ErrorInfo("Channel has been attached, but attachSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest), + ErrorInfo("Channel has been attached, but attachSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest.errorCode), ) } diff --git a/chat-android/src/main/java/com/ably/chat/Occupancy.kt b/chat-android/src/main/java/com/ably/chat/Occupancy.kt index a9e18ed..9e3c19b 100644 --- a/chat-android/src/main/java/com/ably/chat/Occupancy.kt +++ b/chat-android/src/main/java/com/ably/chat/Occupancy.kt @@ -61,9 +61,15 @@ data class OccupancyEvent( internal class DefaultOccupancy( private val messages: Messages, -) : Occupancy { - override val channel: Channel - get() = messages.channel +) : Occupancy, ContributesToRoomLifecycle, ResolvedContributor { + + override val channel: Channel = messages.channel + + override val contributor: ContributesToRoomLifecycle = this + + override val attachmentErrorCode: ErrorCodes = ErrorCodes.OccupancyAttachmentFailed + + override val detachmentErrorCode: ErrorCodes = ErrorCodes.OccupancyDetachmentFailed override fun subscribe(listener: Occupancy.Listener): Subscription { TODO("Not yet implemented") diff --git a/chat-android/src/main/java/com/ably/chat/Presence.kt b/chat-android/src/main/java/com/ably/chat/Presence.kt index 2973e48..9c2470f 100644 --- a/chat-android/src/main/java/com/ably/chat/Presence.kt +++ b/chat-android/src/main/java/com/ably/chat/Presence.kt @@ -131,10 +131,15 @@ data class PresenceEvent( internal class DefaultPresence( private val messages: Messages, -) : Presence { +) : Presence, ContributesToRoomLifecycle, ResolvedContributor { - override val channel: Channel - get() = messages.channel + override val channel: Channel = messages.channel + + override val contributor: ContributesToRoomLifecycle = this + + override val attachmentErrorCode: ErrorCodes = ErrorCodes.PresenceAttachmentFailed + + override val detachmentErrorCode: ErrorCodes = ErrorCodes.PresenceDetachmentFailed override suspend fun get(params: List): List { TODO("Not yet implemented") diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index f2458bb..422cb84 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -2,6 +2,7 @@ package com.ably.chat +import io.ably.lib.realtime.Channel import io.ably.lib.types.AblyException import io.ably.lib.types.ErrorInfo import io.ably.lib.util.Log @@ -94,43 +95,50 @@ internal class DefaultRoom( override val options: RoomOptions, realtimeClient: RealtimeClient, chatApi: ChatApi, - override val status: RoomStatus = DefaultStatus(), val logger: LogHandler?, ) : Room { - private val _messages = DefaultMessages( + private val _logger = logger + override val status = DefaultStatus(logger) + + override val messages = DefaultMessages( roomId = roomId, realtimeChannels = realtimeClient.channels, chatApi = chatApi, ) - override val messages: Messages = _messages - - override val presence: Presence = DefaultPresence( + override val presence = DefaultPresence( messages = messages, ) - override val reactions: RoomReactions = DefaultRoomReactions( + override val typing = DefaultTyping( roomId = roomId, realtimeClient = realtimeClient, ) - override val typing: Typing = DefaultTyping( + override val reactions = DefaultRoomReactions( roomId = roomId, realtimeClient = realtimeClient, ) - override val occupancy: Occupancy = DefaultOccupancy( + override val occupancy = DefaultOccupancy( messages = messages, ) + private var _lifecycleManager : RoomLifecycleManager? = null + + init { + val features = listOf(messages, presence, typing, reactions, occupancy) + _lifecycleManager = RoomLifecycleManager(status, features, _logger) + } + override suspend fun attach() { when (status.current) { RoomLifecycle.Attached -> return RoomLifecycle.Releasing -> - throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASING state", ErrorCodes.RoomIsReleasing)) + throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASING state", ErrorCodes.RoomIsReleasing.errorCode)) RoomLifecycle.Released -> - throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASED state", ErrorCodes.RoomIsReleased)) + throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASED state", ErrorCodes.RoomIsReleased.errorCode)) else -> {} } try { @@ -149,7 +157,7 @@ internal class DefaultRoom( } fun release() { - _messages.release() + messages.release() } companion object { diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index 09c9c3c..b65dac8 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -1,9 +1,9 @@ package com.ably.chat +import io.ably.lib.realtime.Channel import io.ably.lib.realtime.Channel as AblyRealtimeChannel import io.ably.lib.types.ErrorInfo import io.ably.lib.util.Log.LogHandler -import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -15,7 +15,7 @@ interface ContributesToRoomLifecycle : EmitsDiscontinuities { * Gets the channel on which the feature operates. This promise is never * rejected except in the case where room initialization is canceled. */ - val channel: CompletableDeferred + val channel: Channel /** * Gets the ErrorInfo code that should be used when the feature fails to attach. @@ -69,7 +69,7 @@ interface RoomAttachmentResult : NewRoomStatus { * @internal */ class RoomLifecycleManager - (status: DefaultStatus, contributors: List, logger: LogHandler) { + (status: DefaultStatus, contributors: List, logger: LogHandler?) { /** * The status of the room. @@ -84,7 +84,7 @@ class RoomLifecycleManager /** * Logger for RoomLifeCycleManager */ - private val _logger: LogHandler = logger + private val _logger: LogHandler? = logger /** * sequentialCoroutineScope is to ensure the integrity and atomicity of operations that affect the room status, such as diff --git a/chat-android/src/main/java/com/ably/chat/RoomReactions.kt b/chat-android/src/main/java/com/ably/chat/RoomReactions.kt index 06b214c..787d5c9 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomReactions.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomReactions.kt @@ -101,11 +101,17 @@ data class SendReactionParams( internal class DefaultRoomReactions( roomId: String, private val realtimeClient: RealtimeClient, -) : RoomReactions { +) : RoomReactions, ContributesToRoomLifecycle, ResolvedContributor { + private val roomReactionsChannelName = "$roomId::\$chat::\$reactions" - override val channel: Channel - get() = realtimeClient.channels.get(roomReactionsChannelName, ChatChannelOptions()) + override val channel: Channel = realtimeClient.channels.get(roomReactionsChannelName, ChatChannelOptions()) + + override val contributor: ContributesToRoomLifecycle = this + + override val attachmentErrorCode: ErrorCodes = ErrorCodes.ReactionsAttachmentFailed + + override val detachmentErrorCode: ErrorCodes = ErrorCodes.ReactionsDetachmentFailed override suspend fun send(params: SendReactionParams) { TODO("Not yet implemented") diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 82fab66..1703c94 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -3,6 +3,7 @@ package com.ably.chat import io.ably.lib.types.ErrorInfo import io.ably.lib.util.EventEmitter import io.ably.lib.util.Log +import io.ably.lib.util.Log.LogHandler /** * Represents the status of a Room. @@ -173,7 +174,9 @@ open class RoomStatusEvenEmitter : EventEmitter) internal class DefaultTyping( roomId: String, private val realtimeClient: RealtimeClient, -) : Typing { +) : Typing, ContributesToRoomLifecycle, ResolvedContributor { + private val typingIndicatorsChannelName = "$roomId::\$chat::\$typingIndicators" - override val channel: Channel - get() = realtimeClient.channels.get(typingIndicatorsChannelName, ChatChannelOptions()) + override val channel: Channel = realtimeClient.channels.get(typingIndicatorsChannelName, ChatChannelOptions()) + + override val contributor: ContributesToRoomLifecycle = this + + override val attachmentErrorCode: ErrorCodes = ErrorCodes.TypingAttachmentFailed + + override val detachmentErrorCode: ErrorCodes = ErrorCodes.TypingDetachmentFailed override fun subscribe(listener: Typing.Listener): Subscription { TODO("Not yet implemented") From 5dfb4749a09cb2a5373cd71416686d70a4168ccc Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Mon, 21 Oct 2024 20:31:09 +0530 Subject: [PATCH 18/22] Fixed linting errors in local files --- .../src/main/java/com/ably/chat/ErrorCodes.kt | 50 +++++++++---------- .../src/main/java/com/ably/chat/Messages.kt | 8 ++- .../src/main/java/com/ably/chat/Room.kt | 11 ++-- .../com/ably/chat/RoomLifecycleManager.kt | 6 +-- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt b/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt index d604ff4..a86dd8e 100644 --- a/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt +++ b/chat-android/src/main/java/com/ably/chat/ErrorCodes.kt @@ -3,106 +3,106 @@ package com.ably.chat /** * Error codes for the Chat SDK. */ -enum class ErrorCodes (val errorCode: Int) { +enum class ErrorCodes(val errorCode: Int) { /** * The messages feature failed to attach. */ - MessagesAttachmentFailed(102001), + MessagesAttachmentFailed(102_001), /** * The presence feature failed to attach. */ - PresenceAttachmentFailed(102002), + PresenceAttachmentFailed(102_002), /** * The reactions feature failed to attach. */ - ReactionsAttachmentFailed(102003), + ReactionsAttachmentFailed(102_003), /** * The occupancy feature failed to attach. */ - OccupancyAttachmentFailed(102004), + OccupancyAttachmentFailed(102_004), /** * The typing feature failed to attach. */ - TypingAttachmentFailed(102005), - // 102006 - 102049 reserved for future use for attachment errors + TypingAttachmentFailed(102_005), + // 102_006 - 102_049 reserved for future use for attachment errors /** * The messages feature failed to detach. */ - MessagesDetachmentFailed(102050), + MessagesDetachmentFailed(102_050), /** * The presence feature failed to detach. */ - PresenceDetachmentFailed(102051), + PresenceDetachmentFailed(102_051), /** * The reactions feature failed to detach. */ - ReactionsDetachmentFailed(102052), + ReactionsDetachmentFailed(102_052), /** * The occupancy feature failed to detach. */ - OccupancyDetachmentFailed(102053), + OccupancyDetachmentFailed(102_053), /** * The typing feature failed to detach. */ - TypingDetachmentFailed(102054), - // 102055 - 102099 reserved for future use for detachment errors + TypingDetachmentFailed(102_054), + // 102_055 - 102_099 reserved for future use for detachment errors /** * The room has experienced a discontinuity. */ - RoomDiscontinuity(102100), + RoomDiscontinuity(102_100), // Unable to perform operation; /** * Cannot perform operation because the room is in a failed state. */ - RoomInFailedState(102101), + RoomInFailedState(102_101), /** * Cannot perform operation because the room is in a releasing state. */ - RoomIsReleasing(102102), + RoomIsReleasing(102_102), /** * Cannot perform operation because the room is in a released state. */ - RoomIsReleased(102103), + RoomIsReleased(102_103), /** * Cannot perform operation because the previous operation failed. */ - PreviousOperationFailed(102104), + PreviousOperationFailed(102_104), /** * An unknown error has happened in the room lifecycle. */ - RoomLifecycleError(102105), + RoomLifecycleError(102_105), /** * The request cannot be understood - */ + */ BadRequest(40_000), /** * Invalid request body - */ + */ InvalidRequestBody(40_001), - /** - * Internal error - */ - InternalError(50_000) + /** + * Internal error + */ + InternalError(50_000), } /** diff --git a/chat-android/src/main/java/com/ably/chat/Messages.kt b/chat-android/src/main/java/com/ably/chat/Messages.kt index 67b1afc..240708a 100644 --- a/chat-android/src/main/java/com/ably/chat/Messages.kt +++ b/chat-android/src/main/java/com/ably/chat/Messages.kt @@ -329,14 +329,18 @@ internal class DefaultMessages( private fun requireChannelSerial(): String { return channel.properties.channelSerial ?: throw AblyException.fromErrorInfo( - ErrorInfo("Channel has been attached, but channelSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest.errorCode), + ErrorInfo("Channel has been attached, but channelSerial is not defined", + HttpStatusCodes.BadRequest, + ErrorCodes.BadRequest.errorCode), ) } private fun requireAttachSerial(): String { return channel.properties.attachSerial ?: throw AblyException.fromErrorInfo( - ErrorInfo("Channel has been attached, but attachSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest.errorCode), + ErrorInfo("Channel has been attached, but attachSerial is not defined", + HttpStatusCodes.BadRequest, + ErrorCodes.BadRequest.errorCode), ) } diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index 422cb84..d234684 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -2,7 +2,6 @@ package com.ably.chat -import io.ably.lib.realtime.Channel import io.ably.lib.types.AblyException import io.ably.lib.types.ErrorInfo import io.ably.lib.util.Log @@ -125,7 +124,7 @@ internal class DefaultRoom( messages = messages, ) - private var _lifecycleManager : RoomLifecycleManager? = null + private var _lifecycleManager: RoomLifecycleManager? = null init { val features = listOf(messages, presence, typing, reactions, occupancy) @@ -136,9 +135,13 @@ internal class DefaultRoom( when (status.current) { RoomLifecycle.Attached -> return RoomLifecycle.Releasing -> - throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASING state", ErrorCodes.RoomIsReleasing.errorCode)) + throw AblyException.fromErrorInfo( + ErrorInfo("Can't ATTACH since room is in RELEASING state", ErrorCodes.RoomIsReleasing.errorCode), + ) RoomLifecycle.Released -> - throw AblyException.fromErrorInfo(ErrorInfo("Can't ATTACH since room is in RELEASED state", ErrorCodes.RoomIsReleased.errorCode)) + throw AblyException.fromErrorInfo( + ErrorInfo("Can't ATTACH since room is in RELEASED state", ErrorCodes.RoomIsReleased.errorCode), + ) else -> {} } try { diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index b65dac8..cb1e1bd 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -1,11 +1,11 @@ package com.ably.chat import io.ably.lib.realtime.Channel -import io.ably.lib.realtime.Channel as AblyRealtimeChannel import io.ably.lib.types.ErrorInfo import io.ably.lib.util.Log.LogHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import io.ably.lib.realtime.Channel as AblyRealtimeChannel /** * An interface for features that contribute to the room status. @@ -69,7 +69,7 @@ interface RoomAttachmentResult : NewRoomStatus { * @internal */ class RoomLifecycleManager - (status: DefaultStatus, contributors: List, logger: LogHandler?) { +(status: DefaultStatus, contributors: List, logger: LogHandler?) { /** * The status of the room. @@ -101,7 +101,7 @@ class RoomLifecycleManager * It is used to prevent the room status from being changed by individual channel state changes and ignore * underlying channel events until we reach a consistent state. */ - private var _operationInProgress = false; + private var _operationInProgress = false /** * A map of contributors to whether their first attach has completed. From e0d5dab81cc3080b505502e67f4db38865fb7a88 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Tue, 22 Oct 2024 13:56:08 +0530 Subject: [PATCH 19/22] Made AblyRealtimeChannel as a explicit type for all room feature channels --- .../src/main/java/com/ably/chat/Messages.kt | 20 +++++++++++-------- .../src/main/java/com/ably/chat/Occupancy.kt | 6 +++--- .../src/main/java/com/ably/chat/Presence.kt | 6 +++--- .../com/ably/chat/RoomLifecycleManager.kt | 3 +-- .../main/java/com/ably/chat/RoomReactions.kt | 6 +++--- .../src/main/java/com/ably/chat/Typing.kt | 6 +++--- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Messages.kt b/chat-android/src/main/java/com/ably/chat/Messages.kt index 240708a..d992ac2 100644 --- a/chat-android/src/main/java/com/ably/chat/Messages.kt +++ b/chat-android/src/main/java/com/ably/chat/Messages.kt @@ -5,13 +5,13 @@ package com.ably.chat import com.ably.chat.QueryOptions.MessageOrder.NewestFirst import com.google.gson.JsonObject import io.ably.lib.realtime.AblyRealtime -import io.ably.lib.realtime.Channel import io.ably.lib.realtime.ChannelState import io.ably.lib.realtime.ChannelStateListener import io.ably.lib.types.AblyException import io.ably.lib.types.ErrorInfo +import io.ably.lib.realtime.Channel as AblyRealtimeChannel -typealias PubSubMessageListener = Channel.MessageListener +typealias PubSubMessageListener = AblyRealtimeChannel.MessageListener typealias PubSubMessage = io.ably.lib.types.Message /** @@ -26,7 +26,7 @@ interface Messages : EmitsDiscontinuities { * * @returns the realtime channel */ - val channel: Channel + val channel: AblyRealtimeChannel /** * Subscribe to new messages in this chat room. @@ -239,7 +239,7 @@ internal class DefaultMessages( */ private val messagesChannelName = "$roomId::\$chat::\$chatMessages" - override val channel: Channel = realtimeChannels.get(messagesChannelName, ChatChannelOptions()) + override val channel = realtimeChannels.get(messagesChannelName, ChatChannelOptions()) override val contributor: ContributesToRoomLifecycle = this @@ -329,18 +329,22 @@ internal class DefaultMessages( private fun requireChannelSerial(): String { return channel.properties.channelSerial ?: throw AblyException.fromErrorInfo( - ErrorInfo("Channel has been attached, but channelSerial is not defined", + ErrorInfo( + "Channel has been attached, but channelSerial is not defined", HttpStatusCodes.BadRequest, - ErrorCodes.BadRequest.errorCode), + ErrorCodes.BadRequest.errorCode, + ), ) } private fun requireAttachSerial(): String { return channel.properties.attachSerial ?: throw AblyException.fromErrorInfo( - ErrorInfo("Channel has been attached, but attachSerial is not defined", + ErrorInfo( + "Channel has been attached, but attachSerial is not defined", HttpStatusCodes.BadRequest, - ErrorCodes.BadRequest.errorCode), + ErrorCodes.BadRequest.errorCode, + ), ) } diff --git a/chat-android/src/main/java/com/ably/chat/Occupancy.kt b/chat-android/src/main/java/com/ably/chat/Occupancy.kt index 9e3c19b..335ebf0 100644 --- a/chat-android/src/main/java/com/ably/chat/Occupancy.kt +++ b/chat-android/src/main/java/com/ably/chat/Occupancy.kt @@ -2,7 +2,7 @@ package com.ably.chat -import io.ably.lib.realtime.Channel +import io.ably.lib.realtime.Channel as AblyRealtimeChannel /** * This interface is used to interact with occupancy in a chat room: subscribing to occupancy updates and @@ -16,7 +16,7 @@ interface Occupancy : EmitsDiscontinuities { * * @returns The underlying Ably channel for occupancy events. */ - val channel: Channel + val channel: AblyRealtimeChannel /** * Subscribe a given listener to occupancy updates of the chat room. @@ -63,7 +63,7 @@ internal class DefaultOccupancy( private val messages: Messages, ) : Occupancy, ContributesToRoomLifecycle, ResolvedContributor { - override val channel: Channel = messages.channel + override val channel = messages.channel override val contributor: ContributesToRoomLifecycle = this diff --git a/chat-android/src/main/java/com/ably/chat/Presence.kt b/chat-android/src/main/java/com/ably/chat/Presence.kt index 9c2470f..857f2bf 100644 --- a/chat-android/src/main/java/com/ably/chat/Presence.kt +++ b/chat-android/src/main/java/com/ably/chat/Presence.kt @@ -3,8 +3,8 @@ package com.ably.chat import android.text.PrecomputedText.Params -import io.ably.lib.realtime.Channel import io.ably.lib.types.PresenceMessage +import io.ably.lib.realtime.Channel as AblyRealtimeChannel typealias PresenceData = Any @@ -19,7 +19,7 @@ interface Presence : EmitsDiscontinuities { * Get the underlying Ably realtime channel used for presence in this chat room. * @returns The realtime channel. */ - val channel: Channel + val channel: AblyRealtimeChannel /** * Method to get list of the current online users and returns the latest presence messages associated to it. @@ -133,7 +133,7 @@ internal class DefaultPresence( private val messages: Messages, ) : Presence, ContributesToRoomLifecycle, ResolvedContributor { - override val channel: Channel = messages.channel + override val channel = messages.channel override val contributor: ContributesToRoomLifecycle = this diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index cb1e1bd..5143dcd 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -1,6 +1,5 @@ package com.ably.chat -import io.ably.lib.realtime.Channel import io.ably.lib.types.ErrorInfo import io.ably.lib.util.Log.LogHandler import kotlinx.coroutines.CoroutineScope @@ -15,7 +14,7 @@ interface ContributesToRoomLifecycle : EmitsDiscontinuities { * Gets the channel on which the feature operates. This promise is never * rejected except in the case where room initialization is canceled. */ - val channel: Channel + val channel: AblyRealtimeChannel /** * Gets the ErrorInfo code that should be used when the feature fails to attach. diff --git a/chat-android/src/main/java/com/ably/chat/RoomReactions.kt b/chat-android/src/main/java/com/ably/chat/RoomReactions.kt index 787d5c9..4a7abb2 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomReactions.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomReactions.kt @@ -2,7 +2,7 @@ package com.ably.chat -import io.ably.lib.realtime.Channel +import io.ably.lib.realtime.Channel as AblyRealtimeChannel /** * This interface is used to interact with room-level reactions in a chat room: subscribing to reactions and sending them. @@ -16,7 +16,7 @@ interface RoomReactions : EmitsDiscontinuities { * * @returns The Ably realtime channel instance. */ - val channel: Channel + val channel: AblyRealtimeChannel /** * Send a reaction to the room including some metadata. @@ -105,7 +105,7 @@ internal class DefaultRoomReactions( private val roomReactionsChannelName = "$roomId::\$chat::\$reactions" - override val channel: Channel = realtimeClient.channels.get(roomReactionsChannelName, ChatChannelOptions()) + override val channel: AblyRealtimeChannel = realtimeClient.channels.get(roomReactionsChannelName, ChatChannelOptions()) override val contributor: ContributesToRoomLifecycle = this diff --git a/chat-android/src/main/java/com/ably/chat/Typing.kt b/chat-android/src/main/java/com/ably/chat/Typing.kt index 9991671..3ff695a 100644 --- a/chat-android/src/main/java/com/ably/chat/Typing.kt +++ b/chat-android/src/main/java/com/ably/chat/Typing.kt @@ -2,7 +2,7 @@ package com.ably.chat -import io.ably.lib.realtime.Channel +import io.ably.lib.realtime.Channel as AblyRealtimeChannel /** * base retry interval, we double it each time @@ -30,7 +30,7 @@ interface Typing : EmitsDiscontinuities { * Get the name of the realtime channel underpinning typing events. * @returns The name of the realtime channel. */ - val channel: Channel + val channel: AblyRealtimeChannel /** * Subscribe a given listener to all typing events from users in the chat room. @@ -82,7 +82,7 @@ internal class DefaultTyping( private val typingIndicatorsChannelName = "$roomId::\$chat::\$typingIndicators" - override val channel: Channel = realtimeClient.channels.get(typingIndicatorsChannelName, ChatChannelOptions()) + override val channel = realtimeClient.channels.get(typingIndicatorsChannelName, ChatChannelOptions()) override val contributor: ContributesToRoomLifecycle = this From 0460c5c3e703f097a5259b992ec623da3f62cecc Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Tue, 22 Oct 2024 15:52:20 +0530 Subject: [PATCH 20/22] Added TODOs before initializing RoomLifeCycleManager, refactored room attach method to call lifecyclemanager attach method instead --- .../src/main/java/com/ably/chat/Room.kt | 43 ++++++++----------- .../src/main/java/com/ably/chat/RoomStatus.kt | 8 ++++ 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/Room.kt b/chat-android/src/main/java/com/ably/chat/Room.kt index d234684..e6cf12f 100644 --- a/chat-android/src/main/java/com/ably/chat/Room.kt +++ b/chat-android/src/main/java/com/ably/chat/Room.kt @@ -1,10 +1,6 @@ @file:Suppress("StringLiteralDuplication", "NotImplementedDeclaration") package com.ably.chat - -import io.ably.lib.types.AblyException -import io.ably.lib.types.ErrorInfo -import io.ably.lib.util.Log import io.ably.lib.util.Log.LogHandler /** @@ -127,30 +123,29 @@ internal class DefaultRoom( private var _lifecycleManager: RoomLifecycleManager? = null init { + /** + * TODO + * Initialize features based on provided RoomOptions. + * By default, only messages feature should be initialized. + * Currently, all features are initialized by default. + */ val features = listOf(messages, presence, typing, reactions, occupancy) _lifecycleManager = RoomLifecycleManager(status, features, _logger) + /** + * TODO + * Make sure previous release op. for same was a success. + * Make sure channels were removed using realtime.channels.release(contributor.channel.name); + * Once this is a success, set room to initialized, if not set it to failed and throw error. + * Note that impl. can change based on recent proposed changes to chat-room-lifecycle DR. + */ + this.status.setStatus(RoomLifecycle.Initialized) } override suspend fun attach() { - when (status.current) { - RoomLifecycle.Attached -> return - RoomLifecycle.Releasing -> - throw AblyException.fromErrorInfo( - ErrorInfo("Can't ATTACH since room is in RELEASING state", ErrorCodes.RoomIsReleasing.errorCode), - ) - RoomLifecycle.Released -> - throw AblyException.fromErrorInfo( - ErrorInfo("Can't ATTACH since room is in RELEASED state", ErrorCodes.RoomIsReleased.errorCode), - ) - else -> {} - } - try { - messages.channel.attachCoroutine() - typing.channel.attachCoroutine() - reactions.channel.attachCoroutine() - } catch (e: Exception) { - logger?.println(Log.ERROR, TAG, "Error handling room ATTACH ", e) + if (_lifecycleManager == null) { + // TODO - wait for room to be initialized inside init } + _lifecycleManager?.attach() } override suspend fun detach() { @@ -162,8 +157,4 @@ internal class DefaultRoom( fun release() { messages.release() } - - companion object { - const val TAG = "DefaultRoom" - } } diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 1703c94..0671011 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -210,4 +210,12 @@ class DefaultStatus(private val logger: LogHandler?) : InternalRoomStatus, RoomS this.internalEmitter.emit(change.current, change) this.emit(change.current, change) } + + fun setStatus(status: RoomLifecycle, error: ErrorInfo? = null) { + val newStatus = object :NewRoomStatus { + override val status: RoomLifecycle = status + override val error: ErrorInfo? = error + } + this.setStatus(newStatus) + } } From b5a5e549e7d914d40031b4d611de16338d2bb46b Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Tue, 22 Oct 2024 15:58:00 +0530 Subject: [PATCH 21/22] Removed unused coroutinescope from the RoomLifeCycleManager, added missing attach suspending method --- .../java/com/ably/chat/RoomLifecycleManager.kt | 15 ++++----------- .../src/main/java/com/ably/chat/RoomStatus.kt | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt index 5143dcd..f35a277 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomLifecycleManager.kt @@ -2,8 +2,6 @@ package com.ably.chat import io.ably.lib.types.ErrorInfo import io.ably.lib.util.Log.LogHandler -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import io.ably.lib.realtime.Channel as AblyRealtimeChannel /** @@ -85,15 +83,6 @@ class RoomLifecycleManager */ private val _logger: LogHandler? = logger - /** - * sequentialCoroutineScope is to ensure the integrity and atomicity of operations that affect the room status, such as - * attaching, detaching, and releasing the room. It makes sure that we don't have multiple operations happening - * at once which could leave us in an inconsistent state. - * It is used as a CoroutineContext for with [kotlinx.coroutines.selects.select] statement. - * See [Kotlin Dispatchers](https://kt.academy/article/cc-dispatchers) for more information. - */ - private val sequentialCoroutineScope = CoroutineScope(Dispatchers.Default.limitedParallelism(1)) - /** * This flag indicates whether some sort of controlled operation is in progress (e.g. attaching, detaching, releasing). * @@ -115,4 +104,8 @@ class RoomLifecycleManager } // TODO - [CHA-RL4] set up room monitoring here } + + suspend fun attach() { + TODO("Not yet implemented") + } } diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index 0671011..c169554 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -212,7 +212,7 @@ class DefaultStatus(private val logger: LogHandler?) : InternalRoomStatus, RoomS } fun setStatus(status: RoomLifecycle, error: ErrorInfo? = null) { - val newStatus = object :NewRoomStatus { + val newStatus = object : NewRoomStatus { override val status: RoomLifecycle = status override val error: ErrorInfo? = error } From ce9c0baf4085c8199771f89c29b3d60fe399db26 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Fri, 8 Nov 2024 15:35:34 +0530 Subject: [PATCH 22/22] Fixed RoomStatusEventEmitter typo, Refactored DefaultStatus, using composition over inheritance for RoomStatusEventEmitter --- .../src/main/java/com/ably/chat/RoomStatus.kt | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt index c169554..25823cd 100644 --- a/chat-android/src/main/java/com/ably/chat/RoomStatus.kt +++ b/chat-android/src/main/java/com/ably/chat/RoomStatus.kt @@ -163,7 +163,7 @@ data class RoomStatusChange( val error: ErrorInfo? = null, ) -open class RoomStatusEvenEmitter : EventEmitter() { +class RoomStatusEventEmitter : EventEmitter() { override fun apply(listener: RoomStatus.Listener?, event: RoomLifecycle?, vararg args: Any?) { try { @@ -174,7 +174,7 @@ open class RoomStatusEvenEmitter : EventEmitter