feat(chat): message persistence + background notifications (#2)
* feat(chat): message persistence + background notifications
Two critical gaps for daily-driver use:
1. Message persistence (Room database):
- New ChatMessageEntity / ChatMessageDao / HermesDatabase layer
- Every user message, streaming token, tool result, and assistant
reply is written to a local Room DB as it arrives
- switchSession loads cached messages instantly from Room before
the REST call returns — no more blank screen while loading
- Session resume also loads from cache first
- Messages survive app kills, process death, and configuration
changes
- ChatViewModel changed from ViewModel() to AndroidViewModel(app)
to access the database context
2. Background notifications (foreground service):
- ChatNotificationService keeps the WebSocket alive while the app
is backgrounded and posts a high-priority notification when a
MessageComplete or ClarifyRequest event arrives while the app
is not in the foreground
- Lifecycle wired via LifecycleEventObserver in ChatScreen:
ON_STOP starts the service, ON_START stops it
- Tapping the notification returns to the chat
- New permissions: POST_NOTIFICATIONS, FOREGROUND_SERVICE,
FOREGROUND_SERVICE_DATA_SYNC
Build changes:
- Added Room 2.7.1 + KSP to version catalog
- Added WorkManager 2.10.0 (reserved for future polling fallback)
- Updated ChatViewModelTest to mock HermesDatabase + use
AndroidViewModel constructor
* fix(build): use KSP 2.3.9 (new versioning scheme for Kotlin 2.3.x)
* fix(build): add Room schemaDirectory (required by Room Gradle plugin)
* fix(compile): add else branch to WsEvent when expression (exhaustiveness)
* ci(release): add release workflow + R8 minification + proguard rules
New .github/workflows/release.yml:
- Triggers on tag pushes (v*) and manual workflow_dispatch
- Runs full CI (ktlint + lint + test) before building
- Builds assembleRelease with version injection from tag
- Creates a GitHub Release with auto-generated changelog
- Attaches the release APK to the release
Build changes:
- Release builds now use R8 minification (isMinifyEnabled=true)
and resource shrinking (isShrinkResources=true) for smaller APKs
- versionCode/versionName injectable via -PversionCode / -PversionName
Gradle properties (passed by CI from the git tag)
- Local dev builds default to versionCode=1, versionName=1.0-dev
ProGuard rules:
- Keep all data model classes (Gson reflection)
- Keep Room entities and RoomDatabase subclasses
- Keep OkHttp/Retrofit interface methods
- Suppress warnings for Compose, Security Crypto, Coroutines
* feat(ci): release workflow + proguard rules + test fix
Release workflow (.github/workflows/release.yml):
- Triggers on tag push (v*) or manual workflow_dispatch
- Two jobs: ci (ktlint+lint+test) → build-release
- Builds a minified, shrunk release APK with version from tag
- Creates a GitHub Release with auto-generated release notes
- Attaches the APK as a release asset
Build config:
- versionCode/versionName now injected from CI via -P flags
- Release build enables isMinifyEnabled + isShrinkResources
- proguard-rules.pro: keep rules for Gson models, Room entities,
OkHttp/Retrofit interfaces, Coroutines, Compose, BuildConfig
Test fix:
- ChatViewModelTest: use coEvery for suspend DAO methods instead
of every (compile error — suspend functions need coEvery)
- Added stubs for all suspend DAO methods used by the ViewModel
---------
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
7daf73ce0d
commit
5130c2e308
13 files changed
+722
-47
No files matched your search
@@ -0,0 +1,124 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Release tag (e.g. v1.0.0)"
|
||||
required: true
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# ── Run full CI before building release ───────────────────────────────
|
||||
ci:
|
||||
name: CI Checks
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Grant Execute Permission for Gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Install ktlint
|
||||
run: |
|
||||
curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.2.1/ktlint
|
||||
chmod +x ktlint
|
||||
sudo mv ktlint /usr/local/bin/
|
||||
|
||||
- name: ktlintCheck
|
||||
run: ./gradlew ktlintCheck
|
||||
|
||||
- name: lintDebug
|
||||
run: ./gradlew lintDebug
|
||||
|
||||
- name: test
|
||||
run: ./gradlew test
|
||||
|
||||
# ── Build release APK ─────────────────────────────────────────────────
|
||||
build-release:
|
||||
name: Build Release APK
|
||||
needs: ci
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "17"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Grant Execute Permission for Gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Determine version
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
else
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
fi
|
||||
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
||||
# Strip leading 'v' for versionName
|
||||
VERSION="${TAG#v}"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
# versionCode: strip dots from version, pad to keep sortable
|
||||
CODE=$(echo "$VERSION" | tr -d '.' | sed 's/^0*//')
|
||||
[ -z "$CODE" ] && CODE=0
|
||||
echo "code=$CODE" >> "$GITHUB_OUTPUT"
|
||||
echo "Release: tag=$TAG version=$VERSION code=$CODE"
|
||||
|
||||
- name: Build Release APK
|
||||
env:
|
||||
VERSION_NAME: ${{ steps.version.outputs.version }}
|
||||
VERSION_CODE: ${{ steps.version.outputs.code }}
|
||||
run: |
|
||||
./gradlew assembleRelease \
|
||||
-PversionName="$VERSION_NAME" \
|
||||
-PversionCode="$VERSION_CODE"
|
||||
|
||||
- name: Rename APK
|
||||
run: |
|
||||
mkdir -p release
|
||||
cp app/build/outputs/apk/release/app-release.apk \
|
||||
release/hermes-control-${{ steps.version.outputs.tag }}.apk
|
||||
ls -lh release/
|
||||
|
||||
- name: Upload Release APK Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: hermes-control-release
|
||||
path: release/*.apk
|
||||
retention-days: 90
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.tag }}
|
||||
name: Hermes Control ${{ steps.version.outputs.tag }}
|
||||
generate_release_notes: true
|
||||
files: release/*.apk
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
Reference in new issue
Block a user