fix AlarmSoundService crashing

This commit is contained in:
Milos Kozak 2020-11-28 09:02:12 +01:00
parent 3f77496c88
commit 7bc7388f06
4 changed files with 105 additions and 35 deletions

View file

@ -22,7 +22,7 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.services.AlarmSoundService
import info.nightscout.androidaps.services.AlarmSoundServiceHelper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.resources.IconsProvider
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -39,6 +39,7 @@ class NotificationStore @Inject constructor(
private val resourceHelper: ResourceHelper,
private val context: Context,
private val iconsProvider: IconsProvider,
private val alarmSoundServiceHelper: AlarmSoundServiceHelper,
private val dateUtil: DateUtil
) {
@ -46,10 +47,12 @@ class NotificationStore @Inject constructor(
private var usesChannels = false
companion object {
private const val CHANNEL_ID = "AndroidAPS-Overview"
}
inner class NotificationComparator : Comparator<Notification> {
override fun compare(o1: Notification, o2: Notification): Int {
return o1.level - o2.level
}
@ -68,17 +71,9 @@ class NotificationStore @Inject constructor(
store.add(n)
if (sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false) && n !is NotificationWithAction) {
raiseSystemNotification(n)
if (usesChannels && n.soundId != null && n.soundId != 0) {
val alarm = Intent(context, AlarmSoundService::class.java)
alarm.putExtra("soundid", n.soundId)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(alarm) else context.startService(alarm)
}
if (usesChannels && n.soundId != null && n.soundId != 0) alarmSoundServiceHelper.startAlarm(context, n.soundId)
} else {
if (n.soundId != null && n.soundId != 0) {
val alarm = Intent(context, AlarmSoundService::class.java)
alarm.putExtra("soundid", n.soundId)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(alarm) else context.startService(alarm)
}
if (n.soundId != null && n.soundId != 0) alarmSoundServiceHelper.startAlarm(context, n.soundId)
}
Collections.sort(store, NotificationComparator())
return true
@ -87,10 +82,7 @@ class NotificationStore @Inject constructor(
@Synchronized fun remove(id: Int): Boolean {
for (i in store.indices) {
if (store[i].id == id) {
if (store[i].soundId != null) {
val alarm = Intent(context, AlarmSoundService::class.java)
context.stopService(alarm)
}
if (store[i].soundId != null) alarmSoundServiceHelper.stopService(context)
store.removeAt(i)
return true
}
@ -195,10 +187,10 @@ class NotificationStore @Inject constructor(
@Suppress("SetTextI18n")
holder.text.text = dateUtil.timeString(notification.date) + " " + notification.text
when (notification.level) {
Notification.URGENT -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationUrgent))
Notification.NORMAL -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationNormal))
Notification.LOW -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationLow))
Notification.INFO -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationInfo))
Notification.URGENT -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationUrgent))
Notification.NORMAL -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationNormal))
Notification.LOW -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationLow))
Notification.INFO -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationInfo))
Notification.ANNOUNCEMENT -> holder.cv.setBackgroundColor(resourceHelper.gc(R.color.notificationAnnouncement))
}
}
@ -208,6 +200,7 @@ class NotificationStore @Inject constructor(
}
inner class NotificationsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var cv: CardView = itemView.findViewById(R.id.notification_cardview)
var text: TextView = itemView.findViewById(R.id.notification_text)
var dismiss: Button = itemView.findViewById(R.id.notification_dismiss)

View file

@ -1,8 +1,5 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -13,11 +10,13 @@ import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.services.AlarmSoundService
import info.nightscout.androidaps.services.AlarmSoundServiceHelper
import kotlinx.android.synthetic.main.dialog_error.*
import javax.inject.Inject
class ErrorDialog : DaggerDialogFragment() {
@Inject lateinit var alarmSoundServiceHelper: AlarmSoundServiceHelper
@Inject lateinit var aapsLogger: AAPSLogger
var helperActivity: ErrorHelperActivity? = null
@ -80,17 +79,10 @@ class ErrorDialog : DaggerDialogFragment() {
}
private fun startAlarm() {
if (sound != 0) {
val alarm = Intent(context, AlarmSoundService::class.java)
alarm.putExtra("soundid", sound)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context?.startForegroundService(alarm)
} else {
context?.startService(alarm)
}
}
if (sound != 0)
context?.let { context -> alarmSoundServiceHelper.startAlarm(context, sound) }
}
private fun stopAlarm() =
context?.stopService(Intent(context, AlarmSoundService::class.java))
context?.let { context -> alarmSoundServiceHelper.stopService(context) }
}

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Binder
import android.os.Handler
import android.os.IBinder
import dagger.android.DaggerService
@ -43,11 +44,17 @@ class AlarmSoundService : DaggerService() {
}
inner class LocalBinder : Binder() {
fun getService(): AlarmSoundService = this@AlarmSoundService
}
private val binder = LocalBinder()
override fun onBind(intent: Intent): IBinder = binder
private val increaseVolumeHandler = Handler()
private var currentVolumeLevel = 0
override fun onBind(intent: Intent): IBinder? = null
override fun onCreate() {
super.onCreate()
aapsLogger.debug(LTag.CORE, "onCreate parent called")

View file

@ -0,0 +1,78 @@
package info.nightscout.androidaps.services
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Build
import android.os.IBinder
import info.nightscout.androidaps.interfaces.NotificationHolderInterface
import javax.inject.Inject
import javax.inject.Singleton
/*
This code replaces following
val alarm = Intent(context, AlarmSoundService::class.java)
alarm.putExtra("soundid", n.soundId)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(alarm) else context.startService(alarm)
it fails randomly with error
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{e317f7e u0 info.nightscout.nsclient/info.nightscout.androidaps.services.AlarmSoundService}
*/
@Singleton
class AlarmSoundServiceHelper @Inject constructor(
private val notificationHolder: NotificationHolderInterface
) {
fun startAlarm(context: Context, sound: Int) {
val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
// The binder of the service that returns the instance that is created.
val binder: AlarmSoundService.LocalBinder = service as AlarmSoundService.LocalBinder
val alarmSoundService: AlarmSoundService = binder.getService()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
context.startForegroundService(getServiceIntent(context, sound))
else
context.startService(getServiceIntent(context, sound))
// This is the key: Without waiting Android Framework to call this method
// inside Service.onCreate(), immediately call here to post the notification.
alarmSoundService.startForeground(notificationHolder.notificationID, notificationHolder.notification)
// Release the connection to prevent leaks.
context.unbindService(this)
}
override fun onServiceDisconnected(name: ComponentName?) {
TODO("Not yet implemented")
}
}
try {
context.bindService(getServiceIntent(context, sound), connection, Context.BIND_AUTO_CREATE)
} catch (ignored: RuntimeException) {
// This is probably a broadcast receiver context even though we are calling getApplicationContext().
// Just call startForegroundService instead since we cannot bind a service to a
// broadcast receiver context. The service also have to call startForeground in
// this case.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
context.startForegroundService(getServiceIntent(context, sound))
else
context.startService(getServiceIntent(context, sound))
}
}
fun stopService(context: Context) {
val alarm = Intent(context, AlarmSoundService::class.java)
context.stopService(alarm)
}
private fun getServiceIntent(context: Context, sound: Int): Intent {
val alarm = Intent(context, AlarmSoundService::class.java)
alarm.putExtra("soundid", sound)
return alarm
}
}