Start alarms as a foreground service
Related to #142. Can't be sure this fixed anything because I can't replicate the error on my emulators running android 13. I need to install android 13 on a real device and try replicate + see if this fixes it.
This commit is contained in:
parent
05237fc293
commit
cffc458338
|
@ -63,7 +63,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||||
countdownTimer?.start()
|
countdownTimer?.start()
|
||||||
running = true
|
running = true
|
||||||
val manager = getManager()
|
val manager = getManager()
|
||||||
manager.cancel(NOTIFICATION_ID_DONE)
|
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||||
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||||
reactApplicationContext.stopService(intent)
|
reactApplicationContext.stopService(intent)
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||||
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||||
reactApplicationContext?.stopService(intent)
|
reactApplicationContext?.stopService(intent)
|
||||||
val manager = getManager()
|
val manager = getManager()
|
||||||
manager.cancel(NOTIFICATION_ID_DONE)
|
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||||
manager.cancel(NOTIFICATION_ID_PENDING)
|
manager.cancel(NOTIFICATION_ID_PENDING)
|
||||||
val params = Arguments.createMap().apply {
|
val params = Arguments.createMap().apply {
|
||||||
putString("minutes", "00")
|
putString("minutes", "00")
|
||||||
|
@ -93,7 +93,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||||
fun timer(milliseconds: Int) {
|
fun timer(milliseconds: Int) {
|
||||||
Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
|
Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
|
||||||
val manager = getManager()
|
val manager = getManager()
|
||||||
manager.cancel(NOTIFICATION_ID_DONE)
|
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||||
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||||
reactApplicationContext.stopService(intent)
|
reactApplicationContext.stopService(intent)
|
||||||
countdownTimer?.cancel()
|
countdownTimer?.cancel()
|
||||||
|
@ -133,25 +133,8 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
val context = reactApplicationContext
|
val context = reactApplicationContext
|
||||||
val finishIntent = Intent(context, StopAlarm::class.java)
|
context.startForegroundService(Intent(context, AlarmService::class.java))
|
||||||
val finishPending = PendingIntent.getActivity(
|
context
|
||||||
context, 0, finishIntent, PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
val fullIntent = Intent(context, TimerDone::class.java)
|
|
||||||
val fullPending = PendingIntent.getActivity(
|
|
||||||
context, 0, fullIntent, PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
builder.setContentText("Timer finished.").setProgress(0, 0, false)
|
|
||||||
.setAutoCancel(true).setOngoing(true).setFullScreenIntent(fullPending, true)
|
|
||||||
.setContentIntent(finishPending).setChannelId(CHANNEL_ID_DONE)
|
|
||||||
.setCategory(NotificationCompat.CATEGORY_ALARM).priority =
|
|
||||||
NotificationCompat.PRIORITY_HIGH
|
|
||||||
val manager = getManager()
|
|
||||||
manager.notify(NOTIFICATION_ID_DONE, builder.build())
|
|
||||||
manager.cancel(NOTIFICATION_ID_PENDING)
|
|
||||||
val alarmIntent = Intent(context, AlarmService::class.java)
|
|
||||||
context.startService(alarmIntent)
|
|
||||||
reactApplicationContext
|
|
||||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||||
.emit("finish", Arguments.createMap().apply {
|
.emit("finish", Arguments.createMap().apply {
|
||||||
putString("minutes", "00")
|
putString("minutes", "00")
|
||||||
|
@ -169,12 +152,12 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||||
val pendingContent =
|
val pendingContent =
|
||||||
PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
val addBroadcast = Intent(ADD_BROADCAST).apply {
|
val addBroadcast = Intent(ADD_BROADCAST).apply {
|
||||||
setPackage(reactApplicationContext.packageName)
|
setPackage(context.packageName)
|
||||||
}
|
}
|
||||||
val pendingAdd =
|
val pendingAdd =
|
||||||
PendingIntent.getBroadcast(context, 0, addBroadcast, PendingIntent.FLAG_MUTABLE)
|
PendingIntent.getBroadcast(context, 0, addBroadcast, PendingIntent.FLAG_MUTABLE)
|
||||||
val stopBroadcast = Intent(STOP_BROADCAST)
|
val stopBroadcast = Intent(STOP_BROADCAST)
|
||||||
stopBroadcast.setPackage(reactApplicationContext.packageName)
|
stopBroadcast.setPackage(context.packageName)
|
||||||
val pendingStop =
|
val pendingStop =
|
||||||
PendingIntent.getBroadcast(context, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getBroadcast(context, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE)
|
||||||
return NotificationCompat.Builder(context, CHANNEL_ID_PENDING)
|
return NotificationCompat.Builder(context, CHANNEL_ID_PENDING)
|
||||||
|
@ -187,16 +170,9 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
private fun getManager(): NotificationManager {
|
private fun getManager(): NotificationManager {
|
||||||
val alarmsChannel = NotificationChannel(
|
|
||||||
CHANNEL_ID_DONE, CHANNEL_ID_DONE, NotificationManager.IMPORTANCE_HIGH
|
|
||||||
)
|
|
||||||
alarmsChannel.description = "Alarms for rest timers."
|
|
||||||
alarmsChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
|
||||||
alarmsChannel.setSound(null, null)
|
|
||||||
val notificationManager = reactApplicationContext.getSystemService(
|
val notificationManager = reactApplicationContext.getSystemService(
|
||||||
NotificationManager::class.java
|
NotificationManager::class.java
|
||||||
)
|
)
|
||||||
notificationManager.createNotificationChannel(alarmsChannel)
|
|
||||||
val timersChannel = NotificationChannel(
|
val timersChannel = NotificationChannel(
|
||||||
CHANNEL_ID_PENDING, CHANNEL_ID_PENDING, NotificationManager.IMPORTANCE_LOW
|
CHANNEL_ID_PENDING, CHANNEL_ID_PENDING, NotificationManager.IMPORTANCE_LOW
|
||||||
)
|
)
|
||||||
|
@ -210,8 +186,6 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
||||||
const val STOP_BROADCAST = "stop-timer-event"
|
const val STOP_BROADCAST = "stop-timer-event"
|
||||||
const val ADD_BROADCAST = "add-timer-event"
|
const val ADD_BROADCAST = "add-timer-event"
|
||||||
const val CHANNEL_ID_PENDING = "Timer"
|
const val CHANNEL_ID_PENDING = "Timer"
|
||||||
const val CHANNEL_ID_DONE = "Alarm"
|
|
||||||
const val NOTIFICATION_ID_PENDING = 1
|
const val NOTIFICATION_ID_PENDING = 1
|
||||||
const val NOTIFICATION_ID_DONE = 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.massive
|
package com.massive
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Service
|
import android.app.*
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.AudioAttributes
|
import android.media.AudioAttributes
|
||||||
|
@ -9,42 +9,57 @@ import android.media.MediaPlayer
|
||||||
import android.media.MediaPlayer.OnPreparedListener
|
import android.media.MediaPlayer.OnPreparedListener
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import android.util.Log
|
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
|
||||||
|
class Settings(val sound: String?, val noSound: Boolean, val vibrate: Boolean)
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
class AlarmService : Service(), OnPreparedListener {
|
class AlarmService : Service(), OnPreparedListener {
|
||||||
private var mediaPlayer: MediaPlayer? = null
|
private var mediaPlayer: MediaPlayer? = null
|
||||||
private var vibrator: Vibrator? = null
|
private var vibrator: Vibrator? = null
|
||||||
|
|
||||||
@SuppressLint("Recycle")
|
private fun getBuilder(): NotificationCompat.Builder {
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
val context = applicationContext
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
val contentIntent = Intent(context, MainActivity::class.java)
|
||||||
if (intent.action == "stop") {
|
val pendingContent =
|
||||||
onDestroy()
|
PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
return START_STICKY
|
val addBroadcast = Intent(AlarmModule.ADD_BROADCAST).apply {
|
||||||
|
setPackage(context.packageName)
|
||||||
}
|
}
|
||||||
|
val pendingAdd =
|
||||||
|
PendingIntent.getBroadcast(context, 0, addBroadcast, PendingIntent.FLAG_MUTABLE)
|
||||||
|
val stopBroadcast = Intent(AlarmModule.STOP_BROADCAST)
|
||||||
|
stopBroadcast.setPackage(context.packageName)
|
||||||
|
val pendingStop =
|
||||||
|
PendingIntent.getBroadcast(context, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
return NotificationCompat.Builder(context, AlarmModule.CHANNEL_ID_PENDING)
|
||||||
|
.setSmallIcon(R.drawable.ic_baseline_hourglass_bottom_24).setContentTitle("Resting")
|
||||||
|
.setContentIntent(pendingContent)
|
||||||
|
.addAction(R.drawable.ic_baseline_stop_24, "Stop", pendingStop)
|
||||||
|
.addAction(R.drawable.ic_baseline_stop_24, "Add 1 min", pendingAdd)
|
||||||
|
.setDeleteIntent(pendingStop)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("Range")
|
||||||
|
private fun getSettings(): Settings {
|
||||||
val db = DatabaseHelper(applicationContext).readableDatabase
|
val db = DatabaseHelper(applicationContext).readableDatabase
|
||||||
|
val cursor = db.rawQuery("SELECT sound, noSound, vibrate FROM settings", null)
|
||||||
|
cursor.moveToFirst()
|
||||||
|
val sound = cursor.getString(cursor.getColumnIndex("sound"))
|
||||||
|
val noSound = cursor.getInt(cursor.getColumnIndex("noSound")) == 1
|
||||||
|
val vibrate = cursor.getInt(cursor.getColumnIndex("vibrate")) == 1
|
||||||
|
cursor.close()
|
||||||
|
return Settings(sound, noSound, vibrate)
|
||||||
|
}
|
||||||
|
|
||||||
val sound = db.rawQuery("SELECT sound FROM settings", null)
|
private fun playSound(settings: Settings) {
|
||||||
.let {
|
if (settings.sound == null && !settings.noSound) {
|
||||||
it.moveToFirst()
|
|
||||||
it.getString(0)
|
|
||||||
}
|
|
||||||
Log.d("AlarmService", "sound=$sound")
|
|
||||||
|
|
||||||
val noSound = db.rawQuery("SELECT noSound FROM settings", null)
|
|
||||||
.let {
|
|
||||||
it.moveToFirst()
|
|
||||||
it.getInt(0) == 1
|
|
||||||
}
|
|
||||||
Log.d("AlarmService", "noSound=$noSound")
|
|
||||||
|
|
||||||
if (sound == null && !noSound) {
|
|
||||||
mediaPlayer = MediaPlayer.create(applicationContext, R.raw.argon)
|
mediaPlayer = MediaPlayer.create(applicationContext, R.raw.argon)
|
||||||
mediaPlayer?.start()
|
mediaPlayer?.start()
|
||||||
mediaPlayer?.setOnCompletionListener { vibrator?.cancel() }
|
mediaPlayer?.setOnCompletionListener { vibrator?.cancel() }
|
||||||
} else if (sound != null && !noSound) {
|
} else if (settings.sound != null && !settings.noSound) {
|
||||||
mediaPlayer = MediaPlayer().apply {
|
mediaPlayer = MediaPlayer().apply {
|
||||||
setAudioAttributes(
|
setAudioAttributes(
|
||||||
AudioAttributes.Builder()
|
AudioAttributes.Builder()
|
||||||
|
@ -52,20 +67,56 @@ class AlarmService : Service(), OnPreparedListener {
|
||||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
setDataSource(applicationContext, Uri.parse(sound))
|
setDataSource(applicationContext, Uri.parse(settings.sound))
|
||||||
prepare()
|
prepare()
|
||||||
start()
|
start()
|
||||||
setOnCompletionListener { vibrator?.cancel() }
|
setOnCompletionListener { vibrator?.cancel() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val vibrate = db.rawQuery("SELECT vibrate FROM settings", null)
|
private fun doNotify(): Notification {
|
||||||
.let {
|
val alarmsChannel = NotificationChannel(
|
||||||
it.moveToFirst()
|
CHANNEL_ID_DONE,
|
||||||
it.getInt(0) == 1
|
CHANNEL_ID_DONE,
|
||||||
}
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
if (!vibrate) return START_STICKY
|
)
|
||||||
|
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)
|
||||||
|
val finishPending = PendingIntent.getActivity(
|
||||||
|
context, 0, finishIntent, PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
val fullIntent = Intent(context, TimerDone::class.java)
|
||||||
|
val fullPending = PendingIntent.getActivity(
|
||||||
|
context, 0, fullIntent, PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
builder.setContentText("Timer finished.").setProgress(0, 0, false)
|
||||||
|
.setAutoCancel(true).setOngoing(true).setFullScreenIntent(fullPending, true)
|
||||||
|
.setContentIntent(finishPending).setChannelId(CHANNEL_ID_DONE)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_ALARM).priority =
|
||||||
|
NotificationCompat.PRIORITY_HIGH
|
||||||
|
val notification = builder.build()
|
||||||
|
manager.notify(NOTIFICATION_ID_DONE, notification)
|
||||||
|
manager.cancel(AlarmModule.NOTIFICATION_ID_PENDING)
|
||||||
|
return notification
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("Recycle")
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
|
val notification = doNotify()
|
||||||
|
startForeground(NOTIFICATION_ID_DONE, notification)
|
||||||
|
val settings = getSettings()
|
||||||
|
playSound(settings)
|
||||||
|
if (!settings.vibrate) return START_STICKY
|
||||||
val pattern = longArrayOf(0, 300, 1300, 300, 1300, 300)
|
val pattern = longArrayOf(0, 300, 1300, 300, 1300, 300)
|
||||||
vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
val vibratorManager =
|
val vibratorManager =
|
||||||
|
@ -97,4 +148,9 @@ class AlarmService : Service(), OnPreparedListener {
|
||||||
mediaPlayer?.release()
|
mediaPlayer?.release()
|
||||||
vibrator?.cancel()
|
vibrator?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CHANNEL_ID_DONE = "Alarm"
|
||||||
|
const val NOTIFICATION_ID_DONE = 2
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -23,7 +23,7 @@ class TimerDone : AppCompatActivity() {
|
||||||
Log.d("TimerDone", "Stopping...")
|
Log.d("TimerDone", "Stopping...")
|
||||||
applicationContext.stopService(Intent(applicationContext, AlarmService::class.java))
|
applicationContext.stopService(Intent(applicationContext, AlarmService::class.java))
|
||||||
val manager = getManager()
|
val manager = getManager()
|
||||||
manager.cancel(AlarmModule.NOTIFICATION_ID_DONE)
|
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||||
manager.cancel(AlarmModule.NOTIFICATION_ID_PENDING)
|
manager.cancel(AlarmModule.NOTIFICATION_ID_PENDING)
|
||||||
val intent = Intent(applicationContext, MainActivity::class.java)
|
val intent = Intent(applicationContext, MainActivity::class.java)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
@ -33,8 +33,8 @@ class TimerDone : AppCompatActivity() {
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
fun getManager(): NotificationManager {
|
fun getManager(): NotificationManager {
|
||||||
val alarmsChannel = NotificationChannel(
|
val alarmsChannel = NotificationChannel(
|
||||||
AlarmModule.CHANNEL_ID_DONE,
|
AlarmService.CHANNEL_ID_DONE,
|
||||||
AlarmModule.CHANNEL_ID_DONE,
|
AlarmService.CHANNEL_ID_DONE,
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
).apply {
|
).apply {
|
||||||
description = "Alarms for rest timers."
|
description = "Alarms for rest timers."
|
||||||
|
|
Loading…
Reference in New Issue
Block a user