Add setting to start the app always on the first dashboard view (#3024)

* Add setting to start the app always on the first dashboard view

* Run linting

* Do not close SettingsActivity

* Only show first view if not in HA Config + Fix flash of dashboard when navigating to app settings

* Scroll to top when calling first view of dashboard

If we are on a another view a we are scrolled to the bottom, the first view will be also at the bottom

* Use default dashboard as starting dashboard

* Allow HA config dashboard to call the first view of dashboard

- Also disallow /hassio (Home Assistant addons) to call the first view

* Enhance show first view setting description

* Fix class name spelling LifecycleHandler

* Fix calling first view of dashboard on config/areas and config/automation

Co-authored-by: Justin Bassett <bassett.justint@gmail.com>
This commit is contained in:
chriss158 2022-12-18 21:08:22 +00:00 committed by GitHub
parent 7e5e3adfe9
commit e5191aed51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 107 additions and 0 deletions

View file

@ -18,6 +18,7 @@ import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.database.settings.SensorUpdateFrequencySetting
import io.homeassistant.companion.android.sensors.SensorReceiver
import io.homeassistant.companion.android.settings.language.LanguagesManager
import io.homeassistant.companion.android.util.LifecycleHandler
import io.homeassistant.companion.android.websocket.WebsocketBroadcastReceiver
import io.homeassistant.companion.android.widgets.button.ButtonWidget
import io.homeassistant.companion.android.widgets.entity.EntityWidget
@ -46,6 +47,8 @@ open class HomeAssistantApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(LifecycleHandler)
ioScope.launch {
initCrashReporting(
applicationContext,

View file

@ -52,6 +52,7 @@ class SettingsPresenterImpl @Inject constructor(
"app_lock_home_bypass" -> authenticationUseCase.isLockHomeBypassEnabled()
"crash_reporting" -> prefsRepository.isCrashReporting()
"autoplay_video" -> integrationUseCase.isAutoPlayVideoEnabled()
"always_show_first_view_on_app_start" -> integrationUseCase.isAlwaysShowFirstViewOnAppStartEnabled()
"webview_debug" -> integrationUseCase.isWebViewDebugEnabled()
else -> throw IllegalArgumentException("No boolean found by this key: $key")
}
@ -68,6 +69,7 @@ class SettingsPresenterImpl @Inject constructor(
"app_lock_home_bypass" -> authenticationUseCase.setLockHomeBypassEnabled(value)
"crash_reporting" -> prefsRepository.setCrashReporting(value)
"autoplay_video" -> integrationUseCase.setAutoPlayVideo(value)
"always_show_first_view_on_app_start" -> integrationUseCase.setAlwaysShowFirstViewOnAppStart(value)
"webview_debug" -> integrationUseCase.setWebViewDebugEnabled(value)
else -> throw IllegalArgumentException("No boolean found by this key: $key")
}

View file

@ -0,0 +1,44 @@
package io.homeassistant.companion.android.util
import android.app.Activity
import android.app.Application
import android.os.Bundle
object LifecycleHandler : Application.ActivityLifecycleCallbacks {
private var activityReferences = 0
private var isActivityChangingConfigurations = false
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
// Not implemented
}
override fun onActivityStarted(activity: Activity) {
isActivityChangingConfigurations = activity.isChangingConfigurations
activityReferences++
}
override fun onActivityResumed(activity: Activity) {
// Not implemented
}
override fun onActivityPaused(activity: Activity) {
// Not implemented
}
override fun onActivityStopped(activity: Activity) {
isActivityChangingConfigurations = activity.isChangingConfigurations
activityReferences--
}
override fun onActivitySaveInstanceState(activity: Activity, bunle: Bundle) {
// Not implemented
}
override fun onActivityDestroyed(activity: Activity) {
// Not implemented
}
fun isAppInBackground(): Boolean {
// No activities left and activity is not changing configuration (ex. change the orientation)
return activityReferences == 0 && !isActivityChangingConfigurations
}
}

View file

@ -94,6 +94,7 @@ import io.homeassistant.companion.android.settings.SettingsActivity
import io.homeassistant.companion.android.themes.ThemesManager
import io.homeassistant.companion.android.util.ChangeLog
import io.homeassistant.companion.android.util.DataUriDownloadManager
import io.homeassistant.companion.android.util.LifecycleHandler
import io.homeassistant.companion.android.util.OnSwipeListener
import io.homeassistant.companion.android.util.TLSWebViewClient
import io.homeassistant.companion.android.util.isStarted
@ -746,6 +747,11 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi
changeLog.showChangeLog(this, false)
}
override fun onStop() {
super.onStop()
openFirstViewOnDashboardIfNeeded()
}
override fun onPause() {
super.onPause()
SensorReceiver.updateAllSensors(this)
@ -1433,4 +1439,30 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi
"""
) {}
}
private fun openFirstViewOnDashboardIfNeeded() {
if (presenter.isAlwaysShowFirstViewOnAppStartEnabled() &&
LifecycleHandler.isAppInBackground()
) {
// Pattern matches urls which are NOT allowed to show the first view after app is started
// This is
// /config/* as these are the settings of HA but NOT /config/dashboard. This is just the overview of the HA settings
// /hassio/* as these are the addons section of HA settings.
if (webView.url?.matches(".*://.*/(config/(?!\\bdashboard\\b)|hassio)/*.*".toRegex()) == false) {
Log.d(TAG, "Show first view of default dashboard.")
webView.evaluateJavascript(
"""
var anchor = 'a:nth-child(1)';
var defaultPanel = window.localStorage.getItem('defaultPanel')?.replaceAll('"',"");
if(defaultPanel) anchor = 'a[href="/' + defaultPanel + '"]';
document.querySelector('body > home-assistant').shadowRoot.querySelector('home-assistant-main')
.shadowRoot.querySelector('#drawer > ha-sidebar')
.shadowRoot.querySelector('paper-listbox > ' + anchor).click();
window.scrollTo(0, 0);
""",
null
)
} else Log.d(TAG, "User is in the Home Assistant config. Will not show first view of the default dashboard.")
}
}
}

View file

@ -29,6 +29,7 @@ interface WebViewPresenter {
fun isLockEnabled(): Boolean
fun isAutoPlayVideoEnabled(): Boolean
fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean
fun sessionTimeOut(): Int

View file

@ -192,6 +192,12 @@ class WebViewPresenterImpl @Inject constructor(
}
}
override fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean {
return runBlocking {
integrationUseCase.isAlwaysShowFirstViewOnAppStartEnabled()
}
}
override fun sessionTimeOut(): Int {
return runBlocking {
integrationUseCase.getSessionTimeOut()

View file

@ -91,6 +91,11 @@
android:icon="@drawable/ic_baseline_video_settings_24"
android:title="@string/autoplay_video"
android:summary="@string/autoplay_video_summary" />
<SwitchPreference
android:key="always_show_first_view_on_app_start"
android:icon="@drawable/ic_home_variant_outline"
android:title="@string/always_show_first_view_on_app_start"
android:summary="@string/always_show_first_view_on_app_start_summary" />
<SwitchPreference
android:key="webview_debug"
android:icon="@drawable/ic_android_debug_bridge"

View file

@ -33,6 +33,9 @@ interface IntegrationRepository {
suspend fun setAutoPlayVideo(enabled: Boolean)
suspend fun isAutoPlayVideoEnabled(): Boolean
suspend fun setAlwaysShowFirstViewOnAppStart(enabled: Boolean)
suspend fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean
suspend fun setWebViewDebugEnabled(enabled: Boolean)
suspend fun isWebViewDebugEnabled(): Boolean

View file

@ -69,6 +69,7 @@ class IntegrationRepositoryImpl @Inject constructor(
private const val PREF_WEAR_TOAST_CONFIRMATION = "wear_toast_confirmation"
private const val PREF_HA_VERSION = "ha_version"
private const val PREF_AUTOPLAY_VIDEO = "autoplay_video"
private const val PREF_ALWAYS_SHOW_FIRST_VIEW_ON_APP_START = "always_show_first_view_on_app_start"
private const val PREF_FULLSCREEN_ENABLED = "fullscreen_enabled"
private const val PREF_KEEP_SCREEN_ON_ENABLED = "keep_screen_on_enabled"
private const val PREF_PINCH_TO_ZOOM_ENABLED = "pinch_to_zoom_enabled"
@ -373,6 +374,14 @@ class IntegrationRepositoryImpl @Inject constructor(
return localStorage.getBoolean(PREF_AUTOPLAY_VIDEO)
}
override suspend fun setAlwaysShowFirstViewOnAppStart(enabled: Boolean) {
localStorage.putBoolean(PREF_ALWAYS_SHOW_FIRST_VIEW_ON_APP_START, enabled)
}
override suspend fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean {
return localStorage.getBoolean(PREF_ALWAYS_SHOW_FIRST_VIEW_ON_APP_START)
}
override suspend fun setAutoPlayVideo(enabled: Boolean) {
localStorage.putBoolean(PREF_AUTOPLAY_VIDEO, enabled)
}

View file

@ -11,6 +11,8 @@
<string name="add_widget">Add widget</string>
<string name="all_entities">All entities</string>
<string name="allow">Allow</string>
<string name="always_show_first_view_on_app_start">Always show first view on app start</string>
<string name="always_show_first_view_on_app_start_summary">The first view of the default dashboard is shown as soon as the app is opened</string>
<string name="app_name">Home Assistant</string>
<string name="app_version_info">App Version Info</string>
<string name="application_version">Application Version</string>