Skip to content
Open
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
2 changes: 1 addition & 1 deletion .agents/skills/ha-android-committing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Use this skill when finalizing a change and preparing it for commit or a pull re

1. Format: `./gradlew :build-logic:convention:ktlintFormat ktlintFormat`
2. Tests: `./gradlew test`
3. If the change is visible to end users or changes behavior, add it to `app/src/main/res/xml/changelog_master.xml`.
3. If the change is visible to end users or changes behavior, add it to `app/src/main/kotlin/io/homeassistant/companion/android/changelog/ChangelogContent.kt`.
4. After adding or updating any dependency (in `gradle/libs.versions.toml` or module declarations), run `./gradlew alldependencies --write-locks`.
5. Run the `ha-android-review` skill over the diff before handing it off.

Expand Down
100 changes: 0 additions & 100 deletions .github/workflows/prepareNextRelease.yml

This file was deleted.

11 changes: 0 additions & 11 deletions .github/workflows/weekly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,3 @@ jobs:
"beta": "true",
},
})

- name: Trigger Prepare Next Release
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'prepareNextRelease.yml',
ref: 'main',
})
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Detailed guidance lives in project skills under `.agents/skills/`. Load the matc

## Pull Requests

When creating a pull request, use `.github/pull_request_template.md` as the PR body. Keep PRs small and focused. If the change is visible to end users, add it to the changelog in `app/src/main/res/xml/changelog_master.xml`. Before committing, format with ktlint and run the tests.
When creating a pull request, use `.github/pull_request_template.md` as the PR body. Keep PRs small and focused. If the change is visible to end users, add it to the changelog in `app/src/main/kotlin/io/homeassistant/companion/android/changelog/ChangelogContent.kt`. Before committing, format with ktlint and run the tests.

## AI policy

Expand Down
1 change: 0 additions & 1 deletion app/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ com.android.tools:repository:32.3.1=androidLintTool,unified-test-platform-androi
com.android.tools:sdk-common:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.android.tools:sdklib:32.3.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle
com.caverock:androidsvg-aar:1.4=fullDebugAndroidTestLintChecksClasspath,fullDebugLintChecksClasspath,fullDebugRuntimeClasspath,fullDebugScreenshotTestLintChecksClasspath,fullDebugScreenshotTestRuntimeClasspath,fullDebugUnitTestLintChecksClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseLintChecksClasspath,fullReleaseRuntimeClasspath,fullReleaseScreenshotTestLintChecksClasspath,fullReleaseScreenshotTestRuntimeClasspath,minimalDebugAndroidTestLintChecksClasspath,minimalDebugLintChecksClasspath,minimalDebugRuntimeClasspath,minimalDebugScreenshotTestLintChecksClasspath,minimalDebugScreenshotTestRuntimeClasspath,minimalDebugUnitTestLintChecksClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseLintChecksClasspath,minimalReleaseRuntimeClasspath,minimalReleaseScreenshotTestLintChecksClasspath,minimalReleaseScreenshotTestRuntimeClasspath
com.github.AppDevNext:ChangeLog:3.8.2=fullDebugAndroidTestCompileClasspath,fullDebugAndroidTestLintChecksClasspath,fullDebugCompileClasspath,fullDebugLintChecksClasspath,fullDebugRuntimeClasspath,fullDebugScreenshotTestCompileClasspath,fullDebugScreenshotTestLintChecksClasspath,fullDebugScreenshotTestRuntimeClasspath,fullDebugUnitTestCompileClasspath,fullDebugUnitTestLintChecksClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseCompileClasspath,fullReleaseLintChecksClasspath,fullReleaseRuntimeClasspath,fullReleaseScreenshotTestCompileClasspath,fullReleaseScreenshotTestLintChecksClasspath,fullReleaseScreenshotTestRuntimeClasspath,minimalDebugAndroidTestCompileClasspath,minimalDebugAndroidTestLintChecksClasspath,minimalDebugCompileClasspath,minimalDebugLintChecksClasspath,minimalDebugRuntimeClasspath,minimalDebugScreenshotTestCompileClasspath,minimalDebugScreenshotTestLintChecksClasspath,minimalDebugScreenshotTestRuntimeClasspath,minimalDebugUnitTestCompileClasspath,minimalDebugUnitTestLintChecksClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseCompileClasspath,minimalReleaseLintChecksClasspath,minimalReleaseRuntimeClasspath,minimalReleaseScreenshotTestCompileClasspath,minimalReleaseScreenshotTestLintChecksClasspath,minimalReleaseScreenshotTestRuntimeClasspath
com.github.Dimezis:BlurView:version-2.0.6=fullDebugAndroidTestCompileClasspath,fullDebugAndroidTestLintChecksClasspath,fullDebugCompileClasspath,fullDebugLintChecksClasspath,fullDebugRuntimeClasspath,fullDebugScreenshotTestCompileClasspath,fullDebugScreenshotTestLintChecksClasspath,fullDebugScreenshotTestRuntimeClasspath,fullDebugUnitTestCompileClasspath,fullDebugUnitTestLintChecksClasspath,fullDebugUnitTestRuntimeClasspath,fullReleaseCompileClasspath,fullReleaseLintChecksClasspath,fullReleaseRuntimeClasspath,fullReleaseScreenshotTestCompileClasspath,fullReleaseScreenshotTestLintChecksClasspath,fullReleaseScreenshotTestRuntimeClasspath,minimalDebugAndroidTestCompileClasspath,minimalDebugAndroidTestLintChecksClasspath,minimalDebugCompileClasspath,minimalDebugLintChecksClasspath,minimalDebugRuntimeClasspath,minimalDebugScreenshotTestCompileClasspath,minimalDebugScreenshotTestLintChecksClasspath,minimalDebugScreenshotTestRuntimeClasspath,minimalDebugUnitTestCompileClasspath,minimalDebugUnitTestLintChecksClasspath,minimalDebugUnitTestRuntimeClasspath,minimalReleaseCompileClasspath,minimalReleaseLintChecksClasspath,minimalReleaseRuntimeClasspath,minimalReleaseScreenshotTestCompileClasspath,minimalReleaseScreenshotTestLintChecksClasspath,minimalReleaseScreenshotTestRuntimeClasspath
com.github.ajalt.clikt:clikt-jvm:5.0.2=ktlint
com.github.ajalt.clikt:clikt:5.0.2=ktlint
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package io.homeassistant.companion.android.changelog

