PersistenNotificationPlugin, services

This commit is contained in:
Milos Kozak 2020-01-02 23:25:29 +01:00
parent 45afd5ec34
commit 4dbf199685
22 changed files with 593 additions and 739 deletions

View file

@ -230,6 +230,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.android.gms:play-services-wearable:17.0.0' 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-core:17.2.1'
implementation 'com.google.firebase:firebase-auth:19.2.0' implementation 'com.google.firebase:firebase-auth:19.2.0'
implementation 'com.google.firebase:firebase-database:19.2.0' implementation 'com.google.firebase:firebase-database:19.2.0'

View file

@ -307,7 +307,7 @@ public class MainActivity extends NoSplashAppCompatActivity {
case R.id.nav_about: case R.id.nav_about:
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(resourceHelper.gs(R.string.app_name) + " " + BuildConfig.VERSION); builder.setTitle(resourceHelper.gs(R.string.app_name) + " " + BuildConfig.VERSION);
builder.setIcon(MainApp.getIcon()); builder.setIcon(resourceHelper.getIcon());
String message = "Build: " + BuildConfig.BUILDVERSION + "\n"; String message = "Build: " + BuildConfig.BUILDVERSION + "\n";
message += "Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + "\n"; message += "Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + "\n";
message += resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.getNightscoutVersionName(); message += resourceHelper.gs(R.string.configbuilder_nightscoutversion_label) + " " + nsSettingsStatus.getNightscoutVersionName();

View file

@ -108,7 +108,7 @@ public class MainApp extends DaggerApplication {
static Logger log = LoggerFactory.getLogger(L.CORE); static Logger log = LoggerFactory.getLogger(L.CORE);
static MainApp sInstance; static MainApp sInstance;
public static Resources sResources; private static Resources sResources;
static FirebaseAnalytics mFirebaseAnalytics; static FirebaseAnalytics mFirebaseAnalytics;
@ -147,6 +147,7 @@ public class MainApp extends DaggerApplication {
@Inject OpenAPSAMAPlugin openAPSAMAPlugin; @Inject OpenAPSAMAPlugin openAPSAMAPlugin;
@Inject OpenAPSSMBPlugin openAPSSMBPlugin; @Inject OpenAPSSMBPlugin openAPSSMBPlugin;
@Inject OverviewPlugin overviewPlugin; @Inject OverviewPlugin overviewPlugin;
@Inject PersistentNotificationPlugin persistentNotificationPlugin;
@Inject RandomBgPlugin randomBgPlugin; @Inject RandomBgPlugin randomBgPlugin;
@Inject DexcomPlugin dexcomPlugin; @Inject DexcomPlugin dexcomPlugin;
@Inject EversensePlugin eversensePlugin; @Inject EversensePlugin eversensePlugin;
@ -263,7 +264,7 @@ public class MainApp extends DaggerApplication {
pluginsList.add(wearPlugin); pluginsList.add(wearPlugin);
pluginsList.add(statusLinePlugin); pluginsList.add(statusLinePlugin);
pluginsList.add(PersistentNotificationPlugin.getPlugin()); pluginsList.add(persistentNotificationPlugin);
pluginsList.add(NSClientPlugin.getPlugin()); pluginsList.add(NSClientPlugin.getPlugin());
// if (engineeringMode) pluginsList.add(tidepoolPlugin); // if (engineeringMode) pluginsList.add(tidepoolPlugin);
pluginsList.add(maintenancePlugin); pluginsList.add(maintenancePlugin);
@ -357,6 +358,11 @@ public class MainApp extends DaggerApplication {
return ContextCompat.getColor(instance(), id); return ContextCompat.getColor(instance(), id);
} }
@Deprecated
public static Resources resources() {
return sResources;
}
@Deprecated @Deprecated
public static MainApp instance() { public static MainApp instance() {
return sInstance; return sInstance;
@ -442,24 +448,6 @@ public class MainApp extends DaggerApplication {
return devBranch; 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 @Override
public void onTerminate() { public void onTerminate() {
@ -472,6 +460,7 @@ public class MainApp extends DaggerApplication {
super.onTerminate(); super.onTerminate();
} }
@Deprecated
public static int dpToPx(int dp) { public static int dpToPx(int dp) {
float scale = sResources.getDisplayMetrics().density; float scale = sResources.getDisplayMetrics().density;
return (int) (dp * scale + 0.5f); return (int) (dp * scale + 0.5f);

View file

@ -260,7 +260,7 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval {
@Override @Override
public float getSize() { public float getSize() {
boolean isTablet = MainApp.sResources.getBoolean(R.bool.isTablet); boolean isTablet = MainApp.resources().getBoolean(R.bool.isTablet);
return isTablet ? 12 : 10; return isTablet ? 12 : 10;
} }

View file

@ -3,12 +3,18 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService 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.DataService
import info.nightscout.androidaps.services.LocationService
@Module @Module
@Suppress("unused") @Suppress("unused")
abstract class ServicesModule { abstract class ServicesModule {
@ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService
@ContributesAndroidInjector abstract fun contributesDataService(): DataService @ContributesAndroidInjector abstract fun contributesDataService(): DataService
@ContributesAndroidInjector abstract fun contributesDummyService(): DummyService
@ContributesAndroidInjector abstract fun contributesLocationService(): LocationService
@ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService @ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService
} }

View file

@ -19,7 +19,6 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog 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) { private fun addImage(@DrawableRes res: Int, context: Context, layout: LinearLayout) {
val iv = ImageView(context) val iv = ImageView(context)
iv.setImageResource(res) 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) layout.addView(iv)
} }
@ -148,8 +147,8 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
// arrow icon // arrow icon
val iv = ImageView(holder.context) val iv = ImageView(holder.context)
iv.setImageResource(R.drawable.ic_arrow_forward_white_24dp) iv.setImageResource(R.drawable.ic_arrow_forward_white_24dp)
iv.layoutParams = LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)) iv.layoutParams = LinearLayout.LayoutParams(resourceHelper.dpToPx(24), resourceHelper.dpToPx(24))
iv.setPadding(MainApp.dpToPx(4), 0, MainApp.dpToPx(4), 0) iv.setPadding(resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4), 0)
holder.iconLayout.addView(iv) holder.iconLayout.addView(iv)
// action icons // action icons
val actionIcons = HashSet<Int>() val actionIcons = HashSet<Int>()

View file

@ -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) { TriggerLocation lastRun(long lastRun) {
this.lastRun = lastRun; this.lastRun = lastRun;
return this; return this;
} }
TriggerLocation setMode(InputLocationMode.Mode value) {
modeSelected.setValue(value);
return this;
}
@Override @Override
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) { public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
new LayoutBuilder() new LayoutBuilder()
@ -202,6 +182,7 @@ public class TriggerLocation extends Trigger {
return OUTSIDE; return OUTSIDE;
} }
// for mocking only TODO remove
static Location getCurrentLocation(){ static Location getCurrentLocation(){
return LocationService.getLastLocation(); return LocationService.getLastLocation();
} }

View file

@ -237,7 +237,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList
View view; 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); view = inflater.inflate(R.layout.overview_fragment_nsclient_tablet, container, false);
} else if (Config.NSCLIENT) { } else if (Config.NSCLIENT) {
view = inflater.inflate(R.layout.overview_fragment_nsclient, container, false); 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"; + resourceHelper.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U\n";
OKDialog.show(getActivity(), resourceHelper.gs(R.string.iob), iobtext1); 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 (" String iobtext = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U ("
+ resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " + resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U "
+ resourceHelper.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U)"; + resourceHelper.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U)";

View file

@ -5,7 +5,6 @@ import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.BitmapFactory
import android.media.AudioManager import android.media.AudioManager
import android.media.RingtoneManager import android.media.RingtoneManager
import android.os.Build import android.os.Build
@ -31,9 +30,6 @@ import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
/**
* Created by mike on 03.12.2016.
*/
@Singleton @Singleton
class NotificationStore @Inject constructor( class NotificationStore @Inject constructor(
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
@ -113,8 +109,8 @@ class NotificationStore @Inject constructor(
private fun raiseSystemNotification(n: Notification) { private fun raiseSystemNotification(n: Notification) {
val mgr = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val mgr = mainApp.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val largeIcon = BitmapFactory.decodeResource(mainApp.resources, MainApp.getIcon()) val largeIcon = resourceHelper.decodeResource(resourceHelper.getIcon())
val smallIcon = MainApp.getNotificationIcon() val smallIcon = resourceHelper.getNotificationIcon()
val sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM) val sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
val notificationBuilder = NotificationCompat.Builder(mainApp, CHANNEL_ID) val notificationBuilder = NotificationCompat.Builder(mainApp, CHANNEL_ID)
.setSmallIcon(smallIcon) .setSmallIcon(smallIcon)

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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");
}
}
}

View file

@ -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!!
}
}

View file

@ -117,7 +117,7 @@ class VirtualPumpPlugin @Inject constructor(
disposable += rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(Schedulers.io()) .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() refreshConfiguration()
} }

