Getting current state from android service - android

I have a service, where I manage music playing. Also I have activity sending intents with user's music. When I open activity, I want to get current status of playing.
I have specific player, what have only two events: playing started and playing ends. So if I use broadcast, I will get only next event.
I save events in variable lastAction when getting it. I can create new command ACTION_SEND_CURRENT_STATE. but it looks not good.
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
null -> {
player?.cancel()
}
ACTION_PLAY -> {
player?.cancel()
player = createPlayer(intent)
player?.start()
}
ACTION_STOP -> {
player?.cancel()
}
}
return START_STICKY
}
override fun onPlayingBegin(p0: player?) {
lastAction = BRODCAST_PLAYING_BEGIN
sendBroadcast(Intent(BRODCAST_PLAYING_BEGIN)
.putExtra(EXTRA_SONG, currentSong)
)
}
How to get current state from service correctly? as state I mean last action.

Use this method
public static boolean isServiceRunning(Context context, Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Hope help you.

As pskink mentioned you need to bind your service to your activity to be able to get information from your service. If your service is working in a remote process then you need to use AIDL to communicate with your service, but I believe that if you do so you're able to find how to do that by yourself.
In your particular case the service communication might look like this(please note that the code may be not completely correct, I wrote it right from my head):
class LocalBinder(val service: MusicService) : Binder
class MusicService : Service() {
var lastAction: String? = null
private set
private val binder: IBinder = LocalBinder(this)
override fun onBind(intent: Intent) = binder
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
lastAction = intent?.action
when (intent?.action) {
null -> {
player?.cancel()
}
ACTION_PLAY -> {
player?.cancel()
player = createPlayer(intent)
player?.start()
}
ACTION_STOP -> {
player?.cancel()
}
}
return START_STICKY
}
}
class MusicPlayerActivity : Activity() {
private var musicService: MusicService? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = (LocalBinder) service
musicService = binder.service
}
override fun onServiceDisconnected(className: ComponentName) {
musicService = null
}
}
override fun protected onStart() {
super.onStart()
val intent = Intent(this, MusicService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun protected onStop() {
super.onStop()
if (musicService != null) {
unbind(connection)
musicService = null
}
}
fun onClick() {
if (musicService != null) {
musicService.lastAction // do something
}
}
}

No need to worry about the service just use foreground service as used by all music playing applications.

Related

How to send data from Android Service to its AIDL stub

I am developing a application to initiate the Bluetooth device application.
I have a service called MyService.
I have stub MyServiceImpl to handle the AIDL interface which will be called from outside.
I have broadcastreceiver to get the "ACTION_BOND_STATE_CHANGED".
My requirement is whenever device is connected(i.e device?.bondState == BluetoothDevice.BOND_BONDED, I need to send deviceinfo from Broadcast receiver to MyServiceImpl stub.
The Reason - AIDL interface is already handled there, once the aidl is called, new class will be created from there. For that new class MyServiceImpl is needed. In addition, response to that interface call is sent through callbacks is also handled there.
The code is below. Can someone help me to send the device information from Broadcastreceiver to the Service stub Implementation?
class MyService : Service() {
override fun onBind(intent: Intent): IBinder {
return binder
}
override fun onUnbind(intent: Intent?): Boolean {
return super.onUnbind(intent)
}
override fun onCreate() {
binder = MyServiceImpl()
filterNewDevices()
}
private fun filterNewDevices() {
val filter = IntentFilter()
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
registerReceiver(mBroadcastReceiverBond, filter)
}
private val mBroadcastReceiverBond: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
if (device?.bondState == BluetoothDevice.BOND_BONDED) {
//HERE I NEED TO SEND THE device TO
}
if (device?.bondState == BluetoothDevice.BOND_BONDING) {
}
if (device?.bondState == BluetoothDevice.BOND_NONE) {
}
}
}
}
class MyServiceImpl : IBluetoothConnection.Stub(), IBinder {
private lateinit var btConnection: BTConnection
override fun registerUuid(name : String?, uuid : String?, connectionId : Int, btDevice : BluetoothDevice?) {
btConnection = BTConnection(this)
btConnection.initialize(name!!, UUID.fromString(uuid), connectionId, btDevice)
}
}
}
Create a method in MyServiceImpl that takes the device as a parameter. In your BroadcastReceiver call the method on binder to pass the device to the service implementation.

Using startForeground Service not keeping after Activity destroyed

