diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt index 1dfc2320ce8..f3f257f3851 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt @@ -24,6 +24,7 @@ import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.settings.notification.NotificationHistoryFragment import io.homeassistant.companion.android.settings.qs.ManageTilesFragment import io.homeassistant.companion.android.settings.sensor.SensorDetailFragment +import io.homeassistant.companion.android.settings.server.ServerSettingsFragment import io.homeassistant.companion.android.settings.websocket.WebsocketSettingFragment import kotlinx.coroutines.runBlocking import javax.inject.Inject @@ -113,48 +114,28 @@ class SettingsActivity : BaseActivity() { override fun onUserLeaveHint() { super.onUserLeaveHint() - runBlocking { - if (serverManager.isRegistered()) serverManager.integrationRepository().setAppActive(false) - } + setAppActive(false) } override fun onPause() { super.onPause() - runBlocking { - if (serverManager.isRegistered()) serverManager.integrationRepository().setAppActive(false) - } + setAppActive(false) } override fun onResume() { super.onResume() - - val appLocked = runBlocking { - if (serverManager.isRegistered()) serverManager.integrationRepository().isAppLocked() - else false - } - - blurView.setBlurEnabled(appLocked) + blurView.setBlurEnabled(isAppLocked()) } override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) if (hasFocus && !isFinishing) { - val appLocked = runBlocking { - if (serverManager.isRegistered()) { - try { - serverManager.integrationRepository().isAppLocked() - } catch (e: IllegalArgumentException) { - Log.w(TAG, "Cannot determine app locked state") - false - } - } else false - } - - if (appLocked) { + if (isAppLocked()) { authenticating = true authenticator.authenticate(getString(commonR.string.biometric_title)) blurView.setBlurEnabled(true) } else { + setAppActive(true) blurView.setBlurEnabled(false) } } @@ -176,9 +157,7 @@ class SettingsActivity : BaseActivity() { Authenticator.SUCCESS -> { Log.d(TAG, "Authentication successful, unlocking app") blurView.setBlurEnabled(false) - runBlocking { - if (serverManager.isRegistered()) serverManager.integrationRepository().setAppActive(true) - } + setAppActive(true) } Authenticator.CANCELED -> { Log.d(TAG, "Authentication canceled by user, closing activity") @@ -189,6 +168,42 @@ class SettingsActivity : BaseActivity() { } } + /** + * @return `true` if the app is locked for the active server or the currently visible server + */ + private fun isAppLocked(): Boolean { + val serverFragment = supportFragmentManager.findFragmentByTag(ServerSettingsFragment.TAG) + val serverLocked = serverFragment?.let { isAppLocked((it as ServerSettingsFragment).getServerId()) } ?: false + return serverLocked || isAppLocked(ServerManager.SERVER_ID_ACTIVE) + } + + fun isAppLocked(serverId: Int?): Boolean = runBlocking { + serverManager.getServer(serverId ?: ServerManager.SERVER_ID_ACTIVE)?.let { + try { + serverManager.integrationRepository(it.id).isAppLocked() + } catch (e: IllegalArgumentException) { + Log.w(TAG, "Cannot determine app locked state") + false + } + } ?: false + } + + /** + * Set the app active for the currently active server, and the currently visible server if + * different + */ + private fun setAppActive(active: Boolean) { + val serverFragment = supportFragmentManager.findFragmentByTag(ServerSettingsFragment.TAG) + serverFragment?.let { setAppActive((it as ServerSettingsFragment).getServerId(), active) } + setAppActive(ServerManager.SERVER_ID_ACTIVE, active) + } + + fun setAppActive(serverId: Int?, active: Boolean) = runBlocking { + serverManager.getServer(serverId ?: ServerManager.SERVER_ID_ACTIVE)?.let { + serverManager.integrationRepository(it.id).setAppActive(active) + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt index d711b78ad0e..2e27eb49cda 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt @@ -27,6 +27,7 @@ import androidx.preference.SwitchPreference import com.google.android.material.snackbar.Snackbar import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.authenticator.Authenticator import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.nfc.NfcSetupActivity import io.homeassistant.companion.android.onboarding.OnboardApp @@ -73,6 +74,7 @@ class SettingsFragment( private val requestOnboardingResult = registerForActivityResult(OnboardApp(), this::onOnboardingComplete) + private var serverAuth: Int? = null private val serverMutex = Mutex() private var snackbar: Snackbar? = null @@ -366,13 +368,16 @@ class SettingsFragment( Log.e(TAG, "Unable to set the server icon", e) } serverPreference.setOnPreferenceClickListener { - parentFragmentManager.commit { - replace( - R.id.content, - ServerSettingsFragment::class.java, - Bundle().apply { putInt(ServerSettingsFragment.EXTRA_SERVER, server.id) } - ) - addToBackStack(getString(commonR.string.server_settings)) + serverAuth = server.id + val settingsActivity = requireActivity() as SettingsActivity + val needsAuth = settingsActivity.isAppLocked(server.id) + if (!needsAuth) { + onServerLockResult(Authenticator.SUCCESS) + } else { + val canAuth = settingsActivity.requestAuthentication(getString(commonR.string.biometric_set_title), ::onServerLockResult) + if (!canAuth) { + onServerLockResult(Authenticator.SUCCESS) + } } return@setOnPreferenceClickListener true } @@ -397,6 +402,22 @@ class SettingsFragment( } } + private fun onServerLockResult(result: Int): Boolean { + if (result == Authenticator.SUCCESS && serverAuth != null) { + (activity as? SettingsActivity)?.setAppActive(serverAuth, true) + parentFragmentManager.commit { + replace( + R.id.content, + ServerSettingsFragment::class.java, + Bundle().apply { putInt(ServerSettingsFragment.EXTRA_SERVER, serverAuth!!) }, + ServerSettingsFragment.TAG + ) + addToBackStack(getString(commonR.string.server_settings)) + } + } + return true + } + private fun onOnboardingComplete(result: OnboardApp.Output?) { lifecycleScope.launch { presenter.addServer(result) diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt index a9d2337012e..fae091b73cb 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt @@ -42,7 +42,7 @@ import io.homeassistant.companion.android.common.R as commonR class ServerSettingsFragment : ServerSettingsView, PreferenceFragmentCompat() { companion object { - private const val TAG = "ServerSettingsFragment" + const val TAG = "ServerSettingsFragment" const val EXTRA_SERVER = "server" } @@ -305,7 +305,7 @@ class ServerSettingsFragment : ServerSettingsView, PreferenceFragmentCompat() { switchLock?.isChecked = success // Prevent requesting authentication after just enabling the app lock - presenter.setAppActive() + presenter.setAppActive(true) findPreference("app_lock_home_bypass")?.isVisible = success findPreference("session_timeout")?.isVisible = success @@ -385,4 +385,6 @@ class ServerSettingsFragment : ServerSettingsView, PreferenceFragmentCompat() { presenter.onFinish() super.onDestroy() } + + fun getServerId(): Int = serverId } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenter.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenter.kt index d4ef05f7e3b..67910848544 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenter.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenter.kt @@ -14,5 +14,5 @@ interface ServerSettingsPresenter { fun isSsidUsed(): Boolean fun clearSsids() - fun setAppActive() + fun setAppActive(active: Boolean) } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt index 1ee0b2c412d..d5d962e49f3 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt @@ -119,6 +119,9 @@ class ServerSettingsPresenterImpl @Inject constructor( } override fun onFinish() { + if (serverManager.getServer()?.id != serverId) { + setAppActive(false) + } mainScope.cancel() } @@ -174,7 +177,7 @@ class ServerSettingsPresenterImpl @Inject constructor( } } - override fun setAppActive() = runBlocking { - serverManager.integrationRepository(serverId).setAppActive(true) + override fun setAppActive(active: Boolean) = runBlocking { + serverManager.integrationRepository(serverId).setAppActive(active) } } diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebView.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebView.kt index c2889b4da02..faa67e6756f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebView.kt @@ -18,5 +18,7 @@ interface WebView { fun relaunchApp() + fun unlockAppIfNeeded() + fun showError(errorType: ErrorType = ErrorType.TIMEOUT, error: SslError? = null, description: String? = null) } diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt index 7a74443d5a0..3602edd6be4 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt @@ -1022,14 +1022,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) if (hasFocus && !isFinishing) { - appLocked = presenter.isAppLocked() - if (appLocked) { - binding.blurView.setBlurEnabled(true) - authenticator.authenticate(getString(commonR.string.biometric_title)) - } else { - binding.blurView.setBlurEnabled(false) - } - + unlockAppIfNeeded() val path = intent.getStringExtra(EXTRA_PATH) presenter.onViewReady(path) if (path?.startsWith("entityId:") == true) @@ -1043,6 +1036,16 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi } } + override fun unlockAppIfNeeded() { + appLocked = presenter.isAppLocked() + if (appLocked) { + binding.blurView.setBlurEnabled(true) + authenticator.authenticate(getString(commonR.string.biometric_title)) + } else { + binding.blurView.setBlurEnabled(false) + } + } + private fun hideSystemUI() { windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt index b6ee070c058..20d9b310698 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt @@ -131,8 +131,12 @@ class WebViewPresenterImpl @Inject constructor( } override fun switchActiveServer(id: Int) { + if (serverId != id && serverId != ServerManager.SERVER_ID_ACTIVE) { + setAppActive(false) // 'Lock' old server + } setActiveServer(id) onViewReady(null) + view.unlockAppIfNeeded() } override fun nextServer() = moveToServer(next = true)