diff --git a/app/gradle.lockfile b/app/gradle.lockfile
index 0dd0916090c..2312c37d449 100644
--- a/app/gradle.lockfile
+++ b/app/gradle.lockfile
@@ -266,6 +266,7 @@ com.google.code.findbugs:jsr305:3.0.2=_agp_internal_javaPreCompileFullDebug_kspC
com.google.code.gson:gson:2.10.1=_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core
com.google.code.gson:gson:2.11.0=fullDebugRuntimeClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseRuntimeClasspath,fullReleaseUnitTestRuntimeClasspath,minimalDebugRuntimeClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseRuntimeClasspath,minimalReleaseUnitTestRuntimeClasspath
com.google.code.gson:gson:2.8.9=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-launcher
+com.google.crypto.tink:tink:1.17.0=fullDebugRuntimeClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseRuntimeClasspath,fullReleaseUnitTestRuntimeClasspath,minimalDebugRuntimeClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseRuntimeClasspath,minimalReleaseUnitTestRuntimeClasspath
com.google.crypto.tink:tink:1.7.0=_internal-unified-test-platform-android-test-plugin-host-emulator-control
com.google.dagger:dagger-compiler:2.56.2=_agp_internal_javaPreCompileFullDebug_kspClasspath,_agp_internal_javaPreCompileFullRelease_kspClasspath,_agp_internal_javaPreCompileMinimalDebug_kspClasspath,_agp_internal_javaPreCompileMinimalRelease_kspClasspath,hiltAnnotationProcessorFullDebug,hiltAnnotationProcessorFullDebugAndroidTest,hiltAnnotationProcessorFullDebugUnitTest,hiltAnnotationProcessorFullRelease,hiltAnnotationProcessorFullReleaseUnitTest,hiltAnnotationProcessorMinimalDebug,hiltAnnotationProcessorMinimalDebugAndroidTest,hiltAnnotationProcessorMinimalDebugUnitTest,hiltAnnotationProcessorMinimalRelease,hiltAnnotationProcessorMinimalReleaseUnitTest,kspFullDebugAndroidTestKotlinProcessorClasspath,kspFullDebugKotlinProcessorClasspath,kspFullDebugUnitTestKotlinProcessorClasspath,kspFullReleaseKotlinProcessorClasspath,kspFullReleaseUnitTestKotlinProcessorClasspath,kspMinimalDebugAndroidTestKotlinProcessorClasspath,kspMinimalDebugKotlinProcessorClasspath,kspMinimalDebugUnitTestKotlinProcessorClasspath,kspMinimalReleaseKotlinProcessorClasspath,kspMinimalReleaseUnitTestKotlinProcessorClasspath
com.google.dagger:dagger-lint-aar:2.56.2=fullDebugAndroidTestCompileClasspath,fullDebugCompileClasspath,fullDebugRuntimeClasspath,fullDebugUnitTestCompileClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseCompileClasspath,fullReleaseRuntimeClasspath,fullReleaseUnitTestCompileClasspath,fullReleaseUnitTestRuntimeClasspath,minimalDebugAndroidTestCompileClasspath,minimalDebugCompileClasspath,minimalDebugRuntimeClasspath,minimalDebugUnitTestCompileClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseCompileClasspath,minimalReleaseRuntimeClasspath,minimalReleaseUnitTestCompileClasspath,minimalReleaseUnitTestRuntimeClasspath
@@ -310,6 +311,7 @@ com.google.j2objc:j2objc-annotations:3.0.0=fullDebugRuntimeClasspath,fullDebugUn
com.google.protobuf:protobuf-java-util:3.22.3=_internal-unified-test-platform-core
com.google.protobuf:protobuf-java-util:3.24.4=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-launcher
com.google.protobuf:protobuf-java:3.24.4=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher
+com.google.protobuf:protobuf-java:4.28.2=fullDebugRuntimeClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseRuntimeClasspath,fullReleaseUnitTestRuntimeClasspath
com.google.protobuf:protobuf-javalite:3.22.3=minimalDebugRuntimeClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseRuntimeClasspath,minimalReleaseUnitTestRuntimeClasspath
com.google.protobuf:protobuf-kotlin:3.24.4=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher
com.google.testing.platform:android-device-provider-local:0.0.9-alpha03=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle
@@ -523,4 +525,5 @@ org.jetbrains:annotations:23.0.0=_internal-unified-test-platform-android-device-
org.jspecify:jspecify:1.0.0=_agp_internal_javaPreCompileFullDebug_kspClasspath,_agp_internal_javaPreCompileFullRelease_kspClasspath,_agp_internal_javaPreCompileMinimalDebug_kspClasspath,_agp_internal_javaPreCompileMinimalRelease_kspClasspath,fullDebugAndroidTestCompileClasspath,fullDebugAndroidTestRuntimeClasspath,fullDebugCompileClasspath,fullDebugRuntimeClasspath,fullDebugUnitTestCompileClasspath,fullDebugUnitTestRuntimeClasspath,fullImplementationDependenciesMetadata,fullReleaseCompileClasspath,fullReleaseRuntimeClasspath,fullReleaseUnitTestCompileClasspath,fullReleaseUnitTestRuntimeClasspath,hiltAnnotationProcessorFullDebug,hiltAnnotationProcessorFullDebugAndroidTest,hiltAnnotationProcessorFullDebugUnitTest,hiltAnnotationProcessorFullRelease,hiltAnnotationProcessorFullReleaseUnitTest,hiltAnnotationProcessorMinimalDebug,hiltAnnotationProcessorMinimalDebugAndroidTest,hiltAnnotationProcessorMinimalDebugUnitTest,hiltAnnotationProcessorMinimalRelease,hiltAnnotationProcessorMinimalReleaseUnitTest,kspFullDebugAndroidTestKotlinProcessorClasspath,kspFullDebugKotlinProcessorClasspath,kspFullDebugUnitTestKotlinProcessorClasspath,kspFullReleaseKotlinProcessorClasspath,kspFullReleaseUnitTestKotlinProcessorClasspath,kspMinimalDebugAndroidTestKotlinProcessorClasspath,kspMinimalDebugKotlinProcessorClasspath,kspMinimalDebugUnitTestKotlinProcessorClasspath,kspMinimalReleaseKotlinProcessorClasspath,kspMinimalReleaseUnitTestKotlinProcessorClasspath,minimalDebugAndroidTestCompileClasspath,minimalDebugAndroidTestRuntimeClasspath,minimalDebugCompileClasspath,minimalDebugRuntimeClasspath,minimalDebugUnitTestCompileClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseCompileClasspath,minimalReleaseRuntimeClasspath,minimalReleaseUnitTestCompileClasspath,minimalReleaseUnitTestRuntimeClasspath
org.openhomefoundation.improv-wifi:sdk-android:0.1.1=fullDebugAndroidTestCompileClasspath,fullDebugCompileClasspath,fullDebugRuntimeClasspath,fullDebugUnitTestCompileClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseCompileClasspath,fullReleaseRuntimeClasspath,fullReleaseUnitTestCompileClasspath,fullReleaseUnitTestRuntimeClasspath,minimalDebugAndroidTestCompileClasspath,minimalDebugCompileClasspath,minimalDebugRuntimeClasspath,minimalDebugUnitTestCompileClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseCompileClasspath,minimalReleaseRuntimeClasspath,minimalReleaseUnitTestCompileClasspath,minimalReleaseUnitTestRuntimeClasspath
org.slf4j:slf4j-api:2.0.4=ktlint
+org.unifiedpush.android:connector:3.0.8=fullDebugAndroidTestCompileClasspath,fullDebugCompileClasspath,fullDebugRuntimeClasspath,fullDebugUnitTestCompileClasspath,fullDebugUnitTestRuntimeClasspath,fullImplementationDependenciesMetadata,fullReleaseCompileClasspath,fullReleaseRuntimeClasspath,fullReleaseUnitTestCompileClasspath,fullReleaseUnitTestRuntimeClasspath,minimalDebugAndroidTestCompileClasspath,minimalDebugCompileClasspath,minimalDebugRuntimeClasspath,minimalDebugUnitTestCompileClasspath,minimalDebugUnitTestRuntimeClasspath,minimalImplementationDependenciesMetadata,minimalReleaseCompileClasspath,minimalReleaseRuntimeClasspath,minimalReleaseUnitTestCompileClasspath,minimalReleaseUnitTestRuntimeClasspath
empty=_agp_internal_javaPreCompileFullDebugAndroidTest_kspClasspath,_agp_internal_javaPreCompileFullDebugUnitTest_kspClasspath,_agp_internal_javaPreCompileFullReleaseUnitTest_kspClasspath,_agp_internal_javaPreCompileMinimalDebugAndroidTest_kspClasspath,_agp_internal_javaPreCompileMinimalDebugUnitTest_kspClasspath,_agp_internal_javaPreCompileMinimalReleaseUnitTest_kspClasspath,androidApis,androidJdkImage,androidTestApiDependenciesMetadata,androidTestCompileOnlyDependenciesMetadata,androidTestDebugApiDependenciesMetadata,androidTestDebugCompileOnlyDependenciesMetadata,androidTestDebugImplementationDependenciesMetadata,androidTestDebugIntransitiveDependenciesMetadata,androidTestFullApiDependenciesMetadata,androidTestFullCompileOnlyDependenciesMetadata,androidTestFullDebugApiDependenciesMetadata,androidTestFullDebugCompileOnlyDependenciesMetadata,androidTestFullDebugImplementationDependenciesMetadata,androidTestFullDebugIntransitiveDependenciesMetadata,androidTestFullImplementationDependenciesMetadata,androidTestFullIntransitiveDependenciesMetadata,androidTestIntransitiveDependenciesMetadata,androidTestMinimalApiDependenciesMetadata,androidTestMinimalCompileOnlyDependenciesMetadata,androidTestMinimalDebugApiDependenciesMetadata,androidTestMinimalDebugCompileOnlyDependenciesMetadata,androidTestMinimalDebugImplementationDependenciesMetadata,androidTestMinimalDebugIntransitiveDependenciesMetadata,androidTestMinimalImplementationDependenciesMetadata,androidTestMinimalIntransitiveDependenciesMetadata,androidTestReleaseApiDependenciesMetadata,androidTestReleaseCompileOnlyDependenciesMetadata,androidTestReleaseImplementationDependenciesMetadata,androidTestReleaseIntransitiveDependenciesMetadata,androidTestUtil,compileOnlyDependenciesMetadata,debugApiDependenciesMetadata,debugCompileOnlyDependenciesMetadata,debugIntransitiveDependenciesMetadata,fullApiDependenciesMetadata,fullCompileOnlyDependenciesMetadata,fullDebugAndroidTestAnnotationProcessorClasspath,fullDebugAndroidTestApiDependenciesMetadata,fullDebugAndroidTestCompileOnlyDependenciesMetadata,fullDebugAndroidTestImplementationDependenciesMetadata,fullDebugAndroidTestIntransitiveDependenciesMetadata,fullDebugAnnotationProcessorClasspath,fullDebugApiDependenciesMetadata,fullDebugCompileOnlyDependenciesMetadata,fullDebugImplementationDependenciesMetadata,fullDebugIntransitiveDependenciesMetadata,fullDebugReverseMetadataValues,fullDebugUnitTestAnnotationProcessorClasspath,fullDebugUnitTestApiDependenciesMetadata,fullDebugUnitTestCompileOnlyDependenciesMetadata,fullDebugUnitTestImplementationDependenciesMetadata,fullDebugUnitTestIntransitiveDependenciesMetadata,fullDebugWearBundling,fullIntransitiveDependenciesMetadata,fullReleaseAnnotationProcessorClasspath,fullReleaseApiDependenciesMetadata,fullReleaseCompileOnlyDependenciesMetadata,fullReleaseImplementationDependenciesMetadata,fullReleaseIntransitiveDependenciesMetadata,fullReleaseReverseMetadataValues,fullReleaseUnitTestAnnotationProcessorClasspath,fullReleaseUnitTestApiDependenciesMetadata,fullReleaseUnitTestCompileOnlyDependenciesMetadata,fullReleaseUnitTestImplementationDependenciesMetadata,fullReleaseUnitTestIntransitiveDependenciesMetadata,fullReleaseWearBundling,hiltCompileOnlyFullDebug,hiltCompileOnlyFullDebugAndroidTest,hiltCompileOnlyFullDebugUnitTest,hiltCompileOnlyFullRelease,hiltCompileOnlyFullReleaseUnitTest,hiltCompileOnlyMinimalDebug,hiltCompileOnlyMinimalDebugAndroidTest,hiltCompileOnlyMinimalDebugUnitTest,hiltCompileOnlyMinimalRelease,hiltCompileOnlyMinimalReleaseUnitTest,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,lintChecks,lintPublish,minimalApiDependenciesMetadata,minimalCompileOnlyDependenciesMetadata,minimalDebugAndroidTestAnnotationProcessorClasspath,minimalDebugAndroidTestApiDependenciesMetadata,minimalDebugAndroidTestCompileOnlyDependenciesMetadata,minimalDebugAndroidTestImplementationDependenciesMetadata,minimalDebugAndroidTestIntransitiveDependenciesMetadata,minimalDebugAnnotationProcessorClasspath,minimalDebugApiDependenciesMetadata,minimalDebugCompileOnlyDependenciesMetadata,minimalDebugImplementationDependenciesMetadata,minimalDebugIntransitiveDependenciesMetadata,minimalDebugReverseMetadataValues,minimalDebugUnitTestAnnotationProcessorClasspath,minimalDebugUnitTestApiDependenciesMetadata,minimalDebugUnitTestCompileOnlyDependenciesMetadata,minimalDebugUnitTestImplementationDependenciesMetadata,minimalDebugUnitTestIntransitiveDependenciesMetadata,minimalDebugWearBundling,minimalIntransitiveDependenciesMetadata,minimalReleaseAnnotationProcessorClasspath,minimalReleaseApiDependenciesMetadata,minimalReleaseCompileOnlyDependenciesMetadata,minimalReleaseImplementationDependenciesMetadata,minimalReleaseIntransitiveDependenciesMetadata,minimalReleaseReverseMetadataValues,minimalReleaseUnitTestAnnotationProcessorClasspath,minimalReleaseUnitTestApiDependenciesMetadata,minimalReleaseUnitTestCompileOnlyDependenciesMetadata,minimalReleaseUnitTestImplementationDependenciesMetadata,minimalReleaseUnitTestIntransitiveDependenciesMetadata,minimalReleaseWearBundling,releaseApiDependenciesMetadata,releaseCompileOnlyDependenciesMetadata,releaseImplementationDependenciesMetadata,releaseIntransitiveDependenciesMetadata,testApiDependenciesMetadata,testCompileOnlyDependenciesMetadata,testDebugApiDependenciesMetadata,testDebugCompileOnlyDependenciesMetadata,testDebugImplementationDependenciesMetadata,testDebugIntransitiveDependenciesMetadata,testFixturesApiDependenciesMetadata,testFixturesCompileOnlyDependenciesMetadata,testFixturesDebugApiDependenciesMetadata,testFixturesDebugCompileOnlyDependenciesMetadata,testFixturesDebugImplementationDependenciesMetadata,testFixturesDebugIntransitiveDependenciesMetadata,testFixturesFullApiDependenciesMetadata,testFixturesFullCompileOnlyDependenciesMetadata,testFixturesFullImplementationDependenciesMetadata,testFixturesFullIntransitiveDependenciesMetadata,testFixturesImplementationDependenciesMetadata,testFixturesIntransitiveDependenciesMetadata,testFixturesMinimalApiDependenciesMetadata,testFixturesMinimalCompileOnlyDependenciesMetadata,testFixturesMinimalImplementationDependenciesMetadata,testFixturesMinimalIntransitiveDependenciesMetadata,testFixturesReleaseApiDependenciesMetadata,testFixturesReleaseCompileOnlyDependenciesMetadata,testFixturesReleaseImplementationDependenciesMetadata,testFixturesReleaseIntransitiveDependenciesMetadata,testFullApiDependenciesMetadata,testFullCompileOnlyDependenciesMetadata,testFullDebugApiDependenciesMetadata,testFullDebugCompileOnlyDependenciesMetadata,testFullDebugImplementationDependenciesMetadata,testFullDebugIntransitiveDependenciesMetadata,testFullImplementationDependenciesMetadata,testFullIntransitiveDependenciesMetadata,testFullReleaseApiDependenciesMetadata,testFullReleaseCompileOnlyDependenciesMetadata,testFullReleaseImplementationDependenciesMetadata,testFullReleaseIntransitiveDependenciesMetadata,testImplementationDependenciesMetadata,testIntransitiveDependenciesMetadata,testMinimalApiDependenciesMetadata,testMinimalCompileOnlyDependenciesMetadata,testMinimalDebugApiDependenciesMetadata,testMinimalDebugCompileOnlyDependenciesMetadata,testMinimalDebugImplementationDependenciesMetadata,testMinimalDebugIntransitiveDependenciesMetadata,testMinimalImplementationDependenciesMetadata,testMinimalIntransitiveDependenciesMetadata,testMinimalReleaseApiDependenciesMetadata,testMinimalReleaseCompileOnlyDependenciesMetadata,testMinimalReleaseImplementationDependenciesMetadata,testMinimalReleaseIntransitiveDependenciesMetadata,testReleaseApiDependenciesMetadata,testReleaseCompileOnlyDependenciesMetadata,testReleaseImplementationDependenciesMetadata,testReleaseIntransitiveDependenciesMetadata
diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml
index 71a441ea626..733d7ea625c 100644
--- a/app/lint-baseline.xml
+++ b/app/lint-baseline.xml
@@ -360,7 +360,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
@@ -624,6 +624,17 @@
column="10"/>
+
+
+
+
@@ -719,7 +730,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1227,7 +1238,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1238,7 +1249,7 @@
errorLine2=" ^">
@@ -1249,7 +1260,7 @@
errorLine2=" ^">
@@ -1260,7 +1271,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1271,7 +1282,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1282,7 +1293,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1293,7 +1304,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1304,7 +1315,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1315,7 +1326,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1326,7 +1337,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1337,7 +1348,7 @@
errorLine2=" ^">
@@ -1348,7 +1359,7 @@
errorLine2=" ^">
@@ -1403,7 +1414,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
@@ -1414,7 +1425,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
@@ -1425,7 +1436,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1436,7 +1447,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1447,7 +1458,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/app/src/full/kotlin/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt b/app/src/full/kotlin/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt
index aa55964c5b1..e06f0933dff 100644
--- a/app/src/full/kotlin/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt
+++ b/app/src/full/kotlin/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt
@@ -7,8 +7,10 @@ import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.onboarding.getMessagingToken
+import io.homeassistant.companion.android.util.tryRegisterCurrentDistributor
import javax.inject.Inject
import kotlinx.coroutines.launch
+import org.unifiedpush.android.connector.UnifiedPush
import timber.log.Timber
@ActivityScoped
@@ -21,11 +23,20 @@ class LaunchPresenterImpl @Inject constructor(
serverManager.defaultServers.forEach {
ioScope.launch {
try {
+ // Don't get a new push token if using UnifiedPush.
+ val messagingToken = if (!UnifiedPush.tryRegisterCurrentDistributor(view as Context)) {
+ getMessagingToken()
+ } else {
+ null
+ }
serverManager.integrationRepository(it.id).updateRegistration(
DeviceRegistration(
- "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
- null,
- getMessagingToken()
+ appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
+ deviceName = null,
+ pushToken = messagingToken,
+ // A blank url indicates to use the build-time push url.
+ pushUrl = messagingToken?.let { "" },
+ pushEncrypt = messagingToken == null && UnifiedPush.getAckDistributor(view as Context) != null
)
)
serverManager.integrationRepository(it.id).getConfig() // Update cached data
diff --git a/app/src/full/kotlin/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt b/app/src/full/kotlin/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt
index 499f0c02432..80b2e867f3d 100644
--- a/app/src/full/kotlin/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt
+++ b/app/src/full/kotlin/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt
@@ -40,6 +40,11 @@ class FirebaseCloudMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
mainScope.launch {
Timber.d("Refreshed token: $token")
+ if (messagingManager.isUnifiedPushEnabled()) {
+ // Updating registration while using UnifiedPush will overwrite its token, so ignore new FCM tokens.
+ Timber.d("Not trying to update registration since UnifiedPush is being used.")
+ return@launch
+ }
if (!serverManager.isRegistered()) {
Timber.d("Not trying to update registration since we aren't authenticated.")
return@launch
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3a1febe0174..1fece505318 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -915,6 +915,18 @@
android:value="true" />
+
+
+
+
+
+
+
+
+
, source: String, serverId: Int = ServerManager.SERVER_ID_ACTIVE) {
+ val flattened = mutableMapOf()
+ if (notificationData.containsKey("data")) {
+ for ((key, value) in notificationData["data"] as Map<*, *>) {
+ if (key == "actions" && value is List<*>) {
+ value.forEachIndexed { i, action ->
+ if (action is Map<*, *>) {
+ flattened["action_${i + 1}_key"] = action["action"].toString()
+ flattened["action_${i + 1}_title"] = action["title"].toString()
+ action["uri"]?.let { uri -> flattened["action_${i + 1}_uri"] = uri.toString() }
+ action["behavior"]?.let { behavior -> flattened["action_${i + 1}_behavior"] = behavior.toString() }
+ }
+ }
+ } else {
+ flattened[key.toString()] = value.toString()
+ }
+ }
+ }
+ // Message and title are in the root unlike all the others.
+ listOf("message", "title").forEach { key ->
+ if (notificationData.containsKey(key)) {
+ flattened[key] = notificationData[key].toString()
+ }
+ }
+ if (notificationData.containsKey("registration_info")) {
+ val registrationInfo = notificationData["registration_info"]
+ if (registrationInfo is Map<*, *> && registrationInfo.containsKey("webhook_id")) {
+ flattened["webhook_id"] = registrationInfo["webhook_id"].toString()
+ }
+ }
+ if (!flattened.containsKey("webhook_id")) {
+ serverManager.getServer(serverId)?.let { server ->
+ flattened["webhook_id"] = server.connection.webhookId.toString()
+ }
+ }
+ handleMessage(flattened, source)
+ }
+
fun handleMessage(notificationData: Map, source: String) {
var now = System.currentTimeMillis()
var jsonData = notificationData
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsFragment.kt b/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsFragment.kt
index 797d7bd0dda..d24c0f9e589 100644
--- a/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsFragment.kt
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsFragment.kt
@@ -47,6 +47,7 @@ import io.homeassistant.companion.android.settings.vehicle.ManageAndroidAutoSett
import io.homeassistant.companion.android.settings.wear.SettingsWearActivity
import io.homeassistant.companion.android.settings.wear.SettingsWearDetection
import io.homeassistant.companion.android.settings.widgets.ManageWidgetsSettingsFragment
+import io.homeassistant.companion.android.unifiedpush.UnifiedPushManager
import io.homeassistant.companion.android.webview.WebViewActivity
import java.time.Instant
import java.time.ZoneId
@@ -229,6 +230,7 @@ class SettingsFragment(
}
updateNotificationChannelPrefs()
+ updateNotificationUnifiedPushPrefs()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
findPreference("notification_permission")?.let {
@@ -480,6 +482,30 @@ class SettingsFragment(
}
}
+ private fun updateNotificationUnifiedPushPrefs() {
+ val notificationsEnabled =
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
+ NotificationManagerCompat.from(requireContext()).areNotificationsEnabled()
+
+ findPreference("notification_unifiedpush")?.let {
+ val distributors = presenter.getUnifiedPushDistributors()
+ it.isVisible = notificationsEnabled && distributors.isNotEmpty()
+ val pm = requireContext().packageManager
+ it.entries = distributors.map { distributor ->
+ // Map package name to app display name.
+ try {
+ pm.getApplicationLabel(pm.getApplicationInfo(distributor, PackageManager.GET_META_DATA)).toString()
+ } catch (_: PackageManager.NameNotFoundException) {
+ distributor
+ }
+ }.toTypedArray() + getString(commonR.string.disabled)
+ it.entryValues = distributors.toTypedArray() + UnifiedPushManager.DISTRIBUTOR_DISABLED
+ if (it.value == null) {
+ it.value = UnifiedPushManager.DISTRIBUTOR_DISABLED
+ }
+ }
+ }
+
private fun onServerLockResult(result: Int): Boolean {
if (result == Authenticator.SUCCESS && serverAuth != null) {
(activity as? SettingsActivity)?.setAppActive(serverAuth, true)
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenter.kt b/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenter.kt
index 0f377c73682..eaf384b092e 100644
--- a/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenter.kt
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenter.kt
@@ -23,5 +23,6 @@ interface SettingsPresenter {
fun getServersFlow(): StateFlow>
fun getServerCount(): Int
suspend fun getNotificationRateLimits(): RateLimitResponse?
+ fun getUnifiedPushDistributors(): List
fun showChangeLog(context: Context)
}
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt b/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt
index 3bdb886b806..964b62772b6 100644
--- a/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt
@@ -31,6 +31,7 @@ import io.homeassistant.companion.android.onboarding.getMessagingToken
import io.homeassistant.companion.android.sensors.LocationSensorManager
import io.homeassistant.companion.android.settings.language.LanguagesManager
import io.homeassistant.companion.android.themes.ThemesManager
+import io.homeassistant.companion.android.unifiedpush.UnifiedPushManager
import io.homeassistant.companion.android.util.ChangeLog
import io.homeassistant.companion.android.util.UrlUtil
import javax.inject.Inject
@@ -50,6 +51,7 @@ class SettingsPresenterImpl @Inject constructor(
private val prefsRepository: PrefsRepository,
private val themesManager: ThemesManager,
private val langsManager: LanguagesManager,
+ private val unifiedPushManager: UnifiedPushManager,
private val changeLog: ChangeLog,
private val settingsDao: SettingsDao,
private val sensorDao: SensorDao
@@ -108,6 +110,7 @@ class SettingsPresenterImpl @Inject constructor(
"languages" -> langsManager.getCurrentLang()
"page_zoom" -> prefsRepository.getPageZoomLevel().toString()
"screen_orientation" -> prefsRepository.getScreenOrientation()
+ "notification_unifiedpush" -> unifiedPushManager.getDistributor()
else -> throw IllegalArgumentException("No string found by this key: $key")
}
}
@@ -119,6 +122,7 @@ class SettingsPresenterImpl @Inject constructor(
"languages" -> langsManager.saveLang(value)
"page_zoom" -> prefsRepository.setPageZoomLevel(value?.toIntOrNull())
"screen_orientation" -> prefsRepository.saveScreenOrientation(value)
+ "notification_unifiedpush" -> unifiedPushManager.saveDistributor(value)
else -> throw IllegalArgumentException("No string found by this key: $key")
}
}
@@ -155,6 +159,9 @@ class SettingsPresenterImpl @Inject constructor(
}
}
+ override fun getUnifiedPushDistributors(): List =
+ unifiedPushManager.getDistributors()
+
override fun showChangeLog(context: Context) {
changeLog.showChangeLog(context, true)
}
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushManager.kt b/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushManager.kt
new file mode 100644
index 00000000000..80e40c10aab
--- /dev/null
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushManager.kt
@@ -0,0 +1,132 @@
+package io.homeassistant.companion.android.unifiedpush
+
+import android.content.Context
+import android.widget.Toast
+import androidx.work.Constraints
+import androidx.work.NetworkType
+import dagger.hilt.android.qualifiers.ApplicationContext
+import io.homeassistant.companion.android.common.R as commonR
+import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
+import io.homeassistant.companion.android.common.data.servers.ServerManager
+import io.homeassistant.companion.android.notifications.MessagingManager
+import io.homeassistant.companion.android.onboarding.getMessagingToken
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import org.unifiedpush.android.connector.FailedReason
+import org.unifiedpush.android.connector.UnifiedPush
+import org.unifiedpush.android.connector.data.PushEndpoint
+import timber.log.Timber
+
+class UnifiedPushManager @Inject constructor(
+ @ApplicationContext val context: Context,
+ private val serverManager: ServerManager,
+ private val messagingManager: MessagingManager,
+) {
+ companion object {
+ const val DISTRIBUTOR_DISABLED = "disabled"
+
+ // Synchronize registration for when it is called from UnifiedPushWorker.
+ @Synchronized
+ fun register(context: Context) =
+ UnifiedPush.register(context)
+
+ @Synchronized
+ fun unregister(context: Context) =
+ UnifiedPush.unregister(context)
+ }
+
+ private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
+
+ private var distributors: List = emptyList()
+ private var retried = false
+
+ fun saveDistributor(distributor: String?) {
+ Timber.d("saveDistributor(): $distributor")
+ if (distributor == null || distributor == DISTRIBUTOR_DISABLED) {
+ unregister(context)
+ updateEndpoint(null)
+ retried = false
+ } else {
+ UnifiedPush.saveDistributor(context, distributor)
+ register(context)
+ }
+ }
+
+ fun getDistributors(): List {
+ if (distributors.isEmpty()) {
+ distributors = UnifiedPush.getDistributors(context)
+ }
+ return distributors
+ }
+
+ fun getDistributor(): String? =
+ UnifiedPush.getAckDistributor(context)
+
+ fun updateEndpoint(endpoint: PushEndpoint?) {
+ Timber.d("updateEndpoint(): ${endpoint?.url}")
+ mainScope.launch {
+ val url = endpoint?.url.orEmpty()
+ val token = if (endpoint != null) {
+ // Use public key as push token to allow for encryption.
+ endpoint.pubKeySet?.let { it.auth + ":" + it.pubKey } ?: ""
+ } else {
+ // Revert to FCM token when disabling UnifiedPush.
+ getMessagingToken()
+ }
+ val registered = messagingManager.isUnifiedPushEnabled()
+ messagingManager.setUnifiedPushEnabled(endpoint != null)
+
+ if (!serverManager.isRegistered()) {
+ Timber.d("Not trying to update registration since we aren't authenticated.")
+ return@launch
+ }
+ val encrypt = endpoint != null && token.isNotBlank()
+ serverManager.defaultServers.forEach {
+ launch {
+ try {
+ serverManager.integrationRepository(it.id).updateRegistration(
+ deviceRegistration = DeviceRegistration(
+ pushUrl = url,
+ pushToken = token,
+ pushEncrypt = encrypt
+ ),
+ allowReregistration = false
+ )
+ } catch (e: Exception) {
+ Timber.e(e, "Issue updating push url")
+ }
+ }
+ }
+ if (!registered) {
+ Toast.makeText(context, commonR.string.unifiedpush_enabled, Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+
+ fun onRegistrationFailed(reason: FailedReason) {
+ if (!retried) { // Only retry once to prevent infinite loop.
+ retried = true
+ when (reason) {
+ FailedReason.INTERNAL_ERROR -> {
+ Timber.d("Retrying registration.")
+ UnifiedPushWorker.start(context) // Retry immediately
+ }
+ FailedReason.NETWORK -> { // Retry once network is connected
+ Timber.d("Retrying registration once network is connected.")
+ val constraints = Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .build()
+ UnifiedPushWorker.start(context, constraints)
+ }
+ else -> mainScope.launch {
+ messagingManager.setUnifiedPushEnabled(false)
+ }
+ }
+ } else {
+ Timber.d("Already retried registering")
+ }
+ }
+}
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushReceiver.kt b/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushReceiver.kt
new file mode 100644
index 00000000000..7def24bd1f6
--- /dev/null
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushReceiver.kt
@@ -0,0 +1,48 @@
+package io.homeassistant.companion.android.unifiedpush
+
+import android.content.Context
+import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
+import com.fasterxml.jackson.module.kotlin.readValue
+import dagger.hilt.android.AndroidEntryPoint
+import io.homeassistant.companion.android.notifications.MessagingManager
+import javax.inject.Inject
+import org.unifiedpush.android.connector.FailedReason
+import org.unifiedpush.android.connector.MessagingReceiver
+import org.unifiedpush.android.connector.data.PushEndpoint
+import org.unifiedpush.android.connector.data.PushMessage
+import timber.log.Timber
+
+@AndroidEntryPoint
+class UnifiedPushReceiver : MessagingReceiver() {
+ companion object {
+ const val SOURCE = "UnifiedPush"
+ }
+
+ @Inject
+ lateinit var unifiedPushManager: UnifiedPushManager
+
+ @Inject
+ lateinit var messagingManager: MessagingManager
+
+ override fun onMessage(context: Context, message: PushMessage, instance: String) {
+ Timber.d("From: $instance")
+
+ val data: Map = jacksonObjectMapper().readValue(message.content)
+ messagingManager.handleMessage(data, SOURCE)
+ }
+
+ override fun onNewEndpoint(context: Context, endpoint: PushEndpoint, instance: String) {
+ Timber.d("New Endpoint: ${endpoint.url} for $instance")
+ unifiedPushManager.updateEndpoint(endpoint)
+ }
+
+ override fun onRegistrationFailed(context: Context, reason: FailedReason, instance: String) {
+ Timber.e("Issue registering: $reason")
+ unifiedPushManager.onRegistrationFailed(reason)
+ }
+
+ override fun onUnregistered(context: Context, instance: String) {
+ Timber.d("Unregistered: $instance")
+ unifiedPushManager.saveDistributor(null)
+ }
+}
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushWorker.kt b/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushWorker.kt
new file mode 100644
index 00000000000..ba25edb5194
--- /dev/null
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/unifiedpush/UnifiedPushWorker.kt
@@ -0,0 +1,33 @@
+package io.homeassistant.companion.android.unifiedpush
+
+import android.content.Context
+import androidx.work.Constraints
+import androidx.work.CoroutineWorker
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.WorkerParameters
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class UnifiedPushWorker(
+ appContext: Context,
+ workerParams: WorkerParameters
+) : CoroutineWorker(appContext, workerParams) {
+
+ companion object {
+ const val TAG = "UnifiedPushWorker"
+
+ fun start(context: Context, constraints: Constraints = Constraints.NONE) {
+ val request = OneTimeWorkRequestBuilder()
+ .setConstraints(constraints)
+ .build()
+ WorkManager.getInstance(context).enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
+ }
+ }
+
+ override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
+ UnifiedPushManager.register(this@UnifiedPushWorker.applicationContext)
+ Result.success()
+ }
+}
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/util/UnifiedPushExtensions.kt b/app/src/main/kotlin/io/homeassistant/companion/android/util/UnifiedPushExtensions.kt
new file mode 100644
index 00000000000..29cc1fe98fd
--- /dev/null
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/util/UnifiedPushExtensions.kt
@@ -0,0 +1,23 @@
+package io.homeassistant.companion.android.util
+
+import android.content.Context
+import org.unifiedpush.android.connector.UnifiedPush
+
+fun UnifiedPush.tryRegisterCurrentOrDefaultDistributor(context: Context): Boolean {
+ var result = false
+ tryUseCurrentOrDefaultDistributor(context) { success ->
+ if (success) {
+ register(context)
+ }
+ result = success
+ }
+ return result
+}
+
+fun UnifiedPush.tryRegisterCurrentDistributor(context: Context): Boolean {
+ val hasDistributor = !getAckDistributor(context).isNullOrBlank()
+ if (hasDistributor) {
+ register(context)
+ }
+ return hasDistributor
+}
diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/websocket/WebsocketManager.kt b/app/src/main/kotlin/io/homeassistant/companion/android/websocket/WebsocketManager.kt
index bae55e7dc09..7b058b67c41 100644
--- a/app/src/main/kotlin/io/homeassistant/companion/android/websocket/WebsocketManager.kt
+++ b/app/src/main/kotlin/io/homeassistant/companion/android/websocket/WebsocketManager.kt
@@ -178,33 +178,7 @@ class WebsocketManager(
Timber.e(e, "Unable to confirm received notification")
}
}
- val flattened = mutableMapOf()
- if (it.containsKey("data")) {
- for ((key, value) in it["data"] as Map<*, *>) {
- if (key == "actions" && value is List<*>) {
- value.forEachIndexed { i, action ->
- if (action is Map<*, *>) {
- flattened["action_${i + 1}_key"] = action["action"].toString()
- flattened["action_${i + 1}_title"] = action["title"].toString()
- action["uri"]?.let { uri -> flattened["action_${i + 1}_uri"] = uri.toString() }
- action["behavior"]?.let { behavior -> flattened["action_${i + 1}_behavior"] = behavior.toString() }
- }
- }
- } else {
- flattened[key.toString()] = value.toString()
- }
- }
- }
- // Message and title are in the root unlike all the others.
- listOf("message", "title").forEach { key ->
- if (it.containsKey(key)) {
- flattened[key] = it[key].toString()
- }
- }
- serverManager.getServer(serverId)?.let { server ->
- flattened["webhook_id"] = server.connection.webhookId.toString()
- }
- messagingManager.handleMessage(flattened, SOURCE)
+ messagingManager.handleMessage(it, SOURCE, serverId)
}
}
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 775c21d0077..ad479d3f990 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -127,6 +127,12 @@
app:enableCopying="true"
android:icon="@drawable/ic_notifications"
android:summary="@string/rate_limit_summary"/>
+
@@ -1313,7 +1313,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1324,7 +1324,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1335,7 +1335,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1346,7 +1346,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1357,7 +1357,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1368,7 +1368,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1379,7 +1379,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1390,7 +1390,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1401,7 +1401,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1412,7 +1412,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1423,7 +1423,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1434,7 +1434,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1445,7 +1445,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1456,7 +1456,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1467,7 +1467,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1478,7 +1478,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1489,7 +1489,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1500,7 +1500,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1511,7 +1511,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1522,7 +1522,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1533,7 +1533,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1544,7 +1544,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1555,7 +1555,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1566,7 +1566,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1577,7 +1577,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1588,7 +1588,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1599,7 +1599,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1610,7 +1610,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1742,7 +1742,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1753,7 +1753,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1764,7 +1764,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1775,7 +1775,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1786,7 +1786,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1797,7 +1797,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1808,7 +1808,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1819,7 +1819,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1830,7 +1830,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1841,7 +1841,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1852,10 +1852,21 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+
+
+
+
@@ -1874,7 +1885,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1885,7 +1896,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1896,7 +1907,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1907,7 +1918,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -1918,7 +1929,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -2633,7 +2644,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -2644,7 +2655,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3220,7 +3231,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3231,7 +3242,7 @@
errorLine2=" ^">
@@ -3242,7 +3253,7 @@
errorLine2=" ^">
@@ -3253,7 +3264,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3264,7 +3275,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3275,7 +3286,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3286,7 +3297,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3297,7 +3308,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3308,7 +3319,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3319,7 +3330,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3330,7 +3341,7 @@
errorLine2=" ^">
@@ -3341,7 +3352,7 @@
errorLine2=" ^">
@@ -3396,7 +3407,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
@@ -3407,7 +3418,7 @@
errorLine2=" ~~~~~~~~~~~~~~~">
@@ -3418,7 +3429,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3429,7 +3440,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3440,7 +3451,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationDependenciesConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationDependenciesConventionPlugin.kt
index 754a37fa6b3..6526695bace 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationDependenciesConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationDependenciesConventionPlugin.kt
@@ -52,6 +52,11 @@ class AndroidApplicationDependenciesConventionPlugin : Plugin {
"implementation"(libs.jackson.module.kotlin)
"implementation"(libs.okhttp)
+ "fullImplementation"(libs.unifiedpush.connector)
+ "minimalImplementation"(libs.unifiedpush.connector) {
+ exclude(group = "com.google.protobuf", module = "protobuf-java")
+ }
+
"implementation"(libs.bundles.coil)
"fullImplementation"(libs.play.services.location)
diff --git a/common/lint-baseline.xml b/common/lint-baseline.xml
index bb11330100a..36d9cb407ba 100644
--- a/common/lint-baseline.xml
+++ b/common/lint-baseline.xml
@@ -1,5 +1,5 @@
-
+
@@ -30,7 +30,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -41,7 +41,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -63,7 +63,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -74,7 +74,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -85,7 +85,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -162,7 +162,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -173,7 +173,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -184,7 +184,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -195,7 +195,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -250,7 +250,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -261,7 +261,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/DeviceRegistration.kt b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/DeviceRegistration.kt
index 90a3e16f860..bf73e0927fe 100644
--- a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/DeviceRegistration.kt
+++ b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/DeviceRegistration.kt
@@ -4,5 +4,7 @@ data class DeviceRegistration(
val appVersion: String? = null,
val deviceName: String? = null,
var pushToken: String? = null,
- var pushWebsocket: Boolean = true
+ var pushUrl: String? = null,
+ var pushWebsocket: Boolean = true,
+ var pushEncrypt: Boolean = false
)
diff --git a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt
index 268b0303a48..d60bb0bb29d 100644
--- a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt
+++ b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt
@@ -57,6 +57,7 @@ class IntegrationRepositoryImpl @AssistedInject constructor(
private const val PREF_APP_VERSION = "app_version" // Note: _not_ server-specific
private const val PREF_PUSH_TOKEN = "push_token" // Note: _not_ server-specific
+ private const val PREF_PUSH_URL = "push_url" // Note: _not_ server-specific
private const val PREF_ORPHANED_THREAD_BORDER_AGENT_IDS = "orphaned_thread_border_agent_ids" // Note: _not_ server-specific
private const val PREF_CHECK_SENSOR_REGISTRATION_NEXT = "sensor_reg_last"
@@ -167,7 +168,8 @@ class IntegrationRepositoryImpl @AssistedInject constructor(
return DeviceRegistration(
localStorage.getString(PREF_APP_VERSION),
server.deviceName,
- localStorage.getString(PREF_PUSH_TOKEN)
+ localStorage.getString(PREF_PUSH_TOKEN),
+ localStorage.getString(PREF_PUSH_URL),
)
}
@@ -181,6 +183,9 @@ class IntegrationRepositoryImpl @AssistedInject constructor(
if (deviceRegistration.pushToken != null) {
localStorage.putString(PREF_PUSH_TOKEN, deviceRegistration.pushToken)
}
+ if (deviceRegistration.pushUrl != null) {
+ localStorage.putString(PREF_PUSH_URL, deviceRegistration.pushUrl)
+ }
}
override suspend fun deletePreferences() {
@@ -815,11 +820,18 @@ class IntegrationRepositoryImpl @AssistedInject constructor(
private suspend fun createUpdateRegistrationRequest(deviceRegistration: DeviceRegistration): RegisterDeviceRequest {
val oldDeviceRegistration = getRegistration()
val pushToken = deviceRegistration.pushToken ?: oldDeviceRegistration.pushToken
+ val pushUrl = deviceRegistration.pushUrl ?: oldDeviceRegistration.pushUrl
val appData = mutableMapOf("push_websocket_channel" to deviceRegistration.pushWebsocket)
if (!pushToken.isNullOrBlank()) {
- appData["push_url"] = PUSH_URL
+ appData["push_url"] = pushUrl?.ifBlank { PUSH_URL } ?: PUSH_URL
appData["push_token"] = pushToken
+ // UnifiedPush notifications should be encrypted when possible.
+ appData["push_encrypt"] = deviceRegistration.pushEncrypt && (pushUrl.isNullOrBlank() || pushUrl == PUSH_URL)
+ } else if (!pushUrl.isNullOrBlank()) {
+ appData["push_url"] = pushUrl
+ appData["push_token"] = ""
+ appData["push_encrypt"] = false
}
return RegisterDeviceRequest(
diff --git a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt
index 16916d834ea..d5a25244b54 100644
--- a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt
+++ b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt
@@ -95,6 +95,10 @@ interface PrefsRepository {
suspend fun addImprovPermissionDisplayedCount()
+ suspend fun isUnifiedPushEnabled(): Boolean
+
+ suspend fun setUnifiedPushEnabled(enabled: Boolean)
+
/** Clean up any app-level preferences that might reference servers */
suspend fun removeServer(serverId: Int)
}
diff --git a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt
index 26cbeb87aa9..f8dca098559 100644
--- a/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt
+++ b/common/src/main/kotlin/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt
@@ -38,6 +38,7 @@ class PrefsRepositoryImpl @Inject constructor(
private const val PREF_AUTO_FAVORITES = "auto_favorites"
private const val PREF_LOCATION_HISTORY_DISABLED = "location_history"
private const val PREF_IMPROV_PERMISSION_DISPLAYED = "improv_permission_displayed"
+ private const val PREF_UNIFIEDPUSH_ENABLED = "unifiedpush_enabled"
}
init {
@@ -260,6 +261,12 @@ class PrefsRepositoryImpl @Inject constructor(
localStorage.putInt(PREF_IMPROV_PERMISSION_DISPLAYED, getImprovPermissionDisplayedCount() + 1)
}
+ override suspend fun isUnifiedPushEnabled(): Boolean =
+ localStorage.getBoolean(PREF_UNIFIEDPUSH_ENABLED)
+
+ override suspend fun setUnifiedPushEnabled(enabled: Boolean) =
+ localStorage.putBoolean(PREF_UNIFIEDPUSH_ENABLED, enabled)
+
override suspend fun removeServer(serverId: Int) {
val controlsAuthEntities = getControlsAuthEntities().filter { it.split(".")[0].toIntOrNull() != serverId }
setControlsAuthEntities(controlsAuthEntities)
diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml
index 26649cb388d..f526dcd8e91 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -976,6 +976,8 @@
Failed to initialize a text to speech engine.
Please set the text for text to speech to process
Unable to register application
+ UnifiedPush
+ UnifiedPush enabled
Unknown address
Update pinned shortcut data
Update shortcut data
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index dfd6eb1f95f..019ee997942 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -70,6 +70,7 @@ screenshot = "0.0.1-alpha09"
sentry-android = "8.10.0"
timber = "5.0.1"
tools-desugar-jdk-libs = "2.1.5"
+unifiedpush-connector = "3.0.8"
watchfaceComplicationsDataSourceKtx = "1.2.1"
wear = "1.3.0"
wear-compose-foundation = "1.4.1"
@@ -201,6 +202,7 @@ robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectr
sentry-android-core = { module = "io.sentry:sentry-android-core", version.ref = "sentry-android" }
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
tools-desugar-jdk = { module = "com.android.tools:desugar_jdk_libs", version.ref = "tools-desugar-jdk-libs" }
+unifiedpush-connector = { module = "org.unifiedpush.android:connector", version.ref = "unifiedpush-connector" }
wear = { module = "androidx.wear:wear", version.ref = "wear" }
wear-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wear-compose-foundation" }
wear-compose-material = { module = "androidx.wear.compose:compose-material3", version.ref = "wear-compose-material" }
@@ -222,4 +224,5 @@ media3 = ["media3-exoplayer", "media3-exoplayer-hls", "media3-ui"]
paging = ["paging-runtime", "paging-compose"]
wear-tiles = ["wear-tiles", "wear-protolayout-main", "wear-protolayout-expression", "wear-protolayout-material"]
androidx-test =["androidx-test-core", "androidx-test-ext", "androidx-test-rules", "androidx-test-runner"]
-androidx-compose-ui-test = ["compose-ui-test", "compose-ui-testManifest"]
\ No newline at end of file
+androidx-compose-ui-test = ["compose-ui-test", "compose-ui-testManifest"]
+unifiedpush = ["unifiedpush-connector"]
\ No newline at end of file
diff --git a/wear/lint-baseline.xml b/wear/lint-baseline.xml
index 1c110c43de7..49bd6d22f8c 100644
--- a/wear/lint-baseline.xml
+++ b/wear/lint-baseline.xml
@@ -1,5 +1,5 @@
-
+
@@ -151,7 +151,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -162,7 +162,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -596,7 +596,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
@@ -607,7 +607,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
@@ -618,7 +618,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
@@ -629,7 +629,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
@@ -662,7 +662,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -673,7 +673,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
@@ -684,7 +684,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
@@ -695,7 +695,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -706,7 +706,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -717,7 +717,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -728,7 +728,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/wear/src/main/kotlin/io/homeassistant/companion/android/home/HomePresenterImpl.kt b/wear/src/main/kotlin/io/homeassistant/companion/android/home/HomePresenterImpl.kt
index 50477391194..e11b21f9640 100644
--- a/wear/src/main/kotlin/io/homeassistant/companion/android/home/HomePresenterImpl.kt
+++ b/wear/src/main/kotlin/io/homeassistant/companion/android/home/HomePresenterImpl.kt
@@ -187,6 +187,7 @@ class HomePresenterImpl @Inject constructor(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
null,
getMessagingToken(),
+ null,
false
)
)
diff --git a/wear/src/main/kotlin/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt b/wear/src/main/kotlin/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt
index 2e7263fbae2..f2409ac5255 100644
--- a/wear/src/main/kotlin/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt
+++ b/wear/src/main/kotlin/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt
@@ -31,6 +31,7 @@ class MobileAppIntegrationPresenterImpl @Inject constructor(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
deviceName,
getMessagingToken(),
+ null,
false
)
}
diff --git a/wear/src/main/kotlin/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt b/wear/src/main/kotlin/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt
index 8ee3e365f78..6f584ce19c0 100755
--- a/wear/src/main/kotlin/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt
+++ b/wear/src/main/kotlin/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt
@@ -181,6 +181,7 @@ class PhoneSettingsListener : WearableListenerService(), DataClient.OnDataChange
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
deviceName,
getMessagingToken(),
+ null,
false
)
)