Add favorites selection for Android Auto (#3670)

* Add favorites selection for Android Auto

* Move reorderable to implementation

* Small clean up

* Hide from minimal build

* Show on automotive builds and change some titles to match that device

* Update strings to be more precise about driving optimized

* Review comments

* Switch from string set to string to preserve order

* Move some conversion logic to PrefsRepository

* clean up

* Review comments

* Consistency updates
This commit is contained in:
Daniel Shokouhi 2023-07-21 11:11:14 -07:00 committed by GitHub
parent 455da053d1
commit 605e6ec914
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 480 additions and 82 deletions

View file

@ -204,7 +204,7 @@ dependencies {
implementation("com.mikepenz:iconics-compose:5.4.0")
implementation("com.mikepenz:community-material-typeface:7.0.96.0-kotlin@aar")
"fullImplementation"("org.burnoutcrew.composereorderable:reorderable:0.9.6")
implementation("org.burnoutcrew.composereorderable:reorderable:0.9.6")
implementation("com.github.AppDevNext:ChangeLog:3.4")
implementation("androidx.car.app:app:1.3.0-rc01")

View file

@ -1,47 +1,26 @@
package io.homeassistant.companion.android.settings.wear.views
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.mikepenz.iconics.compose.Image
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.friendlyName
import io.homeassistant.companion.android.settings.wear.SettingsWearViewModel
import io.homeassistant.companion.android.util.compose.FavoriteEntityRow
import io.homeassistant.companion.android.util.compose.SingleEntityPicker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharedFlow
@ -49,8 +28,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.ReorderableLazyListState
import org.burnoutcrew.reorderable.detectReorderAfterLongPress
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
import org.burnoutcrew.reorderable.reorderable
import io.homeassistant.companion.android.common.R as commonR
@ -136,7 +113,7 @@ fun LoadWearFavoritesSettings(
reorderableState = reorderState,
key = favoriteEntities[index]
) { isDragging ->
WearFavoriteEntityRow(
FavoriteEntityRow(
entityName = it.friendlyName,
entityId = favoriteEntityID,
onClick = {
@ -156,56 +133,3 @@ fun LoadWearFavoritesSettings(
}
}
}
@Composable
fun WearFavoriteEntityRow(
entityName: String,
entityId: String,
onClick: () -> Unit,
checked: Boolean,
draggable: Boolean = false,
isDragging: Boolean = false,
reorderableState: ReorderableLazyListState? = null
) {
val surfaceElevation = animateDpAsState(targetValue = if (isDragging) 8.dp else 0.dp)
var rowModifier = Modifier.fillMaxWidth().heightIn(min = 72.dp)
if (draggable && reorderableState != null) {
rowModifier = rowModifier.then(Modifier.detectReorderAfterLongPress(reorderableState))
}
Surface(
elevation = surfaceElevation.value
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = rowModifier
) {
Column(
modifier = Modifier.weight(1f).padding(start = 16.dp)
) {
Text(text = entityName, style = MaterialTheme.typography.body1)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(text = entityId, style = MaterialTheme.typography.body2)
}
}
IconButton(onClick = onClick) {
Icon(
imageVector = if (checked) Icons.Default.Clear else Icons.Default.Add,
contentDescription = stringResource(if (checked) commonR.string.delete else commonR.string.add_favorite)
)
}
if (draggable) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Image(
asset = CommunityMaterial.Icon.cmd_drag_horizontal_variant,
contentDescription = stringResource(commonR.string.hold_to_reorder),
colorFilter = ColorFilter.tint(LocalContentColor.current),
modifier = Modifier
.size(width = 40.dp, height = 24.dp)
.padding(end = 16.dp)
.alpha(LocalContentAlpha.current)
)
}
}
}
}
}

View file

