Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented internal transcript and minor fixes #14

Merged
merged 3 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 ->
[email protected] = result.contactId
[email protected] = 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
Loading