Currently, I need a bound (Music)Service, because I need to interact with it. But I also want it to not be stopped, even when all components have unbound themselves.
My service code:
class ServicePlayer : LifecycleService() {
var mediaPlayer: MediaPlayer? = null
var notificationManager: NotificationManager? = null
var notificationBuilder: NotificationCompat.Builder? = null
private val mBinder: IBinder = PlayerBinder()
private val NOTIFICATION_ID = 1111
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
return START_REDELIVER_INTENT
}
inner class PlayerBinder : Binder() {
val service: ServicePlayer
get() = this#ServicePlayer
}
override fun onBind(intent: Intent): IBinder? {
super.onBind(intent)
return mBinder
}
override fun onCreate() {
super.onCreate()
mediaPlayer = MediaPlayer()
mediaPlayer!!.setOnCompletionListener(this)
mediaPlayer!!.setOnBufferingUpdateListener(this)
mediaPlayer!!.setOnErrorListener(this)
val filter = IntentFilter()
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED)
filter.addAction(Intent.ACTION_SCREEN_ON)
registerReceiver(receiver, filter)
}
override fun onDestroy() {
super.onDestroy()
mediaPlayer!!.reset()
mediaPlayer!!.release()
Log.i("DESTROY SERVICE", "destroy")
unregisterReceiver(receiver)
}
fun play(trackIndex: Int, tracks: ArrayList<Track>?) {
...
val intent = Intent(BUFFERING)
this#ServicePlayer.sendBroadcast(intent)
}
fun pause() {
if (mediaPlayer!!.isPlaying) {
mediaPlayer!!.pause()
PlayerLiveData.isPlaying.value = false
val intent = Intent(UPDATE_UI)
this#ServicePlayer.sendBroadcast(intent)
//Show notification
CoroutineScope(Dispatchers.Default).launch {
showNotification()
}
}
}
private fun hideNotification() {
notificationManager!!.cancel(NOTIFICATION_ID)
stopForeground(true)
}
private fun showNotification() {
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val CHANNEL_ID = "controls_channel_id"
val CHANNEL_NAME = "Play tracks"
val channel = NotificationChannel(CHANNEL_ID,CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW)
...
val mMediaSession = MediaSessionCompat(applicationContext, getString(R.string.app_name))
mMediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)
notificationManager!!.createNotificationChannel(channel)
notificationBuilder = NotificationCompat.Builder(applicationContext)
.setChannelId(CHANNEL_ID)
.setContentText(artistText)
.setContentTitle(track.title)
...
} else {
notificationBuilder = NotificationCompat.Builder(applicationContext)
...
notificationBuilder!!
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(contentIntent)
.setCustomContentView(remoteSmallViews)
.setCustomBigContentView(remoteViews)
}
CoroutineScope(Dispatchers.Default).launch {
val notification = notificationBuilder!!.build()
startForeground(NOTIFICATION_ID, notification)
val notificationTarget = NotificationTarget(
applicationContext
, R.id.imgThumb, remoteViews
, notification, NOTIFICATION_ID
)
...
lifecycleScope.launch {
val request = ImageRequest.Builder(applicationContext)
.data(thumb)
.error(R.drawable.placeholder_song)
.placeholder(R.drawable.placeholder_song)
.build()
val drawable = imageLoader.execute(request).drawable
val bitmap = (drawable as BitmapDrawable).bitmap
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationBuilder!!.setLargeIcon(bitmap)
val notification = notificationBuilder!!.build()
notificationManager!!.notify(NOTIFICATION_ID,notification)
//Start Foreground service
startForeground(NOTIFICATION_ID, notification)
}
}
}
}
}
Manifest file declaration:
<service android:name=".services.ServicePlayer" android:enabled="true" android:exported="true"/>
Using service in activity
class MainActivity : AppCompatActivity() {
lateinit var binding: MainActivityBinding
private lateinit var audioPlayerService: ServicePlayer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = Intent(this, ServicePlayer::class.java)
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
binding.lifecycleOwner = this
binding.viewmodel = mainViewModel
}
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
// audioPlayerService = null;
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
audioPlayerService = (service as ServicePlayer.PlayerBinder).service
if (audioPlayerService.trackIndex !== -1) {
//updatePlaybackUI()
}
}
}
}
How can I keep my service running in background even after activity destroyed. I refer few threads of StackOverflow but they are not helpful.
Use Service instead LifecycleService as parent class.
Add partial wake lock start and stop calls to onCreate and onDestroy methods respectively.
private val powerManager
get() = (this.getSystemService(Context.POWER_SERVICE) as PowerManager)
private var wakeLock: PowerManager.WakeLock? = null
private fun startWakeLock() {
if (wakeLock == null) {
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"${packageName}:wakeLock"
)
wakeLock?.acquire()
}
}
private fun stopWakeLock() {
if (wakeLock?.isHeld == true) {
wakeLock?.release()
wakeLock = null
}
}
Add the following tag to service at mainfest
android:foregroundServiceType="mediaPlayback"
You should start service as foreground from the activity
A bound service stops once every client unbinds from it, and that happens automatically when the client (your Activity) is destroyed
If your client is still bound to a service when your app destroys the client, destruction causes the client to unbind. It is better practice to unbind the client as soon as it is done interacting with the service. Doing so allows the idle service to shut down.
If you want the service to just keep running, a Started Service will do that. You can still bind to it, but it won't stop until you explicitly tell it to stop and there are no clients bound.
Honestly though, if you're making some kind of media player, you'll probably want to use the MediaBrowserServiceCompat framework. It allows you to create a service that plays nice with MediaBrowser (which does the binding, among other things) and lets you use a MediaSession to get a media notification with controls and all that.
A few links about that stuff:
MediaBrowserServiceCompat and the modern media playback app by Ian Lake from the Android team
Background Audio in Android With MediaSessionCompat - Java but gets into a lot of the nonsense you'll have to wrangle
Developer docs about building media apps - a few sections here and it's all kinda spread out, I feel like the other links give a better overview
If you don't care about any of that then startService/startForegroundService (or ContextCompat#startForegroundService) will get you a service that just runs, but those links might give you some pointers about stuff

