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()
|
||||
running = true
|
||||
val manager = getManager()
|
||||
manager.cancel(NOTIFICATION_ID_DONE)
|
||||
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||
reactApplicationContext.stopService(intent)
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||
reactApplicationContext?.stopService(intent)
|
||||
val manager = getManager()
|
||||
manager.cancel(NOTIFICATION_ID_DONE)
|
||||
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||
manager.cancel(NOTIFICATION_ID_PENDING)
|
||||
val params = Arguments.createMap().apply {
|
||||
putString("minutes", "00")
|
||||
|
@ -93,7 +93,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
fun timer(milliseconds: Int) {
|
||||
Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
|
||||
val manager = getManager()
|
||||
manager.cancel(NOTIFICATION_ID_DONE)
|
||||
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||
val intent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||
reactApplicationContext.stopService(intent)
|
||||
countdownTimer?.cancel()
|
||||
|
@ -133,25 +133,8 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onFinish() {
|
||||
val context = reactApplicationContext
|
||||
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 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
|
||||
context.startForegroundService(Intent(context, AlarmService::class.java))
|
||||
context
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit("finish", Arguments.createMap().apply {
|
||||
putString("minutes", "00")
|
||||
|
@ -169,12 +152,12 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
val pendingContent =
|
||||
PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||
val addBroadcast = Intent(ADD_BROADCAST).apply {
|
||||
setPackage(reactApplicationContext.packageName)
|
||||
setPackage(context.packageName)
|
||||
}
|
||||
val pendingAdd =
|
||||
PendingIntent.getBroadcast(context, 0, addBroadcast, PendingIntent.FLAG_MUTABLE)
|
||||
val stopBroadcast = Intent(STOP_BROADCAST)
|
||||
stopBroadcast.setPackage(reactApplicationContext.packageName)
|
||||
stopBroadcast.setPackage(context.packageName)
|
||||
val pendingStop =
|
||||
PendingIntent.getBroadcast(context, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE)
|
||||
return NotificationCompat.Builder(context, CHANNEL_ID_PENDING)
|
||||
|
@ -187,16 +170,9 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
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(
|
||||
NotificationManager::class.java
|
||||
)
|
||||
notificationManager.createNotificationChannel(alarmsChannel)
|
||||
val timersChannel = NotificationChannel(
|
||||
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 ADD_BROADCAST = "add-timer-event"
|
||||
const val CHANNEL_ID_PENDING = "Timer"
|
||||
const val CHANNEL_ID_DONE = "Alarm"
|
||||
const val NOTIFICATION_ID_PENDING = 1
|
||||
const val NOTIFICATION_ID_DONE = 2
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.massive
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Service
|
||||
import android.app.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.AudioAttributes
|
||||
|
@ -9,42 +9,57 @@ import android.media.MediaPlayer
|
|||
import android.media.MediaPlayer.OnPreparedListener
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
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 {
|
||||
private var mediaPlayer: MediaPlayer? = null
|
||||
private var vibrator: Vibrator? = null
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
if (intent.action == "stop") {
|
||||
onDestroy()
|
||||
return START_STICKY
|
||||
private fun getBuilder(): NotificationCompat.Builder {
|
||||
val context = applicationContext
|
||||
val contentIntent = Intent(context, MainActivity::class.java)
|
||||
val pendingContent =
|
||||
PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||
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 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)
|
||||
.let {
|
||||
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) {
|
||||
private fun playSound(settings: Settings) {
|
||||
if (settings.sound == null && !settings.noSound) {
|
||||
mediaPlayer = MediaPlayer.create(applicationContext, R.raw.argon)
|
||||
mediaPlayer?.start()
|
||||
mediaPlayer?.setOnCompletionListener { vibrator?.cancel() }
|
||||
} else if (sound != null && !noSound) {
|
||||
} else if (settings.sound != null && !settings.noSound) {
|
||||
mediaPlayer = MediaPlayer().apply {
|
||||
setAudioAttributes(
|
||||
AudioAttributes.Builder()
|
||||
|
@ -52,20 +67,56 @@ class AlarmService : Service(), OnPreparedListener {
|
|||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.build()
|
||||
)
|
||||
setDataSource(applicationContext, Uri.parse(sound))
|
||||
setDataSource(applicationContext, Uri.parse(settings.sound))
|
||||
prepare()
|
||||
start()
|
||||
setOnCompletionListener { vibrator?.cancel() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val vibrate = db.rawQuery("SELECT vibrate FROM settings", null)
|
||||
.let {
|
||||
it.moveToFirst()
|
||||
it.getInt(0) == 1
|
||||
}
|
||||
if (!vibrate) return START_STICKY
|
||||
private fun doNotify(): Notification {
|
||||
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 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)
|
||||
vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager =
|
||||
|
@ -97,4 +148,9 @@ class AlarmService : Service(), OnPreparedListener {
|
|||
mediaPlayer?.release()
|
||||
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...")
|
||||
applicationContext.stopService(Intent(applicationContext, AlarmService::class.java))
|
||||
val manager = getManager()
|
||||
manager.cancel(AlarmModule.NOTIFICATION_ID_DONE)
|
||||
manager.cancel(AlarmService.NOTIFICATION_ID_DONE)
|
||||
manager.cancel(AlarmModule.NOTIFICATION_ID_PENDING)
|
||||
val intent = Intent(applicationContext, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
@ -33,8 +33,8 @@ class TimerDone : AppCompatActivity() {
|
|||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun getManager(): NotificationManager {
|
||||
val alarmsChannel = NotificationChannel(
|
||||
AlarmModule.CHANNEL_ID_DONE,
|
||||
AlarmModule.CHANNEL_ID_DONE,
|
||||
AlarmService.CHANNEL_ID_DONE,
|
||||
AlarmService.CHANNEL_ID_DONE,
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
).apply {
|
||||
description = "Alarms for rest timers."
|
||||
|
|
Loading…
Reference in New Issue
Block a user