Merge pull request #4 from mattintech/feature/depInj
adding shared prefs to save the users name
This commit is contained in:
151
TODO.md
151
TODO.md
@@ -9,11 +9,27 @@
|
|||||||
- [x] Implement proper state management with LiveData/StateFlow
|
- [x] Implement proper state management with LiveData/StateFlow
|
||||||
- [x] Add ViewModelFactory if needed
|
- [x] Add ViewModelFactory if needed
|
||||||
|
|
||||||
### 1.2 Dependency Injection with Hilt
|
### 1.2 Dependency Injection with Hilt ✅
|
||||||
- [ ] Add Hilt dependencies
|
- [x] Add Hilt dependencies
|
||||||
- [ ] Set up Hilt modules for WifiAwareManager
|
- [x] Set up Hilt modules for WifiAwareManager
|
||||||
- [ ] Convert singletons to proper DI
|
- [x] Convert singletons to proper DI
|
||||||
- [ ] Inject ViewModels using Hilt
|
- [x] Inject ViewModels using Hilt
|
||||||
|
|
||||||
|
### 1.3 Room Database Setup
|
||||||
|
- [ ] Add Room dependencies
|
||||||
|
- [ ] Create Message and User entities
|
||||||
|
- [ ] Implement DAOs for data access
|
||||||
|
- [ ] Create database migrations
|
||||||
|
- [ ] Store messages in Room database
|
||||||
|
- [ ] Load message history on app restart
|
||||||
|
- [ ] Implement message sync logic
|
||||||
|
|
||||||
|
### 1.4 Coroutines & Flow Optimization
|
||||||
|
- [ ] Convert callbacks to coroutines
|
||||||
|
- [ ] Use Flow for reactive data streams
|
||||||
|
- [ ] Implement proper scope management
|
||||||
|
- [ ] Replace GlobalScope with proper lifecycle scopes
|
||||||
|
- [ ] Add proper error handling with coroutines
|
||||||
|
|
||||||
## Phase 2: Core UX Improvements
|
## Phase 2: Core UX Improvements
|
||||||
|
|
||||||
@@ -29,19 +45,50 @@
|
|||||||
- [ ] Handle user join/leave events
|
- [ ] Handle user join/leave events
|
||||||
- [ ] Show user count in chat header
|
- [ ] Show user count in chat header
|
||||||
|
|
||||||
## Phase 3: Data Persistence
|
### 2.3 Enhanced Messaging Features
|
||||||
|
- [ ] Message status indicators (sent/delivered/read)
|
||||||
|
- [ ] User presence indicators (online/offline/typing)
|
||||||
|
- [ ] Message timestamps with proper formatting
|
||||||
|
- [ ] Offline message queue
|
||||||
|
- [ ] Message retry mechanism
|
||||||
|
- [ ] Long press message actions (copy, delete)
|
||||||
|
|
||||||
### 3.1 Room Database Setup
|
### 2.4 File & Media Sharing
|
||||||
- [ ] Add Room dependencies
|
- [ ] Image sharing support
|
||||||
- [ ] Create Message and User entities
|
- [ ] File transfer capability
|
||||||
- [ ] Implement DAOs for data access
|
- [ ] Image preview in chat
|
||||||
- [ ] Create database migrations
|
- [ ] Progress indicators for transfers
|
||||||
|
- [ ] File size limits and validation
|
||||||
|
|
||||||
### 3.2 Message Persistence
|
## Phase 3: UI/UX Improvements
|
||||||
- [ ] Store messages in Room database
|
|
||||||
- [ ] Load message history on app restart
|
### 3.1 Material 3 Design Update
|
||||||
- [ ] Implement message sync logic
|
- [ ] Migrate to Material 3 components
|
||||||
- [ ] Add message timestamps
|
- [ ] Implement dynamic color theming
|
||||||
|
- [ ] Update typography and spacing
|
||||||
|
- [ ] Add proper elevation and shadows
|
||||||
|
- [ ] Implement Material You design principles
|
||||||
|
|
||||||
|
### 3.2 Dark Theme & Theming
|
||||||
|
- [ ] Implement dark theme
|
||||||
|
- [ ] Add theme toggle in settings
|
||||||
|
- [ ] System theme detection
|
||||||
|
- [ ] Custom color schemes
|
||||||
|
- [ ] Persist theme preference
|
||||||
|
|
||||||
|
### 3.3 Animations & Polish
|
||||||
|
- [ ] Message send/receive animations
|
||||||
|
- [ ] Screen transition animations
|
||||||
|
- [ ] Loading state animations
|
||||||
|
- [ ] Smooth scrolling improvements
|
||||||
|
- [ ] Haptic feedback
|
||||||
|
|
||||||
|
### 3.4 Better Error Handling UI
|
||||||
|
- [ ] User-friendly error messages
|
||||||
|
- [ ] Retry mechanisms with UI feedback
|
||||||
|
- [ ] Connection lost/restored snackbars
|
||||||
|
- [ ] Empty states for no messages/users
|
||||||
|
- [ ] Inline error states
|
||||||
|
|
||||||
## Phase 4: Reliability Improvements
|
## Phase 4: Reliability Improvements
|
||||||
|
|
||||||
@@ -56,26 +103,78 @@
|
|||||||
- [ ] Handle app lifecycle properly
|
- [ ] Handle app lifecycle properly
|
||||||
- [ ] Save and restore connection state
|
- [ ] Save and restore connection state
|
||||||
|
|
||||||
## Phase 5: Advanced Features
|
## Phase 5: Security & Privacy
|
||||||
|
|
||||||
### 5.1 Background Service
|
### 5.1 Message Encryption
|
||||||
|
- [ ] End-to-end encryption implementation
|
||||||
|
- [ ] Key exchange protocol
|
||||||
|
- [ ] Message integrity verification
|
||||||
|
- [ ] Secure key storage
|
||||||
|
- [ ] Forward secrecy
|
||||||
|
|
||||||
|
### 5.2 Privacy Features
|
||||||
|
- [ ] Optional username anonymization
|
||||||
|
- [ ] Message auto-deletion
|
||||||
|
- [ ] Block/unblock users
|
||||||
|
- [ ] Private rooms with passwords
|
||||||
|
- [ ] Data export/import
|
||||||
|
|
||||||
|
## Phase 6: Advanced Features
|
||||||
|
|
||||||
|
### 6.1 Background Service
|
||||||
- [ ] Create foreground service for persistent connection
|
- [ ] Create foreground service for persistent connection
|
||||||
- [ ] Handle Doze mode and battery optimization
|
- [ ] Handle Doze mode and battery optimization
|
||||||
- [ ] Add notification for active chat
|
- [ ] Add notification for active chat
|
||||||
- [ ] Implement proper service lifecycle
|
- [ ] Implement proper service lifecycle
|
||||||
|
- [ ] Wake lock management
|
||||||
|
|
||||||
### 5.2 Additional Features
|
### 6.2 Settings & Preferences
|
||||||
- [ ] Message delivery status
|
- [ ] Create settings screen
|
||||||
- [ ] Typing indicators
|
- [ ] Notification preferences
|
||||||
- [ ] File/image sharing support
|
- [ ] Sound/vibration settings
|
||||||
- [ ] Message encryption improvements
|
- [ ] Auto-reconnect toggle
|
||||||
|
- [ ] Message history limits
|
||||||
|
|
||||||
|
## Phase 7: Testing & Quality
|
||||||
|
|
||||||
|
### 7.1 Unit Testing
|
||||||
|
- [ ] Test ViewModels
|
||||||
|
- [ ] Test Repository logic
|
||||||
|
- [ ] Test data transformations
|
||||||
|
- [ ] Test error scenarios
|
||||||
|
- [ ] Mock dependencies with Hilt testing
|
||||||
|
|
||||||
|
### 7.2 Integration Testing
|
||||||
|
- [ ] Test database operations
|
||||||
|
- [ ] Test network layer
|
||||||
|
- [ ] Test complete user flows
|
||||||
|
- [ ] Test state persistence
|
||||||
|
|
||||||
|
### 7.3 UI Testing
|
||||||
|
- [ ] Espresso tests for main flows
|
||||||
|
- [ ] Test navigation
|
||||||
|
- [ ] Test user interactions
|
||||||
|
- [ ] Screenshot testing
|
||||||
|
- [ ] Accessibility testing
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
- ✅ Phase 1.1 - MVVM Architecture - COMPLETED
|
- ✅ Phase 1.1 - MVVM Architecture - COMPLETED
|
||||||
|
- ✅ Phase 1.2 - Dependency Injection with Hilt - COMPLETED
|
||||||
- ✅ Phase 2.1 - Connection Status Management - COMPLETED
|
- ✅ Phase 2.1 - Connection Status Management - COMPLETED
|
||||||
- 🚀 Next: Phase 1.2 (Dependency Injection) or Phase 3 (Data Persistence)
|
- 🚀 Next Priority Options:
|
||||||
|
- Phase 1.3 - Room Database (Foundation for persistence)
|
||||||
|
- Phase 2.2 - User List Feature (Core UX)
|
||||||
|
- Phase 2.3 - Enhanced Messaging (Better UX)
|
||||||
|
- Phase 3.1 - Material 3 Update (Modern UI)
|
||||||
|
|
||||||
## Completed Work Summary
|
## Completed Work Summary
|
||||||
1. **MVVM Architecture**: ViewModels, Repository pattern, proper separation of concerns
|
1. **MVVM Architecture**: ViewModels, Repository pattern, proper separation of concerns
|
||||||
2. **Connection Status**: Visual indicator with real-time updates, activity-based detection
|
2. **Dependency Injection**: Hilt integration with proper scoping and lifecycle management
|
||||||
3. **Sleep/Wake Handling**: Auto-recovery when messages resume after device sleep
|
3. **Connection Status**: Visual indicator with real-time updates, activity-based detection
|
||||||
|
4. **Sleep/Wake Handling**: Auto-recovery when messages resume after device sleep
|
||||||
|
|
||||||
|
## Development Notes
|
||||||
|
- Architecture foundation (Phase 1) should be completed before moving to advanced features
|
||||||
|
- UI/UX improvements (Phase 3) can be done in parallel with feature development
|
||||||
|
- Testing (Phase 7) should be implemented incrementally as features are added
|
||||||
|
- Security features (Phase 5) are important for production readiness
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.mattintech.lchat.ui
|
package com.mattintech.lchat.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -48,6 +50,18 @@ class LobbyFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupUI() {
|
private fun setupUI() {
|
||||||
|
binding.nameInput.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
s?.toString()?.trim()?.let { name ->
|
||||||
|
viewModel.saveUserName(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
binding.modeRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
binding.modeRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||||
when (checkedId) {
|
when (checkedId) {
|
||||||
R.id.hostRadio -> {
|
R.id.hostRadio -> {
|
||||||
@@ -80,6 +94,12 @@ class LobbyFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeViewModel() {
|
private fun observeViewModel() {
|
||||||
|
viewModel.savedUserName.observe(viewLifecycleOwner) { savedName ->
|
||||||
|
if (!savedName.isNullOrEmpty() && binding.nameInput.text.isNullOrEmpty()) {
|
||||||
|
binding.nameInput.setText(savedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.state.observe(viewLifecycleOwner) { state ->
|
viewModel.state.observe(viewLifecycleOwner) { state ->
|
||||||
when (state) {
|
when (state) {
|
||||||
is LobbyState.Idle -> {
|
is LobbyState.Idle -> {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.mattintech.lchat.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class PreferencesManager @Inject constructor(
|
||||||
|
@ApplicationContext private val context: Context
|
||||||
|
) {
|
||||||
|
private val sharedPreferences: SharedPreferences =
|
||||||
|
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
fun saveUserName(name: String) {
|
||||||
|
if (name.isBlank()) {
|
||||||
|
clearUserName()
|
||||||
|
} else {
|
||||||
|
sharedPreferences.edit().putString(KEY_USER_NAME, name).apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUserName(): String? {
|
||||||
|
return sharedPreferences.getString(KEY_USER_NAME, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearUserName() {
|
||||||
|
sharedPreferences.edit().remove(KEY_USER_NAME).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PREFS_NAME = "lchat_preferences"
|
||||||
|
private const val KEY_USER_NAME = "user_name"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.mattintech.lchat.repository.ChatRepository
|
import com.mattintech.lchat.repository.ChatRepository
|
||||||
|
import com.mattintech.lchat.utils.PreferencesManager
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -27,7 +28,8 @@ sealed class LobbyEvent {
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LobbyViewModel @Inject constructor(
|
class LobbyViewModel @Inject constructor(
|
||||||
private val chatRepository: ChatRepository
|
private val chatRepository: ChatRepository,
|
||||||
|
private val preferencesManager: PreferencesManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _state = MutableLiveData<LobbyState>(LobbyState.Idle)
|
private val _state = MutableLiveData<LobbyState>(LobbyState.Idle)
|
||||||
@@ -36,8 +38,16 @@ class LobbyViewModel @Inject constructor(
|
|||||||
private val _events = MutableLiveData<LobbyEvent?>()
|
private val _events = MutableLiveData<LobbyEvent?>()
|
||||||
val events: LiveData<LobbyEvent?> = _events
|
val events: LiveData<LobbyEvent?> = _events
|
||||||
|
|
||||||
|
private val _savedUserName = MutableLiveData<String?>()
|
||||||
|
val savedUserName: LiveData<String?> = _savedUserName
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setupConnectionCallback()
|
setupConnectionCallback()
|
||||||
|
loadSavedUserName()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadSavedUserName() {
|
||||||
|
_savedUserName.value = preferencesManager.getUserName()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupConnectionCallback() {
|
private fun setupConnectionCallback() {
|
||||||
@@ -65,6 +75,7 @@ class LobbyViewModel @Inject constructor(
|
|||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_state.value = LobbyState.Connecting
|
_state.value = LobbyState.Connecting
|
||||||
|
preferencesManager.saveUserName(userName)
|
||||||
chatRepository.startHostMode(roomName)
|
chatRepository.startHostMode(roomName)
|
||||||
_events.value = LobbyEvent.NavigateToChat(roomName, userName, true)
|
_events.value = LobbyEvent.NavigateToChat(roomName, userName, true)
|
||||||
}
|
}
|
||||||
@@ -78,11 +89,13 @@ class LobbyViewModel @Inject constructor(
|
|||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_state.value = LobbyState.Connecting
|
_state.value = LobbyState.Connecting
|
||||||
|
preferencesManager.saveUserName(userName)
|
||||||
chatRepository.startClientMode()
|
chatRepository.startClientMode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onConnectedToRoom(roomName: String, userName: String) {
|
fun onConnectedToRoom(roomName: String, userName: String) {
|
||||||
|
preferencesManager.saveUserName(userName)
|
||||||
_events.value = LobbyEvent.NavigateToChat(roomName, userName, false)
|
_events.value = LobbyEvent.NavigateToChat(roomName, userName, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +103,12 @@ class LobbyViewModel @Inject constructor(
|
|||||||
_events.value = null
|
_events.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveUserName(name: String) {
|
||||||
|
if (name.isNotBlank()) {
|
||||||
|
preferencesManager.saveUserName(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
// Clean up resources if needed
|
// Clean up resources if needed
|
||||||
|
|||||||
Reference in New Issue
Block a user