Will unbindService clean Notification icon automatically in Android Studio?

The following code is from the project.
The Notification icon will be displayed when I launch bindService(), it's Ok.
But the Notification icon will not be cleaned when I launch unbindService(), why? I can't find any code to clean Notification icon in following code.
MainActivity.kt
class MainActivity : AppCompatActivity(), View.OnClickListener {
val mTAG = MainActivity::class.java.simpleName
// Variable for storing instance of our service class
var mService: MyBoundService? = null
// Boolean to check if our activity is bound to service or not
var mIsBound: Boolean? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startServiceButton?.setOnClickListener(this)
stopServiceButton?.setOnClickListener(this)
startActivityButton?.setOnClickListener(this)
}
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, iBinder: IBinder) {
Log.d(mTAG, "ServiceConnection: connected to service.")
// We've bound to MyService, cast the IBinder and get MyBinder instance
val binder = iBinder as MyBinder
mService = binder.service
mIsBound = true
getRandomNumberFromService() // return a random number from the service
}
override fun onServiceDisconnected(arg0: ComponentName) {
Log.d(mTAG, "ServiceConnection: disconnected from service.")
mIsBound = false
}
}
/**
* Method for listening to random numbers generated by our service class
*/
private fun getRandomNumberFromService() {
mService?.randomNumberLiveData?.observe(this
, Observer {
resultTextView?.text = "Random number from service: $it"
})
}
override fun onDestroy() {
super.onDestroy()
// Unbinding to the service class
unbindService()
}
private fun bindService() {
Intent(this, MyBoundService::class.java).also { intent ->
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
private fun unbindService() {
Intent(this, MyBoundService::class.java).also { intent ->
unbindService(serviceConnection)
}
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.startServiceButton -> {
bindService()
}
R.id.stopServiceButton -> {
if (mIsBound == true) {
unbindService()
mIsBound = false
}
}
R.id.startActivityButton -> {
val intent = Intent(this, ResultActivity::class.java)
startActivity(intent)
}
}
}
}
MyBoundService.kt
class MyBoundService : Service() {
...
override fun onBind(intent: Intent): IBinder? {
return mBinder
}
override fun onCreate() {
super.onCreate()
Log.d("MyBoundService", "onCreate called")
startNotification()
Handler().postDelayed({
val randomNumber = mGenerator.nextInt(100)
randomNumberLiveData.postValue(randomNumber)
}, 1000)
}
private fun startNotification() {
val channel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel(
CHANNEL_ID,
"My Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
} else {
TODO("VERSION.SDK_INT < O")
}
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("A service is running in the background")
.setContentText("Generating random number").build()
startForeground(1, notification)
}
}
That is true, the code for MyBoundService calls startForeground() which allows MyBoundService to continue running even when MainActivity is destroyed or not in the foreground until the Android OS decides to stop MyBoundService for a reason like freeing up memory.
To stop MyBoundService and remove the notification, it needs to call stopForeground(true) and then stopSelf(). You could make this happen with the sample code you have by adding the below:
MainActivity.kt
private fun unbindService() {
Intent(this, MyBoundService::class.java).also { intent ->
unbindService(serviceConnection)
mService.stopForeground(true)
mService.stopSelf()
}
}
You are starting a foreground service
Please check this link What is the proper way to stop a service running as foreground
Also, you may want to check Stopping a service

