From 8bf442681e2cd7a7a1f34c6112fa1cf43e9a327c Mon Sep 17 00:00:00 2001 From: Andrew Rowson Date: Wed, 2 Nov 2022 17:59:04 +0000 Subject: [PATCH] Pass in locationprocessor to mqttprocessor so that it can publish on reconnect --- .../model/messages/MessageWithCreatedAt.kt | 3 +- .../android/services/MessageProcessor.java | 6 +- .../MessageProcessorEndpointMqtt.java | 16 +++++- .../owntracks/android/support/Preferences.kt | 56 ++++++++++++------- project/app/src/main/res/values/defaults.xml | 2 +- .../values/donottranslate-preference_keys.xml | 2 +- project/app/src/main/res/values/strings.xml | 4 +- .../main/res/xml/preferences_reporting.xml | 4 +- .../MessageProcessorEndpointMqttTest.kt | 10 ++-- .../owntracks/android/support/ParserTest.kt | 4 +- .../support/PreferencesGettersAndSetters.kt | 2 +- util/mqtt-local/mosquitto.conf | 1 + 12 files changed, 71 insertions(+), 39 deletions(-) diff --git a/project/app/src/main/java/org/owntracks/android/model/messages/MessageWithCreatedAt.kt b/project/app/src/main/java/org/owntracks/android/model/messages/MessageWithCreatedAt.kt index b52320bb27..9a93848525 100644 --- a/project/app/src/main/java/org/owntracks/android/model/messages/MessageWithCreatedAt.kt +++ b/project/app/src/main/java/org/owntracks/android/model/messages/MessageWithCreatedAt.kt @@ -1,6 +1,7 @@ package org.owntracks.android.model.messages import com.fasterxml.jackson.annotation.JsonProperty +import kotlin.time.Duration.Companion.milliseconds interface MessageWithCreatedAt { @get:JsonProperty("created_at") @@ -16,5 +17,5 @@ interface Clock { } class RealClock : Clock { - override val time: Long = java.util.concurrent.TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + override val time: Long = System.currentTimeMillis().milliseconds.inWholeSeconds } diff --git a/project/app/src/main/java/org/owntracks/android/services/MessageProcessor.java b/project/app/src/main/java/org/owntracks/android/services/MessageProcessor.java index 2d2a089f73..9f667a9c09 100644 --- a/project/app/src/main/java/org/owntracks/android/services/MessageProcessor.java +++ b/project/app/src/main/java/org/owntracks/android/services/MessageProcessor.java @@ -196,7 +196,7 @@ private void loadOutgoingMessageProcessor() { break; case MessageProcessorEndpointMqtt.MODE_ID: default: - this.endpoint = new MessageProcessorEndpointMqtt(this, this.parser, this.preferences, this.scheduler, this.eventBus, this.runThingsOnOtherThreads, this.applicationContext); + this.endpoint = new MessageProcessorEndpointMqtt(this, this.parser, this.preferences, this.scheduler, this.eventBus, this.runThingsOnOtherThreads, this.applicationContext, this.locationProcessorLazy.get()); } @@ -326,6 +326,10 @@ private void sendAvailableMessages() { Timber.w("Exiting outgoingmessage loop"); } + /** + * Resets the retry backoff timer back to the initial value, because we've most likely had a + * reconnection event. + */ public void resetMessageSleepBlock() { if (waitFuture != null && waitFuture.cancel(false)) { Timber.d("Resetting message send loop wait. Thread: %s", Thread.currentThread()); diff --git a/project/app/src/main/java/org/owntracks/android/services/MessageProcessorEndpointMqtt.java b/project/app/src/main/java/org/owntracks/android/services/MessageProcessorEndpointMqtt.java index d5289d8e93..00623cd39e 100644 --- a/project/app/src/main/java/org/owntracks/android/services/MessageProcessorEndpointMqtt.java +++ b/project/app/src/main/java/org/owntracks/android/services/MessageProcessorEndpointMqtt.java @@ -61,6 +61,7 @@ public class MessageProcessorEndpointMqtt extends MessageProcessorEndpoint implements StatefulServiceMessageProcessor, SharedPreferences.OnSharedPreferenceChangeListener { public static final int MODE_ID = 0; + private final LocationProcessor locationProcessor; private IMqttAsyncClient mqttClient; @@ -78,7 +79,7 @@ public class MessageProcessorEndpointMqtt extends MessageProcessorEndpoint imple private final Semaphore connectingLock = new Semaphore(1); - MessageProcessorEndpointMqtt(MessageProcessor messageProcessor, Parser parser, Preferences preferences, Scheduler scheduler, EventBus eventBus, RunThingsOnOtherThreads runThingsOnOtherThreads, Context applicationContext) { + MessageProcessorEndpointMqtt(MessageProcessor messageProcessor, Parser parser, Preferences preferences, Scheduler scheduler, EventBus eventBus, RunThingsOnOtherThreads runThingsOnOtherThreads, Context applicationContext, LocationProcessor locationProcessor) { super(messageProcessor); this.parser = parser; this.preferences = preferences; @@ -87,6 +88,7 @@ public class MessageProcessorEndpointMqtt extends MessageProcessorEndpoint imple this.messageProcessor = messageProcessor; this.runThingsOnOtherThreads = runThingsOnOtherThreads; this.applicationContext = applicationContext; + this.locationProcessor = locationProcessor; if (preferences != null) { preferences.registerOnPreferenceChangedListener(this); } @@ -145,10 +147,13 @@ synchronized void sendMessage(MessageBase m) throws ConfigurationIncompleteExcep } private final MqttCallbackExtended iCallbackClient = new MqttCallbackExtended() { + private boolean wasConnectionLost = false; + @Override public void connectComplete(boolean reconnect, String serverURI) { Timber.d("Connect Complete. Reconnected: %s, serverUri:%s", reconnect, serverURI); - onConnect(); + onConnect(wasConnectionLost); + wasConnectionLost = false; } @Override @@ -163,6 +168,7 @@ public void connectionLost(Throwable cause) { changeState(EndpointState.DISCONNECTED.withError(cause)); Timber.d("Releasing connectinglock"); connectingLock.release(); + wasConnectionLost = true; scheduler.scheduleMqttReconnect(); } @@ -405,7 +411,7 @@ private void setWill(MqttConnectOptions m) { } } - private void onConnect() { + private void onConnect(boolean wasConnectionLost) { Timber.d("MQTT connected!. Running onconnect handler (threadID %s)", Thread.currentThread()); scheduler.scheduleMqttMaybeReconnectAndPing(preferences.getKeepalive()); @@ -439,6 +445,10 @@ private void onConnect() { subscribe(topics.toArray(new String[0])); messageProcessor.resetMessageSleepBlock(); + + if (preferences.getPublishLocationOnConnect()) { + locationProcessor.publishLocationMessage(null); // TODO fix the trigger here + } } @NotNull diff --git a/project/app/src/main/java/org/owntracks/android/support/Preferences.kt b/project/app/src/main/java/org/owntracks/android/support/Preferences.kt index fa5e8fea1b..6bade92ff2 100644 --- a/project/app/src/main/java/org/owntracks/android/support/Preferences.kt +++ b/project/app/src/main/java/org/owntracks/android/support/Preferences.kt @@ -9,6 +9,14 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM import dagger.hilt.android.qualifiers.ApplicationContext +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import java.util.* +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Singleton import org.eclipse.paho.client.mqttv3.MqttConnectOptions import org.greenrobot.eventbus.EventBus import org.owntracks.android.BuildConfig @@ -23,14 +31,6 @@ import org.owntracks.android.support.preferences.PreferencesStore import org.owntracks.android.ui.AppShortcuts import org.owntracks.android.ui.map.MapLayerStyle import timber.log.Timber -import java.lang.reflect.InvocationTargetException -import java.lang.reflect.Method -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import java.util.* -import java.util.concurrent.TimeUnit -import javax.inject.Inject -import javax.inject.Singleton @Singleton @SuppressLint("NonConstantResourceId") @@ -91,11 +91,15 @@ class Preferences @Inject constructor( // SharedPreferencesImpl stores its listeners as a list of WeakReferences. So we shouldn't use a // lambda as a listener, as that'll just get GC'd and then mysteriously disappear // https://stackoverflow.com/a/3104265/352740 - fun registerOnPreferenceChangedListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) { + fun registerOnPreferenceChangedListener( + listener: SharedPreferences.OnSharedPreferenceChangeListener + ) { preferencesStore.registerOnSharedPreferenceChangeListener(listener) } - fun unregisterOnPreferenceChangedListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) { + fun unregisterOnPreferenceChangedListener( + listener: SharedPreferences.OnSharedPreferenceChangeListener + ) { preferencesStore.unregisterOnSharedPreferenceChangeListener(listener) } @@ -203,7 +207,9 @@ class Preferences @Inject constructor( ) { importMethod.invoke( this, - MonitoringMode.getByValue(messageConfiguration[configurationKey] as Int) + MonitoringMode.getByValue( + messageConfiguration[configurationKey] as Int + ) ) } else { importMethod.invoke(this, messageConfiguration[configurationKey]) @@ -213,7 +219,7 @@ class Preferences @Inject constructor( Timber.e( "Tried to import $configurationKey but value is wrong type. Expected: ${ methods.getValue(configurationKey).parameterTypes.first().canonicalName - }, given ${messageConfiguration[configurationKey]?.javaClass?.canonicalName}", + }, given ${messageConfiguration[configurationKey]?.javaClass?.canonicalName}" ) } } @@ -918,8 +924,12 @@ class Preferences @Inject constructor( setInt(R.string.preferenceKeyTheme, actualValue) when (actualValue) { NIGHT_MODE_AUTO -> AppCompatDelegate.setDefaultNightMode(SYSTEM_NIGHT_AUTO_MODE) - NIGHT_MODE_ENABLE -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - NIGHT_MODE_DISABLE -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + NIGHT_MODE_ENABLE -> AppCompatDelegate.setDefaultNightMode( + AppCompatDelegate.MODE_NIGHT_YES + ) + NIGHT_MODE_DISABLE -> AppCompatDelegate.setDefaultNightMode( + AppCompatDelegate.MODE_NIGHT_NO + ) } } @@ -989,21 +999,27 @@ class Preferences @Inject constructor( ) @set:Import(keyResId = R.string.preferenceKeyEnableMapRotation) var enableMapRotation: Boolean - get() = getBooleanOrDefault(R.string.preferenceKeyEnableMapRotation, R.bool.valEnableMapRotation) + get() = getBooleanOrDefault( + R.string.preferenceKeyEnableMapRotation, + R.bool.valEnableMapRotation + ) set(newValue) { setBoolean(R.string.preferenceKeyEnableMapRotation, newValue) } @get:Export( - keyResId = R.string.preferenceKeyRepublishOnReconnect, + keyResId = R.string.preferenceKeyPublishLocationOnConnect, exportModeMqtt = true, exportModeHttp = false ) - @set:Import(keyResId = R.string.preferenceKeyRepublishOnReconnect) - var republishOnReconnect: Boolean - get() = getBooleanOrDefault(R.string.preferenceKeyRepublishOnReconnect, R.bool.valRepublishOnRecconect) + @set:Import(keyResId = R.string.preferenceKeyPublishLocationOnConnect) + var publishLocationOnConnect: Boolean + get() = getBooleanOrDefault( + R.string.preferenceKeyPublishLocationOnConnect, + R.bool.valPublishLocationOnConnect + ) set(newValue) { - setBoolean(R.string.preferenceKeyRepublishOnReconnect, newValue) + setBoolean(R.string.preferenceKeyPublishLocationOnConnect, newValue) } // Not used on public, as many people might use the same device type diff --git a/project/app/src/main/res/values/defaults.xml b/project/app/src/main/res/values/defaults.xml index 5156350b89..b5a1c8c422 100644 --- a/project/app/src/main/res/values/defaults.xml +++ b/project/app/src/main/res/values/defaults.xml @@ -34,7 +34,7 @@ false false true - false + false false diff --git a/project/app/src/main/res/values/donottranslate-preference_keys.xml b/project/app/src/main/res/values/donottranslate-preference_keys.xml index cd123b1898..1532fe6fdf 100644 --- a/project/app/src/main/res/values/donottranslate-preference_keys.xml +++ b/project/app/src/main/res/values/donottranslate-preference_keys.xml @@ -44,7 +44,7 @@ pubTopicBase cmd remoteConfiguration - republishOnReconnect + publishOnConnect setupNotCompleted sub subQos diff --git a/project/app/src/main/res/values/strings.xml b/project/app/src/main/res/values/strings.xml index 9d6e9d1d39..2e65a21666 100644 --- a/project/app/src/main/res/values/strings.xml +++ b/project/app/src/main/res/values/strings.xml @@ -211,8 +211,8 @@ To allow this, please enable Location in the device settings." "Enable configuration to be updated remotely (requires remote commands)" "Reporting" "Source Code" - "Republish on MQTT reconnect" - "Publish the last known location and current extended data on re-connecting to MQTT" + "Publish location on MQTT connect" + "Publish the last known location and current extended data immediately on connecting to MQTT" "Reverse Geocode Provider" "Security" "Connection" diff --git a/project/app/src/main/res/xml/preferences_reporting.xml b/project/app/src/main/res/xml/preferences_reporting.xml index 70965f89f9..95094ae54d 100644 --- a/project/app/src/main/res/xml/preferences_reporting.xml +++ b/project/app/src/main/res/xml/preferences_reporting.xml @@ -8,9 +8,9 @@ app:summary="@string/preferencesPubExtendedDataSummary" app:title="@string/preferencesPubExtendedData" /> diff --git a/project/app/src/test/java/org/owntracks/android/services/MessageProcessorEndpointMqttTest.kt b/project/app/src/test/java/org/owntracks/android/services/MessageProcessorEndpointMqttTest.kt index dee90574fb..dca9fb4ce9 100644 --- a/project/app/src/test/java/org/owntracks/android/services/MessageProcessorEndpointMqttTest.kt +++ b/project/app/src/test/java/org/owntracks/android/services/MessageProcessorEndpointMqttTest.kt @@ -15,7 +15,7 @@ class MessageProcessorEndpointMqttTest { @Test fun `MQTT Endpoint generates correct topics to subscribe to from single default subTopic`() { val endpoint = - MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext) + MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext, null) val subTopic = "owntracks/+/+" val topics = endpoint.getTopicsToSubscribeTo(subTopic, true, "/info", "/events", "/waypoints") @@ -33,7 +33,7 @@ class MessageProcessorEndpointMqttTest { @Test fun `MQTT Endpoint generates correct topics to subscribe to from single custom subTopic`() { val endpoint = - MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext) + MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext, null) val subTopic = "othertopic/+/+" val topics = endpoint.getTopicsToSubscribeTo(subTopic, true, "/info", "/events", "/waypoints") @@ -45,7 +45,7 @@ class MessageProcessorEndpointMqttTest { @Test fun `MQTT Endpoint generates correct topics to subscribe to from multiple subTopics`() { val endpoint = - MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext) + MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext, null) val subTopic = "owntracks/+/+ othertopic/+" val topics = endpoint.getTopicsToSubscribeTo(subTopic, true, "/info", "/events", "/waypoints") @@ -61,7 +61,7 @@ class MessageProcessorEndpointMqttTest { @Test fun `MQTT Endpoint generates correct topics to subscribe to from multiple subTopics with info not requested`() { val endpoint = - MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext) + MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext, null) val subTopic = "owntracks/+/+ othertopic/+" val topics = endpoint.getTopicsToSubscribeTo(subTopic, false, "/info", "/events", "/waypoints") @@ -77,7 +77,7 @@ class MessageProcessorEndpointMqttTest { @Test fun `MQTT Endpoint generates correct topics to subscribe to from wildcard topic`() { val endpoint = - MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext) + MessageProcessorEndpointMqtt(null, null, null, null, null, null, applicationContext, null) val subTopic = "owntracks/#" val topics = endpoint.getTopicsToSubscribeTo(subTopic, true, "/info", "/events", "/waypoints") diff --git a/project/app/src/test/java/org/owntracks/android/support/ParserTest.kt b/project/app/src/test/java/org/owntracks/android/support/ParserTest.kt index d5625949af..c8aa6cfb90 100644 --- a/project/app/src/test/java/org/owntracks/android/support/ParserTest.kt +++ b/project/app/src/test/java/org/owntracks/android/support/ParserTest.kt @@ -40,7 +40,7 @@ class ParserTest { val regions: MutableList = LinkedList() regions.add("Testregion1") regions.add("Testregion2") - messageLocation = MessageLocation(MessageCreatedAtNow(FakeClock())).apply { + messageLocation = MessageLocation(MessageCreatedAtNow(FakeFixedClock())).apply { accuracy = 10 altitude = 20 latitude = 50.1 @@ -727,7 +727,7 @@ class ParserTest { } //endregion - inner class FakeClock : Clock { + inner class FakeFixedClock : Clock { override val time: Long = 25 } } diff --git a/project/app/src/test/java/org/owntracks/android/support/PreferencesGettersAndSetters.kt b/project/app/src/test/java/org/owntracks/android/support/PreferencesGettersAndSetters.kt index 4e0e8953a9..8621c17e2d 100644 --- a/project/app/src/test/java/org/owntracks/android/support/PreferencesGettersAndSetters.kt +++ b/project/app/src/test/java/org/owntracks/android/support/PreferencesGettersAndSetters.kt @@ -459,7 +459,7 @@ class PreferencesGettersAndSetters( on { getString(eq(R.string.preferenceKeyPubTopicBase)) } doReturn "pubTopicBase" on { getString(eq(R.string.preferenceKeyRemoteCommand)) } doReturn "cmd" on { getString(eq(R.string.preferenceKeyRemoteConfiguration)) } doReturn "remoteConfiguration" - on { getString(eq(R.string.preferenceKeyRepublishOnReconnect)) } doReturn "republishOnReconnect" + on { getString(eq(R.string.preferenceKeyPublishLocationOnConnect)) } doReturn "republishOnReconnect" on { getString(eq(R.string.preferenceKeyReverseGeocodeProvider)) } doReturn "reverseGeocodeProvider" on { getString(eq(R.string.preferenceKeySetupNotCompleted)) } doReturn "setupNotCompleted" on { getString(eq(R.string.preferenceKeySub)) } doReturn "sub" diff --git a/util/mqtt-local/mosquitto.conf b/util/mqtt-local/mosquitto.conf index 1631ecfd81..7c2b1517f9 100644 --- a/util/mqtt-local/mosquitto.conf +++ b/util/mqtt-local/mosquitto.conf @@ -1,4 +1,5 @@ allow_anonymous false +log_type all password_file /mosquitto/config/mosquitto.password listener 1883