Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions app/lint-baseline.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 9.0.0" type="baseline" client="gradle" dependencies="false" name="AGP (9.0.0)" variant="all" version="9.0.0">
<issues format="6" by="lint 9.0.1" type="baseline" client="gradle" dependencies="false" name="AGP (9.0.1)" variant="all" version="9.0.1">

<issue
id="MissingPermission"
Expand Down Expand Up @@ -1389,6 +1389,61 @@
column="14"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a List&lt;Server> is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `servers: ImmutableList&lt;Server>` or create an `@Immutable` wrapper for this class: `@Immutable data class ServersList(val items: List&lt;Server>)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
errorLine1=" servers: List&lt;Server>,"
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt"
line="173"
column="14"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a List&lt;Entity> is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `entities: ImmutableList&lt;Entity>` or create an `@Immutable` wrapper for this class: `@Immutable data class EntitiesList(val items: List&lt;Entity>)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
errorLine1=" entities: List&lt;Entity>,"
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt"
line="176"
column="15"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a List&lt;EntityRegistryResponse>? is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `entityRegistry: ImmutableList&lt;EntityRegistryResponse>?` or create an `@Immutable` wrapper for this class: `@Immutable data class EntityRegistryList(val items: List&lt;EntityRegistryResponse>?)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
errorLine1=" entityRegistry: List&lt;EntityRegistryResponse>? = null,"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt"
line="182"
column="21"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a List&lt;DeviceRegistryResponse>? is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `deviceRegistry: ImmutableList&lt;DeviceRegistryResponse>?` or create an `@Immutable` wrapper for this class: `@Immutable data class DeviceRegistryList(val items: List&lt;DeviceRegistryResponse>?)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
errorLine1=" deviceRegistry: List&lt;DeviceRegistryResponse>? = null,"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt"
line="183"
column="21"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a List&lt;AreaRegistryResponse>? is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `areaRegistry: ImmutableList&lt;AreaRegistryResponse>?` or create an `@Immutable` wrapper for this class: `@Immutable data class AreaRegistryList(val items: List&lt;AreaRegistryResponse>?)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
errorLine1=" areaRegistry: List&lt;AreaRegistryResponse>? = null,"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt"
line="184"
column="19"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a List&lt;Entity> is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `entities: ImmutableList&lt;Entity>` or create an `@Immutable` wrapper for this class: `@Immutable data class EntitiesList(val items: List&lt;Entity>)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
Expand Down Expand Up @@ -1495,7 +1550,7 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/util/compose/ExposedDropdownMenu.kt"
line="29"
line="41"
column="11"/>
</issue>

