State gets reset after opening app from pending intent

I have a countdown timer. I am using Countdown Timer object and in its onTick function I update my state with remaining time. And then I send an intent to service for updating notification to show remaining time.

Now I have a foreground service too. When the user starts the timer and then minimizes app, the foreground service starts and onTick of timer updates notification with remaining time.

Now if I come back to app by clicking on app icon or via recent apps, the state shows me remaining time.

But if I click the notification and go to my app with Pending intent, the state shows initia value which is empty text. It does not shows remaining time.

Can someone help me so that after opening app from pending intent, the timer continues.

Thanks in advance.

I have tried changing launch mode to singleTask and single instance, but same behaviour.

I have tried using collectLatest.

I have tried creating another timer if the timer before minimization was running, but then two timers updates state.

I am expecting the timer to start from where it left.

This is my viewmodel :

@HiltViewModel
    class DashboardViewModel @Inject constructor(
        private val wrap: DashboardWrapper,
        private val timerUtil : TimerStringLongConverterUtils,
        private val timerServiceHelper: TimerServiceHelper
    ) : ViewModel() {
     
        lateinit var timer : CountDownTimer
        private val _state = MutableStateFlow(DashboardState())
        val state = _state.asStateFlow()
        private var dashboardTimerRunning : Boolean = false
     
     
     
     
     
     
        fun habitEvents(e : DashboardHabitEvents){
            when(e){
     
                is DashboardHabitEvents.StartStopTimer -> {
                    when(state.value.timerMode){
     
                        TimerModeConstants.STOP -> {
     
     
                            timer.cancel()
     
                            if(dashboardTimerRunning){
                                dashboardTimerRunning = false
                                timerServiceHelper.stopForegroundService(
                                    TimerServiceConstant.CANCEL.name
                                )
                            }
     
                        }
     
                        TimerModeConstants.START -> {
     
     
     
                            dashboardTimerRunning = true
     
                            var remainingTimeInMills = timerUtil
                                .convertTimeStringToSeconds(
                                    state.value.remainingTimeString
                                ) * 1000
     
                            createTimer(time = remainingTimeInMills)
                            timer.start()
                        }
                    }
     
                }
     
                is DashboardHabitEvents.TimerServiceEvents -> {
                    when(e.v){
                        TimerServiceEventConstants.START -> {
                            if(state.value.
                                isDashboardTimerRunningForDecidingWhetherToStartOrNotStartTimerService){
                                timerServiceHelper.triggerForegroundService(
                                    TimerServiceConstant.START.name
                                )
                            }
                        }
     
                        TimerServiceEventConstants.STOP -> {
                            if(dashboardTimerRunning){
                                dashboardTimerRunning = false
                                timerServiceHelper.stopForegroundService(
                                    TimerServiceConstant.CANCEL.name
                                )
                            }
                        }
                    }
                }
     
                else -> {}
            }
            
        }
     
        private fun createTimer(
            time : Long,
        ){
            timer = object : CountDownTimer(time, 1000){
                override fun onTick(millisUntilFinished: Long) {
     
                    val remainingTimeLong = millisUntilFinished / 1000
                    val remainingTimeString = timerUtil
                        .convertTimeLongToString(
                            remainingTimeLong
                        )
     
     
                    _state.update {
                        it.copy(
                            remainingTimeLong = remainingTimeLong,
                            remainingTimeString = remainingTimeString,
                            isDashboardTimerRunningForDecidingWhetherToStartOrNotStartTimerService =
                            true
                        )
                    }
     
                    timerServiceHelper.updateNotificationWithTimeString(
                        state.value.remainingTimeString
                    )
     
     
     
     
                }
     
                override fun onFinish() {
                    
                }
            }
     
        }
     
     
     
     
        override fun onCleared() {
            super.onCleared()
            if(dashboardTimerRunning){
                timer.cancel()
            }
        }
    }

     ```

This is my Timer Service :

@AndroidEntryPoint
class TimerService : Service(){
    @Inject
    lateinit var notificationM : NotificationManager
 
    @Inject
    lateinit var notificationB : NotificationCompat.Builder
 
 
    private var timerServiceIsRunning : Boolean = false
 
    override fun onBind(intent: Intent?) = null
 
    override fun onUnbind(intent: Intent?): Boolean {
        return super.onUnbind(intent)
    }
 
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        intent?.action.let { intentAction->
            when(intentAction){
 
                TimerServiceConstant.START.name -> {
                    if(!timerServiceIsRunning){
                        timerServiceIsRunning = true
                        createNotificationChannedl()
                        ServiceCompat.startForeground(
                            this,
                            TimerServiceConstant.TIMER_NOTIFICATION_ID.value,
                            notificationB.build(),
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
                                ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE}else 0
 
                        )
                    }
                }
                TimerServiceConstant.CANCEL.name -> {
                    if(timerServiceIsRunning){
                        timerServiceIsRunning = false
                        stopTimerService()
                    }
                }
 
                TimerServiceConstant.UPDATE_NOTIFICATION.name -> {
                    if(timerServiceIsRunning){
                        val timeToUpdate = intent?.getStringExtra("timer_time")
                        updateNotification(timeToUpdate!!)
                    }
                }
            }
 
        }
        return START_NOT_STICKY
    }
 
 
    private fun startTimerService(){
        createNotificationChannedl()
        startForeground(
            TimerServiceConstant.TIMER_NOTIFICATION_ID.value,
            notificationB.build())
    }
 
    private fun stopTimerService(){
        timerServiceIsRunning = false
        stopForeground(STOP_FOREGROUND_REMOVE)
        stopSelf()
        notificationM.cancel(TimerServiceConstant.TIMER_NOTIFICATION_ID.value)
    }
 
    private fun updateNotification(timerTime : String){
        notificationM.notify(
            TimerServiceConstant.TIMER_NOTIFICATION_ID.value,
            notificationB.setContentText(timerTime).build()
        )
    }
 
 
    private fun createNotificationChannedl(){
        val channel = NotificationChannel(
            TimerServiceConstant.TIMER_CHANNEL_ID.name,
            TimerServiceConstant.TIMER_NOTIFICATION_CHANNEL_NAME.name,
            NotificationManager.IMPORTANCE_LOW
 
        )
        notificationM.createNotificationChannel(channel)
    }
 
 
}
 

This is Service Helper :
class TimerServiceHelper @Inject constructor(
    private val appContext: Context
){
    private var isTimerserviceRunning : Boolean = false
 
    fun triggerForegroundService(newAction : String){
        Intent(appContext, TimerService::class.java).apply {
            isTimerserviceRunning = true
            this.action = newAction
            appContext.startForegroundService(this)
        }
    }
 
    fun updateNotificationWithTimeString(time : String){
        Intent(appContext, TimerService::class.java).apply {
            putExtra("timer_time", time)
            this.action = TimerServiceConstant.UPDATE_NOTIFICATION.name
            appContext.startForegroundService(this)
        }
 
    }
 
 
    fun stopForegroundService(newAction: String){
        if(isTimerserviceRunning){
            Intent(appContext, TimerService::class.java).apply {
                this.action = newAction
                appContext.stopService(this)
            }
        }
    }
 
 
 
}

This is Pending Intent Helper Class :

class TimerServicePendingIntentHandler(
    private val appContext: Context
) {
 
    fun clickPendingIntent() : PendingIntent {
        val deepLinkIntent = Intent(
            Intent.ACTION_VIEW,
            DashboardScreenRoutes.MainDashboardScreen.route.toUri(),
            appContext,
            MainActivity::class.java
        )
 
        return TaskStackBuilder.create(appContext).run {
            addNextIntentWithParentStack(deepLinkIntent)
            getPendingIntent(
                TimerServiceConstant.TIMER_SERVICE_REQUEST_CODE.value,
                PendingIntent.FLAG_IMMUTABLE
            )
        }
    }
}

This is Dagger Class :

@Module
@InstallIn(ServiceComponent::class)
object DaggerStopWatchServiceModule {
 
 
    @Provides
    @ServiceScoped
    fun provideNotificationBuilder(
        @ApplicationContext app: Context,
        pendingIntHand : TimerServicePendingIntentHandler
    ) : NotificationCompat.Builder{
        return NotificationCompat
            .Builder(app, TimerServiceConstant.TIMER_CHANNEL_ID.name)
            .setContentTitle("Vijaysarthi Timer")
            .setContentText("00:00:00")
            .setSmallIcon(R.drawable.timer_24px)
            .setOngoing(true)
            .setContentIntent(pendingIntHand.clickPendingIntent())
    }
 
    @ServiceScoped
    @Provides
    fun provideBaseNotificationManager(
        @ApplicationContext app : Context
    ) : NotificationManager {
        return app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }
 
    @ServiceScoped
    @Provides
    fun provideTimerServicePendingIntentHandler(
        @ApplicationContext app : Context
    ) : TimerServicePendingIntentHandler{
        return TimerServicePendingIntentHandler(app)
    }
 
}

This is Main Activity : 

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
 
 
 
 
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        installSplashScreen()
        setContent {
            VijaysarthiTheme {
                SetupRootNavGraph(navHostController =
                rememberNavController()
                )
            }
        }
    }
 
 
    override fun onDestroy() {
        Intent([email protected], TimerService::class.java).apply {
            this.action = TimerServiceConstant.CANCEL.name
            [email protected](this)
        }
        super.onDestroy()
    }
 
 
 
 
 
}

Upvotes: 0

Views: 61

Answers (0)

Related Questions