Merge pull request #3 from MilosKozak/2186

Kotlin refactor
This commit is contained in:
Fabrizio Casellato 2019-11-22 11:13:14 +01:00 committed by GitHub
commit 32f4de2c1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1299 additions and 1393 deletions

View file

@ -204,7 +204,7 @@ public class MainApp extends Application {
pluginsList.add(SourcePoctechPlugin.getPlugin()); pluginsList.add(SourcePoctechPlugin.getPlugin());
pluginsList.add(SourceTomatoPlugin.getPlugin()); pluginsList.add(SourceTomatoPlugin.getPlugin());
pluginsList.add(SourceEversensePlugin.getPlugin()); pluginsList.add(SourceEversensePlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.getPlugin()); if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.INSTANCE);
pluginsList.add(FoodPlugin.getPlugin()); pluginsList.add(FoodPlugin.getPlugin());
pluginsList.add(WearPlugin.initPlugin(this)); pluginsList.add(WearPlugin.initPlugin(this));

View file

@ -184,7 +184,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL); addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL); addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResource(R.xml.pref_others); addPreferencesFromResource(R.xml.pref_others);

View file

@ -36,7 +36,7 @@ public class ActionSendSMS extends Action {
@Override @Override
public void doAction(Callback callback) { public void doAction(Callback callback) {
boolean result = SmsCommunicatorPlugin.getPlugin().sendNotificationToAllNumbers(text.getValue()); boolean result = SmsCommunicatorPlugin.INSTANCE.sendNotificationToAllNumbers(text.getValue());
if (callback != null) if (callback != null)
callback.result(new PumpEnactResult().success(result).comment(result ? R.string.ok : R.string.danar_error)).run(); callback.result(new PumpEnactResult().success(result).comment(result ? R.string.ok : R.string.danar_error)).run();

View file

@ -8,7 +8,7 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
class AuthRequest { public class AuthRequest {
private static Logger log = LoggerFactory.getLogger(L.SMS); private static Logger log = LoggerFactory.getLogger(L.SMS);
Sms requester; Sms requester;

View file

@ -5,7 +5,7 @@ import android.telephony.SmsMessage;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
class Sms { public class Sms {
String phoneNumber; String phoneNumber;
String text; String text;
long date; long date;

View file

@ -4,6 +4,7 @@ abstract class SmsAction implements Runnable {
Double aDouble; Double aDouble;
Integer anInteger; Integer anInteger;
Integer secondInteger; Integer secondInteger;
Long secondLong;
String aString; String aString;
SmsAction() {} SmsAction() {}
@ -30,4 +31,8 @@ abstract class SmsAction implements Runnable {
this.anInteger = anInteger; this.anInteger = anInteger;
this.secondInteger = secondInteger; this.secondInteger = secondInteger;
} }
SmsAction(Integer anInteger, Long secondLong) {
this.anInteger = anInteger;
this.secondLong = secondLong;
}
} }

View file

@ -1,83 +0,0 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import java.util.Collections;
import java.util.Comparator;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
public class SmsCommunicatorFragment extends Fragment {
private CompositeDisposable disposable = new CompositeDisposable();
TextView logView;
public SmsCommunicatorFragment() {
super();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.smscommunicator_fragment, container, false);
logView = (TextView) view.findViewById(R.id.smscommunicator_log);
return view;
}
@Override
public synchronized void onResume() {
super.onResume();
disposable.add(RxBus.INSTANCE
.toObservable(EventSmsCommunicatorUpdateGui.class)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> updateGui(), FabricPrivacy::logException)
);
updateGui();
}
@Override
public synchronized void onPause() {
super.onPause();
disposable.clear();
}
protected void updateGui() {
class CustomComparator implements Comparator<Sms> {
public int compare(Sms object1, Sms object2) {
return (int) (object1.date - object2.date);
}
}
Collections.sort(SmsCommunicatorPlugin.getPlugin().messages, new CustomComparator());
int messagesToShow = 40;
int start = Math.max(0, SmsCommunicatorPlugin.getPlugin().messages.size() - messagesToShow);
String logText = "";
for (int x = start; x < SmsCommunicatorPlugin.getPlugin().messages.size(); x++) {
Sms sms = SmsCommunicatorPlugin.getPlugin().messages.get(x);
if (sms.ignored) {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + "" + sms.phoneNumber + " <b>" + sms.text + "</b><br>";
} else if (sms.received) {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + (sms.processed ? "" : "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>";
} else if (sms.sent) {
logText += DateUtil.timeString(sms.date) + " &gt;&gt;&gt; " + (sms.processed ? "" : "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>";
}
}
logView.setText(Html.fromHtml(logText));
}
}

View file

@ -0,0 +1,70 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus.toObservable
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.smscommunicator_fragment.*
import java.util.*
import kotlin.math.max
class SmsCommunicatorFragment : Fragment() {
private val disposable = CompositeDisposable()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.smscommunicator_fragment, container, false)
}
@Synchronized
override fun onResume() {
super.onResume()
disposable.add(toObservable(EventSmsCommunicatorUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateGui() }) { FabricPrivacy.logException(it) }
)
updateGui()
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
fun updateGui() {
class CustomComparator : Comparator<Sms> {
override fun compare(object1: Sms, object2: Sms): Int {
return (object1.date - object2.date).toInt()
}
}
Collections.sort(SmsCommunicatorPlugin.messages, CustomComparator())
val messagesToShow = 40
val start = max(0, SmsCommunicatorPlugin.messages.size - messagesToShow)
var logText = ""
for (x in start until SmsCommunicatorPlugin.messages.size) {
val sms = SmsCommunicatorPlugin.messages[x]
when {
sms.ignored -> {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + "" + sms.phoneNumber + " <b>" + sms.text + "</b><br>"
}
sms.received -> {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + (if (sms.processed) "" else "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>"
}
sms.sent -> {
logText += DateUtil.timeString(sms.date) + " &gt;&gt;&gt; " + (if (sms.processed) "" else "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>"
}
}
}
smscommunicator_log?.text = HtmlHelper.fromHtml(logText)
}
}

View file

@ -0,0 +1,864 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator
import android.content.Intent
import android.preference.EditTextPreference
import android.preference.Preference
import android.preference.Preference.OnPreferenceChangeListener
import android.preference.PreferenceFragment
import android.telephony.SmsManager
import android.telephony.SmsMessage
import android.text.TextUtils
import com.andreabaccega.widget.ValidatingEditTextPreference
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.DatabaseHelper
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBus.send
import info.nightscout.androidaps.plugins.bus.RxBus.toObservable
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import java.text.Normalizer
import java.util.*
/**
* Created by mike on 05.08.2016.
*/
object SmsCommunicatorPlugin : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(SmsCommunicatorFragment::class.java.name)
.pluginName(R.string.smscommunicator)
.shortName(R.string.smscommunicator_shortname)
.preferencesId(R.xml.pref_smscommunicator)
.description(R.string.description_sms_communicator)
) {
private val log = LoggerFactory.getLogger(L.SMS)
private val disposable = CompositeDisposable()
var allowedNumbers: MutableList<String> = ArrayList()
var messageToConfirm: AuthRequest? = null
var lastRemoteBolusTime: Long = 0
var messages = ArrayList<Sms>()
init {
processSettings(null)
}
override fun onStart() {
super.onStart()
disposable.add(toObservable(EventPreferenceChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventPreferenceChange? -> processSettings(event) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }
)
}
override fun onStop() {
disposable.clear()
super.onStop()
}
override fun preprocessPreferences(preferenceFragment: PreferenceFragment) {
super.preprocessPreferences(preferenceFragment)
val distance = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)) as ValidatingEditTextPreference
val allowedNumbers = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)) as EditTextPreference
if (distance != null && allowedNumbers != null) {
if (!areMoreNumbers(allowedNumbers.text)) {
distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
+ ".\n"
+ MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat))
distance.isEnabled = false
} else {
distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
distance.isEnabled = true
}
allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { preference: Preference?, newValue: Any ->
if (!areMoreNumbers(newValue as String)) {
distance.text = (Constants.remoteBolusMinDistance / (60 * 1000L)).toString()
distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
+ ".\n"
+ MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat))
distance.isEnabled = false
} else {
distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
distance.isEnabled = true
}
true
}
}
}
override fun updatePreferenceSummary(pref: Preference) {
super.updatePreferenceSummary(pref)
if (pref is EditTextPreference) {
val editTextPref = pref
if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.text == null || TextUtils.isEmpty(editTextPref.text.trim { it <= ' ' }))) {
pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary))
}
}
}
private fun processSettings(ev: EventPreferenceChange?) {
if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) {
val settings = SP.getString(R.string.key_smscommunicator_allowednumbers, "")
allowedNumbers.clear()
val substrings = settings.split(";").toTypedArray()
for (number in substrings) {
val cleaned = number.replace("\\s+".toRegex(), "")
allowedNumbers.add(cleaned)
log.debug("Found allowed number: $cleaned")
}
}
}
fun isCommand(command: String, number: String): Boolean {
when (command.toUpperCase(Locale.getDefault())) {
"BG", "LOOP", "TREATMENTS", "NSCLIENT", "PUMP", "BASAL", "BOLUS", "EXTENDED", "CAL", "PROFILE", "TARGET", "SMS", "CARBS" -> return true
}
return messageToConfirm?.requester?.phoneNumber == number
}
fun isAllowedNumber(number: String): Boolean {
for (num in allowedNumbers) {
if (num == number) return true
}
return false
}
fun handleNewData(intent: Intent) {
val bundle = intent.extras ?: return
val pdus = bundle["pdus"] as Array<*>
for (pdu in pdus) {
val message = SmsMessage.createFromPdu(pdu as ByteArray)
processSms(Sms(message))
}
}
fun processSms(receivedSms: Sms) {
if (!isEnabled(PluginType.GENERAL)) {
log.debug("Ignoring SMS. Plugin disabled.")
return
}
if (!isAllowedNumber(receivedSms.phoneNumber)) {
log.debug("Ignoring SMS from: " + receivedSms.phoneNumber + ". Sender not allowed")
receivedSms.ignored = true
messages.add(receivedSms)
send(EventSmsCommunicatorUpdateGui())
return
}
val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return
messages.add(receivedSms)
log.debug(receivedSms.toString())
val splitted = receivedSms.text.split(Regex("\\s+")).toTypedArray()
val remoteCommandsAllowed = SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)
if (splitted.isNotEmpty() && isCommand(splitted[0].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) {
when (splitted[0].toUpperCase(Locale.getDefault())) {
"BG" ->
if (splitted.size == 1) processBG(receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"LOOP" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processLOOP(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"TREATMENTS" ->
if (splitted.size == 2) processTREATMENTS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"NSCLIENT" ->
if (splitted.size == 2) processNSCLIENT(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"PUMP" ->
if (splitted.size == 1) processPUMP(receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"PROFILE" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processPROFILE(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"BASAL" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processBASAL(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"EXTENDED" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processEXTENDED(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"BOLUS" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 && DateUtil.now() - lastRemoteBolusTime < Constants.remoteBolusMinDistance) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed))
else if (splitted.size == 2 && pump.isSuspended) sendSMS(Sms(receivedSms.phoneNumber, R.string.pumpsuspended))
else if (splitted.size == 2 || splitted.size == 3) processBOLUS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"CARBS" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processCARBS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"CAL" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2) processCAL(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"TARGET" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2) processTARGET(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"SMS" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2) processSMS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else ->
if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) {
messageToConfirm?.action(splitted[0])
messageToConfirm = null
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand))
}
}
send(EventSmsCommunicatorUpdateGui())
}
private fun processBG(receivedSms: Sms) {
val actualBG = DatabaseHelper.actualBg()
val lastBG = DatabaseHelper.lastBg()
var reply = ""
val units = ProfileFunctions.getInstance().profileUnits
if (actualBG != null) {
reply = MainApp.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", "
} else if (lastBG != null) {
val agoMsec = System.currentTimeMillis() - lastBG.date
val agoMin = (agoMsec / 60.0 / 1000.0).toInt()
reply = MainApp.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(MainApp.gs(R.string.sms_minago), agoMin) + ", "
}
val glucoseStatus = GlucoseStatus.getGlucoseStatusData()
if (glucoseStatus != null) reply += MainApp.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", "
TreatmentsPlugin.getPlugin().updateTotalIOBTreatments()
val bolusIob = TreatmentsPlugin.getPlugin().lastCalculationTreatments.round()
TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals()
val basalIob = TreatmentsPlugin.getPlugin().lastCalculationTempBasals.round()
val cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB")
reply += (MainApp.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U ("
+ MainApp.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U "
+ MainApp.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), "
+ MainApp.gs(R.string.cob) + ": " + cobInfo.generateCOBString())
sendSMS(Sms(receivedSms.phoneNumber, reply))
receivedSms.processed = true
}
private fun processLOOP(splitted: Array<String>, receivedSms: Sms) {
when (splitted[1].toUpperCase(Locale.getDefault())) {
"DISABLE", "STOP" -> {
val loopPlugin = LoopPlugin.getPlugin()
if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
send(EventRefreshOverview("SMS_LOOP_STOP"))
val replyText = MainApp.gs(R.string.smscommunicator_loophasbeendisabled) + " " +
MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
})
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled))
receivedSms.processed = true
}
"ENABLE", "START" -> {
val loopPlugin = LoopPlugin.getPlugin()
if (!loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled))
send(EventRefreshOverview("SMS_LOOP_START"))
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled))
receivedSms.processed = true
}
"STATUS" -> {
val loopPlugin = LoopPlugin.getPlugin()
val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) {
if (loopPlugin.isSuspended()) String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend())
else MainApp.gs(R.string.smscommunicator_loopisenabled)
} else
MainApp.gs(R.string.smscommunicator_loopisdisabled)
sendSMS(Sms(receivedSms.phoneNumber, reply))
receivedSms.processed = true
}
"RESUME" -> {
LoopPlugin.getPlugin().suspendTo(0)
send(EventRefreshOverview("SMS_LOOP_RESUME"))
NSUpload.uploadOpenAPSOffline(0.0)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopresumed))
}
"SUSPEND" -> {
var duration = 0
if (splitted.size == 3) duration = SafeParse.stringToInt(splitted[2])
duration = Math.max(0, duration)
duration = Math.min(180, duration)
if (duration == 0) {
receivedSms.processed = true
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_wrongduration))
return
} else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (result.success) {
LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger * 60L * 1000)
NSUpload.uploadOpenAPSOffline(anInteger * 60.toDouble())
send(EventRefreshOverview("SMS_LOOP_SUSPENDED"))
val replyText = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " +
MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
else -> sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
}
private fun processTREATMENTS(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "REFRESH") {
TreatmentsPlugin.getPlugin().service.resetTreatments()
send(EventNSClientRestart())
sendSMS(Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT"))
receivedSms.processed = true
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processNSCLIENT(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "RESTART") {
send(EventNSClientRestart())
sendSMS(Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT"))
receivedSms.processed = true
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processPUMP(receivedSms: Sms) {
ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() {
override fun run() {
val pump = ConfigBuilderPlugin.getPlugin().activePump
if (result.success) {
if (pump != null) {
val reply = pump.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, reply))
}
} else {
val reply = MainApp.gs(R.string.readstatusfailed)
sendSMS(Sms(receivedSms.phoneNumber, reply))
}
}
})
receivedSms.processed = true
}
private fun processPROFILE(splitted: Array<String>, receivedSms: Sms) { // load profiles
val anInterface = ConfigBuilderPlugin.getPlugin().activeProfileInterface
if (anInterface == null) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured))
receivedSms.processed = true
return
}
val store = anInterface.profile
if (store == null) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured))
receivedSms.processed = true
return
}
val list = store.profileList
if (splitted[1].toUpperCase(Locale.getDefault()) == "STATUS") {
sendSMS(Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().profileName))
} else if (splitted[1].toUpperCase(Locale.getDefault()) == "LIST") {
if (list.isEmpty()) sendSMS(Sms(receivedSms.phoneNumber, R.string.invalidprofile))
else {
var reply = ""
for (i in list.indices) {
if (i > 0) reply += "\n"
reply += (i + 1).toString() + ". "
reply += list[i]
}
sendSMS(Sms(receivedSms.phoneNumber, reply))
}
} else {
val pindex = SafeParse.stringToInt(splitted[1])
var percentage = 100
if (splitted.size > 2) percentage = SafeParse.stringToInt(splitted[2])
if (pindex > list.size) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (percentage == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (pindex == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
val profile = store.getSpecificProfile(list[pindex - 1] as String)
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile))
else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list[pindex - 1], percentage, passCode)
receivedSms.processed = true
val finalPercentage = percentage
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(list[pindex - 1] as String, finalPercentage) {
override fun run() {
ProfileFunctions.doProfileSwitch(store, list[pindex - 1] as String, 0, finalPercentage, 0)
sendSMS(Sms(receivedSms.phoneNumber, R.string.profileswitchcreated))
}
})
}
}
}
receivedSms.processed = true
}
private fun processBASAL(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_basalstopreplywithcode), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (result.success) {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcanceled)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
} else if (splitted[1].endsWith("%")) {
var tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(splitted[1], "%"))
var duration = 30
if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2])
val profile = ProfileFunctions.getInstance().profile
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile))
else if (tempBasalPct == 0 && splitted[1] != "0%") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value()
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(anInteger, secondInteger, true, profile, object : Callback() {
override fun run() {
if (result.success) {
var replyText: String
replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
} else {
var tempBasal = SafeParse.stringToDouble(splitted[1])
var duration = 30
if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2])
val profile = ProfileFunctions.getInstance().profile
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile))
else if (tempBasal == 0.0 && splitted[1] != "0") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(Constraint(tempBasal), profile).value()
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(aDouble, secondInteger, true, profile, object : Callback() {
override fun run() {
if (result.success) {
var replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration)
else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
}
private fun processEXTENDED(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.cancelExtended(object : Callback() {
override fun run() {
if (result.success) {
var replyText = MainApp.gs(R.string.smscommunicator_extendedcanceled)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_extendedcancelfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
} else if (splitted.size != 3) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
} else {
var extended = SafeParse.stringToDouble(splitted[1])
val duration = SafeParse.stringToInt(splitted[2])
extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(Constraint(extended)).value()
if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(extended, duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.extendedBolus(aDouble, secondInteger, object : Callback() {
override fun run() {
if (result.success) {
var replyText = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_extendedfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
}
private fun processBOLUS(splitted: Array<String>, receivedSms: Sms) {
var bolus = SafeParse.stringToDouble(splitted[1])
val isMeal = splitted.size > 2 && splitted[2].equals("MEAL", ignoreCase = true)
bolus = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(bolus)).value()
if (splitted.size == 3 && !isMeal) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
} else if (bolus > 0.0) {
val passCode = generatePasscode()
val reply = if (isMeal)
String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode)
else
String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(bolus) {
override fun run() {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = aDouble
detailedBolusInfo.source = Source.USER
ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
val resultSuccess = result.success
val resultBolusDelivered = result.bolusDelivered
ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() {
override fun run() {
if (resultSuccess) {
var replyText = if (isMeal)
String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered)
else
String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
lastRemoteBolusTime = DateUtil.now()
if (isMeal) {
ProfileFunctions.getInstance().profile?.let { currentProfile ->
var eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration)
eatingSoonTTDuration =
if (eatingSoonTTDuration > 0) eatingSoonTTDuration
else Constants.defaultEatingSoonTTDuration
var eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl)
eatingSoonTT =
if (eatingSoonTT > 0) eatingSoonTT
else if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol
else Constants.defaultEatingSoonTTmgdl
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(eatingSoonTT, currentProfile.units))
.high(Profile.toMgdl(eatingSoonTT, currentProfile.units))
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget)
val tt = if (currentProfile.units == Constants.MMOL) {
DecimalFormatter.to1Decimal(eatingSoonTT)
} else DecimalFormatter.to0Decimal(eatingSoonTT)
replyText += "\n" + String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration)
}
}
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_bolusfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
})
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processCARBS(splitted: Array<String>, receivedSms: Sms) {
var grams = SafeParse.stringToInt(splitted[1])
var time = DateUtil.now()
if (splitted.size > 2) {
val seconds = DateUtil.toSeconds(splitted[2].toUpperCase(Locale.getDefault()))
val midnight = MidnightTime.calc()
if (seconds == 0 && (!splitted[2].startsWith("00:00") || !splitted[2].startsWith("12:00"))) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
return
}
time = midnight + T.secs(seconds.toLong()).msecs()
}
grams = MainApp.getConstraintChecker().applyCarbsConstraints(Constraint(grams)).value()
if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_carbsreplywithcode), grams, DateUtil.timeString(time), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(grams, time) {
override fun run() {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.carbs = anInteger.toDouble()
detailedBolusInfo.date = secondLong
ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (result.success) {
var replyText = String.format(MainApp.gs(R.string.smscommunicator_carbsset), anInteger)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_carbsfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
private fun processTARGET(splitted: Array<String>, receivedSms: Sms) {
val isMeal = splitted[1].equals("MEAL", ignoreCase = true)
val isActivity = splitted[1].equals("ACTIVITY", ignoreCase = true)
val isHypo = splitted[1].equals("HYPO", ignoreCase = true)
if (isMeal || isActivity || isHypo) {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(Locale.getDefault()), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
val currentProfile = ProfileFunctions.getInstance().profile
if (currentProfile != null) {
var keyDuration = 0
var defaultTargetDuration = 0
var keyTarget = 0
var defaultTargetMMOL = 0.0
var defaultTargetMGDL = 0.0
if (isMeal) {
keyDuration = R.string.key_eatingsoon_duration
defaultTargetDuration = Constants.defaultEatingSoonTTDuration
keyTarget = R.string.key_eatingsoon_target
defaultTargetMMOL = Constants.defaultEatingSoonTTmmol
defaultTargetMGDL = Constants.defaultEatingSoonTTmgdl
} else if (isActivity) {
keyDuration = R.string.key_activity_duration
defaultTargetDuration = Constants.defaultActivityTTDuration
keyTarget = R.string.key_activity_target
defaultTargetMMOL = Constants.defaultActivityTTmmol
defaultTargetMGDL = Constants.defaultActivityTTmgdl
} else if (isHypo) {
keyDuration = R.string.key_hypo_duration
defaultTargetDuration = Constants.defaultHypoTTDuration
keyTarget = R.string.key_hypo_target
defaultTargetMMOL = Constants.defaultHypoTTmmol
defaultTargetMGDL = Constants.defaultHypoTTmgdl
}
var ttDuration = SP.getInt(keyDuration, defaultTargetDuration)
ttDuration = if (ttDuration > 0) ttDuration else defaultTargetDuration
var tt = SP.getDouble(keyTarget, if (currentProfile.units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL)
tt = if (tt > 0) tt else if (currentProfile.units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(ttDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(tt, currentProfile.units))
.high(Profile.toMgdl(tt, currentProfile.units))
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget)
val ttString = if (currentProfile.units == Constants.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt)
val replyText = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand))
}
}
})
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processSMS(splitted: Array<String>, receivedSms: Sms) {
val isStop = (splitted[1].equals("STOP", ignoreCase = true)
|| splitted[1].equals("DISABLE", ignoreCase = true))
if (isStop) {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_stopsmswithcode), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)
val replyText = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms))
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
}
})
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processCAL(splitted: Array<String>, receivedSms: Sms) {
val cal = SafeParse.stringToDouble(splitted[1])
if (cal > 0.0) {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(cal) {
override fun run() {
val result = XdripCalibrations.sendIntent(aDouble)
if (result) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationsent)) else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationfailed))
}
})
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
fun sendNotificationToAllNumbers(text: String?): Boolean {
var result = true
for (i in allowedNumbers.indices) {
val sms = Sms(allowedNumbers[i], text)
result = result && sendSMS(sms)
}
return result
}
private fun sendSMSToAllNumbers(sms: Sms) {
for (number in allowedNumbers) {
sms.phoneNumber = number
sendSMS(sms)
}
}
fun sendSMS(sms: Sms): Boolean {
val smsManager = SmsManager.getDefault()
sms.text = stripAccents(sms.text)
try {
if (L.isEnabled(L.SMS)) log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text)
if (sms.text.toByteArray().size <= 140) smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null)
else {
val parts = smsManager.divideMessage(sms.text)
smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts,
null, null)
}
messages.add(sms)
} catch (e: IllegalArgumentException) {
return if (e.message == "Invalid message body") {
val notification = Notification(Notification.INVALID_MESSAGE_BODY, MainApp.gs(R.string.smscommunicator_messagebody), Notification.NORMAL)
send(EventNewNotification(notification))
false
} else {
val notification = Notification(Notification.INVALID_PHONE_NUMBER, MainApp.gs(R.string.smscommunicator_invalidphonennumber), Notification.NORMAL)
send(EventNewNotification(notification))
false
}
} catch (e: SecurityException) {
val notification = Notification(Notification.MISSING_SMS_PERMISSION, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.NORMAL)
send(EventNewNotification(notification))
return false
}
send(EventSmsCommunicatorUpdateGui())
return true
}
private fun generatePasscode(): String {
val startChar1 = 'A'.toInt() // on iphone 1st char is uppercase :)
var passCode = Character.toString((startChar1 + Math.random() * ('z' - 'a' + 1)).toChar())
val startChar2: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt()
passCode += Character.toString((startChar2 + Math.random() * ('z' - 'a' + 1)).toChar())
val startChar3: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt()
passCode += Character.toString((startChar3 + Math.random() * ('z' - 'a' + 1)).toChar())
passCode = passCode.replace('l', 'k').replace('I', 'J')
return passCode
}
private fun stripAccents(str: String): String {
var s = str
s = Normalizer.normalize(s, Normalizer.Form.NFD)
s = s.replace("[\\p{InCombiningDiacriticalMarks}]".toRegex(), "")
return s
}
fun areMoreNumbers(allowednumbers: String): Boolean {
var countNumbers = 0
val substrings = allowednumbers.split(";").toTypedArray()
for (number in substrings) {
var cleaned = number.replace(Regex("\\s+"), "")
if (cleaned.length < 4) continue
if (cleaned.substring(0, 1).compareTo("+") != 0) continue
cleaned = cleaned.replace("+", "")
if (!cleaned.matches(Regex("[0-9]+"))) continue
countNumbers++
}
return countNumbers > 1
}
}