@ -43,6 +43,7 @@ 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.vehicle.ManageAndroidAutoSettingsFragment
import io.homeassistant.companion.android.settings.wear.SettingsWearActivity
import io.homeassistant.companion.android.settings.wear.SettingsWearDetection
import io.homeassistant.companion.android.settings.widgets.ManageWidgetsSettingsFragment
@ -324,6 +325,28 @@ class SettingsFragment(
}
return@setOnPreferenceClickListener true
}
val isAutomotive = requireContext().packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
findPreference<PreferenceCategory>("android_auto")?.let {
it.isVisible =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && (BuildConfig.FLAVOR == "full" || isAutomotive)
if (isAutomotive) {
it.title = getString(commonR.string.android_automotive)
}
}
findPreference<Preference>("auto_favorites")?.let { pref ->
if (isAutomotive) {
pref.title = getString(commonR.string.android_automotive_favorites)
}
pref.setOnPreferenceClickListener {
parentFragmentManager.commit {
replace(R.id.content, ManageAndroidAutoSettingsFragment::class.java, null)
addToBackStack(getString(commonR.string.basic_sensor_name_android_auto))
}
return@setOnPreferenceClickListener true
}
}
}
private fun removeSystemFromThemesIfNeeded() {

View file

@ -0,0 +1,75 @@
package io.homeassistant.companion.android.settings.vehicle
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.google.accompanist.themeadapter.material.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.settings.vehicle.views.AndroidAutoFavoritesSettings
import javax.inject.Inject
import io.homeassistant.companion.android.common.R as commonR
@AndroidEntryPoint
class ManageAndroidAutoSettingsFragment : Fragment() {
@Inject
lateinit var serverManager: ServerManager
val viewModel: ManageAndroidAutoViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
@Deprecated("Deprecated in Java")
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.get_help)?.let {
it.isVisible = true
it.intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://companion.home-assistant.io/docs/android-auto"))
}
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
MdcTheme {
AndroidAutoFavoritesSettings(
androidAutoViewModel = viewModel,
serversList = serverManager.defaultServers,
defaultServer = serverManager.getServer()?.id ?: 0
)
}
}
}
}
override fun onResume() {
super.onResume()
activity?.title =
if (requireContext().packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
getString(commonR.string.android_automotive_favorites)
} else {
getString(commonR.string.aa_favorites)
}
}
}

View file

@ -0,0 +1,88 @@
package io.homeassistant.companion.android.settings.vehicle
import android.app.Application
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import io.homeassistant.companion.android.common.data.integration.Entity
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.servers.ServerManager
import io.homeassistant.companion.android.vehicle.MainVehicleScreen
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import org.burnoutcrew.reorderable.ItemPosition
import javax.inject.Inject
@RequiresApi(Build.VERSION_CODES.O)
@HiltViewModel
class ManageAndroidAutoViewModel @Inject constructor(
private val serverManager: ServerManager,
private val prefsRepository: PrefsRepository,
application: Application
) : AndroidViewModel(application) {
companion object {
private const val TAG = "AAViewModel"
}
val favoritesList = mutableStateListOf<String>()
var sortedEntities by mutableStateOf<List<Entity<*>>>(emptyList())
private set
val entities = mutableMapOf<Int, List<Entity<*>>>()
init {
viewModelScope.launch {
favoritesList.addAll(prefsRepository.getAutoFavorites())
serverManager.defaultServers.map {
async {
entities[it.id] = try {
serverManager.integrationRepository(it.id).getEntities().orEmpty()
.filter { it.domain in MainVehicleScreen.SUPPORTED_DOMAINS }
} catch (e: Exception) {
Log.e(TAG, "Couldn't load entities for server", e)
emptyList()
}
}
}.awaitAll()
loadEntities(serverManager.getServer()?.id ?: 0)
}
}
fun onMove(fromItem: ItemPosition, toItem: ItemPosition) {
favoritesList.apply {
add(
favoritesList.indexOfFirst { it == toItem.key },
removeAt(favoritesList.indexOfFirst { it == fromItem.key })
)
}
}
fun canDragOver(position: ItemPosition) = favoritesList.any { it == position.key }
fun saveFavorites() {
viewModelScope.launch {
prefsRepository.setAutoFavorites(favoritesList.toList())
}
}
fun loadEntities(serverId: Int) {
sortedEntities = entities[serverId] ?: emptyList()
}
fun onEntitySelected(checked: Boolean, entityId: String, serverId: Int) {
if (checked) {
favoritesList.add("$serverId-$entityId")
} else {
favoritesList.remove("$serverId-$entityId")
}
viewModelScope.launch { prefsRepository.setAutoFavorites(favoritesList.toList()) }
}
}

