mirror of
https://github.com/home-assistant/android
synced 2024-10-07 00:29:32 +00:00
Smarter discovery for already added instances (#3723)
- Hide already added instances when adding another server to the app when found in discovery - Show already added instances with their external URL in discovery when logging in a Wear OS device
This commit is contained in:
parent
447ad5e30a
commit
7b40dec713
|
@ -70,7 +70,8 @@ class SettingsWearMainView : AppCompatActivity() {
|
|||
defaultDeviceName = currentNodes.firstOrNull()?.displayName ?: "unknown",
|
||||
locationTrackingPossible = false,
|
||||
notificationsPossible = false,
|
||||
isWatch = true
|
||||
isWatch = true,
|
||||
discoveryOptions = OnboardApp.DiscoveryOptions.ADD_EXISTING_EXTERNAL
|
||||
) // While notifications are technically possible, the app can't handle this for the Wear device
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,22 +14,33 @@ class OnboardApp : ActivityResultContract<OnboardApp.Input, OnboardApp.Output?>(
|
|||
private const val EXTRA_LOCATION_TRACKING_POSSIBLE = "location_tracking_possible"
|
||||
private const val EXTRA_NOTIFICATIONS_POSSIBLE = "notifications_possible"
|
||||
private const val EXTRA_IS_WATCH = "extra_is_watch"
|
||||
private const val EXTRA_DISCOVERY_OPTIONS = "extra_discovery_options"
|
||||
|
||||
fun parseInput(intent: Intent): Input = Input(
|
||||
url = intent.getStringExtra(EXTRA_URL),
|
||||
defaultDeviceName = intent.getStringExtra(EXTRA_DEFAULT_DEVICE_NAME) ?: Build.MODEL,
|
||||
locationTrackingPossible = intent.getBooleanExtra(EXTRA_LOCATION_TRACKING_POSSIBLE, false),
|
||||
notificationsPossible = intent.getBooleanExtra(EXTRA_NOTIFICATIONS_POSSIBLE, true),
|
||||
isWatch = intent.getBooleanExtra(EXTRA_IS_WATCH, false)
|
||||
isWatch = intent.getBooleanExtra(EXTRA_IS_WATCH, false),
|
||||
discoveryOptions = intent.getStringExtra(EXTRA_DISCOVERY_OPTIONS)?.let { DiscoveryOptions.valueOf(it) }
|
||||
)
|
||||
}
|
||||
|
||||
enum class DiscoveryOptions {
|
||||
/** Add existing servers in the app to discovery results using their external URL */
|
||||
ADD_EXISTING_EXTERNAL,
|
||||
|
||||
/** Hide existing servers in the app from discovery results if discovered */
|
||||
HIDE_EXISTING
|
||||
}
|
||||
|
||||
data class Input(
|
||||
val url: String? = null,
|
||||
val defaultDeviceName: String = Build.MODEL,
|
||||
val locationTrackingPossible: Boolean = BuildConfig.FLAVOR == "full",
|
||||
val notificationsPossible: Boolean = true,
|
||||
val isWatch: Boolean = false
|
||||
val isWatch: Boolean = false,
|
||||
val discoveryOptions: DiscoveryOptions? = null
|
||||
)
|
||||
|
||||
data class Output(
|
||||
|
@ -57,6 +68,7 @@ class OnboardApp : ActivityResultContract<OnboardApp.Input, OnboardApp.Output?>(
|
|||
putExtra(EXTRA_LOCATION_TRACKING_POSSIBLE, input.locationTrackingPossible)
|
||||
putExtra(EXTRA_NOTIFICATIONS_POSSIBLE, input.notificationsPossible)
|
||||
putExtra(EXTRA_IS_WATCH, input.isWatch)
|
||||
putExtra(EXTRA_DISCOVERY_OPTIONS, input.discoveryOptions?.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ class OnboardingActivity : BaseActivity() {
|
|||
false
|
||||
}
|
||||
viewModel.deviceIsWatch = input.isWatch
|
||||
viewModel.discoveryOptions = input.discoveryOptions
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
supportFragmentManager.commit {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.homeassistant.companion.android.onboarding
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import android.webkit.URLUtil
|
||||
import android.widget.Toast
|
||||
import androidx.compose.runtime.getValue
|
||||
|
@ -12,23 +13,27 @@ import androidx.lifecycle.AndroidViewModel
|
|||
import androidx.lifecycle.LifecycleObserver
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.homeassistant.companion.android.common.R
|
||||
import io.homeassistant.companion.android.common.data.servers.ServerManager
|
||||
import io.homeassistant.companion.android.database.server.ServerConnectionInfo
|
||||
import io.homeassistant.companion.android.onboarding.discovery.HomeAssistantInstance
|
||||
import io.homeassistant.companion.android.onboarding.discovery.HomeAssistantSearcher
|
||||
import java.net.URL
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class OnboardingViewModel @Inject constructor(
|
||||
val serverManager: ServerManager,
|
||||
app: Application
|
||||
) : AndroidViewModel(app) {
|
||||
|
||||
companion object {
|
||||
const val TAG = "OnboardingViewModel"
|
||||
}
|
||||
|
||||
private val _homeAssistantSearcher = HomeAssistantSearcher(
|
||||
nsdManager = app.getSystemService()!!,
|
||||
wifiManager = app.getSystemService(),
|
||||
onInstanceFound = { instance ->
|
||||
if (foundInstances.none { it.url == instance.url }) {
|
||||
foundInstances.add(instance)
|
||||
}
|
||||
},
|
||||
onInstanceFound = ::onInstanceFound,
|
||||
onError = {
|
||||
Toast.makeText(app, R.string.failed_scan, Toast.LENGTH_LONG).show()
|
||||
// TODO: Go to manual setup?
|
||||
|
@ -38,6 +43,7 @@ class OnboardingViewModel @Inject constructor(
|
|||
|
||||
val foundInstances = mutableStateListOf<HomeAssistantInstance>()
|
||||
val manualUrl = mutableStateOf("")
|
||||
var discoveryOptions: OnboardApp.DiscoveryOptions? = null
|
||||
var manualContinueEnabled by mutableStateOf(false)
|
||||
private set
|
||||
var authCode by mutableStateOf("")
|
||||
|
@ -78,7 +84,50 @@ class OnboardingViewModel @Inject constructor(
|
|||
notificationsEnabled = notificationsEnabled
|
||||
)
|
||||
|
||||
fun onDiscoveryActive() {
|
||||
if (discoveryOptions != OnboardApp.DiscoveryOptions.ADD_EXISTING_EXTERNAL || !foundInstances.isEmpty()) return
|
||||
|
||||
serverManager.defaultServers.forEach {
|
||||
val url = it.connection.getUrl(isInternal = false) ?: return@forEach
|
||||
val version = it.version ?: return@forEach
|
||||
foundInstances.add(
|
||||
HomeAssistantInstance(
|
||||
name = it.friendlyName,
|
||||
url = url,
|
||||
version = version
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onInstanceFound(instance: HomeAssistantInstance) {
|
||||
if (
|
||||
(discoveryOptions == OnboardApp.DiscoveryOptions.ADD_EXISTING_EXTERNAL || discoveryOptions == OnboardApp.DiscoveryOptions.HIDE_EXISTING) &&
|
||||
serverManager.defaultServers.any { it.connection.hasUrl(instance.url) }
|
||||
) {
|
||||
// Skip anything with a URL known to the app, as it is added initially or should be hidden
|
||||
Log.i(TAG, "Skipping instance ${instance.name} (${instance.url}) because of option $discoveryOptions")
|
||||
return
|
||||
}
|
||||
|
||||
if (foundInstances.none { it.url == instance.url }) foundInstances.add(instance)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
_homeAssistantSearcher.stopSearch()
|
||||
}
|
||||
|
||||
private fun ServerConnectionInfo.hasUrl(url: URL): Boolean {
|
||||
val urls = listOf(internalUrl, externalUrl, cloudUrl)
|
||||
urls.forEach {
|
||||
if (it.isNullOrBlank()) return@forEach
|
||||
try {
|
||||
val parsed = URL(it)
|
||||
if (parsed.protocol == url.protocol && parsed.host == url.host && parsed.port == url.port) return true
|
||||
} catch (e: Exception) {
|
||||
// Unable to compare
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@ import android.view.ViewGroup
|
|||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.accompanist.themeadapter.material.MdcTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.onboarding.OnboardingViewModel
|
||||
import io.homeassistant.companion.android.onboarding.authentication.AuthenticationFragment
|
||||
import io.homeassistant.companion.android.onboarding.manual.ManualSetupFragment
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -20,6 +24,15 @@ class DiscoveryFragment @Inject constructor() : Fragment() {
|
|||
|
||||
private val viewModel by activityViewModels<OnboardingViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launch {
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
viewModel.onDiscoveryActive()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package io.homeassistant.companion.android.onboarding.discovery
|
||||
|
||||
import io.homeassistant.companion.android.common.data.HomeAssistantVersion
|
||||
import java.net.URL
|
||||
|
||||
data class HomeAssistantInstance(
|
||||
val name: String,
|
||||
val url: URL,
|
||||
val version: String
|
||||
val version: HomeAssistantVersion
|
||||
)
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.net.wifi.WifiManager
|
|||
import android.util.Log
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import io.homeassistant.companion.android.common.data.HomeAssistantVersion
|
||||
import okio.internal.commonToUtf8String
|
||||
import java.net.URL
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
@ -29,7 +30,7 @@ class HomeAssistantSearcher constructor(
|
|||
|
||||
private var multicastLock: WifiManager.MulticastLock? = null
|
||||
|
||||
fun beginSearch() {
|
||||
private fun beginSearch() {
|
||||
if (isSearching) {
|
||||
return
|
||||
}
|
||||
|
@ -93,13 +94,14 @@ class HomeAssistantSearcher constructor(
|
|||
Log.i(TAG, "Service resolved: $resolvedService")
|
||||
resolvedService?.let {
|
||||
val baseUrl = it.attributes["base_url"]
|
||||
val version = it.attributes["version"]
|
||||
val versionAttr = it.attributes["version"]
|
||||
val version = versionAttr?.let { ver -> HomeAssistantVersion.fromString(ver.commonToUtf8String()) }
|
||||
if (baseUrl != null && version != null) {
|
||||
onInstanceFound(
|
||||
HomeAssistantInstance(
|
||||
it.serviceName,
|
||||
URL(baseUrl.commonToUtf8String()),
|
||||
version.commonToUtf8String()
|
||||
version
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -136,7 +136,10 @@ class SettingsFragment(
|
|||
findPreference<Preference>("server_add")?.let {
|
||||
it.setOnPreferenceClickListener {
|
||||
requestOnboardingResult.launch(
|
||||
OnboardApp.Input(url = "") // Empty url skips the 'Welcome' screen
|
||||
OnboardApp.Input(
|
||||
url = "", // Empty url skips the 'Welcome' screen
|
||||
discoveryOptions = OnboardApp.DiscoveryOptions.HIDE_EXISTING
|
||||
)
|
||||
)
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue