PersistenNotificationPlugin, services
This commit is contained in:
parent
45afd5ec34
commit
4dbf199685
22 changed files with 593 additions and 739 deletions
|
@ -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'
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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>()
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)";
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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!!
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue