adding DepedencyInjnection with HILT

This commit is contained in:
2025-07-03 21:13:23 -04:00
parent 996de88bfd
commit e44ce67873
14 changed files with 103 additions and 55 deletions

View File

@@ -2,6 +2,12 @@ plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("androidx.navigation.safeargs.kotlin")
id("kotlin-kapt")
id("com.google.dagger.hilt.android")
}
kapt {
correctErrorTypes = true
}
android {
@@ -11,6 +17,12 @@ android {
lint {
abortOnError = false
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
defaultConfig {
applicationId = "com.mattintech.lchat"
@@ -50,6 +62,7 @@ android {
}
buildFeatures {
viewBinding = true
buildConfig = true
}
}
@@ -66,6 +79,10 @@ dependencies {
implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
// Hilt dependencies
implementation("com.google.dagger:hilt-android:2.50")
kapt("com.google.dagger:hilt-compiler:2.50")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

View File

@@ -11,4 +11,17 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
# Keep Application class
-keep class com.mattintech.lchat.LChatApplication { *; }
# Hilt rules
-keep class dagger.hilt.** { *; }
-keep class javax.inject.** { *; }
-keep class * extends dagger.hilt.android.internal.managers.ViewComponentManager { *; }
# Keep all @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel annotated classes
-keep @dagger.hilt.android.HiltAndroidApp class * { *; }
-keep @dagger.hilt.android.AndroidEntryPoint class * { *; }
-keep @dagger.hilt.android.lifecycle.HiltViewModel class * { *; }

View File

@@ -15,6 +15,7 @@
<uses-feature android:name="android.hardware.wifi.aware" android:required="true" />
<application
android:name=".LChatApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

View File

@@ -0,0 +1,11 @@
package com.mattintech.lchat
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class LChatApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}

View File

@@ -11,7 +11,9 @@ import androidx.core.content.ContextCompat
import com.google.android.material.snackbar.Snackbar
import com.mattintech.lchat.databinding.ActivityMainBinding
import com.mattintech.lchat.utils.LOG_PREFIX
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
companion object {

View File

@@ -0,0 +1,23 @@
package com.mattintech.lchat.di
import android.content.Context
import com.mattintech.lchat.network.WifiAwareManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideWifiAwareManager(
@ApplicationContext context: Context
): WifiAwareManager {
return WifiAwareManager(context)
}
}

View File

@@ -3,24 +3,19 @@ package com.mattintech.lchat.repository
import android.content.Context
import com.mattintech.lchat.data.Message
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 java.util.UUID
import javax.inject.Inject
import javax.inject.Singleton
class ChatRepository private constructor(context: Context) {
companion object {
@Volatile
private var INSTANCE: ChatRepository? = null
fun getInstance(context: Context): ChatRepository {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: ChatRepository(context.applicationContext).also { INSTANCE = it }
}
}
}
@Singleton
class ChatRepository @Inject constructor(
@ApplicationContext private val context: Context
) {
private val wifiAwareManager = WifiAwareManager(context)
@@ -140,7 +135,7 @@ class ChatRepository private constructor(context: Context) {
private fun startConnectionMonitoring() {
connectionCheckJob?.cancel()
connectionCheckJob = GlobalScope.launch {
connectionCheckJob = CoroutineScope(Dispatchers.IO).launch {
while (isActive) {
delay(5000) // Check every 5 seconds
val timeSinceLastActivity = System.currentTimeMillis() - lastActivityTime

View File

@@ -6,7 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.core.content.ContextCompat
@@ -17,9 +17,10 @@ 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
import com.mattintech.lchat.viewmodel.ViewModelFactory
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
@AndroidEntryPoint
class ChatFragment : Fragment() {
companion object {
@@ -30,7 +31,7 @@ class ChatFragment : Fragment() {
private val binding get() = _binding!!
private val args: ChatFragmentArgs by navArgs()
private lateinit var viewModel: ChatViewModel
private val viewModel: ChatViewModel by viewModels()
private lateinit var messageAdapter: MessageAdapter
override fun onCreateView(
@@ -47,8 +48,6 @@ class ChatFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
Log.d(TAG, "onViewCreated - room: ${args.roomName}, user: ${args.userName}, isHost: ${args.isHost}")
val factory = ViewModelFactory(requireContext())
viewModel = ViewModelProvider(this, factory)[ChatViewModel::class.java]
viewModel.initialize(args.roomName, args.userName, args.isHost)
setupUI()

View File

@@ -7,16 +7,17 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.mattintech.lchat.R
import com.mattintech.lchat.databinding.FragmentLobbyBinding
import com.mattintech.lchat.viewmodel.LobbyEvent
import com.mattintech.lchat.viewmodel.LobbyState
import com.mattintech.lchat.viewmodel.LobbyViewModel
import com.mattintech.lchat.viewmodel.ViewModelFactory
import com.mattintech.lchat.utils.LOG_PREFIX
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class LobbyFragment : Fragment() {
companion object {
@@ -26,7 +27,7 @@ class LobbyFragment : Fragment() {
private var _binding: FragmentLobbyBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: LobbyViewModel
private val viewModel: LobbyViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
@@ -42,9 +43,6 @@ class LobbyFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
Log.d(TAG, "onViewCreated")
val factory = ViewModelFactory(requireContext())
viewModel = ViewModelProvider(this, factory)[LobbyViewModel::class.java]
setupUI()
observeViewModel()
}

View File

@@ -6,11 +6,13 @@ import androidx.lifecycle.ViewModel
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.launch
import java.util.UUID
import javax.inject.Inject
sealed class ChatState {
object Connected : ChatState()
@@ -18,7 +20,8 @@ sealed class ChatState {
data class Error(val message: String) : ChatState()
}
class ChatViewModel(
@HiltViewModel
class ChatViewModel @Inject constructor(
private val chatRepository: ChatRepository
) : ViewModel() {

View File

@@ -5,7 +5,9 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mattintech.lchat.repository.ChatRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
sealed class LobbyState {
object Idle : LobbyState()
@@ -23,7 +25,8 @@ sealed class LobbyEvent {
data class ShowError(val message: String) : LobbyEvent()
}
class LobbyViewModel(
@HiltViewModel
class LobbyViewModel @Inject constructor(
private val chatRepository: ChatRepository
) : ViewModel() {

View File

@@ -1,24 +0,0 @@
package com.mattintech.lchat.viewmodel
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.mattintech.lchat.repository.ChatRepository
class ViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
private val chatRepository by lazy { ChatRepository.getInstance(context) }
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return when {
modelClass.isAssignableFrom(LobbyViewModel::class.java) -> {
LobbyViewModel(chatRepository) as T
}
modelClass.isAssignableFrom(ChatViewModel::class.java) -> {
ChatViewModel(chatRepository) as T
}
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
}
}