Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d81bfb7
Dictate into text fields from the keypad typing bar
yeradis Nov 8, 2026
6a17591
Keep the typing bar on one line when a screen adds a button
yeradis Nov 8, 2026
a31f3f8
Fill the whole screen instead of a fixed 480 x 640 canvas
yeradis Nov 8, 2026
5ea4225
Show the town for Sideweather's current location
yeradis Nov 8, 2026
560956e
Give the crowded typing bar equal buttons, like the home dock
yeradis Nov 8, 2026
c5fc1f0
Silence the RestrictedApi lint error on dispatchKeyEvent
manuel-rtsch Nov 7, 2026
329eec0
Read the install confirmation intent on API 31 and 32 too
manuel-rtsch Nov 7, 2026
0ce22ea
Format the two touched files the way ktlint expects
manuel-rtsch Nov 7, 2026
cff57dc
Add a GitHub Actions CI pipeline
manuel-rtsch Nov 7, 2026
6257a30
Make Spotless read the repository's .editorconfig
manuel-rtsch Nov 7, 2026
a74016f
Pass the SDK packages to setup-android on one line
manuel-rtsch Nov 7, 2026
59df9b2
Read versionName and versionCode from git
manuel-rtsch Nov 7, 2026
1cf21e1
Mark the dev build in the header and in About
manuel-rtsch Nov 7, 2026
f966e8e
Format the files this change touches with spotlessApply
manuel-rtsch Nov 7, 2026
d624e2e
Share the app row, and keep Sidekit out of Sideport
manuel-rtsch Nov 7, 2026
416171c
Format the files this change touches with spotlessApply
manuel-rtsch Nov 7, 2026
7143d26
Stop the screen title bouncing past its place on navigation
yeradis Nov 8, 2026
77a8ad0
Don't draw the old title over the launcher clock when going home
yeradis Nov 8, 2026
bc51db1
Let D-pad Up leave the action bar for the screen above it
yeradis Nov 8, 2026
362e432
Park focus in a spot the D-pad can leave
yeradis Nov 8, 2026
f4dbe52
Let the D-pad move past the map until OK starts panning
yeradis Nov 8, 2026
09e9c51
Let the D-pad walk the contacts instead of holding the whole list
yeradis Nov 8, 2026
aac57cf
Add a compact layout for the header and action bar
manuel-rtsch Nov 7, 2026
1841bfe
Refresh screenshot baselines and pin the calendar date
manuel-rtsch Nov 7, 2026
d4c69ca
Record the two Settings screenshots on the compact layout
manuel-rtsch Nov 7, 2026
6cef26e
Merge pull request #27 from manuel-rtsch/pr/lint-and-ci
HarlenPostill Nov 9, 2026
0368e29
Merge pull request #28 from manuel-rtsch/pr/build-identity
HarlenPostill Nov 9, 2026
7403791
Merge pull request #29 from manuel-rtsch/pr/app-rows
HarlenPostill Nov 9, 2026
12511a7
Merge pull request #30 from manuel-rtsch/pr/chrome
HarlenPostill Nov 9, 2026
0ba6fb8
Merge pull request #33 from yeradis/pr/full-screen-canvas
HarlenPostill Nov 9, 2026
a416a3e
Merge pull request #41 from yeradis/pr/dpad-fixes
HarlenPostill Nov 9, 2026
dbfa074
Merge pull request #39 from yeradis/pr/title-fixes
HarlenPostill Nov 9, 2026
1dd4280
Merge pull request #34 from yeradis/pr/sideweather-city-name
HarlenPostill Nov 9, 2026
f8155d1
Merge pull request #35 from yeradis/pr/text-field-dictation
HarlenPostill Nov 9, 2026
a1a5b44
Follow up #27: CodeQL v4, keep main's CI runs, and safer intent reads
HarlenPostill Nov 9, 2026
3c406a1
Follow up #28: honest version names without tags or git
HarlenPostill Nov 9, 2026
a500abe
Follow up #29: keep the Apps search Done action and rounded press
HarlenPostill Nov 9, 2026
b55c299
Follow up #33: keep the design width on wide windows
HarlenPostill Nov 9, 2026
a91167c
Follow up #41: Phonebook digits from the action bar, quieter map focus
HarlenPostill Nov 9, 2026
0964da6
Follow up #39: sync the title from apply-time state
HarlenPostill Nov 9, 2026
db8e611
Follow up #34: no forecast flash on rename, and a real geocode timeout
HarlenPostill Nov 9, 2026
b46a093
Follow up #35: dictation only where it fits, and sturdier errors
HarlenPostill Nov 9, 2026
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
25 changes: 25 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Shared formatting rules, used by ktlint (through Spotless) and by editors.
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.{kt,kts}]
ktlint_code_style = android_studio
# Existing Sidekit code does not enforce a wrap column; keep it that way.
max_line_length = off
# Sidekit imports MMS and WorkManager types wholesale in a few adapters.
ktlint_standard_no-wildcard-imports = disabled
# Screens are composables with PascalCase names, which is the Compose convention.
ktlint_function_naming_ignore_when_annotated_with = Composable