import androidx.annotation.StringRes
import androidx.compose.ui.graphics.Color
import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.compose.theme.HAColorScheme
import io.homeassistant.companion.android.frontend.navigation.WidgetType
import io.homeassistant.companion.android.settings.SettingsActivity

/**
* The platforms a changelog entry can apply to, in chip display order.
*/
enum class ChangelogPlatform(@field:StringRes val labelRes: Int) {
APP(commonR.string.changelog_platform_app),
AUTOMOTIVE(commonR.string.changelog_platform_automotive),
WEAR(commonR.string.changelog_platform_wear),
}

/**
* The kinds of changes a changelog groups its entries under, each fully describing how its
* section header is displayed.
*
* @property labelRes The header label of the section.
* @property markerColor The color of the section marker, resolved from the current color scheme
* so it adapts to the theme.
*/
enum class ChangelogCategory(@field:StringRes val labelRes: Int, val markerColor: (HAColorScheme) -> Color) {
NEW(commonR.string.changelog_category_new, HAColorScheme::colorFillSuccessLoudResting),
IMPROVED(commonR.string.changelog_category_improved, HAColorScheme::colorFillPrimaryLoudResting),
FIXED(commonR.string.changelog_category_fixed, HAColorScheme::colorFillNeutralLoudResting),
}

/**
* An action performed when the user taps a changelog entry. An entry with an action is rendered
* as a clickable row with a chevron.
*/
sealed interface ChangelogAction {
/** Opens [url] in the browser or the matching app. */
data class OpenUrl(val url: String) : ChangelogAction

/** Opens the settings on the screen targeted by [deeplink]. */
data class OpenSettings(val deeplink: SettingsActivity.Deeplink) : ChangelogAction

/** Opens the configuration screen of [widgetType] without a preselected entity. */
data class OpenWidgetConfig(val widgetType: WidgetType) : ChangelogAction
}

/**
* One change of a release.
*
* @property contentRes The change description, a string resource from `strings_changelog.xml`
* in `:common`. Escaped inline HTML (like `<b>`) is rendered as styling.
* @property platforms The platforms this change applies to.
* @property action Optional action performed when the user taps the entry.
*/
data class ChangelogEntry(
@param:StringRes val contentRes: Int,
val platforms: Set<ChangelogPlatform>,
val action: ChangelogAction? = null,
)

/**
* The changes of one release, one field per category so a category cannot appear twice.
* A category without entries is not displayed.
*/
data class Changelog(
val new: List<ChangelogEntry> = emptyList(),
val improved: List<ChangelogEntry> = emptyList(),
val fixed: List<ChangelogEntry> = emptyList(),
) {
/** The non-empty sections of this changelog, in display order. */
fun toSections(): List<ChangelogSection> = listOf(
ChangelogSection(ChangelogCategory.NEW, new),
ChangelogSection(ChangelogCategory.IMPROVED, improved),
ChangelogSection(ChangelogCategory.FIXED, fixed),
).filter { it.entries.isNotEmpty() }
}

