Skip to content

Commit

Permalink
- Hooked up websocket manager with SDK Flow (#12)
Browse files Browse the repository at this point in the history
* - Hooked up websocket manager with SDK Flow

* - Feedback addressed
  • Loading branch information
mrajatttt authored Aug 20, 2024
1 parent a81a928 commit 6ef7fba
Show file tree
Hide file tree
Showing 11 changed files with 343 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ package com.amazon.connect.chat.androidchatexample

import com.amazonaws.regions.Regions

//object Config {
// val connectInstanceId: String = "e816d0f3-eda3-46e4-bc67-9999e621eff6"
// val contactFlowId: String = "f22bfa3b-400e-4250-939d-90a79eb1cd24"
// val startChatEndpoint: String = "https://bqo00ujzld.execute-api.us-west-2.amazonaws.com/"
// val region: Regions = Regions.US_WEST_2
// val agentName = "AGENT"
// val customerName = "CUSTOMER"
//}

object Config {
val connectInstanceId: String = "e816d0f3-eda3-46e4-bc67-9999e621eff6"
val contactFlowId: String = "f22bfa3b-400e-4250-939d-90a79eb1cd24"
val startChatEndpoint: String = "https://bqo00ujzld.execute-api.us-west-2.amazonaws.com/"
val region: Regions = Regions.US_WEST_2
val connectInstanceId: String = "6ceda8ca-5e6e-4a60-9bfb-4994cc1fec79"
val contactFlowId: String = "c8d90d07-a28c-4a97-9dfb-f4785b98d8d2"
val startChatEndpoint: String = "https://3r4nj9r68b.execute-api.us-east-1.amazonaws.com/"
val region: Regions = Regions.US_EAST_1
val agentName = "AGENT"
val customerName = "CUSTOMER"
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,9 @@ class MainActivity : ComponentActivity() {
fun ChatScreen(viewModel: ChatViewModel = hiltViewModel()) {
var showCustomSheet by remember { mutableStateOf(false) }
val isLoading = viewModel.isLoading.observeAsState(initial = false)
val createParticipantConnectionResult =
viewModel.createParticipantConnectionResult.observeAsState()
val webSocketUrl = viewModel.webSocketUrl.observeAsState()
val isChatActive = viewModel.isChatActive.observeAsState(initial = false)
var showDialog by remember { mutableStateOf(false) }
var showRestoreDialog by remember { mutableStateOf(false) }
val contactId = viewModel.liveContactId.observeAsState()
val participantToken = viewModel.liveParticipantToken.observeAsState()
var showErrorDialog by remember { mutableStateOf(false) }
val errorMessage by viewModel.errorMessage.observeAsState()
Expand Down Expand Up @@ -161,7 +158,7 @@ fun ChatScreen(viewModel: ChatViewModel = hiltViewModel()) {
if (!showCustomSheet) {
ExtendedFloatingActionButton(
text = {
if (webSocketUrl.value == null) {
if (isChatActive.value) {
Text("Start Chat")
} else {
Text("Resume Chat")
Expand All @@ -173,23 +170,19 @@ fun ChatScreen(viewModel: ChatViewModel = hiltViewModel()) {
}
},
onClick = {
if (!contactId.value.isNullOrEmpty() && participantToken.value.isNullOrEmpty()) {
showRestoreDialog = true
if (isChatActive.value == false) {
viewModel.initiateChat()
} else {
if (webSocketUrl.value == null) {
viewModel.initiateChat()
} else {
showCustomSheet = true
}
showCustomSheet = true
}
},

)
}
}
) {
LaunchedEffect(isLoading.value) {
if (!isLoading.value && createParticipantConnectionResult.value != null) {
LaunchedEffect(isChatActive.value) {
if (!isLoading.value && isChatActive.value) {
showCustomSheet = true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,12 @@ private val LightColorScheme = lightColorScheme(

@Composable
fun androidconnectchatandroidTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
darkTheme: Boolean = false,
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}

darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val colorScheme = LightColorScheme
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ class ChatViewModel @Inject constructor(
private val chatConfiguration = Config
private val _isLoading = MutableLiveData(false)
val isLoading: MutableLiveData<Boolean> = _isLoading
private val _startChatResponse = MutableLiveData<Resource<StartChatResponse>>()
private val startChatResponse: LiveData<Resource<StartChatResponse>> = _startChatResponse

private val _isChatActive = MutableLiveData(false)
val isChatActive: MutableLiveData<Boolean> = _isChatActive

private val _createParticipantConnectionResult = MutableLiveData<CreateParticipantConnectionResult?>()
val createParticipantConnectionResult: MutableLiveData<CreateParticipantConnectionResult?> = _createParticipantConnectionResult
private val _messages = MutableLiveData<List<TranscriptItem>>()
val messages: LiveData<List<TranscriptItem>> = _messages
private val _webSocketUrl = MutableLiveData<String?>()
val webSocketUrl: MutableLiveData<String?> = _webSocketUrl
private val _errorMessage = MutableLiveData<String?>()
val errorMessage: LiveData<String?> = _errorMessage

Expand All @@ -58,10 +58,6 @@ class ChatViewModel @Inject constructor(
private val _liveParticipantToken = MutableLiveData<String?>(sharedPreferences.getString("participantToken", null))
val liveParticipantToken: LiveData<String?> = _liveParticipantToken

init {
webSocketManager.requestNewWsUrl = { createParticipantConnection(null) }
}

// Setters that update LiveData, which in turn update the UI
private var contactId: String?
get() = liveContactId.value
Expand Down Expand Up @@ -100,15 +96,18 @@ class ChatViewModel @Inject constructor(
private fun setupChatHandlers(chatSession: ChatSession) {
chatSession.onConnectionEstablished = {
Log.d("ChatViewModel", "Connection established.")
_isChatActive.value = true
}

chatSession.onMessageReceived = { transcriptItem ->
// Handle received message
Log.d("ChatViewModel", "Received transcript item: $transcriptItem")
this.onMessageReceived(transcriptItem)
}

chatSession.onChatEnded = {
Log.d("ChatViewModel", "Chat ended.")
_isChatActive.value = false
}

chatSession.onConnectionBroken = {
Expand All @@ -117,6 +116,7 @@ class ChatViewModel @Inject constructor(

chatSession.onConnectionReEstablished = {
Log.d("ChatViewModel", "Connection re-established.")
_isChatActive.value = true
}
}

Expand Down Expand Up @@ -181,7 +181,7 @@ class ChatViewModel @Inject constructor(
}
}

private fun createParticipantConnection1(chatDetails: ChatDetails) {
private fun createParticipantConnection(chatDetails: ChatDetails) {
viewModelScope.launch {
_isLoading.value = true // Start loading
val result = chatSession.connect(chatDetails)
Expand All @@ -196,7 +196,7 @@ class ChatViewModel @Inject constructor(
}


private fun createParticipantConnection(chatDetails: ChatDetails?) {
private fun createParticipantConnection1(chatDetails: ChatDetails?) {
val pToken: String = if (chatDetails?.contactId == null) participantToken.toString() else chatDetails?.participantToken.toString()
viewModelScope.launch {
_isLoading.value = true // Start loading
Expand All @@ -214,16 +214,15 @@ class ChatViewModel @Inject constructor(
result?.let { connectionResult ->
_createParticipantConnectionResult.value = connectionResult
val websocketUrl = connectionResult.websocket?.url
_webSocketUrl.value = websocketUrl
// _webSocketUrl.value = websocketUrl
if (chatDetails !== null) {
participantToken = chatDetails?.participantToken;
}

websocketUrl?.let { wsUrl ->
webSocketManager.createWebSocket(
webSocketManager.connect(
wsUrl,
this@ChatViewModel::onMessageReceived,
this@ChatViewModel::onWebSocketError
false
)
}
connectionResult.connectionCredentials?.connectionToken?.let { cToken ->
Expand Down Expand Up @@ -305,7 +304,6 @@ class ChatViewModel @Inject constructor(
}



private fun onWebSocketError(errorMessage: String) {
// Handle WebSocket errors
_isLoading.postValue(false)
Expand Down
20 changes: 18 additions & 2 deletions chat-sdk/src/main/java/com/amazon/connect/chat/sdk/ChatSession.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,20 @@ class ChatSessionImpl @Inject constructor(private val chatService: ChatService)
override var onMessageReceived: ((TranscriptItem) -> Unit)? = null
override var onChatEnded: (() -> Unit)? = null
private val coroutineScope = CoroutineScope(Dispatchers.Main + Job())
private var eventCollectionJob: Job? = null
private var transcriptCollectionJob: Job? = null

init {
setupEventSubscriptions()
}

private fun setupEventSubscriptions() {
coroutineScope.launch {
// Cancel any existing subscriptions before setting up new ones
eventCollectionJob?.cancel()
transcriptCollectionJob?.cancel()

// Set up new subscriptions
eventCollectionJob = coroutineScope.launch {
chatService.eventPublisher.collect { event ->
when (event) {
ChatEvent.ConnectionEstablished -> onConnectionEstablished?.invoke()
Expand All @@ -60,7 +67,8 @@ class ChatSessionImpl @Inject constructor(private val chatService: ChatService)
}
}
}
coroutineScope.launch {

transcriptCollectionJob = coroutineScope.launch {
chatService.transcriptPublisher.collect { transcriptItem ->
onMessageReceived?.invoke(transcriptItem)
}
Expand Down Expand Up @@ -90,6 +98,14 @@ class ChatSessionImpl @Inject constructor(private val chatService: ChatService)
}.getOrElse {
Result.failure(it)
}
}.also {
cleanup()
}
}

private fun cleanup() {
// Cancel flow collection jobs when disconnecting or cleaning up
eventCollectionJob?.cancel()
transcriptCollectionJob?.cancel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import com.amazon.connect.chat.sdk.network.APIClient
import com.amazon.connect.chat.sdk.network.AWSClient
import com.amazon.connect.chat.sdk.network.WebSocketManager
import com.amazon.connect.chat.sdk.network.MetricsManager
import com.amazon.connect.chat.sdk.network.NetworkConnectionManager
import com.amazon.connect.chat.sdk.network.WebSocketManagerImpl
import com.amazon.connect.chat.sdk.repository.ChatService
import com.amazon.connect.chat.sdk.repository.ChatServiceImpl
import com.amazon.connect.chat.sdk.repository.ConnectionDetailsProvider
Expand All @@ -25,9 +27,10 @@ object ChatModule {
/**
* Provides a singleton instance of ChatService.
*
* @param apiClient The API client for network operations.
* @param awsClient The AWS client for connecting to AWS services.
* @param connectionDetailsProvider The provider for connection details.
* @param webSocketManager The WebSocket manager for managing WebSocket connections.
* @param metricsManager The metrics manager for managing metrics.
* @return An instance of ChatServiceImpl.
*/
@Provides
Expand Down Expand Up @@ -64,18 +67,29 @@ object ChatModule {
return ConnectionDetailsProviderImpl()
}

// Provide the Context dependency
/**
* Provides a singleton instance of Context.
*
* @param appContext The application context.
* @return An instance of Context.
*/
@Provides
@Singleton
fun provideContext(@ApplicationContext appContext: Context): Context {
return appContext
}

/**
* Provides a singleton instance of NetworkConnectionManager.
*
* @param networkConnectionManager The network connection manager.
* @return An instance of NetworkConnectionManager.
*/
@Provides
@Singleton
fun provideWebSocketManager(
context: Context,
networkConnectionManager: NetworkConnectionManager,
): WebSocketManager {
return WebSocketManager(context, {})
return WebSocketManagerImpl(networkConnectionManager = networkConnectionManager)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.amazon.connect.chat.sdk.di

import android.content.Context
import com.amazon.connect.chat.sdk.network.APIClient
import com.amazon.connect.chat.sdk.network.AWSClient
import com.amazon.connect.chat.sdk.network.AWSClientImpl
import com.amazon.connect.chat.sdk.network.ApiUrl
import com.amazon.connect.chat.sdk.network.MetricsInterface
import com.amazon.connect.chat.sdk.network.MetricsManager
import com.amazon.connect.chat.sdk.Config
import com.amazon.connect.chat.sdk.network.NetworkConnectionManager
import com.amazon.connect.chat.sdk.utils.MetricsUtils.getMetricsEndpoint
import com.amazonaws.services.connectparticipant.AmazonConnectParticipantClient
import dagger.Module
Expand Down Expand Up @@ -109,11 +110,17 @@ object NetworkModule {
return APIClient(metricsInterface)
}

// @Provides
// @Singleton
// fun provideUploadInterface(retrofitBuilder: Retrofit.Builder): UploadInterface {
// return createService(UploadInterface::class.java, retrofitBuilder)
// }
/**
* Provides a singleton instance of NetworkConnectionManager.
*
* @param context The application context.
* @return An instance of NetworkConnectionManager.
*/
@Provides
@Singleton
fun provideNetworkConnectionManager(context: Context): NetworkConnectionManager {
return NetworkConnectionManager.getInstance(context)
}

/**
* Creates a Retrofit service for the specified class.
Expand Down
Loading

0 comments on commit 6ef7fba

Please sign in to comment.