[*.{yml,yaml}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
21 changes: 21 additions & 0 deletions .github/actions/android-setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Android build setup
description: Install the JDK, Android SDK platform and Gradle caching used by every CI job.

runs:
using: composite
steps:
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"

- name: Set up Android SDK
uses: android-actions/setup-android@v3
with:
# setup-android splits this on spaces, so a YAML block scalar arrives as one package name
# containing newlines and sdkmanager fails with "Failed to find package".
packages: platform-tools platforms;android-37.0 build-tools;37.0.0

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
140 changes: 140 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
# Only supersede PR runs; every push to main keeps its own status.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
wrapper-validation:
name: Gradle wrapper validation
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4

build:
name: Build (debug + R8 release)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
# versionName/versionCode come from `git describe` and the commit count, so a shallow
# clone would build a release that claims versionCode 1.
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ./.github/actions/android-setup
- name: Assemble debug and release
run: >
./gradlew :app:assembleDebug :app:assembleRelease :sidekit:assembleDebug
--stacktrace

unit-tests:
name: Unit tests
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/android-setup
# Robolectric downloads its Android runtime jars outside Gradle's cache.
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: robolectric-${{ runner.os }}-${{ hashFiles('gradle/libs.versions.toml') }}
restore-keys: robolectric-${{ runner.os }}-
- name: Run JVM and Robolectric tests
run: ./gradlew testDebugUnitTest --stacktrace
- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: unit-test-reports
path: |
**/build/reports/tests/testDebugUnitTest
**/build/test-results/testDebugUnitTest
retention-days: 7
if-no-files-found: ignore

android-lint:
name: Android Lint
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/android-setup
- name: Run lint
run: ./gradlew lintDebug --stacktrace
- name: Upload lint reports
if: always()
uses: actions/upload-artifact@v4
with:
name: android-lint-reports
path: |
**/build/reports/lint-results-debug.html
**/build/reports/lint-results-debug.txt
**/build/reports/lint-results-debug.xml
retention-days: 7
if-no-files-found: ignore
- name: Upload lint results to code scanning
if: always()
continue-on-error: true
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: app/build/reports/lint-results-debug.sarif
category: android-lint

kotlin-format:
name: Kotlin formatting
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
# Spotless ratchets against origin/main, so it needs the base ref.
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ./.github/actions/android-setup
- name: Check ktlint formatting of changed files
run: ./gradlew spotlessCheck --stacktrace

secrets:
name: Secret scan
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

dependency-review:
name: Dependency review
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
comment-summary-in-pr: on-failure
40 changes: 40 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CodeQL

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "23 4 * * 1"
workflow_dispatch:

permissions:
contents: read
security-events: write
packages: read
actions: read

concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true

jobs:
analyze:
name: Analyze Kotlin and Java
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/android-setup
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: java-kotlin
build-mode: manual
- name: Build for analysis
run: ./gradlew :app:assembleDebug :sidekit:assembleDebug --stacktrace
- name: Analyze
uses: github/codeql-action/analyze@v4
with:
category: "/language:java-kotlin"
24 changes: 24 additions & 0 deletions .github/workflows/dependency-graph.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Dependency graph

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: write

concurrency:
group: dependency-graph-${{ github.ref }}
cancel-in-progress: true

jobs:
submit:
name: Submit Gradle dependencies
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/android-setup
# Populates GitHub's dependency graph, which the PR dependency-review job reads.
- uses: gradle/actions/dependency-submission@v4
44 changes: 41 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@ plugins {
alias(libs.plugins.kotlin.compose)
}

// Version metadata is read from git instead of being hand-edited here, so a build always reports
// the code it was made from: a tagged commit is exactly "1.2.3", anything after it is
// "1.2.3-3-g<sha>", and uncommitted changes add "-dirty". `versionCode` counts commits, so it only
// ever grows and a newer build always installs over an older one — including over the 1.2.3 stable
// build, which was bumped by hand to 6. A checkout without git (exported source, a CI tarball)
// falls back to the last release; pass -PsidekitVersionName=… / -PsidekitVersionCode=… to override.
val fallbackVersionName = "1.2.3"
val fallbackVersionCode = 6

fun git(vararg args: String) = providers.exec {
commandLine("git", *args)
workingDir = rootDir
isIgnoreExitValue = true
}.standardOutput.asText.map(String::trim)

// `--always` prints the bare sha when no release tag is reachable (a shallow clone, say), so such a
// build reads "0.0.0-g<sha>" rather than passing itself off as the last release.
val gitVersionName = git("describe", "--tags", "--match", "v[0-9]*", "--dirty", "--always")
.map { described ->
val name = described.removePrefix("v")
when {
name.isEmpty() -> fallbackVersionName
name.first().isDigit() -> name
else -> "0.0.0-g$name"
}
}
val gitVersionCode = git("rev-list", "--count", "HEAD")
.map { it.toIntOrNull() ?: fallbackVersionCode }

// A machine without a `git` binary makes the exec itself throw, rather than exit non-zero.
fun versionCodeFromGit() = runCatching { gitVersionCode.get() }.getOrDefault(fallbackVersionCode)
fun versionNameFromGit() = runCatching { gitVersionName.get() }.getOrDefault(fallbackVersionName)

android {
namespace = "hrln.interactive.sidekit"
compileSdk {
Expand All @@ -13,8 +46,11 @@ android {
applicationId = "hrln.interactive.sidekit"
minSdk = 31
targetSdk = 36
versionCode = 2
versionName = "1.1"
versionCode = providers.gradleProperty("sidekitVersionCode")
.map(String::toInt)
.getOrElse(versionCodeFromGit())
versionName = providers.gradleProperty("sidekitVersionName")
.getOrElse(versionNameFromGit())

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand All @@ -40,9 +76,11 @@ android {
// Development build that installs alongside the stable SideKit as a separate app
// (`hrln.interactive.sidekit.dev`), so the stable one can remain the daily driver while
// this tracks the current branch/tip for testing. See app/src/dev/res for its distinct
// name. It derives from `profile` (R8 + debug signing, no debug Compose checks, baseline
// name, and for the DEV pill and About row that mark its UI (values/build.xml). It derives from `profile` (R8 + debug signing, no debug Compose checks, baseline
// profile) so the on-device testing build doesn't stutter like a raw debug build — it
// also falls back to `.sidekit` release. Use `debug` for breakpoint debugging instead.
// The version comes from git describe, so a dev build names the branch tip it was built
// from (e.g. 1.2.3-1-g91da863-dev) rather than a stale hand-written number.
create("dev") {
initWith(getByName("profile"))
applicationIdSuffix = ".dev"
Expand Down
Binary file modified app/screenshots/case_colour_sage_after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/case_colour_sage_before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/keypad_mapping_automatic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/keypad_mapping_editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/keypad_mapping_editor_keys.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/keypad_mapping_settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/keypad_mapping_tester.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/screenshots/launcher_grid_compact.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/launcher_list_36-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/01-home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/02-colour.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/03-layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/03b-hide-apps.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/04-permissions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/05-typing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/06-t9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/07-qwerty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/08-sundial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/09-controller.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/10-search-t9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/11-search-qwerty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/onboarding/13-hide-apps-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/settings_96-891.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/screenshots/settings_compact.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/screenshots/settings_system_home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions app/src/dev/res/values/build.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Dev build only. `hrln.interactive.sidekit.dev` installs alongside the stable Sidekit, and
these overrides are what stops the two being confused on the device: the launcher header
carries a DEV pill and Settings → About names the build.
-->
<resources>
<string name="build_label" translatable="false">DEV</string>
<bool name="is_dev_build">true</bool>
</resources>
4 changes: 3 additions & 1 deletion app/src/dev/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<!--
Overrides applied only to the `dev` build type (installs as a separate app,
hrln.interactive.sidekit.dev, so it can run alongside the stable SideKit).
Only app_name is overridden; every other string is inherited from src/main.
Only app_name is overridden here; every other string is inherited from src/main. The rest of
what marks the UI as dev (the header pill, the About row) lives in
app/src/dev/res/values/build.xml.
-->
<resources>
<string name="app_name">SideKit Dev</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
android:theme="@style/Theme.Sidekit">

<!--
configChanges: SideKit draws a fixed 480 x 640 canvas, so no configuration change
configChanges: SideKit draws its own 480-wide canvas, so no configuration change
needs new resources. Handling them in place avoids tearing down and rebuilding the
whole Compose tree (hundreds of ms on the MT8766) when e.g. a keyboard is attached,
the font scale or dark mode changes.
Expand Down
Loading