Skip to content

Commit

Permalink
Implemented internal transcript and minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mrajatttt committed Aug 22, 2024
1 parent 1b17c3d commit 45ed623
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ fun ChatScreen(viewModel: ChatViewModel = hiltViewModel()) {
dismissButton = {
TextButton(onClick = {
showRestoreDialog = false
viewModel.clearContactId() // Clear contactId
viewModel.clearParticipantToken()
viewModel.initiateChat() // Start new chat
}) { Text("Start new") }
Expand Down Expand Up @@ -158,7 +157,7 @@ fun ChatScreen(viewModel: ChatViewModel = hiltViewModel()) {
if (!showCustomSheet) {
ExtendedFloatingActionButton(
text = {
if (isChatActive.value) {
if (isChatActive.value == false) {
Text("Start Chat")
} else {
Text("Resume Chat")
Expand Down Expand Up @@ -187,7 +186,7 @@ fun ChatScreen(viewModel: ChatViewModel = hiltViewModel()) {
}
}

ContactIdAndTokenSection(viewModel)
ParticipantTokenSection(viewModel)

AnimatedVisibility(
visible = showCustomSheet,
Expand Down Expand Up @@ -307,23 +306,14 @@ fun ChatMessage(transcriptItem: TranscriptItem) {
}

@Composable
fun ContactIdAndTokenSection(viewModel: ChatViewModel) {
val contactId by viewModel.liveContactId.observeAsState()
fun ParticipantTokenSection(viewModel: ChatViewModel) {
val participantToken by viewModel.liveParticipantToken.observeAsState()

Column {
Text(text = "Contact ID: ${if (contactId != null) "Available" else "Not available"}", color = if (contactId != null) Color.Blue else Color.Red)
Button(onClick = viewModel::clearContactId) {
Text("Clear Contact ID")
}
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Participant Token: ${if (participantToken != null) "Available" else "Not available"}", color = if (participantToken != null) Color.Blue else Color.Red)
Button(onClick = viewModel::clearParticipantToken) {
Text("Clear Participant Token")
}
Button(onClick = viewModel::endChat) {
Text(text = "Disconnect")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.google.gson.annotations.SerializedName
data class StartChatRequest(
@SerializedName("InstanceId") val connectInstanceId: String,
@SerializedName("ContactFlowId") val contactFlowId: String,
@SerializedName("PersistentChat") val persistentChat: PersistentChat? = null,
@SerializedName("ParticipantDetails") val participantDetails: ParticipantDetails,
@SerializedName("SupportedMessagingContentTypes") val supportedMessagingContentTypes: List<String> = listOf("text/plain", "text/markdown")
)
Expand All @@ -14,7 +13,3 @@ data class ParticipantDetails(
@SerializedName("DisplayName") val displayName: String
)

data class PersistentChat(
@SerializedName("SourceContactId") val sourceContactId: String,
@SerializedName("RehydrationType") val rehydrationType: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,29 @@ import com.amazon.connect.chat.sdk.model.Message
import com.amazon.connect.chat.sdk.model.MessageDirection
import com.amazon.connect.chat.sdk.model.TranscriptItem
import com.amazon.connect.chat.sdk.model.ContentType
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone

object CommonUtils {

fun formatTime(timeStamp: String): String {
val utcFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
timeZone = TimeZone.getTimeZone("UTC")
}

val date = utcFormatter.parse(timeStamp)
return if (date != null) {
val localFormatter = SimpleDateFormat("HH:mm", Locale.getDefault()).apply {
timeZone = TimeZone.getDefault()
}
localFormatter.format(date)
} else {
timeStamp
}
}


fun getMessageDirection(transcriptItem: TranscriptItem) {
when (transcriptItem) {
is Message -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,29 @@ import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.amazon.connect.chat.androidchatexample.models.StartChatResponse
import com.amazon.connect.chat.androidchatexample.network.Resource
import com.amazon.connect.chat.androidchatexample.repository.ChatRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import androidx.lifecycle.viewModelScope
import com.amazonaws.handlers.AsyncHandler
import com.amazonaws.services.connectparticipant.model.CreateParticipantConnectionRequest
import com.amazonaws.services.connectparticipant.model.CreateParticipantConnectionResult
import com.amazon.connect.chat.androidchatexample.Config
import com.amazon.connect.chat.sdk.model.Message
import com.amazon.connect.chat.androidchatexample.models.ParticipantDetails
import com.amazon.connect.chat.androidchatexample.models.PersistentChat
import com.amazon.connect.chat.androidchatexample.models.StartChatRequest
import com.amazon.connect.chat.androidchatexample.models.StartChatResponse
import com.amazon.connect.chat.androidchatexample.network.Resource
import com.amazon.connect.chat.androidchatexample.repository.ChatRepository
import com.amazon.connect.chat.androidchatexample.utils.CommonUtils
import com.amazon.connect.chat.sdk.network.WebSocketManager
import com.amazon.connect.chat.sdk.utils.CommonUtils.Companion.parseErrorMessage
import com.amazon.connect.chat.sdk.model.ContentType
import com.amazon.connect.chat.sdk.ChatSession
import com.amazon.connect.chat.sdk.model.ChatDetails
import com.amazon.connect.chat.sdk.model.ContentType
import com.amazon.connect.chat.sdk.model.Event
import com.amazon.connect.chat.sdk.model.GlobalConfig
import com.amazon.connect.chat.sdk.model.Message
import com.amazon.connect.chat.sdk.model.TranscriptItem
import com.amazon.connect.chat.sdk.network.WebSocketManager
import com.amazon.connect.chat.sdk.utils.CommonUtils.Companion.parseErrorMessage
import com.amazonaws.handlers.AsyncHandler
import com.amazonaws.services.connectparticipant.model.CreateParticipantConnectionRequest
import com.amazonaws.services.connectparticipant.model.CreateParticipantConnectionResult
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class ChatViewModel @Inject constructor(
Expand All @@ -51,49 +50,34 @@ class ChatViewModel @Inject constructor(
private val _errorMessage = MutableLiveData<String?>()
val errorMessage: LiveData<String?> = _errorMessage

// LiveData for actual string values, updates will reflect in the UI
private val _liveContactId = MutableLiveData<String?>(sharedPreferences.getString("contactID", null))
val liveContactId: LiveData<String?> = _liveContactId

private val _liveParticipantToken = MutableLiveData<String?>(sharedPreferences.getString("participantToken", null))
val liveParticipantToken: LiveData<String?> = _liveParticipantToken

// Setters that update LiveData, which in turn update the UI
private var contactId: String?
get() = liveContactId.value
set(value) {
// sharedPreferences.edit().putString("contactID", value).apply()
_liveContactId.value = value
}

private var participantToken: String?
get() = liveParticipantToken.value
set(value) {
// sharedPreferences.edit().putString("participantToken", value).apply()
_liveParticipantToken.value = value // Reflect the new value in LiveData
}

fun clearContactId() {
sharedPreferences.edit().remove("contactID").apply()
_liveContactId.value = null
}

fun clearParticipantToken() {
sharedPreferences.edit().remove("participantToken").apply()
_liveParticipantToken.value = null
}

init {
configureChatSession()
viewModelScope.launch {
configureChatSession()
}
}

private fun configureChatSession() {
private suspend fun configureChatSession() {
val globalConfig = GlobalConfig(region = chatConfiguration.region)
chatSession.configure(globalConfig)
setupChatHandlers(chatSession)
}

private fun setupChatHandlers(chatSession: ChatSession) {
private suspend fun setupChatHandlers(chatSession: ChatSession) {
chatSession.onConnectionEstablished = {
Log.d("ChatViewModel", "Connection established.")
_isChatActive.value = true
Expand All @@ -102,7 +86,14 @@ class ChatViewModel @Inject constructor(
chatSession.onMessageReceived = { transcriptItem ->
// Handle received message
Log.d("ChatViewModel", "Received transcript item: $transcriptItem")
this.onMessageReceived(transcriptItem)
// this.onMessageReceived(transcriptItem)
}

chatSession.onTranscriptUpdated = { transcriptList ->
Log.d("ChatViewModel", "Transcript onTranscriptUpdated: $transcriptList")
viewModelScope.launch {
onUpdateTranscript(transcriptList)
}
}

chatSession.onChatEnded = {
Expand All @@ -118,6 +109,11 @@ class ChatViewModel @Inject constructor(
Log.d("ChatViewModel", "Connection re-established.")
_isChatActive.value = true
}

chatSession.onChatSessionStateChanged = {
Log.d("ChatViewModel", "Chat session state changed: $it")
_isChatActive.value = it
}
}

fun initiateChat() {
Expand All @@ -129,29 +125,24 @@ class ChatViewModel @Inject constructor(
val chatDetails = ChatDetails(participantToken = it)
createParticipantConnection(chatDetails)
}
} else if (contactId != null) {
startChat(contactId)
} else {
startChat(null) // Start a fresh chat if no tokens are present
startChat() // Start a fresh chat if no tokens are present
}
}
}

private fun startChat(sourceContactId: String?) {
private fun startChat() {
viewModelScope.launch {
_isLoading.value = true
val participantDetails = ParticipantDetails(displayName = chatConfiguration.customerName)
val persistentChat: PersistentChat? = sourceContactId?.let { PersistentChat(it, "ENTIRE_PAST_SESSION") }
val request = StartChatRequest(
connectInstanceId = chatConfiguration.connectInstanceId,
contactFlowId = chatConfiguration.contactFlowId,
participantDetails = participantDetails,
persistentChat = persistentChat
participantDetails = participantDetails
)
when (val response = chatRepository.startChat(startChatRequest = request)) {
is Resource.Success -> {
response.data?.data?.startChatResult?.let { result ->
this@ChatViewModel.contactId = result.contactId
this@ChatViewModel.participantToken = result.participantToken
handleStartChatResponse(result)
} ?: run {
Expand All @@ -161,7 +152,6 @@ class ChatViewModel @Inject constructor(
is Resource.Error -> {
_errorMessage.value = response.message
_isLoading.value = false
clearContactId()
}

is Resource.Loading -> _isLoading.value = true
Expand Down Expand Up @@ -197,7 +187,7 @@ class ChatViewModel @Inject constructor(


private fun createParticipantConnection1(chatDetails: ChatDetails?) {
val pToken: String = if (chatDetails?.contactId == null) participantToken.toString() else chatDetails?.participantToken.toString()
val pToken: String = chatDetails?.participantToken.toString()
viewModelScope.launch {
_isLoading.value = true // Start loading
chatRepository.createParticipantConnection(
Expand Down Expand Up @@ -261,6 +251,18 @@ class ChatViewModel @Inject constructor(
}
}

private fun onUpdateTranscript(transcriptList: List<TranscriptItem>) {
val updatedMessages = transcriptList.map { transcriptItem ->
if (transcriptItem is Event) {
CommonUtils.customizeEvent(transcriptItem)
}
CommonUtils.getMessageDirection(transcriptItem)
transcriptItem
}
_messages.value = updatedMessages
Log.d("ChatViewModel", "Transcript updated: ${_messages.value}")
}

private fun onMessageReceived(transcriptItem: TranscriptItem) {
viewModelScope.launch {
// Log the current state before the update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.amazon.connect.chat.androidchatexample.utils.CommonUtils
import com.amazon.connect.chat.sdk.model.Event
import com.amazon.connect.chat.sdk.model.ListPickerContent
import com.amazon.connect.chat.sdk.model.Message
Expand Down Expand Up @@ -83,7 +84,7 @@ fun SenderChatBubble(message: Message) {
)
message.timeStamp?.let {
Text(
text = it,
text = CommonUtils.formatTime(it),
style = MaterialTheme.typography.bodySmall,
color = Color(0xFFB0BEC5),
modifier = Modifier.align(Alignment.End)
Expand Down Expand Up @@ -130,7 +131,7 @@ fun ReceiverChatBubble(message: Message) {
)
message.timeStamp?.let {
Text(
text = it,
text = CommonUtils.formatTime(it),
style = MaterialTheme.typography.bodySmall,
color = Color.White,
modifier = Modifier.align(Alignment.End).alpha(0.7f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.amazon.connect.chat.androidchatexample.R
import com.amazon.connect.chat.androidchatexample.utils.CommonUtils
import com.amazon.connect.chat.sdk.model.ListPickerContent
import com.amazon.connect.chat.sdk.model.ListPickerElement
import com.amazon.connect.chat.sdk.model.Message
Expand Down Expand Up @@ -115,7 +116,7 @@ fun ListPickerContentView(
)
message.timeStamp?.let {
Text(
text = it,
text = CommonUtils.formatTime(it),
style = MaterialTheme.typography.bodySmall,
color = Color.White,
modifier = Modifier.align(Alignment.End).alpha(0.7f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.amazon.connect.chat.androidchatexample.utils.CommonUtils
import com.amazon.connect.chat.sdk.model.Message
import com.amazon.connect.chat.sdk.model.QuickReplyContent
import com.amazon.connect.chat.androidchatexample.viewmodel.ChatViewModel
Expand Down Expand Up @@ -56,7 +57,7 @@ fun QuickReplyContentView(message: Message, messageContent: QuickReplyContent) {
)
message.timeStamp?.let {
Text(
text = it,
text = CommonUtils.formatTime(it),
style = MaterialTheme.typography.bodySmall,
color = Color.White,
modifier = Modifier.align(Alignment.End).alpha(0.7f)
Expand Down
Loading

0 comments on commit 45ed623

Please sign in to comment.