adding connection status
This commit is contained in:
@@ -3,6 +3,7 @@ package com.mattintech.lchat.repository
|
||||
import android.content.Context
|
||||
import com.mattintech.lchat.data.Message
|
||||
import com.mattintech.lchat.network.WifiAwareManager
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -35,6 +36,10 @@ class ChatRepository private constructor(context: Context) {
|
||||
private var messageCallback: ((String, String, String) -> Unit)? = null
|
||||
private var connectionCallback: ((String, Boolean) -> Unit)? = null
|
||||
|
||||
private var lastActivityTime = System.currentTimeMillis()
|
||||
private var connectionCheckJob: Job? = null
|
||||
private val connectionTimeout = 30000L // 30 seconds
|
||||
|
||||
init {
|
||||
wifiAwareManager.initialize()
|
||||
setupWifiAwareCallbacks()
|
||||
@@ -51,6 +56,19 @@ class ChatRepository private constructor(context: Context) {
|
||||
isOwnMessage = false
|
||||
)
|
||||
addMessage(message)
|
||||
|
||||
// Update last activity time
|
||||
lastActivityTime = System.currentTimeMillis()
|
||||
|
||||
// If we're receiving messages, we must be connected
|
||||
if (_connectionState.value !is ConnectionState.Connected &&
|
||||
_connectionState.value !is ConnectionState.Hosting) {
|
||||
when (_connectionState.value) {
|
||||
is ConnectionState.Hosting -> {} // Keep hosting state
|
||||
else -> _connectionState.value = ConnectionState.Connected("Active")
|
||||
}
|
||||
}
|
||||
|
||||
messageCallback?.invoke(userId, userName, content)
|
||||
}
|
||||
}
|
||||
@@ -58,11 +76,13 @@ class ChatRepository private constructor(context: Context) {
|
||||
fun startHostMode(roomName: String) {
|
||||
wifiAwareManager.startHostMode(roomName)
|
||||
_connectionState.value = ConnectionState.Hosting(roomName)
|
||||
startConnectionMonitoring()
|
||||
}
|
||||
|
||||
fun startClientMode() {
|
||||
wifiAwareManager.startClientMode()
|
||||
_connectionState.value = ConnectionState.Searching
|
||||
startConnectionMonitoring()
|
||||
}
|
||||
|
||||
fun sendMessage(userId: String, userName: String, content: String) {
|
||||
@@ -76,6 +96,15 @@ class ChatRepository private constructor(context: Context) {
|
||||
)
|
||||
addMessage(message)
|
||||
wifiAwareManager.sendMessage(userId, userName, content)
|
||||
|
||||
// Update last activity time
|
||||
lastActivityTime = System.currentTimeMillis()
|
||||
|
||||
// If we can send messages, update connection state if needed
|
||||
if (_connectionState.value is ConnectionState.Disconnected ||
|
||||
_connectionState.value is ConnectionState.Error) {
|
||||
_connectionState.value = ConnectionState.Connected("Active")
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMessage(message: Message) {
|
||||
@@ -92,15 +121,49 @@ class ChatRepository private constructor(context: Context) {
|
||||
|
||||
fun setConnectionCallback(callback: (String, Boolean) -> Unit) {
|
||||
connectionCallback = callback
|
||||
wifiAwareManager.setConnectionCallback(callback)
|
||||
wifiAwareManager.setConnectionCallback { roomName, isConnected ->
|
||||
if (isConnected) {
|
||||
_connectionState.value = ConnectionState.Connected(roomName)
|
||||
} else {
|
||||
_connectionState.value = ConnectionState.Disconnected
|
||||
}
|
||||
callback(roomName, isConnected)
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
stopConnectionMonitoring()
|
||||
wifiAwareManager.stop()
|
||||
_connectionState.value = ConnectionState.Disconnected
|
||||
_connectedUsers.value = emptyList()
|
||||
}
|
||||
|
||||
private fun startConnectionMonitoring() {
|
||||
connectionCheckJob?.cancel()
|
||||
connectionCheckJob = GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(5000) // Check every 5 seconds
|
||||
val timeSinceLastActivity = System.currentTimeMillis() - lastActivityTime
|
||||
|
||||
// If no activity for 30 seconds and we think we're connected, mark as disconnected
|
||||
if (timeSinceLastActivity > connectionTimeout) {
|
||||
when (_connectionState.value) {
|
||||
is ConnectionState.Connected,
|
||||
is ConnectionState.Hosting -> {
|
||||
_connectionState.value = ConnectionState.Disconnected
|
||||
}
|
||||
else -> {} // Keep current state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopConnectionMonitoring() {
|
||||
connectionCheckJob?.cancel()
|
||||
connectionCheckJob = null
|
||||
}
|
||||
|
||||
sealed class ConnectionState {
|
||||
object Disconnected : ConnectionState()
|
||||
object Searching : ConnectionState()
|
||||
|
||||
@@ -9,8 +9,11 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.mattintech.lchat.R
|
||||
import com.mattintech.lchat.databinding.FragmentChatBinding
|
||||
import com.mattintech.lchat.repository.ChatRepository
|
||||
import com.mattintech.lchat.ui.adapters.MessageAdapter
|
||||
import com.mattintech.lchat.utils.LOG_PREFIX
|
||||
import com.mattintech.lchat.viewmodel.ChatViewModel
|
||||
@@ -50,6 +53,7 @@ class ChatFragment : Fragment() {
|
||||
|
||||
setupUI()
|
||||
observeViewModel()
|
||||
updateRoomInfo()
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
@@ -84,7 +88,7 @@ class ChatFragment : Fragment() {
|
||||
lifecycleScope.launch {
|
||||
viewModel.connectionState.collect { state ->
|
||||
Log.d(TAG, "Connection state: $state")
|
||||
// Handle connection state changes if needed
|
||||
updateConnectionStatus(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +101,41 @@ class ChatFragment : Fragment() {
|
||||
binding.messageInput.text?.clear()
|
||||
}
|
||||
|
||||
private fun updateRoomInfo() {
|
||||
val (roomName, _, isHost) = viewModel.getRoomInfo()
|
||||
binding.roomNameText.text = if (isHost) "Hosting: $roomName" else "Room: $roomName"
|
||||
}
|
||||
|
||||
private fun updateConnectionStatus(state: ChatRepository.ConnectionState) {
|
||||
when (state) {
|
||||
is ChatRepository.ConnectionState.Disconnected -> {
|
||||
binding.connectionStatusText.text = "Disconnected"
|
||||
binding.connectionIndicator.backgroundTintList =
|
||||
ContextCompat.getColorStateList(requireContext(), R.color.disconnected_color)
|
||||
}
|
||||
is ChatRepository.ConnectionState.Searching -> {
|
||||
binding.connectionStatusText.text = "Searching..."
|
||||
binding.connectionIndicator.backgroundTintList =
|
||||
ContextCompat.getColorStateList(requireContext(), R.color.connecting_color)
|
||||
}
|
||||
is ChatRepository.ConnectionState.Hosting -> {
|
||||
binding.connectionStatusText.text = "Hosting"
|
||||
binding.connectionIndicator.backgroundTintList =
|
||||
ContextCompat.getColorStateList(requireContext(), R.color.hosting_color)
|
||||
}
|
||||
is ChatRepository.ConnectionState.Connected -> {
|
||||
binding.connectionStatusText.text = "Connected"
|
||||
binding.connectionIndicator.backgroundTintList =
|
||||
ContextCompat.getColorStateList(requireContext(), R.color.connected_color)
|
||||
}
|
||||
is ChatRepository.ConnectionState.Error -> {
|
||||
binding.connectionStatusText.text = "Error: ${state.message}"
|
||||
binding.connectionIndicator.backgroundTintList =
|
||||
ContextCompat.getColorStateList(requireContext(), R.color.disconnected_color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
Log.d(TAG, "onDestroyView")
|
||||
|
||||
5
app/src/main/res/drawable/ic_circle.xml
Normal file
5
app/src/main/res/drawable/ic_circle.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
@@ -4,13 +4,60 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/connectionStatusCard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardBackgroundColor="?attr/colorSurfaceVariant"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="12dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/connectionIndicator"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:background="@drawable/ic_circle"
|
||||
android:backgroundTint="@color/disconnected_color" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connectionStatusText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="Disconnected"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomNameText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text=""
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textStyle="bold"
|
||||
android:gravity="end" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/messagesRecyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:padding="8dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/connectionStatusCard"
|
||||
app:layout_constraintBottom_toTopOf="@id/messageInputLayout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
@@ -7,4 +7,10 @@
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
<!-- Connection Status Colors -->
|
||||
<color name="connected_color">#4CAF50</color>
|
||||
<color name="connecting_color">#FFC107</color>
|
||||
<color name="disconnected_color">#F44336</color>
|
||||
<color name="hosting_color">#2196F3</color>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user