View file

@ -50,7 +50,7 @@ public class CommandSetProfile extends Command {
// Send SMS notification if ProfileSwitch is comming from NS // Send SMS notification if ProfileSwitch is comming from NS
ProfileSwitch profileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(System.currentTimeMillis()); ProfileSwitch profileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(System.currentTimeMillis());
if (profileSwitch != null && r.enacted && profileSwitch.source == Source.NIGHTSCOUT) { if (profileSwitch != null && r.enacted && profileSwitch.source == Source.NIGHTSCOUT) {
SmsCommunicatorPlugin smsCommunicatorPlugin = SmsCommunicatorPlugin.getPlugin(); SmsCommunicatorPlugin smsCommunicatorPlugin = SmsCommunicatorPlugin.INSTANCE;
if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) {
smsCommunicatorPlugin.sendNotificationToAllNumbers(MainApp.gs(R.string.profile_set_ok)); smsCommunicatorPlugin.sendNotificationToAllNumbers(MainApp.gs(R.string.profile_set_ok));
} }

View file

@ -103,7 +103,7 @@ public class DataService extends IntentService {
) { ) {
handleNewDataFromNSClient(intent); handleNewDataFromNSClient(intent);
} else if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action)) { } else if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action)) {
SmsCommunicatorPlugin.getPlugin().handleNewData(intent); SmsCommunicatorPlugin.INSTANCE.handleNewData(intent);
} }
if (L.isEnabled(L.DATASERVICE)) if (L.isEnabled(L.DATASERVICE))

