diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index 9ab4df8497..c7ba3ad57c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -20,6 +20,7 @@ import info.nightscout.androidaps.plugins.general.automation.actions.* import info.nightscout.androidaps.plugins.general.automation.elements.* import info.nightscout.androidaps.plugins.general.automation.triggers.* import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction +import info.nightscout.androidaps.plugins.general.smsCommunicator.AuthRequest import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Thread import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobThread @@ -143,6 +144,8 @@ interface AppComponent : AndroidInjector { fun injectBolusWizard(bolusWizard: BolusWizard) fun injectQuickWizardEntry(quickWizardEntry: QuickWizardEntry) + fun injectAuthRequest(authRequest: AuthRequest) + @Component.Builder interface Builder { diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index a70ce7bdd9..fe7b27ce32 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -30,6 +30,7 @@ import info.nightscout.androidaps.plugins.general.automation.actions.* import info.nightscout.androidaps.plugins.general.automation.elements.* import info.nightscout.androidaps.plugins.general.automation.triggers.* import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction +import info.nightscout.androidaps.plugins.general.smsCommunicator.AuthRequest import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Thread import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobThread @@ -194,6 +195,8 @@ open class AppModule { @ContributesAndroidInjector fun loggerBolusWizard(): BolusWizard @ContributesAndroidInjector fun loggerQuickWizardEntry(): QuickWizardEntry + @ContributesAndroidInjector fun authRequestInjector(): AuthRequest + @Binds fun bindContext(mainApp: MainApp): Context @Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt index 128652e714..bfea4f53e2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt @@ -1,40 +1,50 @@ package info.nightscout.androidaps.plugins.general.smsCommunicator +import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.logging.L -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.resources.ResourceHelper -import org.slf4j.LoggerFactory +import javax.inject.Inject -class AuthRequest internal constructor(val plugin: SmsCommunicatorPlugin, val resourceHelper: ResourceHelper, var requester: Sms, requestText: String, var confirmCode: String, val action: SmsAction) { - private val log = StacktraceLoggerWrapper.getLogger(L.SMS) +class AuthRequest internal constructor( + injector: HasAndroidInjector, + var requester: Sms, + requestText: String, + var confirmCode: String, + val action: SmsAction) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin + @Inject lateinit var resourceHelper: ResourceHelper private val date = DateUtil.now() private var processed = false init { - plugin.sendSMS(Sms(requester.phoneNumber, requestText)) + injector.androidInjector().inject(this) + smsCommunicatorPlugin.sendSMS(Sms(requester.phoneNumber, requestText)) } fun action(codeReceived: String) { if (processed) { - if (L.isEnabled(L.SMS)) log.debug("Already processed") + aapsLogger.debug(LTag.SMS, "Already processed") return } if (confirmCode != codeReceived) { processed = true - if (L.isEnabled(L.SMS)) log.debug("Wrong code") - plugin.sendSMS(Sms(requester.phoneNumber, resourceHelper.gs(R.string.sms_wrongcode))) + aapsLogger.debug(LTag.SMS, "Wrong code") + smsCommunicatorPlugin.sendSMS(Sms(requester.phoneNumber, resourceHelper.gs(R.string.sms_wrongcode))) return } if (DateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) { processed = true - if (L.isEnabled(L.SMS)) log.debug("Processing confirmed SMS: " + requester.text) + aapsLogger.debug(LTag.SMS, "Processing confirmed SMS: " + requester.text) action.run() return } - if (L.isEnabled(L.SMS)) log.debug("Timed out SMS: " + requester.text) + aapsLogger.debug(LTag.SMS, "Timed out SMS: " + requester.text) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index bc0e2792b4..2b3b81b78b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -70,8 +70,8 @@ class SmsCommunicatorPlugin @Inject constructor( ) { private val disposable = CompositeDisposable() - var allowedNumbers: MutableList = ArrayList() - var messageToConfirm: AuthRequest? = null + private var allowedNumbers: MutableList = ArrayList() + private var messageToConfirm: AuthRequest? = null var lastRemoteBolusTime: Long = 0 var messages = ArrayList() @@ -159,7 +159,7 @@ class SmsCommunicatorPlugin @Inject constructor( } } - fun isCommand(command: String, number: String): Boolean { + private fun isCommand(command: String, number: String): Boolean { var found = false commands.forEach { (k, _) -> if (k == command) found = true @@ -167,7 +167,7 @@ class SmsCommunicatorPlugin @Inject constructor( return found || messageToConfirm?.requester?.phoneNumber == number } - fun isAllowedNumber(number: String): Boolean { + private fun isAllowedNumber(number: String): Boolean { for (num in allowedNumbers) { if (num == number) return true } @@ -184,7 +184,7 @@ class SmsCommunicatorPlugin @Inject constructor( } } - fun processSms(receivedSms: Sms) { + private fun processSms(receivedSms: Sms) { if (!isEnabled(PluginType.GENERAL)) { aapsLogger.debug(LTag.SMS, "Ignoring SMS. Plugin disabled.") return @@ -356,7 +356,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(duration) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(duration) { override fun run() { commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { @@ -471,7 +471,7 @@ class SmsCommunicatorPlugin @Inject constructor( val reply = String.format(resourceHelper.gs(R.string.smscommunicator_profilereplywithcode), list[pindex - 1], percentage, passCode) receivedSms.processed = true val finalPercentage = percentage - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(list[pindex - 1] as String, finalPercentage) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(list[pindex - 1] as String, finalPercentage) { override fun run() { activePlugin.activeTreatments.doProfileSwitch(store, list[pindex - 1] as String, 0, finalPercentage, 0, DateUtil.now()) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.profileswitchcreated))) @@ -488,7 +488,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalstopreplywithcode), passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction() { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { @@ -518,7 +518,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { override fun run() { commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, object : Callback() { override fun run() { @@ -550,7 +550,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { override fun run() { commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, object : Callback() { override fun run() { @@ -577,7 +577,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction() { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { commandQueue.cancelExtended(object : Callback() { override fun run() { @@ -605,7 +605,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(extended, duration) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(extended, duration) { override fun run() { commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() { override fun run() { @@ -640,7 +640,7 @@ class SmsCommunicatorPlugin @Inject constructor( else String.format(resourceHelper.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(bolus) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(bolus) { override fun run() { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.insulin = aDouble() @@ -716,7 +716,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_carbsreplywithcode), grams, DateUtil.timeString(time), passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(grams, time) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(grams, time) { override fun run() { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.carbs = anInteger().toDouble() @@ -748,7 +748,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(Locale.getDefault()), passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction() { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { val units = profileFunction.getUnits() var keyDuration = 0 @@ -803,7 +803,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_temptargetcancel), passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction() { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { val tempTarget = TempTarget() .source(Source.USER) @@ -827,7 +827,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_stopsmswithcode), passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction() { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { sp.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) val replyText = String.format(resourceHelper.gs(R.string.smscommunicator_stoppedsms)) @@ -843,7 +843,7 @@ class SmsCommunicatorPlugin @Inject constructor( val passCode = generatePasscode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode) receivedSms.processed = true - messageToConfirm = AuthRequest(this, resourceHelper, receivedSms, reply, passCode, object : SmsAction(cal) { + messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(cal) { override fun run() { val result = XdripCalibrations.sendIntent(aDouble) if (result) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_calibrationsent))) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_calibrationfailed))) diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java deleted file mode 100644 index 4d0ed00415..0000000000 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package info.nightscout.androidaps.plugins.general.smsCommunicator; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.stubbing.Answer; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import info.AAPSMocker; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.SP; -import info.nightscout.androidaps.utils.T; - -import static org.mockito.ArgumentMatchers.any; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({SmsCommunicatorPlugin.class, L.class, SP.class, MainApp.class, DateUtil.class}) - -public class AuthRequestTest { - private SmsCommunicatorPlugin smsCommunicatorPlugin; - private Sms sentSms; - private boolean actionCalled = false; - - @Test - public void doTests() { - Sms requester = new Sms("aNumber", "aText"); - SmsAction action = new SmsAction() { - @Override - public void run() { - actionCalled = true; - } - }; - - // Check if SMS requesting code is sent - AuthRequest authRequest = new AuthRequest(smsCommunicatorPlugin, requester, "Request text", "ABC", action); - - Assert.assertEquals(sentSms.getPhoneNumber(), "aNumber"); - Assert.assertEquals(sentSms.getText(), "Request text"); - - // wrong reply - actionCalled = false; - authRequest.action("EFG"); - Assert.assertEquals(sentSms.getPhoneNumber(), "aNumber"); - Assert.assertEquals(sentSms.getText(), "Wrong code. Command cancelled."); - Assert.assertFalse(actionCalled); - - // correct reply - authRequest = new AuthRequest(smsCommunicatorPlugin, requester, "Request text", "ABC", action); - actionCalled = false; - authRequest.action("ABC"); - Assert.assertTrue(actionCalled); - // second time action should not be called - actionCalled = false; - authRequest.action("ABC"); - Assert.assertFalse(actionCalled); - - // test timed out message - long now = 10000; - when(DateUtil.now()).thenReturn(now); - authRequest = new AuthRequest(smsCommunicatorPlugin, requester, "Request text", "ABC", action); - actionCalled = false; - when(DateUtil.now()).thenReturn(now + T.mins(Constants.SMS_CONFIRM_TIMEOUT).msecs() + 1); - authRequest.action("ABC"); - Assert.assertFalse(actionCalled); - } - - @Before - public void prepareTests() { - AAPSMocker.mockMainApp(); - AAPSMocker.mockApplicationContext(); - AAPSMocker.mockSP(); - AAPSMocker.mockL(); - AAPSMocker.mockStrings(); - - mockStatic(DateUtil.class); - - smsCommunicatorPlugin = mock(SmsCommunicatorPlugin.class); - doAnswer((Answer) invocation -> { - sentSms = invocation.getArgument(0); - return null; - }).when(smsCommunicatorPlugin).sendSMS(any(Sms.class)); - - } -} diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.kt new file mode 100644 index 0000000000..b74625330b --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequestTest.kt @@ -0,0 +1,93 @@ +package info.nightscout.androidaps.plugins.general.smsCommunicator + +import dagger.android.AndroidInjector +import dagger.android.HasAndroidInjector +import info.TestBase +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.resources.ResourceHelper +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.invocation.InvocationOnMock +import org.mockito.stubbing.Answer +import org.powermock.api.mockito.PowerMockito +import org.powermock.core.classloader.annotations.PrepareForTest +import org.powermock.modules.junit4.PowerMockRunner + +@RunWith(PowerMockRunner::class) +@PrepareForTest(SmsCommunicatorPlugin::class, DateUtil::class) +class AuthRequestTest : TestBase() { + + @Mock lateinit var aapsLogger: AAPSLogger + @Mock lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin + @Mock lateinit var resourceHelper: ResourceHelper + + var injector: HasAndroidInjector = HasAndroidInjector { + AndroidInjector { + if (it is AuthRequest) { + it.aapsLogger = aapsLogger + it.resourceHelper = resourceHelper + it.smsCommunicatorPlugin = smsCommunicatorPlugin + } + } + } + + private var sentSms: Sms? = null + private var actionCalled = false + + @Before fun prepareTests() { + `when`(resourceHelper.gs(R.string.sms_wrongcode)).thenReturn("Wrong code. Command cancelled.") + PowerMockito.doAnswer(Answer { invocation: InvocationOnMock -> + sentSms = invocation.getArgument(0) + null + } as Answer<*>).`when`(smsCommunicatorPlugin).sendSMS(anyObject()) + } + + @Test fun doTests() { + val requester = Sms("aNumber", "aText") + val action: SmsAction = object : SmsAction() { + override fun run() { + actionCalled = true + } + } + + // Check if SMS requesting code is sent + var authRequest = AuthRequest(injector, requester, "Request text", "ABC", action) + Assert.assertEquals(sentSms!!.phoneNumber, "aNumber") + Assert.assertEquals(sentSms!!.text, "Request text") + + // wrong reply + actionCalled = false + authRequest.action("EFG") + Assert.assertEquals(sentSms!!.phoneNumber, "aNumber") + Assert.assertEquals(sentSms!!.text, "Wrong code. Command cancelled.") + Assert.assertFalse(actionCalled) + + // correct reply + authRequest = AuthRequest(injector, requester, "Request text", "ABC", action) + actionCalled = false + authRequest.action("ABC") + Assert.assertTrue(actionCalled) + // second time action should not be called + actionCalled = false + authRequest.action("ABC") + Assert.assertFalse(actionCalled) + + // test timed out message + val now: Long = 10000 + PowerMockito.mockStatic(DateUtil::class.java) + PowerMockito.`when`(DateUtil.now()).thenReturn(now) + authRequest = AuthRequest(injector, requester, "Request text", "ABC", action) + actionCalled = false + PowerMockito.`when`(DateUtil.now()).thenReturn(now + T.mins(Constants.SMS_CONFIRM_TIMEOUT).msecs() + 1) + authRequest.action("ABC") + Assert.assertFalse(actionCalled) + } +} \ No newline at end of file