From b1f1aa0cadebfa42a1754e73eb9e83813ea08391 Mon Sep 17 00:00:00 2001 From: Matt Hills Date: Thu, 3 Jul 2025 21:31:35 -0400 Subject: [PATCH] adding shared prefs to save the users name --- TODO.md | 151 +++++++++++++++--- .../com/mattintech/lchat/ui/LobbyFragment.kt | 20 +++ .../lchat/utils/PreferencesManager.kt | 36 +++++ .../lchat/viewmodel/LobbyViewModel.kt | 21 ++- 4 files changed, 201 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/com/mattintech/lchat/utils/PreferencesManager.kt diff --git a/TODO.md b/TODO.md index c472a20..36abcc2 100644 --- a/TODO.md +++ b/TODO.md @@ -9,11 +9,27 @@ - [x] Implement proper state management with LiveData/StateFlow - [x] Add ViewModelFactory if needed -### 1.2 Dependency Injection with Hilt -- [ ] Add Hilt dependencies -- [ ] Set up Hilt modules for WifiAwareManager -- [ ] Convert singletons to proper DI -- [ ] Inject ViewModels using Hilt +### 1.2 Dependency Injection with Hilt ✅ +- [x] Add Hilt dependencies +- [x] Set up Hilt modules for WifiAwareManager +- [x] Convert singletons to proper DI +- [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 @@ -29,19 +45,50 @@ - [ ] Handle user join/leave events - [ ] 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 -- [ ] Add Room dependencies -- [ ] Create Message and User entities -- [ ] Implement DAOs for data access -- [ ] Create database migrations +### 2.4 File & Media Sharing +- [ ] Image sharing support +- [ ] File transfer capability +- [ ] Image preview in chat +- [ ] Progress indicators for transfers +- [ ] File size limits and validation -### 3.2 Message Persistence -- [ ] Store messages in Room database -- [ ] Load message history on app restart -- [ ] Implement message sync logic -- [ ] Add message timestamps +## Phase 3: UI/UX Improvements + +### 3.1 Material 3 Design Update +- [ ] Migrate to Material 3 components +- [ ] 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 @@ -56,26 +103,78 @@ - [ ] Handle app lifecycle properly - [ ] 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 - [ ] Handle Doze mode and battery optimization - [ ] Add notification for active chat - [ ] Implement proper service lifecycle +- [ ] Wake lock management -### 5.2 Additional Features -- [ ] Message delivery status -- [ ] Typing indicators -- [ ] File/image sharing support -- [ ] Message encryption improvements +### 6.2 Settings & Preferences +- [ ] Create settings screen +- [ ] Notification preferences +- [ ] Sound/vibration settings +- [ ] 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 - ✅ Phase 1.1 - MVVM Architecture - COMPLETED +- ✅ Phase 1.2 - Dependency Injection with Hilt - 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 1. **MVVM Architecture**: ViewModels, Repository pattern, proper separation of concerns -2. **Connection Status**: Visual indicator with real-time updates, activity-based detection -3. **Sleep/Wake Handling**: Auto-recovery when messages resume after device sleep \ No newline at end of file +2. **Dependency Injection**: Hilt integration with proper scoping and lifecycle management +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 \ No newline at end of file diff --git a/app/src/main/java/com/mattintech/lchat/ui/LobbyFragment.kt b/app/src/main/java/com/mattintech/lchat/ui/LobbyFragment.kt index 2dba077..e9635eb 100644 --- a/app/src/main/java/com/mattintech/lchat/ui/LobbyFragment.kt +++ b/app/src/main/java/com/mattintech/lchat/ui/LobbyFragment.kt @@ -1,6 +1,8 @@ package com.mattintech.lchat.ui import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher import android.util.Log import android.view.LayoutInflater import android.view.View @@ -48,6 +50,18 @@ class LobbyFragment : Fragment() { } 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 -> when (checkedId) { R.id.hostRadio -> { @@ -80,6 +94,12 @@ class LobbyFragment : Fragment() { } private fun observeViewModel() { + viewModel.savedUserName.observe(viewLifecycleOwner) { savedName -> + if (!savedName.isNullOrEmpty() && binding.nameInput.text.isNullOrEmpty()) { + binding.nameInput.setText(savedName) + } + } + viewModel.state.observe(viewLifecycleOwner) { state -> when (state) { is LobbyState.Idle -> { diff --git a/app/src/main/java/com/mattintech/lchat/utils/PreferencesManager.kt b/app/src/main/java/com/mattintech/lchat/utils/PreferencesManager.kt new file mode 100644 index 0000000..0b8ca75 --- /dev/null +++ b/app/src/main/java/com/mattintech/lchat/utils/PreferencesManager.kt @@ -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" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mattintech/lchat/viewmodel/LobbyViewModel.kt b/app/src/main/java/com/mattintech/lchat/viewmodel/LobbyViewModel.kt index 8e8c90f..6cf60b1 100644 --- a/app/src/main/java/com/mattintech/lchat/viewmodel/LobbyViewModel.kt +++ b/app/src/main/java/com/mattintech/lchat/viewmodel/LobbyViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.mattintech.lchat.repository.ChatRepository +import com.mattintech.lchat.utils.PreferencesManager import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -27,7 +28,8 @@ sealed class LobbyEvent { @HiltViewModel class LobbyViewModel @Inject constructor( - private val chatRepository: ChatRepository + private val chatRepository: ChatRepository, + private val preferencesManager: PreferencesManager ) : ViewModel() { private val _state = MutableLiveData(LobbyState.Idle) @@ -36,8 +38,16 @@ class LobbyViewModel @Inject constructor( private val _events = MutableLiveData() val events: LiveData = _events + private val _savedUserName = MutableLiveData() + val savedUserName: LiveData = _savedUserName + init { setupConnectionCallback() + loadSavedUserName() + } + + private fun loadSavedUserName() { + _savedUserName.value = preferencesManager.getUserName() } private fun setupConnectionCallback() { @@ -65,6 +75,7 @@ class LobbyViewModel @Inject constructor( viewModelScope.launch { _state.value = LobbyState.Connecting + preferencesManager.saveUserName(userName) chatRepository.startHostMode(roomName) _events.value = LobbyEvent.NavigateToChat(roomName, userName, true) } @@ -78,11 +89,13 @@ class LobbyViewModel @Inject constructor( viewModelScope.launch { _state.value = LobbyState.Connecting + preferencesManager.saveUserName(userName) chatRepository.startClientMode() } } fun onConnectedToRoom(roomName: String, userName: String) { + preferencesManager.saveUserName(userName) _events.value = LobbyEvent.NavigateToChat(roomName, userName, false) } @@ -90,6 +103,12 @@ class LobbyViewModel @Inject constructor( _events.value = null } + fun saveUserName(name: String) { + if (name.isNotBlank()) { + preferencesManager.saveUserName(name) + } + } + override fun onCleared() { super.onCleared() // Clean up resources if needed