View file

@ -93,15 +93,15 @@ public class DateUtil {
} }
public static int toSeconds(String hh_colon_mm) { public static int toSeconds(String hh_colon_mm) {
Pattern p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM | PM|)"); Pattern p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)");
Matcher m = p.matcher(hh_colon_mm); Matcher m = p.matcher(hh_colon_mm);
int retval = 0; int retval = 0;
if (m.find()) { if (m.find()) {
retval = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60; retval = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60;
if ((m.group(3).equals(" a.m.") || m.group(3).equals(" AM")) && m.group(1).equals("12")) if ((m.group(3).equals(" a.m.") || m.group(3).equals(" AM") || m.group(3).equals("AM")) && m.group(1).equals("12"))
retval -= 12 * 60 * 60; retval -= 12 * 60 * 60;
if ((m.group(3).equals(" p.m.") || m.group(3).equals(" PM")) && !(m.group(1).equals("12"))) if ((m.group(3).equals(" p.m.") || m.group(3).equals(" PM") || m.group(3).equals("PM")) && !(m.group(1).equals("12")))
retval += 12 * 60 * 60; retval += 12 * 60 * 60;
} }
return retval; return retval;

View file

@ -307,7 +307,7 @@
<string name="smscommunicator_bolusdelivered">Bolus %1$.2fU delivered successfully</string> <string name="smscommunicator_bolusdelivered">Bolus %1$.2fU delivered successfully</string>
<string name="smscommunicator_mealbolusdelivered">Meal Bolus %1$.2fU delivered successfully</string> <string name="smscommunicator_mealbolusdelivered">Meal Bolus %1$.2fU delivered successfully</string>
<string name="smscommunicator_mealbolusdelivered_tt">Target %1$s for %2$d minutes</string> <string name="smscommunicator_mealbolusdelivered_tt">Target %1$s for %2$d minutes</string>
<string name="smscommunicator_tt_set">Target %1$s for %2$d minutes set succesfully</string> <string name="smscommunicator_tt_set">Target %1$s for %2$d minutes set successfully</string>
<string name="bolusdelivering">Delivering %1$.2fU</string> <string name="bolusdelivering">Delivering %1$.2fU</string>
<string name="smscommunicator_remotecommandsallowed">Allow remote commands via SMS</string> <string name="smscommunicator_remotecommandsallowed">Allow remote commands via SMS</string>
<string name="glucosetype_finger">Finger</string> <string name="glucosetype_finger">Finger</string>
@ -367,10 +367,13 @@
<string name="smscommunicator_basalreplywithcode">To start basal %1$.2fU/h for %2$d min reply with code %3$s</string> <string name="smscommunicator_basalreplywithcode">To start basal %1$.2fU/h for %2$d min reply with code %3$s</string>
<string name="smscommunicator_profilereplywithcode">To switch profile to %1$s %2$d%% reply with code %3$s</string> <string name="smscommunicator_profilereplywithcode">To switch profile to %1$s %2$d%% reply with code %3$s</string>
<string name="smscommunicator_extendedreplywithcode">To start extended bolus %1$.2fU for %2$d min reply with code %3$s</string> <string name="smscommunicator_extendedreplywithcode">To start extended bolus %1$.2fU for %2$d min reply with code %3$s</string>
<string name="smscommunicator_carbsreplywithcode">To enter %1$dg at %2$s reply with code %3$s</string>
<string name="smscommunicator_basalpctreplywithcode">To start basal %1$d%% for %2$d min reply with code %3$s</string> <string name="smscommunicator_basalpctreplywithcode">To start basal %1$d%% for %2$d min reply with code %3$s</string>
<string name="smscommunicator_suspendreplywithcode">To suspend loop for %1$d minutes reply with code %2$s</string> <string name="smscommunicator_suspendreplywithcode">To suspend loop for %1$d minutes reply with code %2$s</string>
<string name="smscommunicator_tempbasalset">Temp basal %1$.2fU/h for %2$d min started successfully</string> <string name="smscommunicator_tempbasalset">Temp basal %1$.2fU/h for %2$d min started successfully</string>
<string name="smscommunicator_extendedset">Extended bolus %1$.2fU for %2$d min started successfully</string> <string name="smscommunicator_extendedset">Extended bolus %1$.2fU for %2$d min started successfully</string>
<string name="smscommunicator_carbsset">Carbs %1$dg entered successfully</string>
<string name="smscommunicator_carbsfailed">Entering %1$dg of carbs failed</string>
<string name="smscommunicator_tempbasalset_percent">Temp basal %1$d%% for %2$d min started successfully</string> <string name="smscommunicator_tempbasalset_percent">Temp basal %1$d%% for %2$d min started successfully</string>
<string name="smscommunicator_tempbasalfailed">Temp basal start failed</string> <string name="smscommunicator_tempbasalfailed">Temp basal start failed</string>
<string name="smscommunicator_extendedfailed">Extended bolus start failed</string> <string name="smscommunicator_extendedfailed">Extended bolus start failed</string>