View file

@ -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");
}
}

View file

@ -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")
}
}

View file

@ -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;
}
}

View file

@ -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
}
}
}

View file

@ -32,11 +32,11 @@ public class SWRadioButton extends SWItem {
} }
public String[] labels() { public String[] labels() {
return MainApp.sResources.getStringArray(labelsArray); return MainApp.resources().getStringArray(labelsArray);
} }
public String[] values() { public String[] values() {
return MainApp.sResources.getStringArray(valuesArray); return MainApp.resources().getStringArray(valuesArray);
} }
@Override @Override

View file

@ -1,7 +1,11 @@
package info.nightscout.androidaps.utils.resources 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.ColorRes
import androidx.annotation.PluralsRes import androidx.annotation.PluralsRes
import androidx.annotation.RawRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
interface ResourceHelper { interface ResourceHelper {
@ -9,5 +13,12 @@ interface ResourceHelper {
fun gs(@StringRes id: Int, vararg args: Any?): String fun gs(@StringRes id: Int, vararg args: Any?): String
fun gq(@PluralsRes id: Int, quantity: Int, vararg args: Any?): String fun gq(@PluralsRes id: Int, quantity: Int, vararg args: Any?): String
fun gc(@ColorRes id: Int): Int fun gc(@ColorRes id: Int): Int
fun gb(@BoolRes id :Int) : Boolean
fun gcs(@ColorRes id: Int): String 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
} }

View file

@ -1,11 +1,17 @@
package info.nightscout.androidaps.utils.resources package info.nightscout.androidaps.utils.resources
import android.annotation.SuppressLint 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.ColorRes
import androidx.annotation.PluralsRes import androidx.annotation.PluralsRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import javax.inject.Inject 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 gc(@ColorRes id: Int): Int = ContextCompat.getColor(mainApp, id)
override fun gb(@BoolRes id :Int) : Boolean = mainApp.resources.getBoolean(id)
@SuppressLint("ResourceType") @SuppressLint("ResourceType")
override fun gcs(@ColorRes id: Int): String = override fun gcs(@ColorRes id: Int): String =
gs(id).replace("#ff", "#") 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()
}
} }

View file

@ -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 @Test
public void lastRunTest() { public void lastRunTest() {
TriggerLocation t = new TriggerLocation(); TriggerLocation t = new TriggerLocation();