Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ class SettingsWearViewModel @Inject constructor(private val serverManager: Serve
}
}

private fun readUriData(uri: String): ByteArray {
if (uri.isEmpty()) return ByteArray(0)
private fun readUriData(uri: String?): ByteArray {
if (uri.isNullOrEmpty()) return ByteArray(0)
return getApplication<HomeAssistantApplication>().contentResolver.openInputStream(
uri.toUri(),
)!!.buffered().use {
Expand All @@ -291,8 +291,8 @@ class SettingsWearViewModel @Inject constructor(private val serverManager: Serve
deviceName: String,
deviceTrackingEnabled: Boolean,
notificationsEnabled: Boolean,
tlsClientCertificateUri: String,
tlsClientCertificatePassword: String,
tlsClientCertificateUri: String?,
tlsClientCertificatePassword: String?,
) {
_hasData.value = false // Show loading indicator
val putDataRequest = PutDataMapRequest.create("/authenticate").run {
Expand All @@ -304,7 +304,7 @@ class SettingsWearViewModel @Inject constructor(private val serverManager: Serve
dataMap.putBoolean("LocationTracking", deviceTrackingEnabled)
dataMap.putBoolean("Notifications", notificationsEnabled)
dataMap.putByteArray("TLSClientCertificateData", readUriData(tlsClientCertificateUri))
dataMap.putString("TLSClientCertificatePassword", tlsClientCertificatePassword)
dataMap.putString("TLSClientCertificatePassword", tlsClientCertificatePassword.orEmpty())
setUrgent()
asPutDataRequest()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.wearable.Node
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.HomeAssistantApplication
import io.homeassistant.companion.android.onboarding.OnboardApp
import io.homeassistant.companion.android.onboarding.WearOnboardApp
import io.homeassistant.companion.android.settings.wear.SettingsWearViewModel
import io.homeassistant.companion.android.util.enableEdgeToEdgeCompat
import kotlinx.coroutines.cancel
Expand All @@ -23,7 +22,7 @@ class SettingsWearMainView : AppCompatActivity() {
private val settingsWearViewModel by viewModels<SettingsWearViewModel>()

private val registerActivityResult = registerForActivityResult(
OnboardApp(),
WearOnboardApp(),
this::onOnboardingComplete,
)

Expand Down Expand Up @@ -69,28 +68,21 @@ class SettingsWearMainView : AppCompatActivity() {

private fun loginWearOs() {
registerActivityResult.launch(
OnboardApp.Input(
WearOnboardApp.Input(
url = registerUrl,
defaultDeviceName = currentNodes.firstOrNull()?.displayName ?: "unknown",
locationTrackingPossible = false,
// While notifications are technically possible, the app can't handle this for the Wear device
notificationsPossible = false,
isWatch = true,
discoveryOptions = OnboardApp.DiscoveryOptions.ADD_EXISTING_EXTERNAL,
mayRequireTlsClientCertificate =
(application as HomeAssistantApplication).keyChainRepository.getPrivateKey() != null,
),
)
}

private fun onOnboardingComplete(result: OnboardApp.Output?) {
private fun onOnboardingComplete(result: WearOnboardApp.Output?) {
result?.apply {
settingsWearViewModel.sendAuthToWear(
url,
authCode,
deviceName,
deviceTrackingEnabled,
true,
deviceTrackingEnabled = false,
notificationsEnabled = true,
tlsClientCertificateUri,
tlsClientCertificatePassword,
)
Expand Down
1 change: 0 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<!-- TODO validate that new onboarding works properly on TV -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.homeassistant.companion.android.onboarding

import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.activity.result.contract.ActivityResultContract
import io.homeassistant.companion.android.launcher.intentLauncherWearOnboarding

class WearOnboardApp : ActivityResultContract<WearOnboardApp.Input, WearOnboardApp.Output?>() {
data class Input(val url: String? = null, val defaultDeviceName: String = Build.MODEL)

data class Output(
val url: String,
val authCode: String,
val deviceName: String,
val tlsClientCertificateUri: String?,
val tlsClientCertificatePassword: String?,
) {
fun toIntent(): Intent {
return Intent().apply {
putExtra(EXTRA_OUTPUT_URL, url)
putExtra(EXTRA_OUTPUT_AUTH_CODE, authCode)
putExtra(EXTRA_OUTPUT_DEVICE_NAME, deviceName)
putExtra(EXTRA_OUTPUT_TLS_CLIENT_CERTIFICATE_URI, tlsClientCertificateUri)
putExtra(EXTRA_OUTPUT_TLS_CLIENT_CERTIFICATE_PASSWORD, tlsClientCertificatePassword)
}
}

companion object {
private const val EXTRA_OUTPUT_URL = "URL"
private const val EXTRA_OUTPUT_AUTH_CODE = "AuthCode"
private const val EXTRA_OUTPUT_DEVICE_NAME = "DeviceName"
private const val EXTRA_OUTPUT_TLS_CLIENT_CERTIFICATE_URI = "TLSClientCertificateUri"
private const val EXTRA_OUTPUT_TLS_CLIENT_CERTIFICATE_PASSWORD = "TLSClientCertificatePassword"

fun fromIntent(intent: Intent): Output {
return Output(
url = intent.getStringExtra(EXTRA_OUTPUT_URL).toString(),
authCode = intent.getStringExtra(EXTRA_OUTPUT_AUTH_CODE).toString(),
deviceName = intent.getStringExtra(EXTRA_OUTPUT_DEVICE_NAME).toString(),
tlsClientCertificateUri = intent.getStringExtra(EXTRA_OUTPUT_TLS_CLIENT_CERTIFICATE_URI),
tlsClientCertificatePassword = intent.getStringExtra(EXTRA_OUTPUT_TLS_CLIENT_CERTIFICATE_PASSWORD),
)
}
}
}

override fun createIntent(context: Context, input: Input): Intent {
return context.intentLauncherWearOnboarding(input.defaultDeviceName, input.url)
}

override fun parseResult(resultCode: Int, intent: Intent?): Output? {
if (intent == null) {
return null
}

return Output.fromIntent(intent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,13 @@ internal class NameYourDeviceViewModel @VisibleForTesting constructor(
messagingTokenProvider(),
),
)
return serverManager.convertTemporaryServer(tempServerId)
val serverId = serverManager.convertTemporaryServer(tempServerId)
?: throw IllegalStateException("Server still temporary")

// Active the newly added server
serverManager.activateServer(serverId)
Comment on lines +178 to +179
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is for Wear, and it's also a behavior change I don't fully understand the reasoning behind. Can you elaborate / pull it out into another PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was called in the results of the onboarding after adding a server. If you look at the old code activity result in settings you would see that. I moved it here so it replicates the same behavior as before.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For app usage it may replicate the same usage, but now it also tries to active the Wear server on the phone that started onboarding it, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably not clear enough and would deserve a comment in the nameyourweardevice package that actually it uses the screen but not the view model actually.

So no it doesn't activate the server on the wear onboarding.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please add a comment as that seems like something you may easily miss like I did


return serverId
} catch (e: Exception) {
// Fatal errors: if one of these calls fail, the app cannot proceed.
// Show an error, clean up the session and require new registration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import io.homeassistant.companion.android.common.util.isAutomotive
import io.homeassistant.companion.android.common.util.isIgnoringBatteryOptimizations
import io.homeassistant.companion.android.common.util.maybeAskForIgnoringBatteryOptimizations
import io.homeassistant.companion.android.database.server.Server
import io.homeassistant.companion.android.launcher.intentLauncherOnboarding
import io.homeassistant.companion.android.nfc.NfcSetupActivity
import io.homeassistant.companion.android.onboarding.OnboardApp
import io.homeassistant.companion.android.settings.controls.ManageControlsSettingsFragment
import io.homeassistant.companion.android.settings.developer.DeveloperSettingsFragment
import io.homeassistant.companion.android.settings.gestures.GesturesFragment
Expand Down Expand Up @@ -77,9 +77,6 @@ class SettingsFragment(private val presenter: SettingsPresenter, private val lan
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
updateNotificationChannelPrefs()
}

private val requestOnboardingResult = registerForActivityResult(OnboardApp(), this::onOnboardingComplete)

private var serverAuth: Int? = null
private val serverMutex = Mutex()

Expand Down Expand Up @@ -140,13 +137,15 @@ class SettingsFragment(private val presenter: SettingsPresenter, private val lan

findPreference<Preference>("server_add")?.let {
it.setOnPreferenceClickListener {
requestOnboardingResult.launch(
OnboardApp.Input(
// Empty url skips the 'Welcome' screen
url = "",
discoveryOptions = OnboardApp.DiscoveryOptions.HIDE_EXISTING,
),
)
requireContext().apply {
startActivity(
intentLauncherOnboarding(
urlToOnboard = null,
hideExistingServers = true,
skipWelcome = true,
),
)
}
return@setOnPreferenceClickListener true
}
}
Expand Down Expand Up @@ -560,12 +559,6 @@ class SettingsFragment(private val presenter: SettingsPresenter, private val lan
return true
}

private fun onOnboardingComplete(result: OnboardApp.Output?) {
lifecycleScope.launch {
presenter.addServer(result)
}
}

private fun openNotificationSettings() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requestNotificationPermissionResult.launch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.content.Context
import androidx.preference.PreferenceDataStore
import io.homeassistant.companion.android.common.data.integration.impl.entities.RateLimitResponse
import io.homeassistant.companion.android.database.server.Server
import io.homeassistant.companion.android.onboarding.OnboardApp
import kotlinx.coroutines.flow.StateFlow

interface SettingsPresenter {
Expand All @@ -18,7 +17,6 @@ interface SettingsPresenter {
fun onFinish()
fun updateSuggestions(context: Context)
fun cancelSuggestion(context: Context, id: String)
suspend fun addServer(result: OnboardApp.Output?)
fun getSuggestionFlow(): StateFlow<SettingsHomeSuggestion?>
fun getServersFlow(): StateFlow<List<Server>>
fun getServerCount(): Int
Expand Down
Loading