Move timer logic from AlarmModule -> TimerService
Missing a few of the old features here but ultimately this will fix #210, #212, #196.
This commit is contained in:
parent
1e7c994209
commit
5355b0eb6a
|
@ -48,5 +48,9 @@
|
|||
<service
|
||||
android:name=".AlarmService"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".TimerService"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -104,15 +104,14 @@ class AlarmModule(context: ReactApplicationContext?) :
|
|||
@ReactMethod
|
||||
fun timer(milliseconds: Int, description: String) {
|
||||
Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
|
||||
currentDescription = description
|
||||
val manager = getManager()
|
||||
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||
reactApplicationContext.stopService(intent)
|
||||
countdownTimer?.cancel()
|
||||
countdownTimer = getTimer(milliseconds)
|
||||
countdownTimer?.start()
|
||||
running = true
|
||||
val intent = Intent(reactApplicationContext, TimerService::class.java)
|
||||
intent.putExtra("milliseconds", milliseconds)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
reactApplicationContext.startForegroundService(intent)
|
||||
}
|
||||
else {
|
||||
reactApplicationContext.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTimer(
|
||||
|
@ -147,12 +146,7 @@ class AlarmModule(context: ReactApplicationContext?) :
|
|||
override fun onFinish() {
|
||||
val context = reactApplicationContext
|
||||
val intent = Intent(context, AlarmService::class.java)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent)
|
||||
}
|
||||
else {
|
||||
context.startService(intent)
|
||||
}
|
||||
context.startService(intent)
|
||||
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit(
|
||||
"tick",
|
||||
|
|
155
android/app/src/main/java/com/massive/TimerService.kt
Normal file
155
android/app/src/main/java/com/massive/TimerService.kt
Normal file
|
@ -0,0 +1,155 @@
|
|||
package com.massive
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.*
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
|
||||
class TimerService : Service() {
|
||||
|
||||
private lateinit var timerHandler: Handler
|
||||
private var timerRunnable: Runnable? = null
|
||||
private var timeLeftInSeconds: Int = 0
|
||||
private var timeTotalInSeconds: Int = 0
|
||||
private var notificationId = 1
|
||||
|
||||
private val stopReceiver =
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
Log.d("TimerService", "Received stop broadcast intent")
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
timerHandler = Handler(Looper.getMainLooper())
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
applicationContext.registerReceiver(stopReceiver, IntentFilter(STOP_BROADCAST),
|
||||
Context.RECEIVER_NOT_EXPORTED)
|
||||
}
|
||||
else {
|
||||
applicationContext.registerReceiver(stopReceiver, IntentFilter(STOP_BROADCAST))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
timeLeftInSeconds = (intent?.getIntExtra("milliseconds", 0) ?: 0) / 1000
|
||||
startForeground(notificationId, createNotification(timeLeftInSeconds))
|
||||
Log.d("TimerService", "onStartCommand seconds=$timeLeftInSeconds")
|
||||
timeTotalInSeconds = timeLeftInSeconds
|
||||
|
||||
timerRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
if (timeLeftInSeconds > 0) {
|
||||
timeLeftInSeconds--
|
||||
updateNotification(timeLeftInSeconds)
|
||||
timerHandler.postDelayed(this, 1000)
|
||||
} else {
|
||||
startAlarmService()
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
timerHandler.postDelayed(timerRunnable!!, 1000)
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
timerHandler.removeCallbacks(timerRunnable!!)
|
||||
applicationContext.unregisterReceiver(stopReceiver)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createNotification(timeLeftInSeconds: Int): Notification {
|
||||
val notificationTitle = "Timer"
|
||||
val notificationText = formatTime(timeLeftInSeconds)
|
||||
val notificationChannelId = "timer_channel"
|
||||
val notificationIntent = Intent(this, TimerService::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
notificationIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
val stopBroadcast = Intent(AlarmModule.STOP_BROADCAST)
|
||||
stopBroadcast.setPackage(applicationContext.packageName)
|
||||
val pendingStop =
|
||||
PendingIntent.getBroadcast(applicationContext, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE)
|
||||
|
||||
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
|
||||
.setContentTitle(notificationTitle)
|
||||
.setContentText(notificationText)
|
||||
.setSmallIcon(R.drawable.ic_baseline_timer_24)
|
||||
.setProgress(timeTotalInSeconds, timeLeftInSeconds, false)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setAutoCancel(false)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true)
|
||||
.setDeleteIntent(pendingStop)
|
||||
.addAction(R.drawable.ic_baseline_stop_24, "Stop", pendingStop)
|
||||
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
notificationChannelId,
|
||||
"Timer Channel",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
return notificationBuilder.build()
|
||||
}
|
||||
|
||||
private fun updateNotification(timeLeftInSeconds: Int) {
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
val notification = createNotification(timeLeftInSeconds)
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
// here to request the missing permissions, and then overriding
|
||||
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
||||
// int[] grantResults)
|
||||
// to handle the case where the user grants the permission. See the documentation
|
||||
// for ActivityCompat#requestPermissions for more details.
|
||||
return
|
||||
}
|
||||
notificationManager.notify(notificationId, notification)
|
||||
}
|
||||
|
||||
private fun formatTime(timeInSeconds: Int): String {
|
||||
val minutes = timeInSeconds / 60
|
||||
val seconds = timeInSeconds % 60
|
||||
return String.format("%02d:%02d", minutes, seconds)
|
||||
}
|
||||
|
||||
private fun startAlarmService() {
|
||||
val intent = Intent(applicationContext, AlarmService::class.java)
|
||||
applicationContext.startService(intent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STOP_BROADCAST = "stop-timer-event"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user