View file

@ -0,0 +1,133 @@
package io.homeassistant.companion.android.settings.vehicle.views
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.domain
import io.homeassistant.companion.android.common.data.integration.friendlyName
import io.homeassistant.companion.android.database.server.Server
import io.homeassistant.companion.android.settings.vehicle.ManageAndroidAutoViewModel
import io.homeassistant.companion.android.util.compose.FavoriteEntityRow
import io.homeassistant.companion.android.util.compose.ServerExposedDropdownMenu
import io.homeassistant.companion.android.util.compose.SingleEntityPicker
import io.homeassistant.companion.android.vehicle.MainVehicleScreen
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
import org.burnoutcrew.reorderable.reorderable
import io.homeassistant.companion.android.common.R as commonR
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun AndroidAutoFavoritesSettings(
androidAutoViewModel: ManageAndroidAutoViewModel,
serversList: List<Server>,
defaultServer: Int
) {
val reorderState = rememberReorderableLazyListState(
onMove = { from, to -> androidAutoViewModel.onMove(from, to) },
canDragOver = { draggedOver, _ -> androidAutoViewModel.canDragOver(draggedOver) },
onDragEnd = { _, _ -> androidAutoViewModel.saveFavorites() }
)
var selectedServer by remember { mutableStateOf(defaultServer) }
val favoriteEntities = androidAutoViewModel.favoritesList.toList()
var validEntities by remember { mutableStateOf<List<Entity<*>>>(emptyList()) }
LaunchedEffect(favoriteEntities.size, androidAutoViewModel.sortedEntities.size, selectedServer) {
validEntities = withContext(Dispatchers.IO) {
androidAutoViewModel.sortedEntities
.filter {
!favoriteEntities.contains("$selectedServer-${it.entityId}") &&
(it.domain in MainVehicleScreen.SUPPORTED_DOMAINS)
}
.toList()
}
}
LazyColumn(
state = reorderState.listState,
contentPadding = PaddingValues(vertical = 16.dp),
modifier = Modifier
.reorderable(reorderState)
) {
item {
Text(
text = stringResource(commonR.string.aa_set_favorites),
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(horizontal = 16.dp).padding(bottom = 16.dp)
)
}
if (serversList.size > 1) {
item {
ServerExposedDropdownMenu(
servers = serversList,
current = selectedServer,
onSelected = {
androidAutoViewModel.loadEntities(it)
selectedServer = it
},
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp).padding(bottom = 16.dp)
)
}
}
item {
SingleEntityPicker(
entities = validEntities,
currentEntity = null,
onEntityCleared = { /* Nothing */ },
onEntitySelected = {
androidAutoViewModel.onEntitySelected(true, it, selectedServer)
return@SingleEntityPicker false // Clear input
},
modifier = Modifier.padding(horizontal = 16.dp).padding(bottom = 16.dp),
label = { Text(stringResource(commonR.string.add_favorite)) }
)
}
if (favoriteEntities.isNotEmpty() && androidAutoViewModel.sortedEntities.isNotEmpty()) {
items(favoriteEntities.size, { favoriteEntities[it] }) { index ->
val favoriteEntity =
favoriteEntities[index].split("-")
androidAutoViewModel.sortedEntities.firstOrNull { it.entityId == favoriteEntity[1] && favoriteEntity[0].toInt() == selectedServer }?.let {
ReorderableItem(
reorderableState = reorderState,
key = favoriteEntities[index]
) { isDragging ->
FavoriteEntityRow(
entityName = it.friendlyName,
entityId = it.entityId,
onClick = {
androidAutoViewModel.onEntitySelected(
false,
it.entityId,
selectedServer
)
},
checked = true,
draggable = true,
isDragging = isDragging,
reorderableState = reorderState
)
}
}
}
}
}
}

View file

