storing chats
This commit is contained in:
@@ -83,6 +83,12 @@ dependencies {
|
||||
implementation("com.google.dagger:hilt-android:2.50")
|
||||
kapt("com.google.dagger:hilt-compiler:2.50")
|
||||
|
||||
// Room dependencies
|
||||
val roomVersion = "2.6.1"
|
||||
implementation("androidx.room:room-runtime:$roomVersion")
|
||||
implementation("androidx.room:room-ktx:$roomVersion")
|
||||
kapt("androidx.room:room-compiler:$roomVersion")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.mattintech.lchat.data.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.mattintech.lchat.data.db.dao.MessageDao
|
||||
import com.mattintech.lchat.data.db.entities.MessageEntity
|
||||
|
||||
@Database(
|
||||
entities = [MessageEntity::class],
|
||||
version = 1,
|
||||
exportSchema = false
|
||||
)
|
||||
abstract class LChatDatabase : RoomDatabase() {
|
||||
abstract fun messageDao(): MessageDao
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mattintech.lchat.data.db.dao
|
||||
|
||||
import androidx.room.*
|
||||
import com.mattintech.lchat.data.db.entities.MessageEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface MessageDao {
|
||||
@Query("SELECT * FROM messages WHERE roomName = :roomName ORDER BY timestamp ASC")
|
||||
fun getMessagesForRoom(roomName: String): Flow<List<MessageEntity>>
|
||||
|
||||
@Query("SELECT * FROM messages WHERE roomName = :roomName ORDER BY timestamp ASC")
|
||||
suspend fun getMessagesForRoomOnce(roomName: String): List<MessageEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertMessage(message: MessageEntity)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertMessages(messages: List<MessageEntity>)
|
||||
|
||||
@Query("DELETE FROM messages WHERE roomName = :roomName")
|
||||
suspend fun deleteMessagesForRoom(roomName: String)
|
||||
|
||||
@Query("DELETE FROM messages")
|
||||
suspend fun deleteAllMessages()
|
||||
|
||||
@Query("SELECT COUNT(*) FROM messages WHERE roomName = :roomName")
|
||||
suspend fun getMessageCountForRoom(roomName: String): Int
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mattintech.lchat.data.db.entities
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "messages")
|
||||
data class MessageEntity(
|
||||
@PrimaryKey
|
||||
val id: String,
|
||||
val roomName: String,
|
||||
val userId: String,
|
||||
val userName: String,
|
||||
val content: String,
|
||||
val timestamp: Long,
|
||||
val isOwnMessage: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.mattintech.lchat.data.db.mappers
|
||||
|
||||
import com.mattintech.lchat.data.Message
|
||||
import com.mattintech.lchat.data.db.entities.MessageEntity
|
||||
|
||||
fun MessageEntity.toMessage(): Message {
|
||||
return Message(
|
||||
id = id,
|
||||
userId = userId,
|
||||
userName = userName,
|
||||
content = content,
|
||||
timestamp = timestamp,
|
||||
isOwnMessage = isOwnMessage
|
||||
)
|
||||
}
|
||||
|
||||
fun Message.toEntity(roomName: String): MessageEntity {
|
||||
return MessageEntity(
|
||||
id = id,
|
||||
roomName = roomName,
|
||||
userId = userId,
|
||||
userName = userName,
|
||||
content = content,
|
||||
timestamp = timestamp,
|
||||
isOwnMessage = isOwnMessage
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.mattintech.lchat.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
// Placeholder for future migrations
|
||||
object Migrations {
|
||||
// Example migration from version 1 to 2
|
||||
// val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||
// override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// // Migration code here
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.mattintech.lchat.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import com.mattintech.lchat.data.db.LChatDatabase
|
||||
import com.mattintech.lchat.data.db.dao.MessageDao
|
||||
import com.mattintech.lchat.network.WifiAwareManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
@@ -20,4 +23,21 @@ object AppModule {
|
||||
): WifiAwareManager {
|
||||
return WifiAwareManager(context)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLChatDatabase(
|
||||
@ApplicationContext context: Context
|
||||
): LChatDatabase {
|
||||
return Room.databaseBuilder(
|
||||
context,
|
||||
LChatDatabase::class.java,
|
||||
"lchat_database"
|
||||
).build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideMessageDao(database: LChatDatabase): MessageDao {
|
||||
return database.messageDao()
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,36 @@ package com.mattintech.lchat.repository
|
||||
|
||||
import android.content.Context
|
||||
import com.mattintech.lchat.data.Message
|
||||
import com.mattintech.lchat.data.db.dao.MessageDao
|
||||
import com.mattintech.lchat.data.db.mappers.toEntity
|
||||
import com.mattintech.lchat.data.db.mappers.toMessage
|
||||
import com.mattintech.lchat.network.WifiAwareManager
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ChatRepository @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
@ApplicationContext private val context: Context,
|
||||
private val wifiAwareManager: WifiAwareManager,
|
||||
private val messageDao: MessageDao
|
||||
) {
|
||||
|
||||
private val wifiAwareManager = WifiAwareManager(context)
|
||||
private var currentRoomName: String = ""
|
||||
|
||||
private val _messages = MutableStateFlow<List<Message>>(emptyList())
|
||||
val messages: StateFlow<List<Message>> = _messages.asStateFlow()
|
||||
|
||||
// Flow that combines in-memory and database messages
|
||||
fun getMessagesFlow(roomName: String): Flow<List<Message>> {
|
||||
return messageDao.getMessagesForRoom(roomName)
|
||||
.map { entities -> entities.map { it.toMessage() } }
|
||||
.onStart { loadMessagesFromDatabase(roomName) }
|
||||
}
|
||||
|
||||
private val _connectionState = MutableStateFlow<ConnectionState>(ConnectionState.Disconnected)
|
||||
val connectionState: StateFlow<ConnectionState> = _connectionState.asStateFlow()
|
||||
|
||||
@@ -69,9 +79,19 @@ class ChatRepository @Inject constructor(
|
||||
}
|
||||
|
||||
fun startHostMode(roomName: String) {
|
||||
currentRoomName = roomName
|
||||
wifiAwareManager.startHostMode(roomName)
|
||||
_connectionState.value = ConnectionState.Hosting(roomName)
|
||||
startConnectionMonitoring()
|
||||
loadMessagesFromDatabase(roomName)
|
||||
}
|
||||
|
||||
private fun loadMessagesFromDatabase(roomName: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val storedMessages = messageDao.getMessagesForRoomOnce(roomName)
|
||||
.map { it.toMessage() }
|
||||
_messages.value = storedMessages
|
||||
}
|
||||
}
|
||||
|
||||
fun startClientMode() {
|
||||
@@ -104,6 +124,11 @@ class ChatRepository @Inject constructor(
|
||||
|
||||
private fun addMessage(message: Message) {
|
||||
_messages.value = _messages.value + message
|
||||
|
||||
// Save to database
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
messageDao.insertMessage(message.toEntity(currentRoomName))
|
||||
}
|
||||
}
|
||||
|
||||
fun clearMessages() {
|
||||
@@ -118,7 +143,9 @@ class ChatRepository @Inject constructor(
|
||||
connectionCallback = callback
|
||||
wifiAwareManager.setConnectionCallback { roomName, isConnected ->
|
||||
if (isConnected) {
|
||||
currentRoomName = roomName
|
||||
_connectionState.value = ConnectionState.Connected(roomName)
|
||||
loadMessagesFromDatabase(roomName)
|
||||
} else {
|
||||
_connectionState.value = ConnectionState.Disconnected
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.mattintech.lchat.data.Message
|
||||
import com.mattintech.lchat.repository.ChatRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
@@ -20,6 +19,7 @@ sealed class ChatState {
|
||||
data class Error(val message: String) : ChatState()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@HiltViewModel
|
||||
class ChatViewModel @Inject constructor(
|
||||
private val chatRepository: ChatRepository
|
||||
@@ -28,7 +28,10 @@ class ChatViewModel @Inject constructor(
|
||||
private val _state = MutableLiveData<ChatState>(ChatState.Connected)
|
||||
val state: LiveData<ChatState> = _state
|
||||
|
||||
val messages: StateFlow<List<Message>> = chatRepository.messages
|
||||
private val _messagesFlow = MutableStateFlow<Flow<List<Message>>>(flowOf(emptyList()))
|
||||
|
||||
val messages: StateFlow<List<Message>> = _messagesFlow
|
||||
.flatMapLatest { it }
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
@@ -60,6 +63,9 @@ class ChatViewModel @Inject constructor(
|
||||
this.isHost = isHost
|
||||
this.currentUserId = UUID.randomUUID().toString()
|
||||
|
||||
// Set up messages flow for this room
|
||||
_messagesFlow.value = chatRepository.getMessagesFlow(roomName)
|
||||
|
||||
// Setup message callback if needed for additional processing
|
||||
chatRepository.setMessageCallback { userId, userName, content ->
|
||||
// Can add additional message processing here if needed
|
||||
|
||||
Reference in New Issue
Block a user