Problems with JobScheduler - JobsService firing multiple times

I'm trying to do some background work in my android application. As the web suggested I'm using JobScheduler to do so.
The jobs are sometimes firing 5-15 times instead of once. Sometimes they are never firing.
My testdevices run on 5.1.1 and 7.0. The one with Nougat fires way less then the one with lollipop.
This is how I enable my jobs (the 5 seconds interval is only for test purpose):
fun enableTasks() {
val jobScheduler = App.getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
if (PreferenceDao.getInstance().shouldUpdateJob()) jobScheduler.cancelAll()
scheduleJob(jobScheduler, MoniInfoJob.getJob())
scheduleJob(jobScheduler, QueueJob.getJob())
scheduleJob(jobScheduler, MontageOrderUpdateJob.getJob())
PreferenceDao.getInstance().setJobUpdated()
}
private fun scheduleJob(jobScheduler: JobScheduler, jobInfo: JobInfo) {
val jobExists = jobScheduler.allPendingJobs.any { it.id == jobInfo.id }
if (!jobExists) jobScheduler.schedule(jobInfo)
}
All three jobs look kind of the same so I only post one:
The JobService
class QueueJob : JobService() {
override fun onStartJob(jobParameters: JobParameters?): Boolean {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, p1: Intent?) {
unregisterBroadcastReceiver(this)
jobFinished(jobParameters, false)
}
}
registerBroadcastReceiver(receiver)
MainController.startQueueService()
return true;
}
override fun onStopJob(jobParameters: JobParameters): Boolean {
Log.d(MontageOrderUpdateJob.TAG, "onStopJob")
return false;
}
private fun registerBroadcastReceiver(receiver: BroadcastReceiver) {
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, IntentFilter(JOB_FINISHED))
}
private fun unregisterBroadcastReceiver(receiver: BroadcastReceiver) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
}
companion object {
val TAG = QueueJob::class.java.name
val jobId: Int = 2
val JOB_FINISHED = TAG + "_finished"
fun getJob(): JobInfo {
val builder = JobInfo.Builder(jobId, ComponentName(App.getContext(), TAG))
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
builder.setPeriodic(5000L)
builder.setPersisted(true)
return builder.build()
}
}
}
And the JobIntentService:
class QueueService : JobIntentService() {
private val TAG = QueueService::class.java.name
override fun onHandleWork(intent: Intent) {
try {
Log.d(TAG, "Jobservice started")
TimerecordQueue().workThroughQueue()
DangerAllowanceQueue().workThroughQueue()
ProjektEndQueue().workThroughQueue()
PhotoUploadQueue().workThroughQueue()
} finally {
sendFinishedBroadcast()
}
}
private fun sendFinishedBroadcast() {
val jobFinishedIntent = Intent(QueueJob.JOB_FINISHED)
LocalBroadcastManager.getInstance(this).sendBroadcast(jobFinishedIntent)
}
}
I had a similar problem once. My problem then was that I didn't check for preexisting schedules.
Could it be you need to do the same?

How to keep service alive even after activity destroyed?

