Use foreground service for alarm completion
Many of our errors in production are caused by the alarm module finishing. In devices after android version 7 we are "required" to use startForegroundService or else the following error supposedly occurs: Exception java.lang.IllegalStateException: at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1725) at android.app.ContextImpl.startService (ContextImpl.java:1680) at android.content.ContextWrapper.startService (ContextWrapper.java:731) at android.content.ContextWrapper.startService (ContextWrapper.java:731) at com.massive.AlarmModule$getTimer$1.onFinish (AlarmModule.kt:144) at android.os.CountDownTimer$1.handleMessage (CountDownTimer.java:127) at android.os.Handler.dispatchMessage (Handler.java:106) at android.os.Looper.loop (Looper.java:236) at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run (MessageQueueThreadImpl.java:228) at java.lang.Thread.run (Thread.java:923) I say supposedly because on all of my testing devices (which are android 7+) this error doesn't occur.
This commit is contained in:
parent
1f6100607d
commit
07c704841d
|
@ -9,14 +9,14 @@ import android.content.IntentFilter
|
|||
import android.os.Build
|
||||
import android.os.CountDownTimer
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.facebook.react.bridge.*
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule
|
||||
import kotlin.math.floor
|
||||
|
||||
class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||
ReactContextBaseJavaModule(context) {
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
class AlarmModule(context: ReactApplicationContext?) :
|
||||
ReactContextBaseJavaModule(context), LifecycleEventListener {
|
||||
|
||||
private var countdownTimer: CountDownTimer? = null
|
||||
var currentMs: Long = 0
|
||||
|
@ -29,7 +29,6 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
|
||||
private val stopReceiver =
|
||||
object : BroadcastReceiver() {
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
Log.d("AlarmModule", "Received stop broadcast intent")
|
||||
stop()
|
||||
|
@ -38,24 +37,29 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
|
||||
private val addReceiver =
|
||||
object : BroadcastReceiver() {
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
add()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
reactApplicationContext.registerReceiver(stopReceiver, IntentFilter(STOP_BROADCAST),
|
||||
Context.RECEIVER_NOT_EXPORTED)
|
||||
reactApplicationContext.registerReceiver(addReceiver, IntentFilter(ADD_BROADCAST),
|
||||
Context.RECEIVER_NOT_EXPORTED)
|
||||
}
|
||||
else {
|
||||
reactApplicationContext.registerReceiver(stopReceiver, IntentFilter(STOP_BROADCAST))
|
||||
reactApplicationContext.registerReceiver(addReceiver, IntentFilter(ADD_BROADCAST))
|
||||
}
|
||||
|
||||
override fun onCatalystInstanceDestroy() {
|
||||
reactApplicationContext.unregisterReceiver(stopReceiver)
|
||||
reactApplicationContext.unregisterReceiver(addReceiver)
|
||||
super.onCatalystInstanceDestroy()
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
override fun onHostDestroy() {
|
||||
reactApplicationContext.unregisterReceiver(stopReceiver)
|
||||
reactApplicationContext.unregisterReceiver(addReceiver)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun add() {
|
||||
Log.d("AlarmModule", "Add 1 min to alarm.")
|
||||
|
@ -77,7 +81,6 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
return 0
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@ReactMethod
|
||||
fun stop() {
|
||||
Log.d("AlarmModule", "Stop alarm.")
|
||||
|
@ -98,7 +101,6 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
.emit("tick", params)
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@ReactMethod
|
||||
fun timer(milliseconds: Int, description: String) {
|
||||
Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
|
||||
|
@ -113,13 +115,11 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
running = true
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
private fun getTimer(
|
||||
endMs: Int,
|
||||
): CountDownTimer {
|
||||
val builder = getBuilder()
|
||||
return object : CountDownTimer(endMs.toLong(), 1000) {
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onTick(current: Long) {
|
||||
currentMs = current
|
||||
val seconds =
|
||||
|
@ -144,10 +144,15 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
.emit("tick", params)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onFinish() {
|
||||
val context = reactApplicationContext
|
||||
context.startService(Intent(context, AlarmService::class.java))
|
||||
val intent = Intent(context, AlarmService::class.java)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent)
|
||||
}
|
||||
else {
|
||||
context.startService(intent)
|
||||
}
|
||||
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit(
|
||||
"tick",
|
||||
|
@ -161,7 +166,6 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
}
|
||||
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
private fun getBuilder(): NotificationCompat.Builder {
|
||||
val context = reactApplicationContext
|
||||
val contentIntent = Intent(context, MainActivity::class.java)
|
||||
|
@ -183,10 +187,11 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
.setDeleteIntent(pendingStop)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun getManager(): NotificationManager {
|
||||
val notificationManager =
|
||||
reactApplicationContext.getSystemService(NotificationManager::class.java)
|
||||
reactApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val timersChannel =
|
||||
NotificationChannel(
|
||||
CHANNEL_ID_PENDING,
|
||||
|
@ -196,6 +201,8 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
timersChannel.setSound(null, null)
|
||||
timersChannel.description = "Progress on rest timers."
|
||||
notificationManager.createNotificationChannel(timersChannel)
|
||||
}
|
||||
|
||||
return notificationManager
|
||||
}
|
||||
|
||||
|
@ -205,4 +212,12 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
const val CHANNEL_ID_PENDING = "Timer"
|
||||
const val NOTIFICATION_ID_PENDING = 1
|
||||
}
|
||||
|
||||
override fun onHostResume() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onHostPause() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import androidx.core.app.NotificationCompat
|
|||
|
||||
class Settings(val sound: String?, val noSound: Boolean, val vibrate: Boolean, val duration: Long)
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class AlarmService : Service(), OnPreparedListener {
|
||||
private var mediaPlayer: MediaPlayer? = null
|
||||
private var vibrator: Vibrator? = null
|
||||
|
@ -58,7 +57,7 @@ class AlarmService : Service(), OnPreparedListener {
|
|||
}
|
||||
|
||||
private fun playSound(settings: Settings) {
|
||||
if (settings.noSound) return;
|
||||
if (settings.noSound) return
|
||||
if (settings.sound == null) {
|
||||
mediaPlayer = MediaPlayer.create(applicationContext, R.raw.argon)
|
||||
mediaPlayer?.start()
|
||||
|
@ -80,6 +79,9 @@ class AlarmService : Service(), OnPreparedListener {
|
|||
}
|
||||
|
||||
private fun doNotify(): Notification {
|
||||
val manager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val alarmsChannel = NotificationChannel(
|
||||
CHANNEL_ID_DONE,
|
||||
CHANNEL_ID_DONE,
|
||||
|
@ -88,10 +90,9 @@ class AlarmService : Service(), OnPreparedListener {
|
|||
alarmsChannel.description = "Alarms for rest timers."
|
||||
alarmsChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
alarmsChannel.setSound(null, null)
|
||||
val manager = applicationContext.getSystemService(
|
||||
NotificationManager::class.java
|
||||
)
|
||||
manager.createNotificationChannel(alarmsChannel)
|
||||
}
|
||||
|
||||
val builder = getBuilder()
|
||||
val context = applicationContext
|
||||
val finishIntent = Intent(context, StopAlarm::class.java)
|
||||
|
|
Loading…
Reference in New Issue
Block a user