/**
* A group of [entries] of the same [category], displayed under one header. Derived from a
* [Changelog] through [Changelog.toSections], not meant to be built directly.
*/
data class ChangelogSection(val category: ChangelogCategory, val entries: List<ChangelogEntry>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.homeassistant.companion.android.changelog

import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.frontend.navigation.WidgetType
import io.homeassistant.companion.android.settings.SettingsActivity

/**
* The changelog content of the current release, with the displayed strings in the dedicated
* `strings_changelog.xml` file of `:common` so they get translated. Entries not yet translated
* fall back to English.
*
* Update this together with the release notes; the changelog screenshot test pins the rendered
* result.
*/
Comment thread
TimoPtr marked this conversation as resolved.
internal val currentChangelog = Changelog(
new = listOf(
ChangelogEntry(
contentRes = commonR.string.changelog_entry_assistant_volume,
platforms = setOf(ChangelogPlatform.APP, ChangelogPlatform.AUTOMOTIVE, ChangelogPlatform.WEAR),
),
),
improved = listOf(
ChangelogEntry(
contentRes = commonR.string.changelog_entry_health_connect,
platforms = setOf(ChangelogPlatform.APP, ChangelogPlatform.AUTOMOTIVE),
),
ChangelogEntry(
contentRes = commonR.string.changelog_entry_entity_widgets,
platforms = setOf(ChangelogPlatform.APP),
action = ChangelogAction.OpenWidgetConfig(WidgetType.Entity),
),
ChangelogEntry(
contentRes = commonR.string.changelog_entry_tiles,
platforms = setOf(ChangelogPlatform.APP),
action = ChangelogAction.OpenSettings(SettingsActivity.Deeplink.QSTile()),
),
),
fixed = listOf(
ChangelogEntry(
contentRes = commonR.string.changelog_entry_bug_fixes,
platforms = setOf(ChangelogPlatform.APP, ChangelogPlatform.AUTOMOTIVE, ChangelogPlatform.WEAR),
),
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.homeassistant.companion.android.changelog

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.changelog.ui.ChangelogContent
import io.homeassistant.companion.android.common.R as commonR
import io.homeassistant.companion.android.common.compose.theme.HATheme
import io.homeassistant.companion.android.common.compose.theme.LocalHAColorScheme
import io.homeassistant.companion.android.settings.SettingsActivity
import io.homeassistant.companion.android.util.safeBottomWindowInsets

/**
* Hosts the changelog within the settings, whose activity already provides the toolbar with the
* title and back navigation.
*/
@AndroidEntryPoint
class ChangelogFragment : Fragment() {

private val viewModel: ChangelogViewModel by viewModels()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return ComposeView(requireContext()).apply {
setContent {
HATheme {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
ChangelogContent(
uiState = uiState,
onGotItClick = { parentFragmentManager.popBackStack() },
onActionClick = ::onActionClick,
modifier = Modifier
.background(LocalHAColorScheme.current.colorSurfaceDefault)
.windowInsetsPadding(safeBottomWindowInsets(applyHorizontal = false)),
)
}
}
}
}

override fun onResume() {
super.onResume()
activity?.title = getString(commonR.string.changelog_screen_title)
}

private fun onActionClick(action: ChangelogAction) {
when (action) {
is ChangelogAction.OpenUrl -> startActivity(Intent(Intent.ACTION_VIEW, action.url.toUri()))
is ChangelogAction.OpenSettings ->
startActivity(SettingsActivity.newInstance(requireContext(), action.deeplink))
is ChangelogAction.OpenWidgetConfig ->
startActivity(action.widgetType.toConfigureIntent(requireContext()))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.homeassistant.companion.android.changelog

import androidx.annotation.VisibleForTesting
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
import javax.inject.Inject

/**
* Decides whether the changelog screen should be shown.
*/
@HiltViewModel
class ChangelogShowViewModel @VisibleForTesting constructor(
private val prefsRepository: PrefsRepository,
private val currentVersionCode: Int,
) : ViewModel() {

@Inject
constructor(prefsRepository: PrefsRepository) : this(prefsRepository, BuildConfig.VERSION_CODE)

private var consumed = false

/**
* Returns `true` when the user has enabled the changelog popup and the changelog of the
* current app version has not been seen yet.
*
* The decision is consumed: it returns `true` at most once per instance so configuration
* changes or returning to the frontend don't show the changelog again.
*/
suspend fun consumeShouldShowChangelog(): Boolean {
if (consumed) return false
consumed = true
return prefsRepository.isChangeLogPopupEnabled() &&
prefsRepository.wasAppUpdatedSinceChangelogSeen(currentVersionCode)
}
}
Loading
Loading