Here i am running a service for music play back.
This code snippet is in my onStart() method of my Activity
if(musicServiceStartIntent == null) {
musicServiceStartIntent = new Intent(this, MusicService.class);
startService(musicServiceStartIntent);
bindService(musicServiceStartIntent, musicConnection, Context.BIND_AUTO_CREATE);
}
First i'm starting my service then binding it. And i am calling unbindservice() in onDestroy() method. My Activity got destroyed and service stopped.
unbindService(musicConnection);
Manifest file declaration
<service android:name=".Services.MusicService"/>
How can i keep my service running in background even after activity destroyed. I refer few threads of StackOverflow but they are not helpful.
You just need to start the service, don't bind it to activity lifecycle
Intent intent = new Intent(context, SomeService.class);
startService(intent);
And your service can use START_STICKY / START_REDELIVER_INTENT to make sure that your service will be re-created when the android system kill your service
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
//other code
return START_STICKY;
}
If needed you can use Service.startForeground(notificationId, notification) to make sure that your service will not be killed by the system
Use your service in startForeground, using Notification you can keep your service alive..
Refer to https://developer.android.com/guide/components/services.html#Foreground.
A music player that plays music from a service should be set to run in the foreground, because the user is explicitly aware of its operation. The notification in the status bar might indicate the current song and allow the user to launch an activity to interact with the music player.
To request that your service run in the foreground, call startForeground().
return service.START_STICKY or service.START_REDELIVER_INTENT in onStartCommand
There is three important tricks:
Call startForegroundService which creates a long running service not limited to the binded context and make a promise to call startForeground later.
Return START_STICKY in onStartComand
Call startForeground with a notification as promised in (1).
For example, if you want to run a TimerService, in your TimerActivity you will do:
private var timerService: TimerService? = null
private val timerServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as TimerService.Binder
timerService = binder.getService()
}
override fun onServiceDisconnected(arg0: ComponentName) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
...
startButton.setOnClickListener {
timerService?.startTimer(60L, 0L)
}
}
override fun onStart() {
super.onStart()
Intent(this, TimerService::class.java).also {
ContextCompat.startForegroundService(this, it) // that's the first trick
bindService(it, timerServiceConnection, Context.BIND_AUTO_CREATE)
}
}
Your TimerService will be something like that:
class TimerService : Service() {
private val binder = Binder()
private var serviceLooper: Looper? = null
private var serviceHandler: ServiceHandler? = null
private var timer: CountDownTimer? = null
private val notificationUtil by lazy {
NotificationUtil(this)
}
override fun onCreate() {
HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
serviceLooper = looper
serviceHandler = ServiceHandler(looper)
}
}
override fun onBind(intent: Intent?): IBinder? = binder
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val timerRemaining = intent?.getLongExtra(EXTRA_REMAINING, 0) ?: 0L
if (timerRemaining != 0L) {
serviceHandler?.obtainMessage()?.also { msg ->
msg.arg1 = startId
msg.data.putLong(EXTRA_REMAINING, timerRemaining)
serviceHandler?.sendMessage(msg)
}
}
return START_STICKY // that's the second trick
}
override fun onDestroy() {
super.onDestroy()
timer?.cancel()
}
fun startTimer(secondsRemaining: Long, id: Long) {
Intent(this, TimerService::class.java).apply {
putExtra(EXTRA_REMAINING, secondsRemaining)
}.also {
onStartCommand(it, 0, id.toInt())
}
}
fun stopTimer() {
timer?.cancel()
}
fun updateNotification(secondsRemaining: Long){
val notification = NotificationCompat.Builder(this, NotificationUtil.CHANNEL_ID_TIMER)
.setSmallIcon(R.drawable.ic_timer)
.setAutoCancel(true)
.setDefaults(0)
.setContentTitle(secondsRemaining.formatSeconds())
.setContentText("Timer")
.setContentIntent(notificationUtil.getPendingIntentWithStack(this, TimerActivity::class.java))
.setOngoing(true)
.build()
startForeground(NotificationUtil.NOTIFICATION_ID, notification) // that's the last trick
}
private fun sendMessage(remaining: Long) {
Intent(TimerService::class.java.simpleName).apply {
putExtra(EXTRA_REMAINING, remaining)
}.also {
LocalBroadcastManager.getInstance(this).sendBroadcast(it)
}
}
private inner class ServiceHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
val secondsRemaining = msg.data.getLong(EXTRA_REMAINING)
notificationUtil.showTimerStarted(secondsRemaining)
timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {
override fun onTick(millisUntilFinished: Long) {
Log.i(this::class.java.simpleName, "tick ${(millisUntilFinished / 1000L).formatSeconds()}")
updateNotification(millisUntilFinished / 1000)
sendMessage(millisUntilFinished / 1000)
}
override fun onFinish() {
Log.i(this::class.java.simpleName, "finish")
notificationUtil.showTimerEnded()
sendMessage(0)
stopSelf()
}
}.start()
}
}
inner class Binder : android.os.Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): TimerService = this#TimerService
}
companion object {
const val EXTRA_REMAINING = "EXTRA_REMAINING"
const val NOTIFICATION_ID = 1 // cannot be 0
fun Long.formatSeconds(): String {
val s = this % 60
val m = this / 60 % 60
val h = this / (60 * 60) % 24
return if (h > 0) String.format("%d:%02d:%02d", h, m, s)
else String.format("%02d:%02d", m, s)
}
}
}

Categories

Resources