mirror of
https://github.com/home-assistant/android
synced 2024-10-15 12:32:54 +00:00
Split app-level and server-level settings (#3241)
* Move server settings to a server settings screen - Move server-specific settings to a new screen to be accessed from the main settings only containing server specific settings in preparation for multiserver - Sensors is currently not server-specific even though one setting is stored by server ID, to be fixed later * Store app preferences in another shared preferences file - Store app preferences not in the integration shared preferences file, but in the shared preferences file for general prefs (it is named themes but there are already other app-level prefs in it) - Move Wear specific preferences to it's own repository + shared preferences file * Improve server row with local data - While we don't have a server name or user name, show the registration name when available to improve the server row layout * Simplify location permission request code - Remove the flexible permission requests as it isn't used in the current settings structure, only expect location permissions - Switch from the deprecated functions to the new flow using activity result contracts * Remove unused string
This commit is contained in:
parent
ca8059da65
commit
e382a4b687
|
@ -12,6 +12,7 @@ import io.homeassistant.companion.android.common.data.integration.Entity
|
|||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.applyCompressedStateDiff
|
||||
import io.homeassistant.companion.android.common.data.integration.domain
|
||||
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
|
||||
import io.homeassistant.companion.android.common.data.url.UrlRepository
|
||||
import io.homeassistant.companion.android.common.data.websocket.WebSocketRepository
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
|
||||
|
@ -83,6 +84,9 @@ class HaControlsProviderService : ControlsProviderService() {
|
|||
@Inject
|
||||
lateinit var urlRepository: UrlRepository
|
||||
|
||||
@Inject
|
||||
lateinit var prefsRepository: PrefsRepository
|
||||
|
||||
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
private var areaRegistry: List<AreaRegistryResponse>? = null
|
||||
|
@ -364,9 +368,9 @@ class HaControlsProviderService : ControlsProviderService() {
|
|||
|
||||
private suspend fun entityRequiresAuth(entityId: String): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val setting = integrationRepository.getControlsAuthRequired()
|
||||
val setting = prefsRepository.getControlsAuthRequired()
|
||||
if (setting == ControlsAuthRequiredSetting.SELECTION) {
|
||||
val includeList = integrationRepository.getControlsAuthEntities()
|
||||
val includeList = prefsRepository.getControlsAuthEntities()
|
||||
includeList.contains(entityId)
|
||||
} else {
|
||||
setting == ControlsAuthRequiredSetting.ALL
|
||||
|
|
|
@ -46,6 +46,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.common.data.authentication.AuthenticationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
|
||||
import io.homeassistant.companion.android.common.data.url.UrlRepository
|
||||
import io.homeassistant.companion.android.common.notifications.NotificationData
|
||||
import io.homeassistant.companion.android.common.notifications.createChannelID
|
||||
|
@ -99,6 +100,7 @@ class MessagingManager @Inject constructor(
|
|||
private val integrationUseCase: IntegrationRepository,
|
||||
private val urlUseCase: UrlRepository,
|
||||
private val authenticationUseCase: AuthenticationRepository,
|
||||
private val prefsRepository: PrefsRepository,
|
||||
private val notificationDao: NotificationDao,
|
||||
private val sensorDao: SensorDao,
|
||||
private val settingsDao: SettingsDao
|
||||
|
@ -930,7 +932,7 @@ class MessagingManager @Inject constructor(
|
|||
COMMAND_SCREEN_ON -> {
|
||||
if (!command.isNullOrEmpty()) {
|
||||
mainScope.launch {
|
||||
integrationUseCase.setKeepScreenOnEnabled(
|
||||
prefsRepository.setKeepScreenOnEnabled(
|
||||
command == COMMAND_KEEP_SCREEN_ON
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,27 +1,22 @@
|
|||
package io.homeassistant.companion.android.settings
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.UiModeManager
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.text.InputType
|
||||
import android.util.Log
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
|
@ -29,9 +24,6 @@ import androidx.preference.PreferenceFragmentCompat
|
|||
import androidx.preference.SwitchPreference
|
||||
import io.homeassistant.companion.android.BuildConfig
|
||||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.authenticator.Authenticator
|
||||
import io.homeassistant.companion.android.common.util.DisabledLocationHandler
|
||||
import io.homeassistant.companion.android.common.util.LocationPermissionInfoHandler
|
||||
import io.homeassistant.companion.android.nfc.NfcSetupActivity
|
||||
import io.homeassistant.companion.android.settings.controls.ManageControlsSettingsFragment
|
||||
import io.homeassistant.companion.android.settings.language.LanguagesProvider
|
||||
|
@ -41,16 +33,13 @@ import io.homeassistant.companion.android.settings.notification.NotificationHist
|
|||
import io.homeassistant.companion.android.settings.qs.ManageTilesFragment
|
||||
import io.homeassistant.companion.android.settings.sensor.SensorSettingsFragment
|
||||
import io.homeassistant.companion.android.settings.sensor.SensorUpdateFrequencyFragment
|
||||
import io.homeassistant.companion.android.settings.server.ServerSettingsFragment
|
||||
import io.homeassistant.companion.android.settings.shortcuts.ManageShortcutsSettingsFragment
|
||||
import io.homeassistant.companion.android.settings.ssid.SsidFragment
|
||||
import io.homeassistant.companion.android.settings.url.ExternalUrlFragment
|
||||
import io.homeassistant.companion.android.settings.wear.SettingsWearActivity
|
||||
import io.homeassistant.companion.android.settings.wear.SettingsWearDetection
|
||||
import io.homeassistant.companion.android.settings.websocket.WebsocketSettingFragment
|
||||
import io.homeassistant.companion.android.settings.widgets.ManageWidgetsSettingsFragment
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
@ -60,12 +49,10 @@ import io.homeassistant.companion.android.common.R as commonR
|
|||
class SettingsFragment constructor(
|
||||
val presenter: SettingsPresenter,
|
||||
val langProvider: LanguagesProvider
|
||||
) : PreferenceFragmentCompat(), SettingsView {
|
||||
) : PreferenceFragmentCompat() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SettingsFragment"
|
||||
private const val LOCATION_REQUEST_CODE = 0
|
||||
private const val BACKGROUND_LOCATION_REQUEST_CODE = 1
|
||||
}
|
||||
|
||||
private val requestBackgroundAccessResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
|
@ -77,56 +64,32 @@ class SettingsFragment constructor(
|
|||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
presenter.init(this)
|
||||
|
||||
preferenceManager.preferenceDataStore = presenter.getPreferenceDataStore()
|
||||
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||
|
||||
val onChangeUrlValidator = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val isValid = newValue.toString().isBlank() || newValue.toString().toHttpUrlOrNull() != null
|
||||
if (!isValid) {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(commonR.string.url_invalid)
|
||||
.setMessage(commonR.string.url_parse_error)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
.show()
|
||||
// This should enumerate over all servers in the future
|
||||
val serverPreference = Preference(requireContext())
|
||||
presenter.getServerRegistrationName()?.let {
|
||||
serverPreference.title = it
|
||||
serverPreference.summary = presenter.getServerName()
|
||||
} ?: run {
|
||||
serverPreference.title = presenter.getServerName()
|
||||
}
|
||||
isValid
|
||||
serverPreference.order = 1
|
||||
try {
|
||||
serverPreference.icon = AppCompatResources.getDrawable(requireContext(), commonR.drawable.ic_stat_ic_notification_blue)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the server icon", e)
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>("app_lock")?.setOnPreferenceChangeListener { _, newValue ->
|
||||
val isValid: Boolean
|
||||
if (newValue == false) {
|
||||
isValid = true
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.isVisible = false
|
||||
findPreference<EditTextPreference>("session_timeout")?.isVisible = false
|
||||
} else {
|
||||
val settingsActivity = requireActivity() as SettingsActivity
|
||||
val canAuth = settingsActivity.requestAuthentication(getString(commonR.string.biometric_set_title), ::setLockAuthenticationResult)
|
||||
isValid = canAuth
|
||||
|
||||
if (!canAuth) {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(commonR.string.set_lock_title)
|
||||
.setMessage(commonR.string.set_lock_message)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
.show()
|
||||
serverPreference.setOnPreferenceClickListener {
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, ServerSettingsFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.server_settings))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
isValid
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.let {
|
||||
it.isVisible = findPreference<SwitchPreference>("app_lock")?.isChecked == true
|
||||
}
|
||||
|
||||
findPreference<EditTextPreference>("session_timeout")?.let { pref ->
|
||||
pref.setOnBindEditTextListener {
|
||||
it.inputType = InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
pref.isVisible = findPreference<SwitchPreference>("app_lock")?.isChecked == true
|
||||
}
|
||||
findPreference<PreferenceCategory>("servers_devices_category")?.addPreference(serverPreference)
|
||||
|
||||
findPreference<Preference>("nfc_tags")?.let {
|
||||
val pm: PackageManager = requireContext().packageManager
|
||||
|
@ -141,54 +104,29 @@ class SettingsFragment constructor(
|
|||
|
||||
updateBackgroundAccessPref()
|
||||
|
||||
findPreference<EditTextPreference>("connection_internal")?.let {
|
||||
it.onPreferenceChangeListener =
|
||||
onChangeUrlValidator
|
||||
}
|
||||
|
||||
findPreference<Preference>("connection_external")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, ExternalUrlFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.input_url))
|
||||
.commit()
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
findPreference<Preference>("connection_internal_ssids")?.let {
|
||||
it.setOnPreferenceClickListener {
|
||||
onDisplaySsidScreen()
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<Preference>("sensors")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, SensorSettingsFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.sensors))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, SensorSettingsFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.sensors))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
findPreference<Preference>("sensor_update_frequency")?.let {
|
||||
it.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, SensorUpdateFrequencyFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.sensor_update_frequency))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, SensorUpdateFrequencyFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.sensor_update_frequency))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<PreferenceCategory>("widgets")?.isVisible = Build.MODEL != "Quest"
|
||||
findPreference<PreferenceCategory>("security_category")?.isVisible = Build.MODEL != "Quest"
|
||||
findPreference<Preference>("manage_widgets")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, ManageWidgetsSettingsFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.widgets))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, ManageWidgetsSettingsFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.widgets))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
|
@ -198,11 +136,10 @@ class SettingsFragment constructor(
|
|||
it.isVisible = true
|
||||
}
|
||||
findPreference<Preference>("manage_shortcuts")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, ManageShortcutsSettingsFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.shortcuts))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, ManageShortcutsSettingsFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.shortcuts))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
@ -212,11 +149,10 @@ class SettingsFragment constructor(
|
|||
it.isVisible = true
|
||||
}
|
||||
findPreference<Preference>("manage_tiles")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, ManageTilesFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.tiles))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, ManageTilesFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.tiles))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
@ -226,11 +162,10 @@ class SettingsFragment constructor(
|
|||
it.isVisible = true
|
||||
}
|
||||
findPreference<Preference>("manage_device_controls")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, ManageControlsSettingsFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.controls_setting_title))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, ManageControlsSettingsFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.controls_setting_title))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
@ -240,17 +175,6 @@ class SettingsFragment constructor(
|
|||
it.isVisible = true
|
||||
}
|
||||
|
||||
findPreference<Preference>("websocket")?.let {
|
||||
it.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, WebsocketSettingFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.notifications))
|
||||
.commit()
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
||||
updateNotificationChannelPrefs()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
@ -263,11 +187,10 @@ class SettingsFragment constructor(
|
|||
|
||||
findPreference<Preference>("notification_channels")?.let { pref ->
|
||||
pref.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, NotificationChannelFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.notification_channels))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, NotificationChannelFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.notification_channels))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
@ -276,11 +199,10 @@ class SettingsFragment constructor(
|
|||
findPreference<Preference>("notification_history")?.let {
|
||||
it.isVisible = true
|
||||
it.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, NotificationHistoryFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.notifications))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, NotificationHistoryFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.notifications))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
@ -319,13 +241,14 @@ class SettingsFragment constructor(
|
|||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
findPreference<PreferenceCategory>("wear_category")?.isVisible =
|
||||
SettingsWearDetection.hasAnyNodes(requireContext())
|
||||
findPreference<Preference>("wear_settings")?.setOnPreferenceClickListener {
|
||||
findPreference<Preference>("wear_settings")?.let {
|
||||
it.isVisible = SettingsWearDetection.hasAnyNodes(requireContext())
|
||||
it.setOnPreferenceClickListener {
|
||||
startActivity(SettingsWearActivity.newInstance(requireContext()))
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<Preference>("changelog_github")?.let {
|
||||
val link = if (BuildConfig.VERSION_NAME.startsWith("LOCAL"))
|
||||
|
@ -357,142 +280,12 @@ class SettingsFragment constructor(
|
|||
}
|
||||
|
||||
findPreference<Preference>("show_share_logs")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, LogFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.log))
|
||||
.commit()
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, LogFragment::class.java, null)
|
||||
addToBackStack(getString(commonR.string.log))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
presenter.onCreate()
|
||||
}
|
||||
|
||||
override fun disableInternalConnection() {
|
||||
findPreference<EditTextPreference>("connection_internal")?.let {
|
||||
it.isEnabled = false
|
||||
try {
|
||||
val unwrappedDrawable =
|
||||
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_computer)
|
||||
unwrappedDrawable?.setTint(Color.DKGRAY)
|
||||
it.icon = unwrappedDrawable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the icon tint", e)
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.let {
|
||||
it.isEnabled = false
|
||||
try {
|
||||
val unwrappedDrawable =
|
||||
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_wifi)
|
||||
unwrappedDrawable?.setTint(Color.DKGRAY)
|
||||
it.icon = unwrappedDrawable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the icon tint", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun enableInternalConnection() {
|
||||
findPreference<EditTextPreference>("connection_internal")?.let {
|
||||
it.isEnabled = true
|
||||
try {
|
||||
val unwrappedDrawable =
|
||||
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_computer)
|
||||
unwrappedDrawable?.setTint(resources.getColor(commonR.color.colorAccent))
|
||||
it.icon = unwrappedDrawable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the icon tint", e)
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.let {
|
||||
it.isEnabled = true
|
||||
try {
|
||||
val unwrappedDrawable =
|
||||
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_wifi)
|
||||
unwrappedDrawable?.setTint(resources.getColor(commonR.color.colorAccent))
|
||||
it.icon = unwrappedDrawable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the icon tint", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateExternalUrl(url: String, useCloud: Boolean) {
|
||||
findPreference<Preference>("connection_external")?.let {
|
||||
it.summary =
|
||||
if (useCloud) getString(commonR.string.input_cloud)
|
||||
else url
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSsids(ssids: Set<String>) {
|
||||
findPreference<Preference>("connection_internal_ssids")?.let {
|
||||
it.summary =
|
||||
if (ssids.isEmpty()) getString(commonR.string.pref_connection_ssids_empty)
|
||||
else ssids.joinToString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDisplaySsidScreen() {
|
||||
val permissionsToCheck: Array<String> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
} else {
|
||||
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
}
|
||||
|
||||
if (DisabledLocationHandler.isLocationEnabled(requireContext())) {
|
||||
var permissionsToRequest: Array<String>? = null
|
||||
if (permissionsToCheck.isNotEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// For Android 11 we MUST NOT request Background Location permission with fine or coarse permissions
|
||||
// as for Android 11 the background location request needs to be done separately
|
||||
// See here: https://developer.android.com/about/versions/11/privacy/location#request-background-location-separately
|
||||
permissionsToRequest = permissionsToCheck.toList().minus(Manifest.permission.ACCESS_BACKGROUND_LOCATION).toTypedArray()
|
||||
}
|
||||
|
||||
val hasPermission = checkPermission(permissionsToCheck)
|
||||
if (permissionsToCheck.isNotEmpty() && !hasPermission) {
|
||||
LocationPermissionInfoHandler.showLocationPermInfoDialogIfNeeded(
|
||||
requireContext(), permissionsToCheck,
|
||||
continueYesCallback = {
|
||||
checkAndRequestPermissions(permissionsToCheck, LOCATION_REQUEST_CODE, permissionsToRequest, true)
|
||||
// showSsidSettings() will be called in onRequestPermissionsResult if permission is granted
|
||||
}
|
||||
)
|
||||
} else showSsidSettings()
|
||||
} else {
|
||||
if (presenter.isSsidUsed()) {
|
||||
DisabledLocationHandler.showLocationDisabledWarnDialog(requireActivity(), arrayOf(getString(commonR.string.pref_connection_wifi)), showAsNotification = false, withDisableOption = true) {
|
||||
presenter.clearSsids()
|
||||
presenter.updateInternalUrlStatus()
|
||||
}
|
||||
} else {
|
||||
DisabledLocationHandler.showLocationDisabledWarnDialog(requireActivity(), arrayOf(getString(commonR.string.pref_connection_wifi)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSsidSettings() {
|
||||
parentFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.content, SsidFragment::class.java, null)
|
||||
.addToBackStack(getString(commonR.string.manage_ssids))
|
||||
.commit()
|
||||
}
|
||||
|
||||
private fun setLockAuthenticationResult(result: Int): Boolean {
|
||||
val success = result == Authenticator.SUCCESS
|
||||
val switchLock = findPreference<SwitchPreference>("app_lock")
|
||||
switchLock?.isChecked = success
|
||||
|
||||
// Prevent requesting authentication after just enabling the app lock
|
||||
presenter.setAppActive(success)
|
||||
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.isVisible = success
|
||||
findPreference<EditTextPreference>("session_timeout")?.isVisible = success
|
||||
return (result == Authenticator.SUCCESS || result == Authenticator.CANCELED)
|
||||
}
|
||||
|
||||
private fun removeSystemFromThemesIfNeeded() {
|
||||
|
@ -572,32 +365,6 @@ class SettingsFragment constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkAndRequestPermissions(permissions: Array<String>, requestCode: Int, requestPermissions: Array<String>? = null, forceRequest: Boolean = false): Boolean {
|
||||
val permissionsNeeded = mutableListOf<String>()
|
||||
for (permission in permissions) {
|
||||
if (forceRequest || ContextCompat.checkSelfPermission(requireContext(), permission) === PackageManager.PERMISSION_DENIED) {
|
||||
if (requestPermissions.isNullOrEmpty() || requestPermissions.contains(permission)) {
|
||||
permissionsNeeded.add(permission)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (permissionsNeeded.isNotEmpty()) {
|
||||
requestPermissions(permissionsNeeded.toTypedArray(), requestCode)
|
||||
false
|
||||
} else true
|
||||
}
|
||||
|
||||
fun checkPermission(permissions: Array<String>?): Boolean {
|
||||
if (!permissions.isNullOrEmpty()) {
|
||||
for (permission in permissions) {
|
||||
if (ContextCompat.checkSelfPermission(requireContext(), permission) === PackageManager.PERMISSION_DENIED) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isIgnoringBatteryOptimizations(): Boolean {
|
||||
return Build.VERSION.SDK_INT <= Build.VERSION_CODES.M ||
|
||||
context?.getSystemService<PowerManager>()
|
||||
|
@ -605,38 +372,13 @@ class SettingsFragment constructor(
|
|||
?: false
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
|
||||
val isGreaterR = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
|
||||
if (requestCode == LOCATION_REQUEST_CODE && grantResults.isNotEmpty()) {
|
||||
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
|
||||
if (isGreaterR) {
|
||||
// For Android 11 we MUST NOT request Background Location permission with fine or coarse permissions
|
||||
// as for Android 11 the background location request needs to be done separately
|
||||
// See here: https://developer.android.com/about/versions/11/privacy/location#request-background-location-separately
|
||||
// The separate request of background location is done here
|
||||
requestPermissions(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), BACKGROUND_LOCATION_REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((requestCode == LOCATION_REQUEST_CODE && !isGreaterR || requestCode == BACKGROUND_LOCATION_REQUEST_CODE && isGreaterR) && grantResults.isNotEmpty()) {
|
||||
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
|
||||
showSsidSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
activity?.title = getString(commonR.string.companion_app)
|
||||
}
|
||||
|
||||
presenter.updateExternalUrlStatus()
|
||||
presenter.updateInternalUrlStatus()
|
||||
override fun onDestroy() {
|
||||
presenter.onFinish()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
package io.homeassistant.companion.android.settings
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import io.homeassistant.companion.android.settings.server.ServerSettingsPresenter
|
||||
import io.homeassistant.companion.android.settings.server.ServerSettingsPresenterImpl
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
abstract class SettingsModule {
|
||||
|
||||
companion object {
|
||||
@Provides
|
||||
fun settingsView(@ActivityContext context: Context): SettingsView = context as SettingsView
|
||||
}
|
||||
@Binds
|
||||
abstract fun serverSettingsPresenter(serverSettingsPresenterImpl: ServerSettingsPresenterImpl): ServerSettingsPresenter
|
||||
|
||||
@Binds
|
||||
abstract fun settingsPresenter(settingsPresenterImpl: SettingsPresenterImpl): SettingsPresenter
|
||||
|
|
|
@ -5,16 +5,10 @@ import androidx.preference.PreferenceDataStore
|
|||
import io.homeassistant.companion.android.common.data.integration.impl.entities.RateLimitResponse
|
||||
|
||||
interface SettingsPresenter {
|
||||
fun init(settingsView: SettingsView)
|
||||
fun getPreferenceDataStore(): PreferenceDataStore
|
||||
fun onCreate()
|
||||
fun onFinish()
|
||||
fun updateExternalUrlStatus()
|
||||
fun updateInternalUrlStatus()
|
||||
fun setAppActive(active: Boolean)
|
||||
fun getServerRegistrationName(): String?
|
||||
fun getServerName(): String
|
||||
suspend fun getNotificationRateLimits(): RateLimitResponse?
|
||||
|
||||
fun isSsidUsed(): Boolean
|
||||
fun clearSsids()
|
||||
fun showChangeLog(context: Context)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ package io.homeassistant.companion.android.settings
|
|||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceDataStore
|
||||
import io.homeassistant.companion.android.common.data.authentication.AuthenticationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.impl.entities.RateLimitResponse
|
||||
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
|
||||
|
@ -24,7 +22,6 @@ import javax.inject.Inject
|
|||
class SettingsPresenterImpl @Inject constructor(
|
||||
private val urlUseCase: UrlRepository,
|
||||
private val integrationUseCase: IntegrationRepository,
|
||||
private val authenticationUseCase: AuthenticationRepository,
|
||||
private val prefsRepository: PrefsRepository,
|
||||
private val themesManager: ThemesManager,
|
||||
private val langsManager: LanguagesManager,
|
||||
|
@ -36,77 +33,46 @@ class SettingsPresenterImpl @Inject constructor(
|
|||
}
|
||||
|
||||
private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
private lateinit var settingsView: SettingsView
|
||||
|
||||
override fun init(settingsView: SettingsView) {
|
||||
this.settingsView = settingsView
|
||||
}
|
||||
|
||||
override fun getBoolean(key: String, defValue: Boolean): Boolean {
|
||||
return runBlocking {
|
||||
override fun getBoolean(key: String, defValue: Boolean): Boolean = runBlocking {
|
||||
return@runBlocking when (key) {
|
||||
"fullscreen" -> integrationUseCase.isFullScreenEnabled()
|
||||
"keep_screen_on" -> integrationUseCase.isKeepScreenOnEnabled()
|
||||
"pinch_to_zoom" -> integrationUseCase.isPinchToZoomEnabled()
|
||||
"app_lock" -> authenticationUseCase.isLockEnabledRaw()
|
||||
"app_lock_home_bypass" -> authenticationUseCase.isLockHomeBypassEnabled()
|
||||
"fullscreen" -> prefsRepository.isFullScreenEnabled()
|
||||
"keep_screen_on" -> prefsRepository.isKeepScreenOnEnabled()
|
||||
"pinch_to_zoom" -> prefsRepository.isPinchToZoomEnabled()
|
||||
"crash_reporting" -> prefsRepository.isCrashReporting()
|
||||
"autoplay_video" -> integrationUseCase.isAutoPlayVideoEnabled()
|
||||
"always_show_first_view_on_app_start" -> integrationUseCase.isAlwaysShowFirstViewOnAppStartEnabled()
|
||||
"webview_debug" -> integrationUseCase.isWebViewDebugEnabled()
|
||||
"autoplay_video" -> prefsRepository.isAutoPlayVideoEnabled()
|
||||
"always_show_first_view_on_app_start" -> prefsRepository.isAlwaysShowFirstViewOnAppStartEnabled()
|
||||
"webview_debug" -> prefsRepository.isWebViewDebugEnabled()
|
||||
else -> throw IllegalArgumentException("No boolean found by this key: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun putBoolean(key: String, value: Boolean) {
|
||||
mainScope.launch {
|
||||
when (key) {
|
||||
"fullscreen" -> integrationUseCase.setFullScreenEnabled(value)
|
||||
"keep_screen_on" -> integrationUseCase.setKeepScreenOnEnabled(value)
|
||||
"pinch_to_zoom" -> integrationUseCase.setPinchToZoomEnabled(value)
|
||||
"app_lock" -> authenticationUseCase.setLockEnabled(value)
|
||||
"app_lock_home_bypass" -> authenticationUseCase.setLockHomeBypassEnabled(value)
|
||||
"fullscreen" -> prefsRepository.setFullScreenEnabled(value)
|
||||
"keep_screen_on" -> prefsRepository.setKeepScreenOnEnabled(value)
|
||||
"pinch_to_zoom" -> prefsRepository.setPinchToZoomEnabled(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)
|
||||
"autoplay_video" -> prefsRepository.setAutoPlayVideo(value)
|
||||
"always_show_first_view_on_app_start" -> prefsRepository.setAlwaysShowFirstViewOnAppStart(value)
|
||||
"webview_debug" -> prefsRepository.setWebViewDebugEnabled(value)
|
||||
else -> throw IllegalArgumentException("No boolean found by this key: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getString(key: String, defValue: String?): String? {
|
||||
return runBlocking {
|
||||
override fun getString(key: String, defValue: String?): String? = runBlocking {
|
||||
when (key) {
|
||||
"connection_internal" -> (urlUseCase.getUrl(isInternal = true, force = true) ?: "").toString()
|
||||
"registration_name" -> integrationUseCase.getRegistration().deviceName
|
||||
"session_timeout" -> integrationUseCase.getSessionTimeOut().toString()
|
||||
"themes" -> themesManager.getCurrentTheme()
|
||||
"languages" -> langsManager.getCurrentLang()
|
||||
else -> throw IllegalArgumentException("No string found by this key: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun putString(key: String, value: String?) {
|
||||
mainScope.launch {
|
||||
when (key) {
|
||||
"connection_internal" -> urlUseCase.saveUrl(value ?: "", true)
|
||||
"session_timeout" -> {
|
||||
try {
|
||||
integrationUseCase.sessionTimeOut(value.toString().toInt())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Issue saving session timeout value", e)
|
||||
}
|
||||
}
|
||||
"registration_name" -> {
|
||||
try {
|
||||
integrationUseCase.updateRegistration(DeviceRegistration(deviceName = value!!))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Issue updating registration with new device name", e)
|
||||
}
|
||||
}
|
||||
"themes" -> themesManager.saveTheme(value)
|
||||
"languages" -> langsManager.saveLang(value)
|
||||
else -> throw IllegalArgumentException("No string found by this key: $key")
|
||||
|
@ -114,68 +80,20 @@ class SettingsPresenterImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getInt(key: String, defValue: Int): Int {
|
||||
return runBlocking {
|
||||
when (key) {
|
||||
"session_timeout" -> integrationUseCase.getSessionTimeOut()
|
||||
else -> throw IllegalArgumentException("No int found by this key: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun putInt(key: String, value: Int) {
|
||||
mainScope.launch {
|
||||
when (key) {
|
||||
"session_timeout" -> integrationUseCase.sessionTimeOut(value)
|
||||
else -> throw IllegalArgumentException("No int found by this key: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPreferenceDataStore(): PreferenceDataStore {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
mainScope.launch {
|
||||
handleInternalUrlStatus(urlUseCase.getHomeWifiSsids())
|
||||
updateExternalUrlStatus()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
mainScope.cancel()
|
||||
}
|
||||
|
||||
override fun updateExternalUrlStatus() {
|
||||
mainScope.launch {
|
||||
settingsView.updateExternalUrl(
|
||||
urlUseCase.getUrl(false)?.toString() ?: "",
|
||||
urlUseCase.shouldUseCloud() && urlUseCase.canUseCloud()
|
||||
)
|
||||
}
|
||||
override fun getServerRegistrationName(): String? = runBlocking {
|
||||
integrationUseCase.getRegistration().deviceName
|
||||
}
|
||||
|
||||
override fun updateInternalUrlStatus() {
|
||||
mainScope.launch {
|
||||
handleInternalUrlStatus(urlUseCase.getHomeWifiSsids())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleInternalUrlStatus(ssids: Set<String>) {
|
||||
if (ssids.isEmpty()) {
|
||||
settingsView.disableInternalConnection()
|
||||
urlUseCase.saveUrl("", true)
|
||||
} else {
|
||||
settingsView.enableInternalConnection()
|
||||
}
|
||||
settingsView.updateSsids(ssids)
|
||||
}
|
||||
|
||||
override fun setAppActive(active: Boolean) {
|
||||
runBlocking {
|
||||
integrationUseCase.setAppActive(active)
|
||||
}
|
||||
override fun getServerName(): String = runBlocking {
|
||||
urlUseCase.getUrl()?.toString() ?: ""
|
||||
}
|
||||
|
||||
override suspend fun getNotificationRateLimits(): RateLimitResponse? = withContext(Dispatchers.IO) {
|
||||
|
@ -187,18 +105,6 @@ class SettingsPresenterImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun clearSsids() {
|
||||
mainScope.launch {
|
||||
urlUseCase.saveHomeWifiSsids(emptySet())
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSsidUsed(): Boolean {
|
||||
return runBlocking {
|
||||
urlUseCase.getHomeWifiSsids().isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showChangeLog(context: Context) {
|
||||
changeLog.showChangeLog(context, true)
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package io.homeassistant.companion.android.settings
|
||||
|
||||
interface SettingsView {
|
||||
|
||||
fun disableInternalConnection()
|
||||
|
||||
fun enableInternalConnection()
|
||||
|
||||
fun updateExternalUrl(url: String, useCloud: Boolean)
|
||||
|
||||
fun updateSsids(ssids: Set<String>)
|
||||
}
|
|
@ -14,6 +14,7 @@ import io.homeassistant.companion.android.common.data.integration.ControlsAuthRe
|
|||
import io.homeassistant.companion.android.common.data.integration.Entity
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.domain
|
||||
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
|
||||
import io.homeassistant.companion.android.controls.HaControlsProviderService
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
@ -22,6 +23,7 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
class ManageControlsViewModel @Inject constructor(
|
||||
private val integrationUseCase: IntegrationRepository,
|
||||
private val prefsRepository: PrefsRepository,
|
||||
application: Application
|
||||
) : AndroidViewModel(application) {
|
||||
|
||||
|
@ -37,8 +39,8 @@ class ManageControlsViewModel @Inject constructor(
|
|||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
authRequired = integrationUseCase.getControlsAuthRequired()
|
||||
authRequiredList.addAll(integrationUseCase.getControlsAuthEntities())
|
||||
authRequired = prefsRepository.getControlsAuthRequired()
|
||||
authRequiredList.addAll(prefsRepository.getControlsAuthEntities())
|
||||
|
||||
val entities = integrationUseCase.getEntities()
|
||||
?.filter { it.domain in HaControlsProviderService.getSupportedDomains() }
|
||||
|
@ -59,8 +61,8 @@ class ManageControlsViewModel @Inject constructor(
|
|||
authRequired = setting
|
||||
if (authRequired != ControlsAuthRequiredSetting.SELECTION) authRequiredList.clear()
|
||||
|
||||
integrationUseCase.setControlsAuthRequired(setting)
|
||||
integrationUseCase.setControlsAuthEntities(authRequiredList.toList())
|
||||
prefsRepository.setControlsAuthRequired(setting)
|
||||
prefsRepository.setControlsAuthEntities(authRequiredList.toList())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,8 +91,8 @@ class ManageControlsViewModel @Inject constructor(
|
|||
// Set values for update
|
||||
authRequired = newAuthRequired
|
||||
if (newAuthRequired != ControlsAuthRequiredSetting.SELECTION) authRequiredList.clear()
|
||||
integrationUseCase.setControlsAuthRequired(newAuthRequired)
|
||||
integrationUseCase.setControlsAuthEntities(authRequiredList.toList())
|
||||
prefsRepository.setControlsAuthRequired(newAuthRequired)
|
||||
prefsRepository.setControlsAuthEntities(authRequiredList.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
package io.homeassistant.companion.android.settings.server
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.util.Log
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreference
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.authenticator.Authenticator
|
||||
import io.homeassistant.companion.android.common.util.DisabledLocationHandler
|
||||
import io.homeassistant.companion.android.common.util.LocationPermissionInfoHandler
|
||||
import io.homeassistant.companion.android.settings.SettingsActivity
|
||||
import io.homeassistant.companion.android.settings.ssid.SsidFragment
|
||||
import io.homeassistant.companion.android.settings.url.ExternalUrlFragment
|
||||
import io.homeassistant.companion.android.settings.websocket.WebsocketSettingFragment
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import javax.inject.Inject
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ServerSettingsFragment : ServerSettingsView, PreferenceFragmentCompat() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ServerSettingsFragment"
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: ServerSettingsPresenter
|
||||
|
||||
private val permissionsRequest = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
|
||||
onPermissionsResult(it)
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
presenter.init(this)
|
||||
|
||||
preferenceManager.preferenceDataStore = presenter.getPreferenceDataStore()
|
||||
|
||||
setPreferencesFromResource(R.xml.preferences_server, rootKey)
|
||||
|
||||
val onChangeUrlValidator = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val isValid = newValue.toString().isBlank() || newValue.toString().toHttpUrlOrNull() != null
|
||||
if (!isValid) {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(io.homeassistant.companion.android.common.R.string.url_invalid)
|
||||
.setMessage(io.homeassistant.companion.android.common.R.string.url_parse_error)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
isValid
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>("app_lock")?.setOnPreferenceChangeListener { _, newValue ->
|
||||
val isValid: Boolean
|
||||
if (newValue == false) {
|
||||
isValid = true
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.isVisible = false
|
||||
findPreference<EditTextPreference>("session_timeout")?.isVisible = false
|
||||
} else {
|
||||
val settingsActivity = requireActivity() as SettingsActivity
|
||||
val canAuth = settingsActivity.requestAuthentication(getString(io.homeassistant.companion.android.common.R.string.biometric_set_title), ::setLockAuthenticationResult)
|
||||
isValid = canAuth
|
||||
|
||||
if (!canAuth) {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(io.homeassistant.companion.android.common.R.string.set_lock_title)
|
||||
.setMessage(io.homeassistant.companion.android.common.R.string.set_lock_message)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
isValid
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.let {
|
||||
it.isVisible = findPreference<SwitchPreference>("app_lock")?.isChecked == true
|
||||
}
|
||||
|
||||
findPreference<EditTextPreference>("session_timeout")?.let { pref ->
|
||||
pref.setOnBindEditTextListener {
|
||||
it.inputType = InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
pref.isVisible = findPreference<SwitchPreference>("app_lock")?.isChecked == true
|
||||
}
|
||||
|
||||
findPreference<EditTextPreference>("connection_internal")?.let {
|
||||
it.onPreferenceChangeListener =
|
||||
onChangeUrlValidator
|
||||
}
|
||||
|
||||
findPreference<Preference>("connection_external")?.setOnPreferenceClickListener {
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, ExternalUrlFragment::class.java, null)
|
||||
addToBackStack(getString(io.homeassistant.companion.android.common.R.string.input_url))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
findPreference<Preference>("connection_internal_ssids")?.let {
|
||||
it.setOnPreferenceClickListener {
|
||||
onDisplaySsidScreen()
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<PreferenceCategory>("security_category")?.isVisible = Build.MODEL != "Quest"
|
||||
|
||||
findPreference<Preference>("websocket")?.let {
|
||||
it.setOnPreferenceClickListener {
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, WebsocketSettingFragment::class.java, null)
|
||||
addToBackStack(getString(io.homeassistant.companion.android.common.R.string.notifications))
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun enableInternalConnection(isEnabled: Boolean) {
|
||||
val iconTint = if (isEnabled) ContextCompat.getColor(requireContext(), commonR.color.colorAccent) else Color.DKGRAY
|
||||
val doEnable = isEnabled && hasLocationPermission()
|
||||
|
||||
findPreference<EditTextPreference>("connection_internal")?.let {
|
||||
it.isEnabled = doEnable
|
||||
try {
|
||||
val unwrappedDrawable =
|
||||
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_computer)
|
||||
unwrappedDrawable?.setTint(iconTint)
|
||||
it.icon = unwrappedDrawable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the icon tint", e)
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.let {
|
||||
it.isEnabled = doEnable
|
||||
try {
|
||||
val unwrappedDrawable =
|
||||
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_wifi)
|
||||
unwrappedDrawable?.setTint(iconTint)
|
||||
it.icon = unwrappedDrawable
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to set the icon tint", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateExternalUrl(url: String, useCloud: Boolean) {
|
||||
findPreference<Preference>("connection_external")?.let {
|
||||
it.summary =
|
||||
if (useCloud) getString(io.homeassistant.companion.android.common.R.string.input_cloud)
|
||||
else url
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSsids(ssids: Set<String>) {
|
||||
findPreference<Preference>("connection_internal_ssids")?.let {
|
||||
it.summary =
|
||||
if (ssids.isEmpty()) getString(io.homeassistant.companion.android.common.R.string.pref_connection_ssids_empty)
|
||||
else ssids.joinToString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDisplaySsidScreen() {
|
||||
val permissionsToCheck: Array<String> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
} else {
|
||||
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
}
|
||||
|
||||
if (DisabledLocationHandler.isLocationEnabled(requireContext())) {
|
||||
if (!checkPermission(permissionsToCheck)) {
|
||||
LocationPermissionInfoHandler.showLocationPermInfoDialogIfNeeded(
|
||||
requireContext(), permissionsToCheck,
|
||||
continueYesCallback = {
|
||||
requestLocationPermission()
|
||||
// showSsidSettings() will be called if permission is granted
|
||||
}
|
||||
)
|
||||
} else showSsidSettings()
|
||||
} else {
|
||||
if (presenter.isSsidUsed()) {
|
||||
DisabledLocationHandler.showLocationDisabledWarnDialog(
|
||||
requireActivity(),
|
||||
arrayOf(
|
||||
getString(
|
||||
io.homeassistant.companion.android.common.R.string.pref_connection_wifi
|
||||
)
|
||||
),
|
||||
showAsNotification = false, withDisableOption = true
|
||||
) {
|
||||
presenter.clearSsids()
|
||||
}
|
||||
} else {
|
||||
DisabledLocationHandler.showLocationDisabledWarnDialog(
|
||||
requireActivity(),
|
||||
arrayOf(
|
||||
getString(
|
||||
io.homeassistant.companion.android.common.R.string.pref_connection_wifi
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSsidSettings() {
|
||||
parentFragmentManager.commit {
|
||||
replace(R.id.content, SsidFragment::class.java, null)
|
||||
addToBackStack(getString(io.homeassistant.companion.android.common.R.string.manage_ssids))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLockAuthenticationResult(result: Int): Boolean {
|
||||
val success = result == Authenticator.SUCCESS
|
||||
val switchLock = findPreference<SwitchPreference>("app_lock")
|
||||
switchLock?.isChecked = success
|
||||
|
||||
// Prevent requesting authentication after just enabling the app lock
|
||||
presenter.setAppActive()
|
||||
|
||||
findPreference<SwitchPreference>("app_lock_home_bypass")?.isVisible = success
|
||||
findPreference<EditTextPreference>("session_timeout")?.isVisible = success
|
||||
return (result == Authenticator.SUCCESS || result == Authenticator.CANCELED)
|
||||
}
|
||||
|
||||
private fun hasLocationPermission(): Boolean {
|
||||
val permissionsToCheck: Array<String> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
} else {
|
||||
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
}
|
||||
return checkPermission(permissionsToCheck)
|
||||
}
|
||||
|
||||
private fun requestLocationPermission() {
|
||||
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION) // Background location will be requested later
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
} else {
|
||||
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
}
|
||||
permissionsRequest.launch(permissions)
|
||||
}
|
||||
|
||||
private fun checkPermission(permissions: Array<String>?): Boolean {
|
||||
if (!permissions.isNullOrEmpty()) {
|
||||
for (permission in permissions) {
|
||||
if (ContextCompat.checkSelfPermission(requireContext(), permission) == PackageManager.PERMISSION_DENIED) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onPermissionsResult(results: Map<String, Boolean>) {
|
||||
if (results.keys.contains(Manifest.permission.ACCESS_FINE_LOCATION) &&
|
||||
results[Manifest.permission.ACCESS_FINE_LOCATION] == true &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
) {
|
||||
// For Android 11+ we MUST NOT request Background Location permission with fine or coarse
|
||||
// permissions as for Android 11 the background location request needs to be done separately
|
||||
// See here: https://developer.android.com/about/versions/11/privacy/location#request-background-location-separately
|
||||
// The separate request of background location is done here
|
||||
permissionsRequest.launch(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
|
||||
return
|
||||
}
|
||||
|
||||
if (results.entries.all { it.value }) {
|
||||
showSsidSettings()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
activity?.title = getString(commonR.string.server_settings)
|
||||
|
||||
presenter.updateUrlStatus()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
presenter.onFinish()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.homeassistant.companion.android.settings.server
|
||||
|
||||
import androidx.preference.PreferenceDataStore
|
||||
|
||||
interface ServerSettingsPresenter {
|
||||
fun init(view: ServerSettingsView)
|
||||
fun getPreferenceDataStore(): PreferenceDataStore
|
||||
fun onFinish()
|
||||
|
||||
fun updateUrlStatus()
|
||||
fun isSsidUsed(): Boolean
|
||||
fun clearSsids()
|
||||
|
||||
fun setAppActive()
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package io.homeassistant.companion.android.settings.server
|
||||
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceDataStore
|
||||
import io.homeassistant.companion.android.common.data.authentication.AuthenticationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.url.UrlRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
||||
class ServerSettingsPresenterImpl @Inject constructor(
|
||||
private val authenticationRepository: AuthenticationRepository,
|
||||
private val integrationRepository: IntegrationRepository,
|
||||
private val urlRepository: UrlRepository
|
||||
) : ServerSettingsPresenter, PreferenceDataStore() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ServerSettingsPresImpl"
|
||||
}
|
||||
|
||||
private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
private lateinit var view: ServerSettingsView
|
||||
|
||||
override fun init(view: ServerSettingsView) {
|
||||
this.view = view
|
||||
}
|
||||
|
||||
override fun getPreferenceDataStore(): PreferenceDataStore = this
|
||||
|
||||
override fun getBoolean(key: String?, defValue: Boolean): Boolean = runBlocking {
|
||||
when (key) {
|
||||
"app_lock" -> authenticationRepository.isLockEnabledRaw()
|
||||
"app_lock_home_bypass" -> authenticationRepository.isLockHomeBypassEnabled()
|
||||
else -> throw IllegalArgumentException("No boolean found by this key: $key")
|
||||
}
|
||||
}
|
||||
|
||||
override fun putBoolean(key: String?, value: Boolean) {
|
||||
mainScope.launch {
|
||||
when (key) {
|
||||
"app_lock" -> authenticationRepository.setLockEnabled(value)
|
||||
"app_lock_home_bypass" -> authenticationRepository.setLockHomeBypassEnabled(value)
|
||||
else -> throw IllegalArgumentException("No boolean found by this key: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getString(key: String?, defValue: String?): String? = runBlocking {
|
||||
when (key) {
|
||||
"connection_internal" -> (urlRepository.getUrl(isInternal = true, force = true) ?: "").toString()
|
||||
"registration_name" -> integrationRepository.getRegistration().deviceName
|
||||
"session_timeout" -> integrationRepository.getSessionTimeOut().toString()
|
||||
else -> throw IllegalArgumentException("No string found by this key: $key")
|
||||
}
|
||||
}
|
||||
|
||||
override fun putString(key: String?, value: String?) {
|
||||
mainScope.launch {
|
||||
when (key) {
|
||||
"connection_internal" -> urlRepository.saveUrl(value ?: "", true)
|
||||
"session_timeout" -> {
|
||||
try {
|
||||
integrationRepository.sessionTimeOut(value.toString().toInt())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Issue saving session timeout value", e)
|
||||
}
|
||||
}
|
||||
"registration_name" -> {
|
||||
try {
|
||||
integrationRepository.updateRegistration(DeviceRegistration(deviceName = value!!))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Issue updating registration with new device name", e)
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("No string found by this key: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
mainScope.cancel()
|
||||
}
|
||||
|
||||
override fun updateUrlStatus() {
|
||||
mainScope.launch {
|
||||
view.updateExternalUrl(
|
||||
urlRepository.getUrl(false)?.toString() ?: "",
|
||||
urlRepository.shouldUseCloud() && urlRepository.canUseCloud()
|
||||
)
|
||||
}
|
||||
mainScope.launch {
|
||||
val ssids = urlRepository.getHomeWifiSsids()
|
||||
if (ssids.isEmpty()) urlRepository.saveUrl("", true)
|
||||
|
||||
view.enableInternalConnection(ssids.isNotEmpty())
|
||||
view.updateSsids(ssids)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSsidUsed(): Boolean = runBlocking {
|
||||
urlRepository.getHomeWifiSsids().isNotEmpty()
|
||||
}
|
||||
|
||||
override fun clearSsids() {
|
||||
mainScope.launch {
|
||||
urlRepository.saveHomeWifiSsids(emptySet())
|
||||
updateUrlStatus()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setAppActive() = runBlocking {
|
||||
integrationRepository.setAppActive(true)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package io.homeassistant.companion.android.settings.server
|
||||
|
||||
interface ServerSettingsView {
|
||||
fun enableInternalConnection(isEnabled: Boolean)
|
||||
fun updateExternalUrl(url: String, useCloud: Boolean)
|
||||
fun updateSsids(ssids: Set<String>)
|
||||
}
|
|
@ -9,6 +9,7 @@ import dagger.hilt.android.qualifiers.ActivityContext
|
|||
import io.homeassistant.companion.android.common.data.authentication.AuthenticationRepository
|
||||
import io.homeassistant.companion.android.common.data.authentication.SessionState
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
|
||||
import io.homeassistant.companion.android.common.data.url.UrlRepository
|
||||
import io.homeassistant.companion.android.common.util.DisabledLocationHandler
|
||||
import io.homeassistant.companion.android.matter.MatterFrontendCommissioningStatus
|
||||
|
@ -38,6 +39,7 @@ class WebViewPresenterImpl @Inject constructor(
|
|||
private val urlUseCase: UrlRepository,
|
||||
private val authenticationUseCase: AuthenticationRepository,
|
||||
private val integrationUseCase: IntegrationRepository,
|
||||
private val prefsRepository: PrefsRepository,
|
||||
private val matterUseCase: MatterManager
|
||||
) : WebViewPresenter {
|
||||
|
||||
|
@ -144,28 +146,20 @@ class WebViewPresenterImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun isFullScreen(): Boolean {
|
||||
return runBlocking {
|
||||
integrationUseCase.isFullScreenEnabled()
|
||||
}
|
||||
override fun isFullScreen(): Boolean = runBlocking {
|
||||
prefsRepository.isFullScreenEnabled()
|
||||
}
|
||||
|
||||
override fun isKeepScreenOnEnabled(): Boolean {
|
||||
return runBlocking {
|
||||
integrationUseCase.isKeepScreenOnEnabled()
|
||||
}
|
||||
override fun isKeepScreenOnEnabled(): Boolean = runBlocking {
|
||||
prefsRepository.isKeepScreenOnEnabled()
|
||||
}
|
||||
|
||||
override fun isPinchToZoomEnabled(): Boolean {
|
||||
return runBlocking {
|
||||
integrationUseCase.isPinchToZoomEnabled()
|
||||
}
|
||||
override fun isPinchToZoomEnabled(): Boolean = runBlocking {
|
||||
prefsRepository.isPinchToZoomEnabled()
|
||||
}
|
||||
|
||||
override fun isWebViewDebugEnabled(): Boolean {
|
||||
return runBlocking {
|
||||
integrationUseCase.isWebViewDebugEnabled()
|
||||
}
|
||||
override fun isWebViewDebugEnabled(): Boolean = runBlocking {
|
||||
prefsRepository.isWebViewDebugEnabled()
|
||||
}
|
||||
|
||||
override fun isAppLocked(): Boolean {
|
||||
|
@ -186,16 +180,12 @@ class WebViewPresenterImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun isAutoPlayVideoEnabled(): Boolean {
|
||||
return runBlocking {
|
||||
integrationUseCase.isAutoPlayVideoEnabled()
|
||||
}
|
||||
override fun isAutoPlayVideoEnabled(): Boolean = runBlocking {
|
||||
prefsRepository.isAutoPlayVideoEnabled()
|
||||
}
|
||||
|
||||
override fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean {
|
||||
return runBlocking {
|
||||
integrationUseCase.isAlwaysShowFirstViewOnAppStartEnabled()
|
||||
}
|
||||
override fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean = runBlocking {
|
||||
prefsRepository.isAlwaysShowFirstViewOnAppStartEnabled()
|
||||
}
|
||||
|
||||
override fun sessionTimeOut(): Int {
|
||||
|
|
|
@ -3,28 +3,16 @@
|
|||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_connection_title">
|
||||
android:key="servers_devices_category"
|
||||
android:title="@string/servers_devices_category">
|
||||
<!-- Servers will be added at runtime -->
|
||||
<Preference
|
||||
android:key="connection_external"
|
||||
android:icon="@drawable/ic_globe"
|
||||
android:title="@string/pref_connection_url"/>
|
||||
<Preference
|
||||
android:key="connection_internal_ssids"
|
||||
android:icon="@drawable/ic_wifi"
|
||||
android:title="@string/pref_connection_wifi"
|
||||
android:summary="@string/pref_connection_ssids_empty" />
|
||||
<EditTextPreference
|
||||
android:key="connection_internal"
|
||||
android:title="@string/pref_connection_internal"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/device_registration">
|
||||
<EditTextPreference
|
||||
android:key="registration_name"
|
||||
android:icon="@drawable/ic_edit"
|
||||
android:title="@string/device_name"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
android:key="wear_settings"
|
||||
app:isPreferenceVisible="false"
|
||||
android:title="@string/wear_os_settings_title"
|
||||
android:icon="@drawable/ic_baseline_watch_24"
|
||||
android:order="999"
|
||||
android:summary="@string/wear_os_settings_summary" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/sensors">
|
||||
|
@ -39,36 +27,6 @@
|
|||
android:title="@string/sensor_update_frequency"
|
||||
android:summary="@string/sensor_update_frequency_summary" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="security_category"
|
||||
android:title="@string/security">
|
||||
<SwitchPreference
|
||||
android:key="app_lock"
|
||||
android:icon="@drawable/ic_lock"
|
||||
android:title="@string/lock_title"
|
||||
android:summary="@string/lock_summary"/>
|
||||
<SwitchPreference
|
||||
android:key="app_lock_home_bypass"
|
||||
android:icon="@drawable/ic_wifi"
|
||||
android:title="@string/lock_home_bypass_title"
|
||||
android:summary="@string/lock_home_bypass_summary"/>
|
||||
<EditTextPreference
|
||||
android:key="session_timeout"
|
||||
android:icon="@drawable/ic_timeout"
|
||||
android:title="@string/session_timeout_title"
|
||||
app:isPreferenceVisible="false"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="wear_category"
|
||||
app:isPreferenceVisible="false"
|
||||
android:title="@string/wear_os_category">
|
||||
<Preference
|
||||
android:key="wear_settings"
|
||||
android:title="@string/wear_os_settings_title"
|
||||
android:icon="@drawable/ic_baseline_watch_24"
|
||||
android:summary="@string/wear_os_settings_summary" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/other_settings">
|
||||
<SwitchPreference
|
||||
|
@ -123,11 +81,6 @@
|
|||
<Preference
|
||||
android:key="background"
|
||||
android:title="@string/background_access_title"/>
|
||||
<Preference
|
||||
android:key="websocket"
|
||||
android:icon="@drawable/ic_websocket"
|
||||
android:title="@string/websocket_setting_name"
|
||||
android:summary="@string/websocket_setting_summary" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/notifications"
|
||||
|
|
56
app/src/main/res/xml/preferences_server.xml
Normal file
56
app/src/main/res/xml/preferences_server.xml
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory
|
||||
android:title="@string/pref_connection_title">
|
||||
<Preference
|
||||
android:key="connection_external"
|
||||
android:icon="@drawable/ic_globe"
|
||||
android:title="@string/pref_connection_url"/>
|
||||
<Preference
|
||||
android:key="connection_internal_ssids"
|
||||
android:icon="@drawable/ic_wifi"
|
||||
android:title="@string/pref_connection_wifi"
|
||||
android:summary="@string/pref_connection_ssids_empty" />
|
||||
<EditTextPreference
|
||||
android:key="connection_internal"
|
||||
android:title="@string/pref_connection_internal"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/device_registration">
|
||||
<EditTextPreference
|
||||
android:key="registration_name"
|
||||
android:icon="@drawable/ic_edit"
|
||||
android:title="@string/device_name"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="security_category"
|
||||
android:title="@string/security">
|
||||
<SwitchPreference
|
||||
android:key="app_lock"
|
||||
android:icon="@drawable/ic_lock"
|
||||
android:title="@string/lock_title"
|
||||
android:summary="@string/lock_summary"/>
|
||||
<SwitchPreference
|
||||
android:key="app_lock_home_bypass"
|
||||
android:icon="@drawable/ic_wifi"
|
||||
android:title="@string/lock_home_bypass_title"
|
||||
android:summary="@string/lock_home_bypass_summary"/>
|
||||
<EditTextPreference
|
||||
android:key="session_timeout"
|
||||
android:icon="@drawable/ic_timeout"
|
||||
android:title="@string/session_timeout_title"
|
||||
app:isPreferenceVisible="false"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/other_settings">
|
||||
<Preference
|
||||
android:key="websocket"
|
||||
android:icon="@drawable/ic_websocket"
|
||||
android:title="@string/websocket_setting_name"
|
||||
android:summary="@string/websocket_setting_summary" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
|
@ -53,6 +53,11 @@ class LocalStorageImpl(private val sharedPreferences: SharedPreferences) : Local
|
|||
return sharedPreferences.getBoolean(key, false)
|
||||
}
|
||||
|
||||
override suspend fun getBooleanOrNull(key: String): Boolean? =
|
||||
if (sharedPreferences.contains(key)) {
|
||||
sharedPreferences.getBoolean(key, false)
|
||||
} else null
|
||||
|
||||
override suspend fun putStringSet(key: String, value: Set<String>) {
|
||||
sharedPreferences.edit().putStringSet(key, value).apply()
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import io.homeassistant.companion.android.common.data.keychain.KeyChainRepositor
|
|||
import io.homeassistant.companion.android.common.data.keychain.KeyChainRepositoryImpl
|
||||
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.PrefsRepositoryImpl
|
||||
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepositoryImpl
|
||||
import io.homeassistant.companion.android.common.data.url.UrlRepository
|
||||
import io.homeassistant.companion.android.common.data.url.UrlRepositoryImpl
|
||||
import io.homeassistant.companion.android.common.data.websocket.WebSocketRepository
|
||||
|
@ -98,6 +100,17 @@ abstract class DataModule {
|
|||
)
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Named("wear")
|
||||
@Singleton
|
||||
fun provideWearPrefsLocalStorage(@ApplicationContext appContext: Context): LocalStorage =
|
||||
LocalStorageImpl(
|
||||
appContext.getSharedPreferences(
|
||||
"wear_0",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Named("manufacturer")
|
||||
@Singleton
|
||||
|
@ -143,6 +156,10 @@ abstract class DataModule {
|
|||
@Singleton
|
||||
abstract fun bindPrefsRepository(prefsRepository: PrefsRepositoryImpl): PrefsRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindWearPrefsRepository(wearPrefsRepository: WearPrefsRepositoryImpl): WearPrefsRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindUrlRepository(urlRepository: UrlRepositoryImpl): UrlRepository
|
||||
|
|
|
@ -18,6 +18,8 @@ interface LocalStorage {
|
|||
|
||||
suspend fun getBoolean(key: String): Boolean
|
||||
|
||||
suspend fun getBooleanOrNull(key: String): Boolean?
|
||||
|
||||
suspend fun putStringSet(key: String, value: Set<String>)
|
||||
|
||||
suspend fun getStringSet(key: String): Set<String>?
|
||||
|
|
|
@ -21,24 +21,6 @@ interface IntegrationRepository {
|
|||
|
||||
suspend fun getZones(): Array<Entity<ZoneAttributes>>
|
||||
|
||||
suspend fun setFullScreenEnabled(enabled: Boolean)
|
||||
suspend fun isFullScreenEnabled(): Boolean
|
||||
|
||||
suspend fun setKeepScreenOnEnabled(enabled: Boolean)
|
||||
suspend fun isKeepScreenOnEnabled(): Boolean
|
||||
|
||||
suspend fun setPinchToZoomEnabled(enabled: Boolean)
|
||||
suspend fun isPinchToZoomEnabled(): Boolean
|
||||
|
||||
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
|
||||
|
||||
suspend fun isAppLocked(): Boolean
|
||||
suspend fun setAppActive(active: Boolean)
|
||||
|
||||
|
@ -47,24 +29,6 @@ interface IntegrationRepository {
|
|||
|
||||
suspend fun setSessionExpireMillis(value: Long)
|
||||
|
||||
suspend fun setControlsAuthRequired(setting: ControlsAuthRequiredSetting)
|
||||
suspend fun getControlsAuthRequired(): ControlsAuthRequiredSetting
|
||||
suspend fun setControlsAuthEntities(entities: List<String>)
|
||||
suspend fun getControlsAuthEntities(): List<String>
|
||||
|
||||
suspend fun getTileShortcuts(): List<String>
|
||||
suspend fun setTileShortcuts(entities: List<String>)
|
||||
suspend fun getTemplateTileContent(): String
|
||||
suspend fun setTemplateTileContent(content: String)
|
||||
suspend fun getTemplateTileRefreshInterval(): Int
|
||||
suspend fun setTemplateTileRefreshInterval(interval: Int)
|
||||
suspend fun setWearHapticFeedback(enabled: Boolean)
|
||||
suspend fun getWearHapticFeedback(): Boolean
|
||||
suspend fun setWearToastConfirmation(enabled: Boolean)
|
||||
suspend fun getWearToastConfirmation(): Boolean
|
||||
suspend fun getShowShortcutText(): Boolean
|
||||
suspend fun setShowShortcutTextEnabled(enabled: Boolean)
|
||||
|
||||
suspend fun getHomeAssistantVersion(): String
|
||||
suspend fun isHomeAssistantVersionAtLeast(year: Int, month: Int, release: Int): Boolean
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import io.homeassistant.companion.android.common.BuildConfig
|
|||
import io.homeassistant.companion.android.common.data.HomeAssistantVersion
|
||||
import io.homeassistant.companion.android.common.data.LocalStorage
|
||||
import io.homeassistant.companion.android.common.data.authentication.AuthenticationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.ControlsAuthRequiredSetting
|
||||
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
|
||||
import io.homeassistant.companion.android.common.data.integration.Entity
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationException
|
||||
|
@ -31,7 +30,6 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.json.JSONArray
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
@ -61,23 +59,9 @@ class IntegrationRepositoryImpl @Inject constructor(
|
|||
private const val PREF_SECRET = "secret"
|
||||
|
||||
private const val PREF_CHECK_SENSOR_REGISTRATION_NEXT = "sensor_reg_last"
|
||||
private const val PREF_TILE_SHORTCUTS = "tile_shortcuts_list"
|
||||
private const val PREF_SHOW_TILE_SHORTCUTS_TEXT = "show_tile_shortcuts_text"
|
||||
private const val PREF_TILE_TEMPLATE = "tile_template"
|
||||
private const val PREF_TILE_TEMPLATE_REFRESH_INTERVAL = "tile_template_refresh_interval"
|
||||
private const val PREF_WEAR_HAPTIC_FEEDBACK = "wear_haptic_feedback"
|
||||
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"
|
||||
private const val PREF_WEBVIEW_DEBUG_ENABLED = "webview_debug_enabled"
|
||||
private const val PREF_SESSION_TIMEOUT = "session_timeout"
|
||||
private const val PREF_SESSION_EXPIRE = "session_expire"
|
||||
private const val PREF_CONTROLS_AUTH_REQUIRED = "controls_auth_required"
|
||||
private const val PREF_CONTROLS_AUTH_ENTITIES = "controls_auth_entities"
|
||||
private const val PREF_SEC_WARNING_NEXT = "sec_warning_last"
|
||||
private const val TAG = "IntegrationRepository"
|
||||
private const val RATE_LIMIT_URL = BuildConfig.RATE_LIMIT_URL
|
||||
|
@ -338,54 +322,6 @@ class IntegrationRepositoryImpl @Inject constructor(
|
|||
else throw IntegrationException("Error calling integration request get_zones")
|
||||
}
|
||||
|
||||
override suspend fun setFullScreenEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_FULLSCREEN_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isFullScreenEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_FULLSCREEN_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun setKeepScreenOnEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_KEEP_SCREEN_ON_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isKeepScreenOnEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_KEEP_SCREEN_ON_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun setPinchToZoomEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_PINCH_TO_ZOOM_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isPinchToZoomEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_PINCH_TO_ZOOM_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun setWebViewDebugEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_WEBVIEW_DEBUG_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isWebViewDebugEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_WEBVIEW_DEBUG_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun isAutoPlayVideoEnabled(): Boolean {
|
||||
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)
|
||||
}
|
||||
|
||||
override suspend fun isAppLocked(): Boolean {
|
||||
val lockEnabled = authenticationRepository.isLockEnabled()
|
||||
val sessionExpireMillis = getSessionExpireMillis()
|
||||
|
@ -422,76 +358,6 @@ class IntegrationRepositoryImpl @Inject constructor(
|
|||
return localStorage.getLong(PREF_SESSION_EXPIRE) ?: 0
|
||||
}
|
||||
|
||||
override suspend fun setControlsAuthRequired(setting: ControlsAuthRequiredSetting) {
|
||||
localStorage.putString(PREF_CONTROLS_AUTH_REQUIRED, setting.name)
|
||||
}
|
||||
|
||||
override suspend fun getControlsAuthRequired(): ControlsAuthRequiredSetting {
|
||||
val current = localStorage.getString(PREF_CONTROLS_AUTH_REQUIRED)
|
||||
return ControlsAuthRequiredSetting.values().firstOrNull {
|
||||
it.name == current
|
||||
} ?: ControlsAuthRequiredSetting.NONE
|
||||
}
|
||||
|
||||
override suspend fun setControlsAuthEntities(entities: List<String>) {
|
||||
localStorage.putStringSet(PREF_CONTROLS_AUTH_ENTITIES, entities.toSet())
|
||||
}
|
||||
|
||||
override suspend fun getControlsAuthEntities(): List<String> {
|
||||
return localStorage.getStringSet(PREF_CONTROLS_AUTH_ENTITIES)?.toList() ?: emptyList()
|
||||
}
|
||||
|
||||
override suspend fun getTileShortcuts(): List<String> {
|
||||
val jsonArray = JSONArray(localStorage.getString(PREF_TILE_SHORTCUTS) ?: "[]")
|
||||
return List(jsonArray.length()) {
|
||||
jsonArray.getString(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setTileShortcuts(entities: List<String>) {
|
||||
localStorage.putString(PREF_TILE_SHORTCUTS, JSONArray(entities).toString())
|
||||
}
|
||||
|
||||
override suspend fun getTemplateTileContent(): String {
|
||||
return localStorage.getString(PREF_TILE_TEMPLATE) ?: ""
|
||||
}
|
||||
|
||||
override suspend fun setTemplateTileContent(content: String) {
|
||||
localStorage.putString(PREF_TILE_TEMPLATE, content)
|
||||
}
|
||||
|
||||
override suspend fun getTemplateTileRefreshInterval(): Int {
|
||||
return localStorage.getInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL) ?: 0
|
||||
}
|
||||
|
||||
override suspend fun setTemplateTileRefreshInterval(interval: Int) {
|
||||
localStorage.putInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL, interval)
|
||||
}
|
||||
|
||||
override suspend fun setWearHapticFeedback(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_WEAR_HAPTIC_FEEDBACK, enabled)
|
||||
}
|
||||
|
||||
override suspend fun getWearHapticFeedback(): Boolean {
|
||||
return localStorage.getBoolean(PREF_WEAR_HAPTIC_FEEDBACK)
|
||||
}
|
||||
|
||||
override suspend fun setWearToastConfirmation(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_WEAR_TOAST_CONFIRMATION, enabled)
|
||||
}
|
||||
|
||||
override suspend fun getWearToastConfirmation(): Boolean {
|
||||
return localStorage.getBoolean(PREF_WEAR_TOAST_CONFIRMATION)
|
||||
}
|
||||
|
||||
override suspend fun setShowShortcutTextEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT, enabled)
|
||||
}
|
||||
|
||||
override suspend fun getShowShortcutText(): Boolean {
|
||||
return localStorage.getBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT)
|
||||
}
|
||||
|
||||
override suspend fun getNotificationRateLimits(): RateLimitResponse {
|
||||
val pushToken = localStorage.getString(PREF_PUSH_TOKEN) ?: ""
|
||||
val requestBody = RateLimitRequest(pushToken)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.homeassistant.companion.android.common.data.prefs
|
||||
|
||||
import io.homeassistant.companion.android.common.data.integration.ControlsAuthRequiredSetting
|
||||
|
||||
interface PrefsRepository {
|
||||
suspend fun getAppVersion(): String?
|
||||
|
||||
|
@ -17,6 +19,38 @@ interface PrefsRepository {
|
|||
|
||||
suspend fun saveLocales(lang: String)
|
||||
|
||||
suspend fun getControlsAuthRequired(): ControlsAuthRequiredSetting
|
||||
|
||||
suspend fun setControlsAuthRequired(setting: ControlsAuthRequiredSetting)
|
||||
|
||||
suspend fun getControlsAuthEntities(): List<String>
|
||||
|
||||
suspend fun setControlsAuthEntities(entities: List<String>)
|
||||
|
||||
suspend fun isFullScreenEnabled(): Boolean
|
||||
|
||||
suspend fun setFullScreenEnabled(enabled: Boolean)
|
||||
|
||||
suspend fun isKeepScreenOnEnabled(): Boolean
|
||||
|
||||
suspend fun setKeepScreenOnEnabled(enabled: Boolean)
|
||||
|
||||
suspend fun isPinchToZoomEnabled(): Boolean
|
||||
|
||||
suspend fun setPinchToZoomEnabled(enabled: Boolean)
|
||||
|
||||
suspend fun isAutoPlayVideoEnabled(): Boolean
|
||||
|
||||
suspend fun setAutoPlayVideo(enabled: Boolean)
|
||||
|
||||
suspend fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean
|
||||
|
||||
suspend fun setAlwaysShowFirstViewOnAppStart(enabled: Boolean)
|
||||
|
||||
suspend fun isWebViewDebugEnabled(): Boolean
|
||||
|
||||
suspend fun setWebViewDebugEnabled(enabled: Boolean)
|
||||
|
||||
suspend fun isCrashReporting(): Boolean
|
||||
|
||||
suspend fun setCrashReporting(crashReportingEnabled: Boolean)
|
||||
|
|
|
@ -1,22 +1,70 @@
|
|||
package io.homeassistant.companion.android.common.data.prefs
|
||||
|
||||
import io.homeassistant.companion.android.common.data.LocalStorage
|
||||
import io.homeassistant.companion.android.common.data.integration.ControlsAuthRequiredSetting
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class PrefsRepositoryImpl @Inject constructor(
|
||||
@Named("themes") private val localStorage: LocalStorage
|
||||
@Named("themes") private val localStorage: LocalStorage,
|
||||
@Named("integration") private val integrationStorage: LocalStorage
|
||||
) : PrefsRepository {
|
||||
|
||||
companion object {
|
||||
private const val MIGRATION_PREF = "migration"
|
||||
private const val MIGRATION_VERSION = 1
|
||||
|
||||
private const val PREF_VER = "version"
|
||||
private const val PREF_THEME = "theme"
|
||||
private const val PREF_LANG = "lang"
|
||||
private const val PREF_LOCALES = "locales"
|
||||
private const val PREF_CONTROLS_AUTH_REQUIRED = "controls_auth_required"
|
||||
private const val PREF_CONTROLS_AUTH_ENTITIES = "controls_auth_entities"
|
||||
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"
|
||||
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_WEBVIEW_DEBUG_ENABLED = "webview_debug_enabled"
|
||||
private const val PREF_KEY_ALIAS = "key-alias"
|
||||
private const val PREF_CRASH_REPORTING_DISABLED = "crash_reporting"
|
||||
}
|
||||
|
||||
init {
|
||||
runBlocking {
|
||||
val currentVersion = localStorage.getInt(MIGRATION_PREF)
|
||||
if (currentVersion == null || currentVersion < 1) {
|
||||
integrationStorage.getString(PREF_CONTROLS_AUTH_REQUIRED)?.let {
|
||||
localStorage.putString(PREF_CONTROLS_AUTH_REQUIRED, it)
|
||||
}
|
||||
integrationStorage.getStringSet(PREF_CONTROLS_AUTH_ENTITIES)?.let {
|
||||
localStorage.putStringSet(PREF_CONTROLS_AUTH_ENTITIES, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_FULLSCREEN_ENABLED)?.let {
|
||||
localStorage.putBoolean(PREF_FULLSCREEN_ENABLED, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_KEEP_SCREEN_ON_ENABLED)?.let {
|
||||
localStorage.putBoolean(PREF_KEEP_SCREEN_ON_ENABLED, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_PINCH_TO_ZOOM_ENABLED)?.let {
|
||||
localStorage.putBoolean(PREF_PINCH_TO_ZOOM_ENABLED, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_AUTOPLAY_VIDEO)?.let {
|
||||
localStorage.putBoolean(PREF_AUTOPLAY_VIDEO, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_ALWAYS_SHOW_FIRST_VIEW_ON_APP_START)?.let {
|
||||
localStorage.putBoolean(PREF_ALWAYS_SHOW_FIRST_VIEW_ON_APP_START, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_WEBVIEW_DEBUG_ENABLED)?.let {
|
||||
localStorage.putBoolean(PREF_WEBVIEW_DEBUG_ENABLED, it)
|
||||
}
|
||||
|
||||
localStorage.putInt(MIGRATION_PREF, MIGRATION_VERSION)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getAppVersion(): String? {
|
||||
return localStorage.getString(PREF_VER)
|
||||
}
|
||||
|
@ -49,6 +97,73 @@ class PrefsRepositoryImpl @Inject constructor(
|
|||
localStorage.putString(PREF_LOCALES, locales)
|
||||
}
|
||||
|
||||
override suspend fun getControlsAuthRequired(): ControlsAuthRequiredSetting {
|
||||
val current = localStorage.getString(PREF_CONTROLS_AUTH_REQUIRED)
|
||||
return ControlsAuthRequiredSetting.values().firstOrNull {
|
||||
it.name == current
|
||||
} ?: ControlsAuthRequiredSetting.NONE
|
||||
}
|
||||
|
||||
override suspend fun setControlsAuthRequired(setting: ControlsAuthRequiredSetting) {
|
||||
localStorage.putString(PREF_CONTROLS_AUTH_REQUIRED, setting.name)
|
||||
}
|
||||
|
||||
override suspend fun getControlsAuthEntities(): List<String> {
|
||||
return localStorage.getStringSet(PREF_CONTROLS_AUTH_ENTITIES)?.toList() ?: emptyList()
|
||||
}
|
||||
|
||||
override suspend fun setControlsAuthEntities(entities: List<String>) {
|
||||
localStorage.putStringSet(PREF_CONTROLS_AUTH_ENTITIES, entities.toSet())
|
||||
}
|
||||
|
||||
override suspend fun isFullScreenEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_FULLSCREEN_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun setFullScreenEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_FULLSCREEN_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isKeepScreenOnEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_KEEP_SCREEN_ON_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun setKeepScreenOnEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_KEEP_SCREEN_ON_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isPinchToZoomEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_PINCH_TO_ZOOM_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun setPinchToZoomEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_PINCH_TO_ZOOM_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isAutoPlayVideoEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_AUTOPLAY_VIDEO)
|
||||
}
|
||||
|
||||
override suspend fun setAutoPlayVideo(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_AUTOPLAY_VIDEO, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isAlwaysShowFirstViewOnAppStartEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_ALWAYS_SHOW_FIRST_VIEW_ON_APP_START)
|
||||
}
|
||||
|
||||
override suspend fun setAlwaysShowFirstViewOnAppStart(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_ALWAYS_SHOW_FIRST_VIEW_ON_APP_START, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isWebViewDebugEnabled(): Boolean {
|
||||
return localStorage.getBoolean(PREF_WEBVIEW_DEBUG_ENABLED)
|
||||
}
|
||||
|
||||
override suspend fun setWebViewDebugEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_WEBVIEW_DEBUG_ENABLED, enabled)
|
||||
}
|
||||
|
||||
override suspend fun isCrashReporting(): Boolean {
|
||||
return !localStorage.getBoolean(PREF_CRASH_REPORTING_DISABLED)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package io.homeassistant.companion.android.common.data.prefs
|
||||
|
||||
interface WearPrefsRepository {
|
||||
suspend fun getTileShortcuts(): List<String>
|
||||
suspend fun setTileShortcuts(entities: List<String>)
|
||||
suspend fun getShowShortcutText(): Boolean
|
||||
suspend fun setShowShortcutTextEnabled(enabled: Boolean)
|
||||
suspend fun getTemplateTileContent(): String
|
||||
suspend fun setTemplateTileContent(content: String)
|
||||
suspend fun getTemplateTileRefreshInterval(): Int
|
||||
suspend fun setTemplateTileRefreshInterval(interval: Int)
|
||||
suspend fun getWearHapticFeedback(): Boolean
|
||||
suspend fun setWearHapticFeedback(enabled: Boolean)
|
||||
suspend fun getWearToastConfirmation(): Boolean
|
||||
suspend fun setWearToastConfirmation(enabled: Boolean)
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package io.homeassistant.companion.android.common.data.prefs
|
||||
|
||||
import io.homeassistant.companion.android.common.data.LocalStorage
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.json.JSONArray
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class WearPrefsRepositoryImpl @Inject constructor(
|
||||
@Named("wear") private val localStorage: LocalStorage,
|
||||
@Named("integration") private val integrationStorage: LocalStorage
|
||||
) : WearPrefsRepository {
|
||||
|
||||
companion object {
|
||||
private const val MIGRATION_PREF = "migration"
|
||||
private const val MIGRATION_VERSION = 1
|
||||
|
||||
private const val PREF_TILE_SHORTCUTS = "tile_shortcuts_list"
|
||||
private const val PREF_SHOW_TILE_SHORTCUTS_TEXT = "show_tile_shortcuts_text"
|
||||
private const val PREF_TILE_TEMPLATE = "tile_template"
|
||||
private const val PREF_TILE_TEMPLATE_REFRESH_INTERVAL = "tile_template_refresh_interval"
|
||||
private const val PREF_WEAR_HAPTIC_FEEDBACK = "wear_haptic_feedback"
|
||||
private const val PREF_WEAR_TOAST_CONFIRMATION = "wear_toast_confirmation"
|
||||
}
|
||||
|
||||
init {
|
||||
runBlocking {
|
||||
val currentVersion = localStorage.getInt(MIGRATION_PREF)
|
||||
if (currentVersion == null || currentVersion < 1) {
|
||||
integrationStorage.getString(PREF_TILE_SHORTCUTS)?.let {
|
||||
localStorage.putString(PREF_TILE_SHORTCUTS, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_SHOW_TILE_SHORTCUTS_TEXT)?.let {
|
||||
localStorage.putBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT, it)
|
||||
}
|
||||
integrationStorage.getString(PREF_TILE_TEMPLATE)?.let {
|
||||
localStorage.putString(PREF_TILE_TEMPLATE, it)
|
||||
}
|
||||
integrationStorage.getInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL)?.let {
|
||||
localStorage.putInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_WEAR_HAPTIC_FEEDBACK)?.let {
|
||||
localStorage.putBoolean(PREF_WEAR_HAPTIC_FEEDBACK, it)
|
||||
}
|
||||
integrationStorage.getBooleanOrNull(PREF_WEAR_TOAST_CONFIRMATION)?.let {
|
||||
localStorage.putBoolean(PREF_WEAR_TOAST_CONFIRMATION, it)
|
||||
}
|
||||
|
||||
localStorage.putInt(MIGRATION_PREF, MIGRATION_VERSION)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTileShortcuts(): List<String> {
|
||||
val jsonArray = JSONArray(localStorage.getString(PREF_TILE_SHORTCUTS) ?: "[]")
|
||||
return List(jsonArray.length()) {
|
||||
jsonArray.getString(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setTileShortcuts(entities: List<String>) {
|
||||
localStorage.putString(PREF_TILE_SHORTCUTS, JSONArray(entities).toString())
|
||||
}
|
||||
|
||||
override suspend fun getTemplateTileContent(): String {
|
||||
return localStorage.getString(PREF_TILE_TEMPLATE) ?: ""
|
||||
}
|
||||
|
||||
override suspend fun getShowShortcutText(): Boolean {
|
||||
return localStorage.getBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT)
|
||||
}
|
||||
|
||||
override suspend fun setShowShortcutTextEnabled(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT, enabled)
|
||||
}
|
||||
|
||||
override suspend fun setTemplateTileContent(content: String) {
|
||||
localStorage.putString(PREF_TILE_TEMPLATE, content)
|
||||
}
|
||||
|
||||
override suspend fun getTemplateTileRefreshInterval(): Int {
|
||||
return localStorage.getInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL) ?: 0
|
||||
}
|
||||
|
||||
override suspend fun setTemplateTileRefreshInterval(interval: Int) {
|
||||
localStorage.putInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL, interval)
|
||||
}
|
||||
|
||||
override suspend fun getWearHapticFeedback(): Boolean {
|
||||
return localStorage.getBoolean(PREF_WEAR_HAPTIC_FEEDBACK)
|
||||
}
|
||||
|
||||
override suspend fun setWearHapticFeedback(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_WEAR_HAPTIC_FEEDBACK, enabled)
|
||||
}
|
||||
|
||||
override suspend fun getWearToastConfirmation(): Boolean {
|
||||
return localStorage.getBoolean(PREF_WEAR_TOAST_CONFIRMATION)
|
||||
}
|
||||
|
||||
override suspend fun setWearToastConfirmation(enabled: Boolean) {
|
||||
localStorage.putBoolean(PREF_WEAR_TOAST_CONFIRMATION, enabled)
|
||||
}
|
||||
}
|
|
@ -645,6 +645,8 @@
|
|||
<string name="sensor">Sensor</string>
|
||||
<string name="sensors_with_settings">The following sensors offer custom settings: %1$s</string>
|
||||
<string name="sensors">Sensors</string>
|
||||
<string name="server_settings">Server Settings</string>
|
||||
<string name="servers_devices_category">Servers & Devices</string>
|
||||
<string name="service_call_failure">Unable to send service call</string>
|
||||
<string name="session_timeout_title">Session TimeOut (in seconds)</string>
|
||||
<string name="set_favorite">Set Favorites</string>
|
||||
|
@ -818,7 +820,6 @@
|
|||
<string name="view_password">View Password</string>
|
||||
<string name="wait">Wait</string>
|
||||
<string name="wear_favorite_entities">Favorite Entities</string>
|
||||
<string name="wear_os_category">Wear OS</string>
|
||||
<string name="wear_os_settings_summary">Manage Wear OS App</string>
|
||||
<string name="wear_os_settings_title">Wear OS Settings</string>
|
||||
<string name="wear_set_favorites">Select your favorite entities to appear at the top of the Wear home screen. You can also drag and drop to change the order in which they appear.</string>
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.homeassistant.companion.android.common.data.authentication.SessionStat
|
|||
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
|
||||
import io.homeassistant.companion.android.common.data.integration.Entity
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository
|
||||
import io.homeassistant.companion.android.common.data.websocket.WebSocketRepository
|
||||
import io.homeassistant.companion.android.common.data.websocket.WebSocketState
|
||||
import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse
|
||||
|
@ -29,7 +30,8 @@ import io.homeassistant.companion.android.common.R as commonR
|
|||
class HomePresenterImpl @Inject constructor(
|
||||
private val authenticationUseCase: AuthenticationRepository,
|
||||
private val integrationUseCase: IntegrationRepository,
|
||||
private val webSocketUseCase: WebSocketRepository
|
||||
private val webSocketUseCase: WebSocketRepository,
|
||||
private val wearPrefsRepository: WearPrefsRepository
|
||||
) : HomePresenter {
|
||||
|
||||
companion object {
|
||||
|
@ -223,50 +225,50 @@ class HomePresenterImpl @Inject constructor(
|
|||
}
|
||||
|
||||
override suspend fun getTileShortcuts(): List<SimplifiedEntity> {
|
||||
return integrationUseCase.getTileShortcuts().map { SimplifiedEntity(it) }
|
||||
return wearPrefsRepository.getTileShortcuts().map { SimplifiedEntity(it) }
|
||||
}
|
||||
|
||||
override suspend fun setTileShortcuts(entities: List<SimplifiedEntity>) {
|
||||
integrationUseCase.setTileShortcuts(entities.map { it.entityString })
|
||||
wearPrefsRepository.setTileShortcuts(entities.map { it.entityString })
|
||||
}
|
||||
|
||||
override suspend fun getWearHapticFeedback(): Boolean {
|
||||
return integrationUseCase.getWearHapticFeedback()
|
||||
return wearPrefsRepository.getWearHapticFeedback()
|
||||
}
|
||||
|
||||
override suspend fun setWearHapticFeedback(enabled: Boolean) {
|
||||
integrationUseCase.setWearHapticFeedback(enabled)
|
||||
wearPrefsRepository.setWearHapticFeedback(enabled)
|
||||
}
|
||||
|
||||
override suspend fun getWearToastConfirmation(): Boolean {
|
||||
return integrationUseCase.getWearToastConfirmation()
|
||||
return wearPrefsRepository.getWearToastConfirmation()
|
||||
}
|
||||
|
||||
override suspend fun setWearToastConfirmation(enabled: Boolean) {
|
||||
integrationUseCase.setWearToastConfirmation(enabled)
|
||||
wearPrefsRepository.setWearToastConfirmation(enabled)
|
||||
}
|
||||
|
||||
override suspend fun getShowShortcutText(): Boolean {
|
||||
return integrationUseCase.getShowShortcutText()
|
||||
return wearPrefsRepository.getShowShortcutText()
|
||||
}
|
||||
|
||||
override suspend fun setShowShortcutTextEnabled(enabled: Boolean) {
|
||||
integrationUseCase.setShowShortcutTextEnabled(enabled)
|
||||
wearPrefsRepository.setShowShortcutTextEnabled(enabled)
|
||||
}
|
||||
|
||||
override suspend fun getTemplateTileContent(): String {
|
||||
return integrationUseCase.getTemplateTileContent()
|
||||
return wearPrefsRepository.getTemplateTileContent()
|
||||
}
|
||||
|
||||
override suspend fun setTemplateTileContent(content: String) {
|
||||
integrationUseCase.setTemplateTileContent(content)
|
||||
wearPrefsRepository.setTemplateTileContent(content)
|
||||
}
|
||||
|
||||
override suspend fun getTemplateTileRefreshInterval(): Int {
|
||||
return integrationUseCase.getTemplateTileRefreshInterval()
|
||||
return wearPrefsRepository.getTemplateTileRefreshInterval()
|
||||
}
|
||||
|
||||
override suspend fun setTemplateTileRefreshInterval(interval: Int) {
|
||||
integrationUseCase.setTemplateTileRefreshInterval(interval)
|
||||
wearPrefsRepository.setTemplateTileRefreshInterval(interval)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import io.homeassistant.companion.android.BuildConfig
|
|||
import io.homeassistant.companion.android.common.data.authentication.AuthenticationRepository
|
||||
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository
|
||||
import io.homeassistant.companion.android.common.data.url.UrlRepository
|
||||
import io.homeassistant.companion.android.database.wear.FavoritesDao
|
||||
import io.homeassistant.companion.android.database.wear.getAll
|
||||
|
@ -44,6 +45,9 @@ class PhoneSettingsListener : WearableListenerService(), DataClient.OnDataChange
|
|||
@Inject
|
||||
lateinit var integrationUseCase: IntegrationRepository
|
||||
|
||||
@Inject
|
||||
lateinit var wearPrefsRepository: WearPrefsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var favoritesDao: FavoritesDao
|
||||
|
||||
|
@ -76,8 +80,8 @@ class PhoneSettingsListener : WearableListenerService(), DataClient.OnDataChange
|
|||
dataMap.putBoolean(KEY_IS_AUTHENTICATED, integrationUseCase.isRegistered())
|
||||
dataMap.putString(KEY_SUPPORTED_DOMAINS, objectMapper.writeValueAsString(HomePresenterImpl.supportedDomains))
|
||||
dataMap.putString(KEY_FAVORITES, objectMapper.writeValueAsString(currentFavorites))
|
||||
dataMap.putString(KEY_TEMPLATE_TILE, integrationUseCase.getTemplateTileContent())
|
||||
dataMap.putInt(KEY_TEMPLATE_TILE_REFRESH_INTERVAL, integrationUseCase.getTemplateTileRefreshInterval())
|
||||
dataMap.putString(KEY_TEMPLATE_TILE, wearPrefsRepository.getTemplateTileContent())
|
||||
dataMap.putInt(KEY_TEMPLATE_TILE_REFRESH_INTERVAL, wearPrefsRepository.getTemplateTileRefreshInterval())
|
||||
setUrgent()
|
||||
asPutDataRequest()
|
||||
}
|
||||
|
@ -153,7 +157,7 @@ class PhoneSettingsListener : WearableListenerService(), DataClient.OnDataChange
|
|||
private fun saveTileTemplate(dataMap: DataMap) = mainScope.launch {
|
||||
val content = dataMap.getString(KEY_TEMPLATE_TILE, "")
|
||||
val interval = dataMap.getInt(KEY_TEMPLATE_TILE_REFRESH_INTERVAL, 0)
|
||||
integrationUseCase.setTemplateTileContent(content)
|
||||
integrationUseCase.setTemplateTileRefreshInterval(interval)
|
||||
wearPrefsRepository.setTemplateTileContent(content)
|
||||
wearPrefsRepository.setTemplateTileRefreshInterval(interval)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import com.mikepenz.iconics.utils.colorInt
|
|||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository
|
||||
import io.homeassistant.companion.android.data.SimplifiedEntity
|
||||
import io.homeassistant.companion.android.util.getIcon
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -62,7 +62,7 @@ class ShortcutsTile : TileService() {
|
|||
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||
|
||||
@Inject
|
||||
lateinit var integrationUseCase: IntegrationRepository
|
||||
lateinit var wearPrefsRepository: WearPrefsRepository
|
||||
|
||||
override fun onTileRequest(requestParams: TileRequest): ListenableFuture<Tile> =
|
||||
serviceScope.future {
|
||||
|
@ -77,7 +77,7 @@ class ShortcutsTile : TileService() {
|
|||
}
|
||||
|
||||
val entities = getEntities()
|
||||
val showLabels = integrationUseCase.getShowShortcutText()
|
||||
val showLabels = wearPrefsRepository.getShowShortcutText()
|
||||
|
||||
Tile.Builder()
|
||||
.setResourcesVersion(entities.toString())
|
||||
|
@ -94,7 +94,7 @@ class ShortcutsTile : TileService() {
|
|||
|
||||
override fun onResourcesRequest(requestParams: ResourcesRequest): ListenableFuture<Resources> =
|
||||
serviceScope.future {
|
||||
val showLabels = integrationUseCase.getShowShortcutText()
|
||||
val showLabels = wearPrefsRepository.getShowShortcutText()
|
||||
val iconSize = if (showLabels) ICON_SIZE_SMALL else ICON_SIZE_FULL
|
||||
val density = requestParams.deviceParameters!!.screenDensity
|
||||
val iconSizePx = (iconSize * density).roundToInt()
|
||||
|
@ -146,7 +146,7 @@ class ShortcutsTile : TileService() {
|
|||
}
|
||||
|
||||
private suspend fun getEntities(): List<SimplifiedEntity> {
|
||||
return integrationUseCase.getTileShortcuts().map { SimplifiedEntity(it) }
|
||||
return wearPrefsRepository.getTileShortcuts().map { SimplifiedEntity(it) }
|
||||
}
|
||||
|
||||
fun layout(entities: List<SimplifiedEntity>, showLabels: Boolean): LayoutElement = Column.Builder().apply {
|
||||
|
|
|
@ -38,6 +38,7 @@ import com.google.common.util.concurrent.ListenableFuture
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -53,11 +54,14 @@ class TemplateTile : TileService() {
|
|||
@Inject
|
||||
lateinit var integrationUseCase: IntegrationRepository
|
||||
|
||||
@Inject
|
||||
lateinit var wearPrefsRepository: WearPrefsRepository
|
||||
|
||||
override fun onTileRequest(requestParams: TileRequest): ListenableFuture<Tile> =
|
||||
serviceScope.future {
|
||||
val state = requestParams.state
|
||||
if (state != null && state.lastClickableId == "refresh") {
|
||||
if (integrationUseCase.getWearHapticFeedback()) {
|
||||
if (wearPrefsRepository.getWearHapticFeedback()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager = applicationContext.getSystemService<VibratorManager>()
|
||||
val vibrator = vibratorManager?.defaultVibrator
|
||||
|
@ -69,7 +73,7 @@ class TemplateTile : TileService() {
|
|||
}
|
||||
}
|
||||
|
||||
val template = integrationUseCase.getTemplateTileContent()
|
||||
val template = wearPrefsRepository.getTemplateTileContent()
|
||||
val renderedText = try {
|
||||
integrationUseCase.renderTemplate(template, mapOf()).toString()
|
||||
} catch (e: Exception) {
|
||||
|
@ -82,7 +86,7 @@ class TemplateTile : TileService() {
|
|||
Tile.Builder()
|
||||
.setResourcesVersion("1")
|
||||
.setFreshnessIntervalMillis(
|
||||
integrationUseCase.getTemplateTileRefreshInterval().toLong() * 1000
|
||||
wearPrefsRepository.getTemplateTileRefreshInterval().toLong() * 1000
|
||||
)
|
||||
.setTimeline(
|
||||
Timeline.Builder().addTimelineEntry(
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.os.VibratorManager
|
|||
import androidx.core.content.getSystemService
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository
|
||||
import io.homeassistant.companion.android.home.HomePresenterImpl
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
|
@ -20,12 +21,15 @@ class TileActionReceiver : BroadcastReceiver() {
|
|||
@Inject
|
||||
lateinit var integrationUseCase: IntegrationRepository
|
||||
|
||||
@Inject
|
||||
lateinit var wearPrefsRepository: WearPrefsRepository
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val entityId: String? = intent?.getStringExtra("entity_id")
|
||||
|
||||
if (entityId != null) {
|
||||
runBlocking {
|
||||
if (integrationUseCase.getWearHapticFeedback()) {
|
||||
if (wearPrefsRepository.getWearHapticFeedback()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context?.getSystemService<VibratorManager>()
|
||||
val vibrator = vibratorManager?.defaultVibrator
|
||||
|
|
Loading…
Reference in a new issue