diff --git a/app/build.gradle b/app/build.gradle index dd5e62f00f..457861a91f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -230,6 +230,7 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.android.gms:play-services-wearable:17.0.0' + implementation "com.google.android.gms:play-services-location:17.0.0" implementation 'com.google.firebase:firebase-core:17.2.1' implementation 'com.google.firebase:firebase-auth:19.2.0' implementation 'com.google.firebase:firebase-database:19.2.0' diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 8e52ebaa55..0e8f71a3d1 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -307,7 +307,7 @@ public class MainActivity extends NoSplashAppCompatActivity { case R.id.nav_about: AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(resourceHelper.gs(R.string.app_name) + " " + BuildConfig.VERSION); - builder.setIcon(MainApp.getIcon()); + builder.setIcon(resourceHelper.getIcon()); String message = "Build: " + BuildConfig.BUILDVERSION + "\n"; message += "Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + "\n"; message += resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.getNightscoutVersionName(); diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 57ad69b62a..b6bb60dee8 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -108,7 +108,7 @@ public class MainApp extends DaggerApplication { static Logger log = LoggerFactory.getLogger(L.CORE); static MainApp sInstance; - public static Resources sResources; + private static Resources sResources; static FirebaseAnalytics mFirebaseAnalytics; @@ -147,6 +147,7 @@ public class MainApp extends DaggerApplication { @Inject OpenAPSAMAPlugin openAPSAMAPlugin; @Inject OpenAPSSMBPlugin openAPSSMBPlugin; @Inject OverviewPlugin overviewPlugin; + @Inject PersistentNotificationPlugin persistentNotificationPlugin; @Inject RandomBgPlugin randomBgPlugin; @Inject DexcomPlugin dexcomPlugin; @Inject EversensePlugin eversensePlugin; @@ -263,7 +264,7 @@ public class MainApp extends DaggerApplication { pluginsList.add(wearPlugin); pluginsList.add(statusLinePlugin); - pluginsList.add(PersistentNotificationPlugin.getPlugin()); + pluginsList.add(persistentNotificationPlugin); pluginsList.add(NSClientPlugin.getPlugin()); // if (engineeringMode) pluginsList.add(tidepoolPlugin); pluginsList.add(maintenancePlugin); @@ -357,6 +358,11 @@ public class MainApp extends DaggerApplication { return ContextCompat.getColor(instance(), id); } + @Deprecated + public static Resources resources() { + return sResources; + } + @Deprecated public static MainApp instance() { return sInstance; @@ -442,24 +448,6 @@ public class MainApp extends DaggerApplication { return devBranch; } - public static int getIcon() { - if (Config.NSCLIENT) - return R.mipmap.ic_yellowowl; - else if (Config.PUMPCONTROL) - return R.mipmap.ic_pumpcontrol; - else - return R.mipmap.ic_launcher; - } - - public static int getNotificationIcon() { - if (Config.NSCLIENT) - return R.drawable.ic_notif_nsclient; - else if (Config.PUMPCONTROL) - return R.drawable.ic_notif_pumpcontrol; - else - return R.drawable.ic_notif_aaps; - } - @Override public void onTerminate() { @@ -472,6 +460,7 @@ public class MainApp extends DaggerApplication { super.onTerminate(); } + @Deprecated public static int dpToPx(int dp) { float scale = sResources.getDisplayMetrics().density; return (int) (dp * scale + 0.5f); diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index c2dc6f737e..f9b4d0828f 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -260,7 +260,7 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { @Override public float getSize() { - boolean isTablet = MainApp.sResources.getBoolean(R.bool.isTablet); + boolean isTablet = MainApp.resources().getBoolean(R.bool.isTablet); return isTablet ? 12 : 10; } diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt index c6a745cc38..11b188bfd3 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt @@ -3,12 +3,18 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService +import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService +import info.nightscout.androidaps.services.AlarmSoundService import info.nightscout.androidaps.services.DataService +import info.nightscout.androidaps.services.LocationService @Module @Suppress("unused") abstract class ServicesModule { + @ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService @ContributesAndroidInjector abstract fun contributesDataService(): DataService + @ContributesAndroidInjector abstract fun contributesDummyService(): DummyService + @ContributesAndroidInjector abstract fun contributesLocationService(): LocationService @ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt index 891eafd148..e6dc292f01 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt @@ -19,7 +19,6 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog @@ -129,7 +128,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { private fun addImage(@DrawableRes res: Int, context: Context, layout: LinearLayout) { val iv = ImageView(context) iv.setImageResource(res) - iv.layoutParams = LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)) + iv.layoutParams = LinearLayout.LayoutParams(resourceHelper.dpToPx(24), resourceHelper.dpToPx(24)) layout.addView(iv) } @@ -148,8 +147,8 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { // arrow icon val iv = ImageView(holder.context) iv.setImageResource(R.drawable.ic_arrow_forward_white_24dp) - iv.layoutParams = LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)) - iv.setPadding(MainApp.dpToPx(4), 0, MainApp.dpToPx(4), 0) + iv.layoutParams = LinearLayout.LayoutParams(resourceHelper.dpToPx(24), resourceHelper.dpToPx(24)) + iv.setPadding(resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4), 0) holder.iconLayout.addView(iv) // action icons val actionIcons = HashSet() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java index 8070e074be..521f7df9a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocation.java @@ -156,31 +156,11 @@ public class TriggerLocation extends Trigger { } - TriggerLocation setLatitude(double value) { - latitude.setValue(value); - return this; - } - - TriggerLocation setLongitude(double value) { - longitude.setValue(value); - return this; - } - - TriggerLocation setdistance(double value) { - distance.setValue(value); - return this; - } - TriggerLocation lastRun(long lastRun) { this.lastRun = lastRun; return this; } - TriggerLocation setMode(InputLocationMode.Mode value) { - modeSelected.setValue(value); - return this; - } - @Override public void generateDialog(LinearLayout root, FragmentManager fragmentManager) { new LayoutBuilder() @@ -202,6 +182,7 @@ public class TriggerLocation extends Trigger { return OUTSIDE; } + // for mocking only TODO remove static Location getCurrentLocation(){ return LocationService.getLastLocation(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index e03048dc04..f41d2e3d45 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -237,7 +237,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList View view; - if (MainApp.sResources.getBoolean(R.bool.isTablet) && (Config.NSCLIENT)) { + if (resourceHelper.gb(R.bool.isTablet) && (Config.NSCLIENT)) { view = inflater.inflate(R.layout.overview_fragment_nsclient_tablet, container, false); } else if (Config.NSCLIENT) { view = inflater.inflate(R.layout.overview_fragment_nsclient, container, false); @@ -1310,7 +1310,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList + resourceHelper.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U\n"; OKDialog.show(getActivity(), resourceHelper.gs(R.string.iob), iobtext1); }); - } else if (MainApp.sResources.getBoolean(R.bool.isTablet)) { + } else if (resourceHelper.gb(R.bool.isTablet)) { String iobtext = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" + resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " + resourceHelper.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U)"; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt index 9e36273b57..47169145f5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt @@ -5,7 +5,6 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.content.Intent -import android.graphics.BitmapFactory import android.media.AudioManager import android.media.RingtoneManager import android.os.Build @@ -31,9 +30,6 @@ import java.util.* import javax.inject.Inject import javax.inject.Singleton -/** - * Created by mike on 03.12.2016. - */ @Singleton class NotificationStore @Inject constructor( private val aapsLogger: AAPSLogger, @@ -113,8 +109,8 @@ class NotificationStore @Inject constructor( private fun raiseSystemNotification(n: Notification) { val mgr = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val largeIcon = BitmapFactory.decodeResource(mainApp.resources, MainApp.getIcon()) - val smallIcon = MainApp.getNotificationIcon() + val largeIcon = resourceHelper.decodeResource(resourceHelper.getIcon()) + val smallIcon = resourceHelper.getNotificationIcon() val sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM) val notificationBuilder = NotificationCompat.Builder(mainApp, CHANNEL_ID) .setSmallIcon(smallIcon) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.java deleted file mode 100644 index 90837133e3..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.java +++ /dev/null @@ -1,63 +0,0 @@ -package info.nightscout.androidaps.plugins.general.persistentNotification; - -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; - -import androidx.annotation.Nullable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.utils.FabricPrivacy; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -/** - * Keeps AndroidAPS in foreground state, so it won't be terminated by Android nor get restricted by the background execution limits - */ -public class DummyService extends Service { - private static Logger log = LoggerFactory.getLogger(L.CORE); - private CompositeDisposable disposable = new CompositeDisposable(); - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onCreate() { - super.onCreate(); - // TODO: I guess this was moved here in order to adhere to the 5 seconds rule to call "startForeground" after a Service was called as Foreground service? - // As onCreate() is not called every time a service is started, copied to onStartCommand(). - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventAppExit.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - if (L.isEnabled(L.PUMP)) log.debug("EventAppExit received"); - stopSelf(); - }, FabricPrivacy::logException) - ); - } - - @Override - public void onDestroy() { - if (L.isEnabled(L.CORE)) log.debug("onDestroy"); - disposable.clear(); - super.onDestroy(); - stopForeground(true); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - super.onStartCommand(intent, flags, startId); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); - return START_STICKY; - } - -} 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 new file mode 100644 index 0000000000..4eeb61eabd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt @@ -0,0 +1,56 @@ +package info.nightscout.androidaps.plugins.general.persistentNotification + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import dagger.android.DaggerService +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.utils.FabricPrivacy +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +/** + * Keeps AndroidAPS in foreground state, so it won't be terminated by Android nor get restricted by the background execution limits + */ +class DummyService : DaggerService() { + + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var persistentNotificationPlugin: PersistentNotificationPlugin + + private val disposable = CompositeDisposable() + + override fun onBind(intent: Intent): IBinder? = null + + override fun onCreate() { + super.onCreate() + // TODO: I guess this was moved here in order to adhere to the 5 seconds rule to call "startForeground" after a Service was called as Foreground service? + // As onCreate() is not called every time a service is started, copied to onStartCommand(). + startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, persistentNotificationPlugin.getLastNotification()) + disposable.add(rxBus + .toObservable(EventAppExit::class.java) + .observeOn(Schedulers.io()) + .subscribe({ + aapsLogger.debug(LTag.CORE, "EventAppExit received") + stopSelf() + }) { FabricPrivacy.logException(it) } + ) + } + + override fun onDestroy() { + aapsLogger.debug(LTag.CORE, "onDestroy") + disposable.clear() + super.onDestroy() + stopForeground(true) + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + super.onStartCommand(intent, flags, startId) + startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, persistentNotificationPlugin.getLastNotification()) + return Service.START_STICKY + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.java deleted file mode 100644 index 651be3e65f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.java +++ /dev/null @@ -1,338 +0,0 @@ -package info.nightscout.androidaps.plugins.general.persistentNotification; - -import android.annotation.SuppressLint; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.Build; - -import androidx.core.app.NotificationCompat; -import androidx.core.app.RemoteInput; -import androidx.core.app.TaskStackBuilder; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainActivity; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.events.EventInitializationChanged; -import info.nightscout.androidaps.events.EventNewBasalProfile; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.events.EventRefreshOverview; -import info.nightscout.androidaps.events.EventTempBasalChange; -import info.nightscout.androidaps.events.EventTreatmentChange; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -/** - * Created by adrian on 23/12/16. - */ - -public class PersistentNotificationPlugin extends PluginBase { - - private CompositeDisposable disposable = new CompositeDisposable(); - - private static PersistentNotificationPlugin plugin; - private Notification notification; - - @Deprecated - public static PersistentNotificationPlugin getPlugin() { - if (plugin == null) plugin = new PersistentNotificationPlugin(); - return plugin; - } - - public static final String CHANNEL_ID = "AndroidAPS-Ongoing"; - - public static final int ONGOING_NOTIFICATION_ID = 4711; - - /// For Android Auto - /// Intents are not declared in manifest and not consumed, this is intentionally because actually we can't do anything with - private static final String PACKAGE = "info.nightscout"; - private static final String READ_ACTION = - "info.nightscout.androidaps.ACTION_MESSAGE_READ"; - private static final String REPLY_ACTION = - "info.nightscout.androidaps.ACTION_MESSAGE_REPLY"; - private static final String CONVERSATION_ID = "conversation_id"; - private static final String EXTRA_VOICE_REPLY = "extra_voice_reply"; - /// End Android Auto - - - private PersistentNotificationPlugin() { - super(new PluginDescription() - .mainType(PluginType.GENERAL) - .neverVisible(true) - .pluginName(R.string.ongoingnotificaction) - .enableByDefault(true) - .alwaysEnabled(true) - .showInList(false) - .description(R.string.description_persistent_notification) - ); - } - - @Override - protected void onStart() { - super.onStart(); - createNotificationChannel(); // make sure channels exist before triggering updates through the bus - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventRefreshOverview.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventExtendedBolusChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventTempBasalChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventTreatmentChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventInitializationChanged.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventNewBasalProfile.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventAutosensCalculationFinished.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> triggerNotificationUpdate(false), - FabricPrivacy::logException - )); - triggerNotificationUpdate(true); - } - - private void createNotificationChannel() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - - NotificationManager mNotificationManager = - (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); - @SuppressLint("WrongConstant") NotificationChannel channel = new NotificationChannel(CHANNEL_ID, - CHANNEL_ID, - NotificationManager.IMPORTANCE_HIGH); - mNotificationManager.createNotificationChannel(channel); - } - } - - @Override - protected void onStop() { - disposable.clear(); - MainApp.instance().stopService(new Intent(MainApp.instance(), DummyService.class)); - super.onStop(); - } - - private void triggerNotificationUpdate(boolean boot) { - updateNotification(boot); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - MainApp.instance().startForegroundService(new Intent(MainApp.instance(), DummyService.class)); - else - MainApp.instance().startService(new Intent(MainApp.instance(), DummyService.class)); - } - - private void updateNotification(boolean boot) { - String line1; - String line2 = null; - String line3 = null; - NotificationCompat.CarExtender.UnreadConversation.Builder unreadConversationBuilder = null; - - if (boot) { - line1 = MainApp.gs(R.string.loading); - } else if (ConfigBuilderPlugin.getPlugin().getActiveProfileInterface() != null && ProfileFunctions.getInstance().isProfileValid("Notification")) { - String line1_aa; - String units = ProfileFunctions.getSystemUnits(); - - - BgReading lastBG = DatabaseHelper.lastBg(); - GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); - - if (lastBG != null) { - line1 = line1_aa = lastBG.valueToUnitsToString(units); - if (glucoseStatus != null) { - line1 += " Δ" + deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) - + " avgΔ" + deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units); - line1_aa += " " + lastBG.directionToSymbol(); - } else { - line1 += " " + - MainApp.gs(R.string.old_data) + - " "; - line1_aa += line1 + "."; - } - } else { - line1 = line1_aa = MainApp.gs(R.string.missed_bg_readings); - } - - TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()); - if (activeTemp != null) { - line1 += " " + activeTemp.toStringShort(); - line1_aa += " " + activeTemp.toStringShort() + "."; - } - - //IOB - TreatmentsPlugin.getPlugin().updateTotalIOBTreatments(); - TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals(); - IobTotal bolusIob = TreatmentsPlugin.getPlugin().getLastCalculationTreatments().round(); - IobTotal basalIob = TreatmentsPlugin.getPlugin().getLastCalculationTempBasals().round(); - - - line2 = MainApp.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U " + MainApp.gs(R.string.cob) + ": " + IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "PersistentNotificationPlugin").generateCOBString(); - String line2_aa = MainApp.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U. " + MainApp.gs(R.string.cob) + ": " + IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() + "."; - - - line3 = DecimalFormatter.to2Decimal(ConfigBuilderPlugin.getPlugin().getActivePump().getBaseBasalRate()) + " U/h"; - String line3_aa = DecimalFormatter.to2Decimal(ConfigBuilderPlugin.getPlugin().getActivePump().getBaseBasalRate()) + " U/h."; - - - line3 += " - " + ProfileFunctions.getInstance().getProfileName(); - line3_aa += " - " + ProfileFunctions.getInstance().getProfileName() + "."; - - /// For Android Auto - Intent msgReadIntent = new Intent() - .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) - .setAction(READ_ACTION) - .putExtra(CONVERSATION_ID, ONGOING_NOTIFICATION_ID) - .setPackage(PACKAGE); - - PendingIntent msgReadPendingIntent = - PendingIntent.getBroadcast(MainApp.instance(), - ONGOING_NOTIFICATION_ID, - msgReadIntent, - PendingIntent.FLAG_UPDATE_CURRENT); - - Intent msgReplyIntent = new Intent() - .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) - .setAction(REPLY_ACTION) - .putExtra(CONVERSATION_ID, ONGOING_NOTIFICATION_ID) - .setPackage(PACKAGE); - - PendingIntent msgReplyPendingIntent = PendingIntent.getBroadcast( - MainApp.instance(), - ONGOING_NOTIFICATION_ID, - msgReplyIntent, - PendingIntent.FLAG_UPDATE_CURRENT); - - // Build a RemoteInput for receiving voice input from devices - RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY).build(); - - // Create the UnreadConversation - unreadConversationBuilder = - new NotificationCompat.CarExtender.UnreadConversation.Builder(line1_aa + "\n" + line2_aa) - .setLatestTimestamp(System.currentTimeMillis()) - .setReadPendingIntent(msgReadPendingIntent) - .setReplyAction(msgReplyPendingIntent, remoteInput); - - /// Add dot to produce a "more natural sounding result" - unreadConversationBuilder.addMessage(line3_aa); - /// End Android Auto - } else { - line1 = MainApp.gs(R.string.noprofileset); - } - - NotificationCompat.Builder builder = new NotificationCompat.Builder(MainApp.instance(), CHANNEL_ID); - builder.setOngoing(true); - builder.setOnlyAlertOnce(true); - builder.setCategory(NotificationCompat.CATEGORY_STATUS); - builder.setSmallIcon(MainApp.getNotificationIcon()); - Bitmap largeIcon = BitmapFactory.decodeResource(MainApp.instance().getResources(), MainApp.getIcon()); - builder.setLargeIcon(largeIcon); - if (line1 != null) builder.setContentTitle(line1); - if (line2 != null) builder.setContentText(line2); - if (line3 != null) builder.setSubText(line3); - /// Android Auto - if (unreadConversationBuilder != null) { - builder.extend(new NotificationCompat.CarExtender() - .setUnreadConversation(unreadConversationBuilder.build())); - } - /// End Android Auto - - - Intent resultIntent = new Intent(MainApp.instance(), MainActivity.class); - - TaskStackBuilder stackBuilder = TaskStackBuilder.create(MainApp.instance()); - stackBuilder.addParentStack(MainActivity.class); - stackBuilder.addNextIntent(resultIntent); - PendingIntent resultPendingIntent = - stackBuilder.getPendingIntent( - 0, - PendingIntent.FLAG_UPDATE_CURRENT - ); - builder.setContentIntent(resultPendingIntent); - NotificationManager mNotificationManager = - (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); - - android.app.Notification notification = builder.build(); - mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification); - this.notification = notification; - } - - private String deltastring(double deltaMGDL, double deltaMMOL, String units) { - String deltastring = ""; - if (deltaMGDL >= 0) { - deltastring += "+"; - } else { - deltastring += "-"; - - } - if (units.equals(Constants.MGDL)) { - deltastring += DecimalFormatter.to1Decimal(Math.abs(deltaMGDL)); - } else { - deltastring += DecimalFormatter.to1Decimal(Math.abs(deltaMMOL)); - } - return deltastring; - } - - /*** - * returns the current ongoing notification. - * - * If it does not exist, return a dummy notification. This should only happen if onStart() wasn't called. - */ - - public Notification getLastNotification() { - if (notification != null) return notification; - else { - throw new IllegalStateException("Notification is null"); - } - } -} 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 new file mode 100644 index 0000000000..ec5018f731 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt @@ -0,0 +1,246 @@ +package info.nightscout.androidaps.plugins.general.persistentNotification + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.RemoteInput +import androidx.core.app.TaskStackBuilder +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainActivity +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.DatabaseHelper +import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.resources.ResourceHelper +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class PersistentNotificationPlugin @Inject constructor() : PluginBase(PluginDescription() + .mainType(PluginType.GENERAL) + .neverVisible(true) + .pluginName(R.string.ongoingnotificaction) + .enableByDefault(true) + .alwaysEnabled(true) + .showInList(false) + .description(R.string.description_persistent_notification) +) { + + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var mainApp: MainApp + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin + @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin + + // For Android Auto + // Intents are not declared in manifest and not consumed, this is intentionally because actually we can't do anything with + private val PACKAGE = "info.nightscout" + private val READ_ACTION = "info.nightscout.androidaps.ACTION_MESSAGE_READ" + private val REPLY_ACTION = "info.nightscout.androidaps.ACTION_MESSAGE_REPLY" + private val CONVERSATION_ID = "conversation_id" + private val EXTRA_VOICE_REPLY = "extra_voice_reply" + // End Android auto + + private val disposable = CompositeDisposable() + private var notification: Notification? = null + + val CHANNEL_ID = "AndroidAPS-Ongoing" + val ONGOING_NOTIFICATION_ID = 4711 + + override fun onStart() { + super.onStart() + createNotificationChannel() // make sure channels exist before triggering updates through the bus + disposable.add(rxBus + .toObservable(EventRefreshOverview::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventRefreshOverview? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + disposable.add(rxBus + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventExtendedBolusChange? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + disposable.add(rxBus + .toObservable(EventTempBasalChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventTempBasalChange? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + disposable.add(rxBus + .toObservable(EventTreatmentChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventTreatmentChange? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + disposable.add(rxBus + .toObservable(EventInitializationChanged::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventInitializationChanged? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + disposable.add(rxBus + .toObservable(EventNewBasalProfile::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventNewBasalProfile? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + disposable.add(rxBus + .toObservable(EventAutosensCalculationFinished::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventAutosensCalculationFinished? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + disposable.add(rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ event: EventPreferenceChange? -> triggerNotificationUpdate(false) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }) + triggerNotificationUpdate(true) + } + + private fun createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val mNotificationManager = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val channel = NotificationChannel(CHANNEL_ID, CHANNEL_ID as CharSequence, NotificationManager.IMPORTANCE_HIGH) + mNotificationManager.createNotificationChannel(channel) + } + } + + override fun onStop() { + disposable.clear() + mainApp.stopService(Intent(mainApp, DummyService::class.java)) + super.onStop() + } + + private fun triggerNotificationUpdate(boot: Boolean) { + updateNotification(boot) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + mainApp.startForegroundService(Intent(mainApp, DummyService::class.java)) + else + mainApp.startService(Intent(mainApp, DummyService::class.java)) + } + + private fun updateNotification(boot: Boolean) { + val pump = configBuilderPlugin.activePump ?: return + var line1: String? + var line2: String? = null + var line3: String? = null + var unreadConversationBuilder: NotificationCompat.CarExtender.UnreadConversation.Builder? = null + if (boot) { + line1 = resourceHelper.gs(R.string.loading) + } else if (profileFunction.isProfileValid("Notification")) { + var line1_aa: String + val units = profileFunction.getUnits() + val lastBG = DatabaseHelper.lastBg() + val glucoseStatus = GlucoseStatus.getGlucoseStatusData() + if (lastBG != null) { + line1_aa = lastBG.valueToUnitsToString(units) + line1 = line1_aa + 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() + } else { + line1 += " " + + resourceHelper.gs(R.string.old_data) + + " " + line1_aa += "$line1." + } + } else { + line1_aa = resourceHelper.gs(R.string.missed_bg_readings) + line1 = line1_aa + } + val activeTemp = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()) + if (activeTemp != null) { + line1 += " " + activeTemp.toStringShort() + line1_aa += " " + activeTemp.toStringShort() + "." + } + //IOB + treatmentsPlugin.updateTotalIOBTreatments() + treatmentsPlugin.updateTotalIOBTempBasals() + val bolusIob = treatmentsPlugin.lastCalculationTreatments.round() + val basalIob = treatmentsPlugin.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() + "." + line3 = DecimalFormatter.to2Decimal(pump.baseBasalRate) + " U/h" + var line3_aa = DecimalFormatter.to2Decimal(pump.baseBasalRate) + " U/h." + line3 += " - " + profileFunction.getProfileName() + line3_aa += " - " + profileFunction.getProfileName() + "." + /// For Android Auto + val msgReadIntent = Intent() + .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) + .setAction(READ_ACTION) + .putExtra(CONVERSATION_ID, ONGOING_NOTIFICATION_ID) + .setPackage(PACKAGE) + val msgReadPendingIntent = PendingIntent.getBroadcast(mainApp, + ONGOING_NOTIFICATION_ID, + msgReadIntent, + PendingIntent.FLAG_UPDATE_CURRENT) + val msgReplyIntent = Intent() + .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) + .setAction(REPLY_ACTION) + .putExtra(CONVERSATION_ID, ONGOING_NOTIFICATION_ID) + .setPackage(PACKAGE) + val msgReplyPendingIntent = PendingIntent.getBroadcast( + mainApp, + ONGOING_NOTIFICATION_ID, + msgReplyIntent, + PendingIntent.FLAG_UPDATE_CURRENT) + // 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) + .setLatestTimestamp(System.currentTimeMillis()) + .setReadPendingIntent(msgReadPendingIntent) + .setReplyAction(msgReplyPendingIntent, remoteInput) + /// Add dot to produce a "more natural sounding result" + unreadConversationBuilder.addMessage(line3_aa) + /// End Android Auto + } else { + line1 = resourceHelper.gs(R.string.noprofileset) + } + val builder = NotificationCompat.Builder(mainApp, CHANNEL_ID) + builder.setOngoing(true) + builder.setOnlyAlertOnce(true) + builder.setCategory(NotificationCompat.CATEGORY_STATUS) + builder.setSmallIcon(resourceHelper.getNotificationIcon()) + builder.setLargeIcon(resourceHelper.decodeResource(resourceHelper.getIcon())) + if (line1 != null) builder.setContentTitle(line1) + if (line2 != null) builder.setContentText(line2) + if (line3 != null) builder.setSubText(line3) + /// Android Auto + if (unreadConversationBuilder != null) { + builder.extend(NotificationCompat.CarExtender() + .setUnreadConversation(unreadConversationBuilder.build())) + } + /// End Android Auto + val resultIntent = Intent(mainApp, MainActivity::class.java) + val stackBuilder = TaskStackBuilder.create(mainApp) + stackBuilder.addParentStack(MainActivity::class.java) + stackBuilder.addNextIntent(resultIntent) + val resultPendingIntent = stackBuilder.getPendingIntent( + 0, + PendingIntent.FLAG_UPDATE_CURRENT + ) + builder.setContentIntent(resultPendingIntent) + val mNotificationManager = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notification = builder.build() + mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification) + this.notification = notification + } + + fun getLastNotification(): Notification { + if (notification == null) + updateNotification(true) + return notification!! + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt index e1b9f94640..cbf87338b0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt @@ -117,7 +117,7 @@ class VirtualPumpPlugin @Inject constructor( disposable += rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(Schedulers.io()) - .subscribe({ event: EventPreferenceChange -> if (event.isChanged(R.string.key_virtualpump_type)) refreshConfiguration() }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) } + .subscribe({ event: EventPreferenceChange -> if (event.isChanged(R.string.key_virtualpump_type)) refreshConfiguration() }) { FabricPrivacy.logException(it) } refreshConfiguration() } diff --git a/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.java b/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.java deleted file mode 100644 index 2e9130f7db..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.java +++ /dev/null @@ -1,85 +0,0 @@ -package info.nightscout.androidaps.services; - -import android.app.Notification; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.res.AssetFileDescriptor; -import android.media.AudioManager; -import android.media.MediaPlayer; -import android.os.IBinder; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin; - -public class AlarmSoundService extends Service { - private static Logger log = LoggerFactory.getLogger(L.CORE); - - MediaPlayer player; - int resourceId = R.raw.error; - - public AlarmSoundService() { - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onCreate() { - super.onCreate(); - if (L.isEnabled(L.CORE)) - log.debug("onCreate"); - Notification notification = PersistentNotificationPlugin.getPlugin().getLastNotification(); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification); - } - - public int onStartCommand(Intent intent, int flags, int startId) { - Notification notification = PersistentNotificationPlugin.getPlugin().getLastNotification(); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification); - if (player != null && player.isPlaying()) - player.stop(); - if (L.isEnabled(L.CORE)) - log.debug("onStartCommand"); - if (intent != null && intent.hasExtra("soundid")) - resourceId = intent.getIntExtra("soundid", R.raw.error); - - player = new MediaPlayer(); - try { - AssetFileDescriptor afd = MainApp.sResources.openRawResourceFd(resourceId); - if (afd == null) - return START_STICKY; - player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); - afd.close(); - player.setLooping(true); // Set looping - AudioManager manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); - if (manager == null || !manager.isMusicActive()) { - player.setVolume(100, 100); - } - player.prepare(); - player.start(); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - return START_STICKY; - } - - @Override - public void onDestroy() { - if (player != null) { - player.stop(); - player.release(); - } - - if (L.isEnabled(L.CORE)) - log.debug("onDestroy"); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.kt b/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.kt new file mode 100644 index 0000000000..943f56a17b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.kt @@ -0,0 +1,64 @@ +package info.nightscout.androidaps.services + +import android.content.Context +import android.content.Intent +import android.media.AudioManager +import android.media.MediaPlayer +import android.os.IBinder +import dagger.android.DaggerService +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin +import info.nightscout.androidaps.utils.resources.ResourceHelper +import javax.inject.Inject + +class AlarmSoundService : DaggerService() { + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var persistentNotificationPlugin: PersistentNotificationPlugin + + private var player: MediaPlayer? = null + private var resourceId = R.raw.error + + override fun onBind(intent: Intent): IBinder? = null + + override fun onCreate() { + super.onCreate() + aapsLogger.debug(LTag.CORE, "onCreate") + val notification = persistentNotificationPlugin.getLastNotification() + startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification) + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + val notification = persistentNotificationPlugin.getLastNotification() + startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification) + + player?.let { if (it.isPlaying) it.stop() } + + aapsLogger.debug(LTag.CORE, "onStartCommand") + if (intent.hasExtra("soundid")) resourceId = intent.getIntExtra("soundid", R.raw.error) + player = MediaPlayer() + try { + val afd = resourceHelper.openRawResourceFd(resourceId) ?: return START_STICKY + player?.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length) + afd.close() + player?.isLooping = true // Set looping + val manager = getSystemService(Context.AUDIO_SERVICE) as AudioManager + if (!manager.isMusicActive) { + player?.setVolume(100f, 100f) + } + player?.prepare() + player?.start() + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + } + return START_STICKY + } + + override fun onDestroy() { + player?.stop() + player?.release() + aapsLogger.debug(LTag.CORE, "onDestroy") + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/services/LocationService.java b/app/src/main/java/info/nightscout/androidaps/services/LocationService.java deleted file mode 100644 index cd543db678..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/services/LocationService.java +++ /dev/null @@ -1,168 +0,0 @@ -package info.nightscout.androidaps.services; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.location.Location; -import android.location.LocationManager; -import android.os.Bundle; -import android.os.IBinder; - -import androidx.core.app.ActivityCompat; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventLocationChange; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.bus.RxBus; -import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.T; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -public class LocationService extends Service { - private static Logger log = LoggerFactory.getLogger(L.LOCATION); - private CompositeDisposable disposable = new CompositeDisposable(); - - private LocationManager mLocationManager = null; - private static final float LOCATION_DISTANCE = 10f; - - private static final long LOCATION_INTERVAL_ACTIVE = T.mins(5).msecs(); - private static final long LOCATION_INTERVAL_PASSIVE = T.mins(1).msecs(); // this doesn't cost more power - - private static Location mLastLocation; - - private class LocationListener implements android.location.LocationListener { - - LocationListener(String provider) { - if (L.isEnabled(L.LOCATION)) - log.debug("LocationListener " + provider); - mLastLocation = new Location(provider); - } - - @Override - public void onLocationChanged(Location location) { - if (L.isEnabled(L.LOCATION)) - log.debug("onLocationChanged: " + location); - mLastLocation.set(location); - RxBus.Companion.getINSTANCE().send(new EventLocationChange(location)); - } - - @Override - public void onProviderDisabled(String provider) { - if (L.isEnabled(L.LOCATION)) - log.debug("onProviderDisabled: " + provider); - } - - @Override - public void onProviderEnabled(String provider) { - if (L.isEnabled(L.LOCATION)) - log.debug("onProviderEnabled: " + provider); - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - if (L.isEnabled(L.LOCATION)) - log.debug("onStatusChanged: " + provider); - } - } - - LocationListener mLocationListener; - - @Override - public IBinder onBind(Intent arg0) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - super.onStartCommand(intent, flags, startId); - if (L.isEnabled(L.LOCATION)) - log.debug("onStartCommand"); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); - return START_STICKY; - } - - @Override - public void onCreate() { - super.onCreate(); - startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); - - if (L.isEnabled(L.LOCATION)) - log.debug("onCreate"); - - initializeLocationManager(); - - try { - if (SP.getString(R.string.key_location, "NONE").equals("NETWORK")) - mLocationManager.requestLocationUpdates( - LocationManager.NETWORK_PROVIDER, - LOCATION_INTERVAL_ACTIVE, - LOCATION_DISTANCE, - mLocationListener = new LocationListener(LocationManager.NETWORK_PROVIDER) - ); - if (SP.getString(R.string.key_location, "NONE").equals("GPS")) - mLocationManager.requestLocationUpdates( - LocationManager.GPS_PROVIDER, - LOCATION_INTERVAL_ACTIVE, - LOCATION_DISTANCE, - mLocationListener = new LocationListener(LocationManager.GPS_PROVIDER) - ); - if (SP.getString(R.string.key_location, "NONE").equals("PASSIVE")) - mLocationManager.requestLocationUpdates( - LocationManager.PASSIVE_PROVIDER, - LOCATION_INTERVAL_PASSIVE, - LOCATION_DISTANCE, - mLocationListener = new LocationListener(LocationManager.PASSIVE_PROVIDER) - ); - } catch (java.lang.SecurityException ex) { - log.error("fail to request location update, ignore", ex); - } catch (IllegalArgumentException ex) { - log.error("network provider does not exist, " + ex.getMessage()); - } - disposable.add(RxBus.Companion.getINSTANCE() - .toObservable(EventAppExit.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - if (L.isEnabled(L.CORE)) log.debug("EventAppExit received"); - stopSelf(); - }, FabricPrivacy::logException) - ); - } - - @Override - public void onDestroy() { - if (L.isEnabled(L.LOCATION)) - log.debug("onDestroy"); - super.onDestroy(); - if (mLocationManager != null) { - try { - if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - return; - } - mLocationManager.removeUpdates(mLocationListener); - } catch (Exception ex) { - log.error("fail to remove location listener, ignore", ex); - } - } - disposable.clear(); - } - - private void initializeLocationManager() { - if (L.isEnabled(L.LOCATION)) - log.debug("initializeLocationManager - Provider: " + SP.getString(R.string.key_location, "NONE")); - if (mLocationManager == null) { - mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE); - } - } - - public static Location getLastLocation() { - return mLastLocation; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/services/LocationService.kt b/app/src/main/java/info/nightscout/androidaps/services/LocationService.kt new file mode 100644 index 0000000000..f870bdb60e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/services/LocationService.kt @@ -0,0 +1,152 @@ +package info.nightscout.androidaps.services + +import android.Manifest +import android.app.Service +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.location.Location +import android.location.LocationManager +import android.os.Bundle +import android.os.IBinder +import androidx.core.app.ActivityCompat +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationServices +import com.google.android.gms.tasks.OnSuccessListener +import dagger.android.DaggerService +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.events.EventLocationChange +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.persistentNotification.PersistentNotificationPlugin +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +class LocationService : DaggerService() { + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var sp: SP + @Inject lateinit var persistentNotificationPlugin: PersistentNotificationPlugin + + private val disposable = CompositeDisposable() + private var locationManager: LocationManager? = null + private var locationListener: LocationListener? = null + + private val LOCATION_INTERVAL_ACTIVE = T.mins(5).msecs() + private val LOCATION_INTERVAL_PASSIVE = T.mins(1).msecs() // this doesn't cost more power + + companion object { + private const val LOCATION_DISTANCE = 10f + private var lastLocation: Location? = null + + @JvmStatic + @Deprecated("replace by injection") + fun getLastLocation(): Location? = lastLocation + } + + inner class LocationListener internal constructor(val provider: String) : android.location.LocationListener { + + init { + aapsLogger.debug(LTag.LOCATION, "LocationListener $provider") + } + + override fun onLocationChanged(location: Location) { + aapsLogger.debug(LTag.LOCATION, "onLocationChanged: $location") + lastLocation = location + rxBus.send(EventLocationChange(location)) + } + + override fun onProviderDisabled(provider: String) { + aapsLogger.debug(LTag.LOCATION, "onProviderDisabled: $provider") + } + + override fun onProviderEnabled(provider: String) { + aapsLogger.debug(LTag.LOCATION, "onProviderEnabled: $provider") + } + + override fun onStatusChanged(provider: String, status: Int, extras: Bundle) { + aapsLogger.debug(LTag.LOCATION, "onStatusChanged: $provider") + } + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + super.onStartCommand(intent, flags, startId) + startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, persistentNotificationPlugin.getLastNotification()) + return Service.START_STICKY + } + + override fun onCreate() { + super.onCreate() + startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, persistentNotificationPlugin.getLastNotification()) + + // Get last location once until we get regular update + LocationServices.getFusedLocationProviderClient(this).lastLocation.addOnSuccessListener { + lastLocation = it + } + + initializeLocationManager() + + try { + if (sp.getString(R.string.key_location, "NONE") == "NETWORK") locationManager?.requestLocationUpdates( + LocationManager.NETWORK_PROVIDER, + LOCATION_INTERVAL_ACTIVE, + LOCATION_DISTANCE, + LocationListener(LocationManager.NETWORK_PROVIDER).also { locationListener = it } + ) + if (sp.getString(R.string.key_location, "NONE") == "GPS") locationManager?.requestLocationUpdates( + LocationManager.GPS_PROVIDER, + LOCATION_INTERVAL_ACTIVE, + LOCATION_DISTANCE, + LocationListener(LocationManager.GPS_PROVIDER).also { locationListener = it } + ) + if (sp.getString(R.string.key_location, "NONE") == "PASSIVE") locationManager?.requestLocationUpdates( + LocationManager.PASSIVE_PROVIDER, + LOCATION_INTERVAL_PASSIVE, + LOCATION_DISTANCE, + LocationListener(LocationManager.PASSIVE_PROVIDER).also { locationListener = it } + ) + } catch (ex: SecurityException) { + aapsLogger.error(LTag.LOCATION, "fail to request location update, ignore", ex) + } catch (ex: IllegalArgumentException) { + aapsLogger.error(LTag.LOCATION, "network provider does not exist", ex) + } + disposable.add(rxBus + .toObservable(EventAppExit::class.java) + .observeOn(Schedulers.io()) + .subscribe({ + aapsLogger.debug(LTag.LOCATION, "EventAppExit received") + stopSelf() + }) { FabricPrivacy.logException(it) } + ) + } + + override fun onDestroy() { + super.onDestroy() + if (locationManager != null) { + try { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + return + } + locationManager!!.removeUpdates(locationListener) + } catch (ex: Exception) { + aapsLogger.error(LTag.LOCATION, "fail to remove location listener, ignore", ex) + } + } + disposable.clear() + } + + override fun onBind(intent: Intent?): IBinder? = null + + private fun initializeLocationManager() { + aapsLogger.debug(LTag.LOCATION, "initializeLocationManager - Provider: " + sp.getString(R.string.key_location, "NONE")) + if (locationManager == null) { + locationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java index 67ef9c19ae..00270384ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java @@ -32,11 +32,11 @@ public class SWRadioButton extends SWItem { } public String[] labels() { - return MainApp.sResources.getStringArray(labelsArray); + return MainApp.resources().getStringArray(labelsArray); } public String[] values() { - return MainApp.sResources.getStringArray(valuesArray); + return MainApp.resources().getStringArray(valuesArray); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt index 1e35b488cf..b69529cfc6 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt @@ -1,7 +1,11 @@ package info.nightscout.androidaps.utils.resources +import android.content.res.AssetFileDescriptor +import android.graphics.Bitmap +import androidx.annotation.BoolRes import androidx.annotation.ColorRes import androidx.annotation.PluralsRes +import androidx.annotation.RawRes import androidx.annotation.StringRes interface ResourceHelper { @@ -9,5 +13,12 @@ interface ResourceHelper { fun gs(@StringRes id: Int, vararg args: Any?): String fun gq(@PluralsRes id: Int, quantity: Int, vararg args: Any?): String fun gc(@ColorRes id: Int): Int + fun gb(@BoolRes id :Int) : Boolean fun gcs(@ColorRes id: Int): String + fun openRawResourceFd(@RawRes id : Int) : AssetFileDescriptor? + + fun getIcon() : Int + fun getNotificationIcon() : Int + fun decodeResource(id : Int) : Bitmap + fun dpToPx(dp: Int): Int } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt index 841c80cdca..35753c0333 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt @@ -1,11 +1,17 @@ package info.nightscout.androidaps.utils.resources import android.annotation.SuppressLint +import android.content.res.AssetFileDescriptor +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import androidx.annotation.BoolRes import androidx.annotation.ColorRes import androidx.annotation.PluralsRes import androidx.annotation.StringRes import androidx.core.content.ContextCompat +import info.nightscout.androidaps.Config import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R import javax.inject.Inject /** @@ -22,7 +28,36 @@ class ResourceHelperImplementation @Inject constructor(private val mainApp: Main override fun gc(@ColorRes id: Int): Int = ContextCompat.getColor(mainApp, id) + override fun gb(@BoolRes id :Int) : Boolean = mainApp.resources.getBoolean(id) + @SuppressLint("ResourceType") override fun gcs(@ColorRes id: Int): String = gs(id).replace("#ff", "#") + + override fun openRawResourceFd(id: Int): AssetFileDescriptor = + mainApp.resources.openRawResourceFd(id) + + override fun getIcon(): Int { + return when { + Config.NSCLIENT -> R.mipmap.ic_yellowowl + Config.PUMPCONTROL -> R.mipmap.ic_pumpcontrol + else -> R.mipmap.ic_launcher + } + } + + override fun getNotificationIcon(): Int { + return when { + Config.NSCLIENT -> R.drawable.ic_notif_nsclient + Config.PUMPCONTROL -> R.drawable.ic_notif_pumpcontrol + else -> R.drawable.ic_notif_aaps + } + } + + override fun decodeResource(id: Int): Bitmap = + BitmapFactory.decodeResource(mainApp.resources, id) + + override fun dpToPx(dp: Int): Int { + val scale = mainApp.resources.displayMetrics.density + return (dp * scale + 0.5f).toInt() + } } \ No newline at end of file diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocationTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocationTest.java index dcd98fb2e8..1c4f986b7a 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocationTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerLocationTest.java @@ -141,34 +141,6 @@ public class TriggerLocationTest { } - @Test - public void setLatitudeTest() { - TriggerLocation t = new TriggerLocation(); - t.setLatitude(212); - Assert.assertEquals(t.latitude.getValue(), 212, 0d); - } - - @Test - public void setLongitudeTest() { - TriggerLocation t = new TriggerLocation(); - t.setLongitude(213); - Assert.assertEquals(t.longitude.getValue(), 213, 0d); - } - - @Test - public void setdistanceTest() { - TriggerLocation t = new TriggerLocation(); - t.setdistance(2); - Assert.assertEquals(t.distance.getValue(), 2, 0d); - } - - @Test - public void setModeTest() { - TriggerLocation t = new TriggerLocation(); - t.setMode(InputLocationMode.Mode.INSIDE); - Assert.assertEquals(t.modeSelected.getValue(), InputLocationMode.Mode.INSIDE); - } - @Test public void lastRunTest() { TriggerLocation t = new TriggerLocation();