Reputation: 23
I am looking for a way to return real time progress to my UI when my service is running, currently I'm using a shared hilt viewModel both for progress % and for the result but injecting into Service gives:
Injection of an @HiltViewModel class is prohibited since it does not create a ViewModel instance correctly. Access the ViewModel via the Android APIs (e.g. ViewModelProvider) instead.
my composables:
Button(onClick = {
val intent = Intent(context, TestService::class.java).also {
it.action = TestService.Actions.START.toString()
it.putExtra("imageUri", imageUri) // Pass the image URI to the service
}
ContextCompat.startForegroundService(context, intent)
}) {
Text(text = "Start Service")
}
ProgressBar()
@Composable
fun ProgressBar() {
val serviceViewModel: ServiceViewModel = viewModel()
val progress by serviceViewModel.progress.collectAsState()
val result by serviceViewModel.result.collectAsState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
LinearProgressIndicator(
progress = { progress },
) // Use state
Text("Progress: ${progress}%")
}
}
my service :
@AndroidEntryPoint
class TestService : Service(){
@Inject
lateinit var serviceViewModel: ServiceViewModel
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when(intent?.action) {
Actions.STOP.toString() -> stopSelf()
Actions.START.toString() -> start(intent)
}
return START_NOT_STICKY
}
//.... other stuff
val progressCallback: (Float) -> Unit = { progress ->
serviceViewModel.updateProgress(progress)
}
//....calling progressCallback
and my viewmodel:
@HiltViewModel
class ServiceViewModel @Inject constructor() : ViewModel(){
private val _progress = MutableStateFlow(0f)
val progress: StateFlow<Float> = _progress.asStateFlow()
private val _result = MutableStateFlow<Extracted?>(null)
val result: StateFlow<Extracted?> = _result.asStateFlow()
fun updateProgress(newProgress: Float) {
viewModelScope.launch {
_progress.value = newProgress
}
}
fun updateResult(newResult: Extracted) {
viewModelScope.launch {
_result.value = newResult
}
}
}
I tried ViewModelProvider(this)[ServiceViewModel::class.java]
, solution to the error in stack overflow but seems deprecated and the only way I got it to work created multiple instances.
I also tried broadcast receiver but the composable does not see the broadcast.
Upvotes: 2
Views: 81
Reputation: 73798
Move the flows into a singleton class separate from the ViewModel then inject the new singleton class into the ViewModel and Service. The service then updates the progress and the ViewModel just provides the flow to the composables
Upvotes: 1