@@ -104,6 +104,7 @@ import io.homeassistant.companion.android.common.util.toJsonObject
104104import io.homeassistant.companion.android.common.util.toJsonObjectOrNull
105105import io.homeassistant.companion.android.database.authentication.Authentication
106106import io.homeassistant.companion.android.database.authentication.AuthenticationDao
107+ import io.homeassistant.companion.android.database.server.SecurityStatus
107108import io.homeassistant.companion.android.databinding.DialogAuthenticationBinding
108109import io.homeassistant.companion.android.improv.ui.ImprovPermissionDialog
109110import io.homeassistant.companion.android.improv.ui.ImprovSetupDialog
@@ -131,6 +132,7 @@ import io.homeassistant.companion.android.webview.externalbus.ExternalConfigResp
131132import io.homeassistant.companion.android.webview.externalbus.ExternalEntityAddToAction
132133import io.homeassistant.companion.android.webview.externalbus.NavigateTo
133134import io.homeassistant.companion.android.webview.externalbus.ShowSidebar
135+ import io.homeassistant.companion.android.webview.insecure.BlockInsecureFragment
134136import javax.inject.Inject
135137import kotlinx.coroutines.CoroutineScope
136138import kotlinx.coroutines.Dispatchers
@@ -272,6 +274,11 @@ class WebViewActivity :
272274
273275 private val snackbarHostState = SnackbarHostState ()
274276
277+ /* *
278+ * Indicates if the current state is insecure. If so we should not load the URL.
279+ */
280+ private var isInsecureState: Boolean = false
281+
275282 @SuppressLint(" SetJavaScriptEnabled" )
276283 override fun onCreate (savedInstanceState : Bundle ? ) {
277284 if (
@@ -1392,8 +1399,7 @@ class WebViewActivity :
13921399 clearHistory = ! keepHistory
13931400 lifecycleScope.launch {
13941401 if (! presenter.shouldSetSecurityLevel()) {
1395- webView.loadUrl(url)
1396- waitForConnection()
1402+ secureLoadUrl(url)
13971403 } else {
13981404 val serverId = presenter.getActiveServer()
13991405 Timber .d(" Security level not set for server $serverId , showing ConnectionSecurityLevelFragment" )
@@ -1410,6 +1416,46 @@ class WebViewActivity :
14101416 }
14111417 }
14121418
1419+ /* *
1420+ * Make sure we only load the url if the securityLevel and current states allow it.
1421+ */
1422+ private suspend fun secureLoadUrl (url : String ) {
1423+ fun loadAndWait () {
1424+ webView.loadUrl(url)
1425+ isInsecureState = false
1426+ waitForConnection()
1427+ }
1428+
1429+ if (url.startsWith(" https" )) {
1430+ loadAndWait()
1431+ return
1432+ }
1433+
1434+ val allowInsecureConnection = serverManager.integrationRepository(
1435+ presenter.getActiveServer(),
1436+ ).getAllowInsecureConnection()
1437+
1438+ when (allowInsecureConnection) {
1439+ null , true -> loadAndWait()
1440+ false -> {
1441+ val status = serverManager.getServer(
1442+ presenter.getActiveServer(),
1443+ )?.connection?.currentSecurityStatusForUrl(this , url)
1444+ when (status) {
1445+ is SecurityStatus .Insecure , null -> {
1446+ isInsecureState = true
1447+ showBlockInsecureFragment(
1448+ serverId = presenter.getActiveServer(),
1449+ missingHomeSetup = status?.missingHomeSetup ? : false ,
1450+ missingLocation = status?.missingLocation ? : false ,
1451+ )
1452+ }
1453+ SecurityStatus .Secure -> loadAndWait()
1454+ }
1455+ }
1456+ }
1457+ }
1458+
14131459 private fun showConnectionSecurityLevelFragment (serverId : Int ) {
14141460 supportFragmentManager.setFragmentResultListener(
14151461 ConnectionSecurityLevelFragment .RESULT_KEY ,
@@ -1419,8 +1465,9 @@ class WebViewActivity :
14191465 supportFragmentManager.clearFragmentResultListener(ConnectionSecurityLevelFragment .RESULT_KEY )
14201466
14211467 if (::loadedUrl.isInitialized) {
1422- webView.loadUrl(loadedUrl)
1423- waitForConnection()
1468+ lifecycleScope.launch {
1469+ secureLoadUrl(loadedUrl)
1470+ }
14241471 }
14251472 }
14261473
@@ -1430,6 +1477,33 @@ class WebViewActivity :
14301477 .commit()
14311478 }
14321479
1480+ private fun showBlockInsecureFragment (serverId : Int , missingHomeSetup : Boolean , missingLocation : Boolean ) {
1481+ supportFragmentManager.setFragmentResultListener(
1482+ BlockInsecureFragment .RESULT_KEY ,
1483+ this ,
1484+ ) { _, _ ->
1485+ Timber .d(" Block insecure screen exited by user, retrying URL loading" )
1486+ supportFragmentManager.clearFragmentResultListener(BlockInsecureFragment .RESULT_KEY )
1487+
1488+ if (::loadedUrl.isInitialized) {
1489+ lifecycleScope.launch {
1490+ secureLoadUrl(loadedUrl)
1491+ }
1492+ }
1493+ }
1494+ supportFragmentManager.beginTransaction()
1495+ .replace(
1496+ android.R .id.content,
1497+ BlockInsecureFragment .newInstance(
1498+ serverId = serverId,
1499+ missingHomeSetup = missingHomeSetup,
1500+ missingLocation = missingLocation,
1501+ ),
1502+ )
1503+ .addToBackStack(null )
1504+ .commit()
1505+ }
1506+
14331507 override fun setStatusBarAndBackgroundColor (statusBarColor : Int , backgroundColor : Int ) {
14341508 // Set background colors
14351509 if (statusBarColor != 0 ) {
@@ -1705,18 +1779,22 @@ class WebViewActivity :
17051779 }
17061780
17071781 private fun waitForConnection () {
1708- Handler (Looper .getMainLooper()).postDelayed(
1709- {
1710- if (
1711- ! isConnected &&
1712- ! loadedUrl.toHttpUrl().pathSegments.first().contains(" api" ) &&
1713- ! loadedUrl.toHttpUrl().pathSegments.first().contains(" local" )
1714- ) {
1715- showError(errorType = ErrorType .TIMEOUT_EXTERNAL_BUS )
1716- }
1717- },
1718- CONNECTION_DELAY ,
1719- )
1782+ if (! isInsecureState) {
1783+ Handler (Looper .getMainLooper()).postDelayed(
1784+ {
1785+ if (
1786+ ! isConnected &&
1787+ ! loadedUrl.toHttpUrl().pathSegments.first().contains(" api" ) &&
1788+ ! loadedUrl.toHttpUrl().pathSegments.first().contains(" local" )
1789+ ) {
1790+ showError(errorType = ErrorType .TIMEOUT_EXTERNAL_BUS )
1791+ }
1792+ },
1793+ CONNECTION_DELAY ,
1794+ )
1795+ } else {
1796+ Timber .i(" Not waiting for connection since currently being block because insecure" )
1797+ }
17201798 }
17211799
17221800 override fun sendExternalBusMessage (message : ExternalBusMessage ) {
0 commit comments