Expand All @@ -1506,10 +1561,21 @@
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/util/compose/ExposedDropdownMenu.kt"
line="66"
line="78"
column="14"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a List&lt;String> is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `keys: ImmutableList&lt;String>` or create an `@Immutable` wrapper for this class: `@Immutable data class KeysList(val items: List&lt;String>)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
errorLine1=" keys: List&lt;String>,"
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/kotlin/io/homeassistant/companion/android/util/compose/ExposedDropdownMenu.kt"
line="121"
column="11"/>
</issue>

<issue
id="ComposeUnstableCollections"
message="The Compose Compiler cannot infer the stability of a parameter if a Map&lt;HAGesture, GestureAction> is used in it, even if the item type is stable.&#xA;You should use Kotlinx Immutable Collections instead: `gestureActions: ImmutableMap&lt;HAGesture, GestureAction>` or create an `@Immutable` wrapper for this class: `@Immutable data class GestureActionsMap(val items: Map&lt;HAGesture, GestureAction>)`&#xA;See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.homeassistant.companion.android.util.compose

import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.selection.TextSelectionColors
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are unused imports here (e.g., TextSelectionColors). These will be flagged by ktlint/IDE and should be removed.

Suggested change
import androidx.compose.foundation.text.selection.TextSelectionColors

Copilot uses AI. Check for mistakes.
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ExposedDropdownMenuBox
Expand All @@ -21,6 +22,17 @@ import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.database.server.Server
import io.homeassistant.companion.android.database.widget.WidgetBackgroundType
import io.homeassistant.companion.android.widgets.common.WidgetUtils
import androidx.compose.material3.DropdownMenuItem as DropdownMenuItemM3
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox as ExposedDropdownMenuBoxM3
import androidx.compose.material3.ExposedDropdownMenuDefaults as ExposedDropdownMenuDefaultsM3
import androidx.compose.material3.MenuAnchorType as MenuAnchorTypeM3
import androidx.compose.material3.MenuItemColors
import androidx.compose.material3.TextField as TextFieldM3
import androidx.compose.material3.Text as TextM3
import androidx.compose.ui.graphics.Color
import io.homeassistant.companion.android.common.compose.composable.HATextField
Comment on lines +33 to +34
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These imports appear to be unused (Color, HATextField). Please remove them to keep the file ktlint-clean.

Suggested change
import androidx.compose.ui.graphics.Color
import io.homeassistant.companion.android.common.compose.composable.HATextField

Copilot uses AI. Check for mistakes.
import io.homeassistant.companion.android.common.compose.theme.LocalHAColorScheme

@OptIn(ExperimentalMaterialApi::class)
@Composable
Expand Down Expand Up @@ -72,7 +84,7 @@ fun ServerExposedDropdownMenu(
val keys = servers.map { it.friendlyName }
val ids = servers.map { it.id }
val currentIndex = servers.indexOfFirst { it.id == current }.takeUnless { it == -1 }
ExposedDropdownMenu(
HAExposedDropdownMenu(
label = stringResource(title),
keys = keys,
currentIndex = currentIndex,
Expand All @@ -92,11 +104,76 @@ fun WidgetBackgroundTypeExposedDropdownMenu(
val keys = remember { WidgetUtils.getBackgroundOptionList(context) }
val currentIndex =
remember(current) { current?.let { WidgetUtils.getSelectedBackgroundOption(context, current, keys) } }
ExposedDropdownMenu(
HAExposedDropdownMenu(
label = stringResource(title),
keys = keys.toList(),
currentIndex = currentIndex,
onSelected = { onSelected(WidgetUtils.getWidgetBackgroundType(context, keys[it])) },
modifier = modifier,
)
}


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HAExposedDropdownMenu(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not do that in this PR. We can't replace the ServerExposedDropdown like this because it has an impact everywhere.

It is a nice goal to migrate to M3, but we should make sure to not mix M2 and M3 and today this dropdown is used in a lot of place that are still in M2.

So I would suggest to make a first PR about making a new version of the ServerExposedDropdownMenu close to the other HA* composable like the HAButton. Inspire yourself from the other components (it has tests and integration within the compose catalog and documentation).

Then a second PR for the configure camera widget compose to M3.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually another contributor is also migrating a screen to compose in this PR #6444 he took the approach to first migrate to compose and then do the migration to M3 later.

I feel that both of you are going to miss the Menu for the server and color. If you don't do it I might help you by making the component so you can use it directly.

I think you would learn some stuff by looking at the comments I left in the other PR that could help you on that one.

label: String,
keys: List<String>,

Check failure

Code scanning / Android Lint

Immutable collections should ideally be used in Composables Error

The Compose Compiler cannot infer the stability of a parameter if a List is used in it, even if the item type is stable.
You should use Kotlinx Immutable Collections instead: keys: ImmutableList<String> or create an @Immutable wrapper for this class: @Immutable data class KeysList(val items: List<String>)
See https://slackhq.github.io/compose-lints/rules/#avoid-using-unstable-collections for more information.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can ignore the Immutable collections with an annotation for now.

Comment thread Fixed
currentIndex: Int?,
onSelected: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
val focusManager = LocalFocusManager.current
val colorScheme = LocalHAColorScheme.current
ExposedDropdownMenuBoxM3(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
modifier = modifier,
) {
TextFieldM3(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the other existing components HATextField for instance.

readOnly = true,
value = currentIndex?.let { keys[it] } ?: "",
onValueChange = { },
label = { TextM3(label) },
trailingIcon = { ExposedDropdownMenuDefaultsM3.TrailingIcon(expanded = expanded) },
colors = ExposedDropdownMenuDefaultsM3.textFieldColors(
unfocusedContainerColor = colorScheme.colorSurfaceDefault,
focusedContainerColor = colorScheme.colorSurfaceDefault,
unfocusedLabelColor = colorScheme.colorTextSecondary,
focusedLabelColor = colorScheme.colorOnPrimaryNormal,
unfocusedTrailingIconColor = colorScheme.colorTextSecondary,
focusedTrailingIconColor = colorScheme.colorOnPrimaryNormal,
unfocusedTextColor = colorScheme.colorTextPrimary,
focusedTextColor = colorScheme.colorTextPrimary,
focusedIndicatorColor = colorScheme.colorOnPrimaryNormal,
unfocusedIndicatorColor = colorScheme.colorBorderNeutralQuiet,
),
modifier = Modifier.fillMaxWidth().menuAnchor(MenuAnchorTypeM3.PrimaryNotEditable),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
containerColor = colorScheme.colorSurfaceDefault
) {
keys.forEachIndexed { index, key ->
DropdownMenuItemM3(
text = { TextM3(key) },
onClick = {
onSelected(index)
expanded = false
focusManager.clearFocus()
},
colors = MenuItemColors(
textColor = colorScheme.colorTextPrimary,
leadingIconColor = colorScheme.colorOnPrimaryNormal,
trailingIconColor = colorScheme.colorOnPrimaryNormal,
disabledTextColor = colorScheme.colorTextSecondary,
disabledLeadingIconColor = colorScheme.colorTextDisabled,
disabledTrailingIconColor = colorScheme.colorTextDisabled,
)
)
}
}
}
}
Loading
Loading