-3

I am using the chronometer - but persisting chronometer within the notification - service - application was difficult for me

Scenarios -

  1. Start timer on tap of the button
  2. While the app goes in the background, start the timer in the custom notification.
  3. On Timer notification click, launch the application with the timer count in the notification tray
  4. the Killing app should continue the timer in the notification, opening the application from the app icon or notification should continue the timer from the same count what is there in the notification.

Approach -

  1. Saving time onPause
  2. While resuming the application - setting the timer value to chronometer

Code for onPause -

   override fun onPause() {
    super.onPause()
    MainActivity.appPauseTime = SystemClock.elapsedRealtime()
    
    val intent = Intent(activity, MyTimerService::class.java)
    intent.action = MyTimerService.START_FOREGROUND_SERVICE
    intent.putExtra("timer", chronometer.base)
    activity?.startService(intent)
}

Code for onResume -

 override fun onResume() {
    super.onResume()
    if (MainActivity.appPauseTime > 0L) {
        MainActivity.appResumeTime = SystemClock.elapsedRealtime()
        startTimer(SystemClock.elapsedRealtime() - (MainActivity.appResumeTime - MainActivity.appPauseTime))
        val intent = Intent(activity, MyTimerService::class.java)
        intent.action = MyTimerService.STOP_FOREGROUND_SERVICE
        activity?.stopService(intent)
    }
}

Not looking for spoon feed answer so posted the answer below.

DHRUV SINGH
  • 65
  • 1
  • 5

1 Answers1

0

I found a way for running the timer - i.e. chronometer

setUp chronometer in fragment or activity layout something like this

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">

<Button
    android:id="@+id/button_first"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/next"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/chronometer" />


<Chronometer
    android:id="@+id/chronometer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    android:gravity="center"
    android:textAppearance="@style/TextAppearance.AppCompat.Large"
    android:textColor="@android:color/holo_red_dark"
    android:textSize="48sp"
    android:textStyle="bold"
    android:visibility="visible"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Start timer on button click or whichever scenario you want to -

private fun startTimer(startTime: Long) {
    chronometer.base = startTime
    chronometer.setOnChronometerTickListener {
        Log.i("Timer--->", "Countdown---> " + chronometer.base)
        // formattedTime = getFormattedTime(chronometer.base)
    }
    chronometer.start()
}

The main thing comes to post a notification with chronometer - here I will let you know how to do it

override fun onPause() {
    super.onPause()
    val intent = Intent(activity, MyTimerService::class.java)
    intent.action = MyTimerService.START_FOREGROUND_SERVICE
    intent.putExtra("timer", chronometer.base)
    activity?.startService(intent)
}

This is the Service class - make sure you register it in manifest and provide the foreground user's permission

class MyTimerService : Service() {

private lateinit var notificationView: RemoteViews
private lateinit var notiBuilder: Notification

companion object {
    const val NOTIFICATION_CHANNEL_ID = "100"
    const val NOTIFICATION_ID = 10
    const val START_FOREGROUND_SERVICE = "start_foreground_service"
    const val STOP_FOREGROUND_SERVICE = "stop_foreground_service"
}


override fun onBind(p0: Intent?): IBinder? {
    return null
}

override fun onCreate() {
    super.onCreate()
    notificationView = RemoteViews(packageName, R.layout.notification_layout)
    createNotification()
}

private fun getPendingIntent(): PendingIntent? {
    // Create an explicit intent for an Activity in your app
    val intent = Intent(this, MainActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
    }

    // intent.putExtra("timer", notificationView.)
    return PendingIntent.getActivity(this, 0, intent, 0)


}

private fun createNotification() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        createNotificationChannelForOreo()
    } else {
        createNotificationBelowOreo()
    }
}

private fun createNotificationBelowOreo() {
    // Create notification builder.
    val builder = NotificationCompat.Builder(this)
        .setWhen(System.currentTimeMillis())
        .setSmallIcon(R.drawable.ic_baseline_timer_24)
        .setStyle(NotificationCompat.DecoratedCustomViewStyle())
        .setCustomContentView(notificationView)
        .setContentIntent(getPendingIntent())
        .setContentTitle(baseContext.getString(R.string.app_name))
        .setContentText("Active Session")
        .setAutoCancel(true)
        .setPriority(Notification.PRIORITY_MAX)

    // Build the notification.
    notiBuilder = builder.build()
}


@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannelForOreo() {
    val chan =
        NotificationChannel(
            NOTIFICATION_CHANNEL_ID,
            baseContext.getString(R.string.channel_name),
            NotificationManager.IMPORTANCE_DEFAULT
        )
    // chan.lightColor = Color.BLUE
    chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
    val manager = (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
    manager.createNotificationChannel(chan)
    val notificationBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
    notiBuilder = notificationBuilder.setOngoing(true)
        .setSmallIcon(R.drawable.ic_baseline_timer_24)
        .setContentTitle(baseContext.getString(R.string.app_name))
        .setContentText("Active Session")
        .setAutoCancel(true)
        .setPriority(NotificationManager.IMPORTANCE_MIN)
        .setStyle(NotificationCompat.DecoratedCustomViewStyle())
        .setCustomContentView(notificationView)
        .setCategory(Notification.CATEGORY_SERVICE)
        .setContentIntent(getPendingIntent())
        .build()
    val notificationManager = NotificationManagerCompat.from(this)
    notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
}


private fun showNotification() {
    // Start foreground service.
    startForeground(NOTIFICATION_ID, notiBuilder)
}

override fun onDestroy() {
    super.onDestroy()
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    super.onStartCommand(intent, flags, startId)
    if (intent?.extras != null) {
        when (intent.action) {

            START_FOREGROUND_SERVICE -> {
                val timestamp = intent.getLongExtra("timer", 0)
                // set chronometer
                notificationView.setChronometer(R.id.chronometer, timestamp, null, true)
                // show notification
                showNotification()
            }

            STOP_FOREGROUND_SERVICE -> {
                stopForeground(true)
                stopSelf()
            }
        }
    }
    return START_STICKY
}

}

To handle the timer persistence save the chronometer time in onPause -

 override fun onPause() {
    super.onPause()
    **Make sure you are saving this time at central place**
    MainActivity.appPauseTime = chronometer.base
}

In onResume -

override fun onResume() {
    super.onResume()
    if (MainActivity.appPauseTime > 0L) {
        startTimer(MainActivity.appPauseTime)
        val intent = Intent(activity, MyTimerService::class.java)
        intent.action = MyTimerService.STOP_FOREGROUND_SERVICE
        activity?.stopService(intent)
    }
}

Let me know and upVote incase you found it helpful.

DHRUV SINGH
  • 65
  • 1
  • 5