View file

@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -155,6 +156,11 @@ public class AAPSMocker {
when(MainApp.gs(R.string.pumpNotInitialized)).thenReturn("Pump not initialized!"); when(MainApp.gs(R.string.pumpNotInitialized)).thenReturn("Pump not initialized!");
when(MainApp.gs(R.string.increasingmaxbasal)).thenReturn("Increasing max basal value because setting is lower than your max basal in profile"); when(MainApp.gs(R.string.increasingmaxbasal)).thenReturn("Increasing max basal value because setting is lower than your max basal in profile");
when(MainApp.gs(R.string.overview_bolusprogress_delivered)).thenReturn("Delivered"); when(MainApp.gs(R.string.overview_bolusprogress_delivered)).thenReturn("Delivered");
when(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode)).thenReturn("To deliver meal bolus %1$.2fU reply with code %2$s");
when(MainApp.gs(R.string.smscommunicator_mealbolusdelivered)).thenReturn("Meal Bolus %1$.2fU delivered successfully");
when(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt)).thenReturn("Target %1$s for %2$d minutes");
when(MainApp.gs(R.string.smscommunicator_carbsreplywithcode)).thenReturn("To enter %1$dg at %2$s reply with code %3$s");
when(MainApp.gs(R.string.smscommunicator_carbsset)).thenReturn("Carbs %1$dg entered successfully");
} }
public static MainApp mockMainApp() { public static MainApp mockMainApp() {
@ -183,6 +189,7 @@ public class AAPSMocker {
when(SP.getLong(anyInt(), anyLong())).thenReturn(0L); when(SP.getLong(anyInt(), anyLong())).thenReturn(0L);
when(SP.getBoolean(anyInt(), anyBoolean())).thenReturn(false); when(SP.getBoolean(anyInt(), anyBoolean())).thenReturn(false);
when(SP.getInt(anyInt(), anyInt())).thenReturn(0); when(SP.getInt(anyInt(), anyInt())).thenReturn(0);
when(SP.getString(anyInt(), anyString())).thenReturn("");
} }
public static void mockL() { public static void mockL() {

View file

@ -8,8 +8,6 @@ import org.mockito.stubbing.Answer;
import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import java.util.Date;
import info.AAPSMocker; import info.AAPSMocker;
import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
@ -28,9 +26,9 @@ import static org.powermock.api.mockito.PowerMockito.when;
@PrepareForTest({SmsCommunicatorPlugin.class, L.class, SP.class, MainApp.class, DateUtil.class}) @PrepareForTest({SmsCommunicatorPlugin.class, L.class, SP.class, MainApp.class, DateUtil.class})
public class AuthRequestTest { public class AuthRequestTest {
SmsCommunicatorPlugin smsCommunicatorPlugin; private SmsCommunicatorPlugin smsCommunicatorPlugin;
Sms sentSms; private Sms sentSms;
boolean actionCalled = false; private boolean actionCalled = false;
@Test @Test
public void doTests() { public void doTests() {
@ -77,12 +75,6 @@ public class AuthRequestTest {
@Before @Before
public void prepareTests() { public void prepareTests() {
smsCommunicatorPlugin = mock(SmsCommunicatorPlugin.class);
doAnswer((Answer) invocation -> {
sentSms = invocation.getArgument(0);
return null;
}).when(smsCommunicatorPlugin).sendSMS(any(Sms.class));
AAPSMocker.mockMainApp(); AAPSMocker.mockMainApp();
AAPSMocker.mockApplicationContext(); AAPSMocker.mockApplicationContext();
AAPSMocker.mockSP(); AAPSMocker.mockSP();
@ -90,5 +82,12 @@ public class AuthRequestTest {
AAPSMocker.mockStrings(); AAPSMocker.mockStrings();
mockStatic(DateUtil.class); mockStatic(DateUtil.class);
smsCommunicatorPlugin = mock(SmsCommunicatorPlugin.class);
doAnswer((Answer) invocation -> {
sentSms = invocation.getArgument(0);
return null;
}).when(smsCommunicatorPlugin).sendSMS(any(Sms.class));
} }
} }