@ -0,0 +1,86 @@
package io.homeassistant.companion.android.util.compose
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.mikepenz.iconics.compose.Image
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import io.homeassistant.companion.android.common.R
import org.burnoutcrew.reorderable.ReorderableLazyListState
import org.burnoutcrew.reorderable.detectReorderAfterLongPress
@Composable
fun FavoriteEntityRow(
entityName: String,
entityId: String,
onClick: () -> Unit,
checked: Boolean,
draggable: Boolean = false,
isDragging: Boolean = false,
reorderableState: ReorderableLazyListState? = null
) {
val surfaceElevation = animateDpAsState(targetValue = if (isDragging) 8.dp else 0.dp)
var rowModifier = Modifier.fillMaxWidth().heightIn(min = 72.dp)
if (draggable && reorderableState != null) {
rowModifier = rowModifier.then(Modifier.detectReorderAfterLongPress(reorderableState))
}
Surface(
elevation = surfaceElevation.value
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = rowModifier
) {
Column(
modifier = Modifier.weight(1f).padding(start = 16.dp)
) {
Text(text = entityName, style = MaterialTheme.typography.body1)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(text = entityId, style = MaterialTheme.typography.body2)
}
}
IconButton(onClick = onClick) {
Icon(
imageVector = if (checked) Icons.Default.Clear else Icons.Default.Add,
contentDescription = stringResource(if (checked) R.string.delete else R.string.add_favorite)
)
}
if (draggable) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Image(
asset = CommunityMaterial.Icon.cmd_drag_horizontal_variant,
contentDescription = stringResource(R.string.hold_to_reorder),
colorFilter = ColorFilter.tint(LocalContentColor.current),
modifier = Modifier
.size(width = 40.dp, height = 24.dp)
.padding(end = 16.dp)
.alpha(LocalContentAlpha.current)
)
}
}
}
}
}

View file

@ -17,6 +17,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
import io.homeassistant.companion.android.common.data.servers.ServerManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@ -41,6 +42,9 @@ class HaCarAppService : CarAppService() {
@Inject
lateinit var serverManager: ServerManager
@Inject
lateinit var prefsRepository: PrefsRepository
private val serverId = MutableStateFlow(0)
private val allEntities = MutableStateFlow<Map<String, Entity<*>>>(emptyMap())
private var allEntitiesJob: Job? = null
@ -81,7 +85,8 @@ class HaCarAppService : CarAppService() {
carContext,
serverManager,
serverIdFlow,
entityFlow
entityFlow,
prefsRepository
) { loadEntities(lifecycleScope, it) }
)
@ -101,7 +106,8 @@ class HaCarAppService : CarAppService() {
carContext,
serverManager,
serverIdFlow,
entityFlow
entityFlow,
prefsRepository
) { loadEntities(lifecycleScope, it) }
)
}

View file

