From 68faba89513dbb42b3ba25d471735a77f15c7db2 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 17 Jan 2021 13:23:59 +0100 Subject: [PATCH] DummyServiceHelper --- .../persistentNotification/DummyService.kt | 11 +++- .../DummyServiceHelper.kt | 66 +++++++++++++++++++ .../PersistentNotificationPlugin.kt | 46 ++++++------- .../androidaps/receivers/AutoStartReceiver.kt | 11 ++-- 4 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt index ecca16c581..6d45fcd721 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt @@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.persistentNotification import android.app.Notification import android.app.Service import android.content.Intent +import android.os.Binder import android.os.IBinder import dagger.android.DaggerService import info.nightscout.androidaps.events.EventAppExit @@ -27,7 +28,13 @@ class DummyService : DaggerService() { private val disposable = CompositeDisposable() - override fun onBind(intent: Intent?): IBinder? = null + inner class LocalBinder : Binder() { + + fun getService(): DummyService = this@DummyService + } + + private val binder = LocalBinder() + override fun onBind(intent: Intent): IBinder = binder override fun onCreate() { super.onCreate() @@ -44,7 +51,7 @@ class DummyService : DaggerService() { .subscribe({ aapsLogger.debug(LTag.CORE, "EventAppExit received") stopSelf() - }, fabricPrivacy::logException ) + }, fabricPrivacy::logException) ) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt new file mode 100644 index 0000000000..d1adfc00ed --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt @@ -0,0 +1,66 @@ +package info.nightscout.androidaps.plugins.general.persistentNotification + +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 androidx.annotation.RequiresApi +import info.nightscout.androidaps.interfaces.NotificationHolderInterface +import javax.inject.Inject +import javax.inject.Singleton + +/* + This code replaces following + val alarm = Intent(context, DummyService::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.DummyService} + + */ +@RequiresApi(Build.VERSION_CODES.O) +@Singleton +class DummyServiceHelper @Inject constructor( + private val notificationHolder: NotificationHolderInterface +) { + + fun startService(context: Context) { + 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: DummyService.LocalBinder = service as DummyService.LocalBinder + + val dummyService: DummyService = binder.getService() + + context.startForegroundService(Intent(context, DummyService::class.java)) + + // This is the key: Without waiting Android Framework to call this method + // inside Service.onCreate(), immediately call here to post the notification. + dummyService.startForeground(notificationHolder.notificationID, notificationHolder.notification) + + // Release the connection to prevent leaks. + context.unbindService(this) + } + + override fun onServiceDisconnected(name: ComponentName?) { + } + } + + try { + context.bindService(Intent(context, DummyService::class.java), 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. + context.startForegroundService(Intent(context, DummyService::class.java)) + } + } + + fun stopService(context: Context) { + context.stopService(Intent(context, DummyService::class.java)) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt index 385247ce43..c5ee21253e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt @@ -30,18 +30,20 @@ import io.reactivex.schedulers.Schedulers import javax.inject.Inject import javax.inject.Singleton +@Suppress("PrivatePropertyName") @Singleton class PersistentNotificationPlugin @Inject constructor( injector: HasAndroidInjector, aapsLogger: AAPSLogger, resourceHelper: ResourceHelper, - private var profileFunction: ProfileFunction, - private var fabricPrivacy: FabricPrivacy, - private var activePlugins: ActivePluginProvider, - private var iobCobCalculatorPlugin: IobCobCalculatorPlugin, - private var rxBus: RxBusWrapper, - private var context: Context, - private var notificationHolder: NotificationHolder, + private val profileFunction: ProfileFunction, + private val fabricPrivacy: FabricPrivacy, + private val activePlugins: ActivePluginProvider, + private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val rxBus: RxBusWrapper, + private val context: Context, + private val notificationHolder: NotificationHolder, + private val dummyServiceHelper: DummyServiceHelper, private val iconsProvider: IconsProvider, private val databaseHelper: DatabaseHelperInterface ) : PluginBase(PluginDescription() @@ -112,13 +114,13 @@ class PersistentNotificationPlugin @Inject constructor( override fun onStop() { disposable.clear() - context.stopService(Intent(context, DummyService::class.java)) + dummyServiceHelper.stopService(context) super.onStop() } private fun triggerNotificationUpdate() { updateNotification() - context.startForegroundService(Intent(context, DummyService::class.java)) + dummyServiceHelper.startService(context) } private fun updateNotification() { @@ -128,31 +130,31 @@ class PersistentNotificationPlugin @Inject constructor( var line3: String? = null var unreadConversationBuilder: NotificationCompat.CarExtender.UnreadConversation.Builder? = null if (profileFunction.isProfileValid("Notification")) { - var line1_aa: String + var line1aa: String val units = profileFunction.getUnits() val lastBG = iobCobCalculatorPlugin.lastBg() val glucoseStatus = GlucoseStatus(injector).glucoseStatusData if (lastBG != null) { - line1_aa = lastBG.valueToUnitsToString(units) - line1 = line1_aa + line1aa = lastBG.valueToUnitsToString(units) + line1 = line1aa if (glucoseStatus != null) { line1 += (" Δ" + Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " avgΔ" + Profile.toSignedUnitsString(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units)) - line1_aa += " " + lastBG.directionToSymbol(databaseHelper) + line1aa += " " + lastBG.directionToSymbol(databaseHelper) } else { line1 += " " + resourceHelper.gs(R.string.old_data) + " " - line1_aa += "$line1." + line1aa += "$line1." } } else { - line1_aa = resourceHelper.gs(R.string.missed_bg_readings) - line1 = line1_aa + line1aa = resourceHelper.gs(R.string.missed_bg_readings) + line1 = line1aa } val activeTemp = activePlugins.activeTreatments.getTempBasalFromHistory(System.currentTimeMillis()) if (activeTemp != null) { line1 += " " + activeTemp.toStringShort() - line1_aa += " " + activeTemp.toStringShort() + "." + line1aa += " " + activeTemp.toStringShort() + "." } //IOB activePlugins.activeTreatments.updateTotalIOBTreatments() @@ -160,11 +162,11 @@ class PersistentNotificationPlugin @Inject constructor( val bolusIob = activePlugins.activeTreatments.lastCalculationTreatments.round() val basalIob = activePlugins.activeTreatments.lastCalculationTempBasals.round() line2 = resourceHelper.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U " + resourceHelper.gs(R.string.cob) + ": " + iobCobCalculatorPlugin.getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() - val line2_aa = resourceHelper.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U. " + resourceHelper.gs(R.string.cob) + ": " + iobCobCalculatorPlugin.getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() + "." + val line2aa = resourceHelper.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U. " + resourceHelper.gs(R.string.cob) + ": " + iobCobCalculatorPlugin.getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() + "." line3 = DecimalFormatter.to2Decimal(pump.baseBasalRate) + " U/h" - var line3_aa = DecimalFormatter.to2Decimal(pump.baseBasalRate) + " U/h." + var line3aa = DecimalFormatter.to2Decimal(pump.baseBasalRate) + " U/h." line3 += " - " + profileFunction.getProfileName() - line3_aa += " - " + profileFunction.getProfileName() + "." + line3aa += " - " + profileFunction.getProfileName() + "." /// For Android Auto val msgReadIntent = Intent() .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) @@ -188,12 +190,12 @@ class PersistentNotificationPlugin @Inject constructor( // Build a RemoteInput for receiving voice input from devices val remoteInput = RemoteInput.Builder(EXTRA_VOICE_REPLY).build() // Create the UnreadConversation - unreadConversationBuilder = NotificationCompat.CarExtender.UnreadConversation.Builder(line1_aa + "\n" + line2_aa) + unreadConversationBuilder = NotificationCompat.CarExtender.UnreadConversation.Builder(line1aa + "\n" + line2aa) .setLatestTimestamp(System.currentTimeMillis()) .setReadPendingIntent(msgReadPendingIntent) .setReplyAction(msgReplyPendingIntent, remoteInput) /// Add dot to produce a "more natural sounding result" - unreadConversationBuilder.addMessage(line3_aa) + unreadConversationBuilder.addMessage(line3aa) /// End Android Auto } else { line1 = resourceHelper.gs(R.string.noprofileset) diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.kt index 1260299f09..782a23d2de 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/AutoStartReceiver.kt @@ -1,14 +1,17 @@ package info.nightscout.androidaps.receivers -import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService +import dagger.android.DaggerBroadcastReceiver +import info.nightscout.androidaps.plugins.general.persistentNotification.DummyServiceHelper -class AutoStartReceiver : BroadcastReceiver() { +class AutoStartReceiver constructor( + private val dummyServiceHelper: DummyServiceHelper +) : DaggerBroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) if (intent.action == Intent.ACTION_BOOT_COMPLETED) - context.startForegroundService(Intent(context, DummyService::class.java)) + dummyServiceHelper.startService(context) } } \ No newline at end of file