@ -31,6 +31,7 @@ import io.homeassistant.companion.android.common.data.authentication.SessionStat
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.domain
import io.homeassistant.companion.android.common.data.integration.getIcon
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.common.util.capitalize
import io.homeassistant.companion.android.launch.LaunchActivity
@ -49,6 +50,7 @@ class MainVehicleScreen(
val serverManager: ServerManager,
private val serverId: StateFlow<Int>,
private val allEntities: Flow<Map<String, Entity<*>>>,
private val prefsRepository: PrefsRepository,
private val onChangeServer: (Int) -> Unit
) : Screen(carContext) {
@ -66,7 +68,7 @@ class MainVehicleScreen(
"script" to commonR.string.scripts,
"switch" to commonR.string.switches
)
private val SUPPORTED_DOMAINS = SUPPORTED_DOMAINS_WITH_STRING.keys
val SUPPORTED_DOMAINS = SUPPORTED_DOMAINS_WITH_STRING.keys
private val MAP_DOMAINS = listOf(
"device_tracker",
@ -76,6 +78,7 @@ class MainVehicleScreen(
)
}
private var favoritesList = emptyList<String>()
private var isLoggedIn: Boolean? = null
private val domains = mutableSetOf<String>()
private var car: Car? = null
@ -92,6 +95,7 @@ class MainVehicleScreen(
init {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
favoritesList = prefsRepository.getAutoFavorites()
isLoggedIn = serverManager.isRegistered() &&
serverManager.authenticationRepository()
.getSessionState() == SessionState.CONNECTED
@ -134,6 +138,33 @@ class MainVehicleScreen(
override fun onGetTemplate(): Template {
val listBuilder = ItemList.Builder()
if (favoritesList.isNotEmpty()) {
listBuilder.addItem(
Row.Builder().apply {
setImage(
CarIcon.Builder(
IconicsDrawable(carContext, CommunityMaterial.Icon3.cmd_star).apply {
sizeDp = 48
}.toAndroidIconCompat()
)
.setTint(CarColor.DEFAULT)
.build()
)
setTitle(carContext.getString(commonR.string.favorites))
setOnClickListener {
Log.i(TAG, "Favorites clicked: $favoritesList, current server: ${serverId.value}")
screenManager.push(
EntityGridVehicleScreen(
carContext,
serverManager.integrationRepository(serverId.value),
carContext.getString(commonR.string.favorites),
allEntities.map { it.values.filter { entity -> favoritesList.contains("${serverId.value}-${entity.entityId}") }.sortedBy { entity -> favoritesList.indexOf("${serverId.value}-${entity.entityId}") } }
)
)
}
}.build()
)
}
domains.forEach { domain ->
val friendlyDomain =
SUPPORTED_DOMAINS_WITH_STRING[domain]?.let { carContext.getString(it) }

View file

@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/colorAccent" android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11L5,11z"/>
</vector>

View file

@ -123,6 +123,15 @@
android:icon="@drawable/ic_notifications"
android:summary="@string/rate_limit_summary"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/basic_sensor_name_android_auto"
android:key="android_auto">
<Preference
android:key="auto_favorites"
android:icon="@drawable/ic_car"
android:title="@string/aa_favorites"
android:summary="@string/aa_favorites_summary" />
</PreferenceCategory>
<PreferenceCategory
android:key="device_controls"
android:title="@string/controls_setting_category"

View file

@ -66,4 +66,8 @@ interface PrefsRepository {
suspend fun getIgnoredSuggestions(): List<String>
suspend fun setIgnoredSuggestions(ignored: List<String>)
suspend fun getAutoFavorites(): List<String>
suspend fun setAutoFavorites(favorites: List<String>)
}

View file

@ -31,6 +31,7 @@ class PrefsRepositoryImpl @Inject constructor(
private const val PREF_KEY_ALIAS = "key-alias"
private const val PREF_CRASH_REPORTING_DISABLED = "crash_reporting"
private const val PREF_IGNORED_SUGGESTIONS = "ignored_suggestions"
private const val PREF_AUTO_FAVORITES = "auto_favorites"
}
init {
@ -197,4 +198,12 @@ class PrefsRepositoryImpl @Inject constructor(
override suspend fun setIgnoredSuggestions(ignored: List<String>) {
localStorage.putStringSet(PREF_IGNORED_SUGGESTIONS, ignored.toSet())
}
override suspend fun getAutoFavorites(): List<String> {
return localStorage.getString(PREF_AUTO_FAVORITES)?.removeSurrounding("[", "]")?.split(", ") ?: emptyList()
}
override suspend fun setAutoFavorites(favorites: List<String>) {
localStorage.putString(PREF_AUTO_FAVORITES, favorites.toString())
}
}

View file

@ -1121,4 +1121,9 @@
<string name="sensor_description_car_fuel_type">List of available fuel types for the connected car</string>
<string name="basic_sensor_name_car_ev_connector_type">Car EV Connector Type</string>
<string name="sensor_description_car_ev_connector_type">List of available EV connectors for the connected car</string>
<string name="aa_set_favorites">Select your favorite entities to appear in the Favorites category in the Home Assistant driving interface. You can also drag and drop to change the order in which they appear. Keep in mind that the amount of entities shown will vary from vehicle to vehicle.</string>
<string name="aa_favorites">Android Auto Favorites</string>
<string name="aa_favorites_summary">Select your favorite entities to be shown in the app while viewing the Home Assistant driving interface</string>
<string name="android_automotive">Android Automotive</string>
<string name="android_automotive_favorites">Driving Favorites</string>
</resources>