Skip to content
Merged
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
10 changes: 6 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ jobs:
- uses: actions/upload-artifact@v7
id: apk
with:
name: message487-apks
path: app/build/outputs/apk/**/*.apk
name: message487-builds
path: |
app/build/outputs/apk/**/*.apk
app/build/outputs/bundle/**/*.aab
if-no-files-found: error
retention-days: 14
- name: Link APK artifacts
- name: Link build artifacts
env:
ARTIFACT_URL: ${{ steps.apk.outputs.artifact-url }}
run: echo "[Download debug and unsigned release APKs]($ARTIFACT_URL)" >> "$GITHUB_STEP_SUMMARY"
run: echo "[Download debug APK and unsigned release APK/AAB]($ARTIFACT_URL)" >> "$GITHUB_STEP_SUMMARY"

python:
name: Python tests and style
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ concurrency:

jobs:
release:
name: Build and verify signed APK
name: Build and verify signed APK and AAB
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
Expand Down Expand Up @@ -97,6 +97,6 @@ jobs:
run: |
cd dist/release
sha256sum -c SHA256SUMS
gh release create "$GITHUB_REF_NAME" ./*.apk mapping.txt SHA256SUMS \
gh release create "$GITHUB_REF_NAME" ./*.apk ./*.aab mapping.txt SHA256SUMS \
--repo "$GITHUB_REPOSITORY" --verify-tag --generate-notes \
--title "Message487 $GITHUB_REF_NAME"
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
# Message487

[![CI](https://github.com/andre487/AndroidMessage487/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/andre487/AndroidMessage487/actions/workflows/ci.yml?query=branch%3Amain+event%3Apush)
[![Release](https://img.shields.io/github/v/release/andre487/AndroidMessage487)](https://github.com/andre487/AndroidMessage487/releases/latest)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Android 8+](https://img.shields.io/badge/Android-8.0%2B-3DDC84?logo=android&logoColor=white)](https://developer.android.com/about/versions/oreo)

<p align="center">
<img src="assets/branding/message487-icon.png" width="160" alt="Message487 app icon">
</p>

Message487 connects selected Android notifications and incoming SMS to your n8n workflows.
A custom webhook is also supported. Telegram forwarding is one possible workflow; the Android
app does not depend on Telegram.

**Status:** development preview with notification/SMS capture, a persistent encrypted outbox,
background delivery, automatic retries and a delivery journal. Webhook requests require a Bearer token, stored encrypted on the device. Signed APK release automation is configured; see [Releases](docs/en/releases.md).
background delivery, automatic retries and a delivery journal. Webhook requests require a Bearer token, stored encrypted on the device. Signed APK/AAB release automation is configured; see [Releases](docs/en/releases.md).

## Getting started

**[Download the latest signed APK](https://github.com/andre487/AndroidMessage487/releases/latest/download/message487.apk)**

The stable link follows the latest published release; it does not point to development builds.
Read the [release notes](https://github.com/andre487/AndroidMessage487/releases/latest) for supported features.
Release 0.0.1 predates Bearer authentication.

Guides: [Install from APK](docs/en/apk-installation.md) · [n8n webhook](docs/en/n8n-webhook.md) · [Telegram forwarding](docs/en/n8n-telegram.md).
На русском: [Установка из APK](docs/ru/apk-installation.md) · [n8n webhook](docs/ru/n8n-webhook.md) · [Пересылка в Telegram](docs/ru/n8n-telegram.md).

1. Save the full published webhook URL and a device code in **Connection**. Send a test event.
1. Save the full published webhook URL, a Bearer token and a device code in **Connection**. Send a test event.
2. Check **Journal** and find the same event ID in n8n **Executions**.
3. In **Sources**, enable notification forwarding, grant notification access in Android settings,
and select applications. Add a package manually if it has no launcher icon.
Expand Down Expand Up @@ -46,7 +61,7 @@ connection failures and transient HTTP errors retry with backoff; other HTTP fai
confirmations need a manual retry from **Journal** after fixing the server. A lost response can
cause duplicate delivery: downstream workflows should deduplicate using `event_id`.

Queued events retain the URL and confirmation mode from capture time. Changing the connection
Queued events retain the URL, token and confirmation mode from capture time. Changing the connection
does not reroute them. Undelivered events remain until confirmed or explicitly deleted. The
journal persists across restarts and hides message contents. Tap an event for its ID, HTTP result,
retry and deletion controls. Confirmed payloads are removed;
Expand Down Expand Up @@ -95,8 +110,11 @@ bottom navigation on phones and rail navigation on wider windows. See the [desig
for the visual conventions and references.

Fastlane's `debug_artifact` lane builds only the debug APK. `checks` runs JVM/Robolectric tests,
debug/release lint, and builds debug and unsigned release APKs under `app/build/outputs/apk/`.
debug/release lint, and builds debug and unsigned release APKs under `app/build/outputs/apk/`, plus an unsigned AAB under
`app/build/outputs/bundle/`.
PR CI has no release signing credentials and does not require an emulator.
For signed APK/AAB artifacts and manual Play Console upload, see [Releases](docs/en/releases.md).
Store graphics and their provenance are documented in [Branding](assets/branding/README.md).

See the [project context](docs/en/project-context.md) for remaining product decisions.
This project succeeds [sms487](https://github.com/andre487/sms487).
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ android {
applicationId = "life.andre.message487"
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "0.0.1"
versionCode = 2
versionName = "0.0.2"
}
signingConfigs {
if (signingInputs.all { !it.isNullOrBlank() }) {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
android:networkSecurityConfig="@xml/network_security_config"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Message487">
<provider
Expand Down
Binary file added app/src/main/res/drawable-hdpi/ic_notification.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/src/main/res/drawable-mdpi/ic_notification.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/src/main/res/drawable-xhdpi/ic_notification.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/src/main/res/drawable-xxhdpi/ic_notification.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 11 additions & 3 deletions app/src/main/res/drawable/ic_launcher_foreground.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp" android:height="108dp" android:viewportWidth="108" android:viewportHeight="108">
<path android:fillColor="#FFFFFF" android:pathData="M28,30 L80,30 L80,68 L48,68 L34,80 L34,68 L28,68 Z" />
<path android:fillColor="#315DA8" android:pathData="M39,42 L69,42 L69,47 L39,47 Z M39,53 L60,53 L60,58 L39,58 Z" />
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108"
android:tint="#FFFFFF">
<group android:scaleX="1.815"
android:scaleY="1.815"
android:translateX="32.22"
android:translateY="32.22">
<path android:fillColor="@android:color/white" android:pathData="M23,11.01L18,11c-0.55,0 -1,0.45 -1,1v9c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-9c0,-0.55 -0.45,-0.99 -1,-0.99zM23,20h-5v-7h5v7zM20,2L2,2C0.89,2 0,2.89 0,4v12c0,1.1 0.89,2 2,2h7v2L7,20v2h8v-2h-2v-2h2v-2L2,16L2,4h18v5h2L22,4c0,-1.11 -0.9,-2 -2,-2zM11.97,9L11,6l-0.97,3L7,9l2.47,1.76 -0.94,2.91 2.47,-1.8 2.47,1.8 -0.94,-2.91L15,9h-3.03z"/>
</group>
</vector>
1 change: 1 addition & 0 deletions app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
5 changes: 5 additions & 0 deletions app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
Binary file added app/src/main/res/mipmap-hdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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/src/main/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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/src/main/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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/src/main/res/mipmap-xxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<resources>
<color name="launcher_background">#315DA8</color>
<color name="launcher_background">#3F51B5</color>
</resources>
27 changes: 27 additions & 0 deletions assets/branding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Message487 branding

| Asset | Use |
| --- | --- |
| `message487-icon.png` | Google Play app icon, 512×512 RGBA PNG; also used in README |
| `message487-feature.png` | Google Play feature graphic, 1024×500 RGB PNG without alpha; shared by both locales |
| `message487-feature-master.png` | Original generated banner, retained for future exports |
| `screenshots/en/`, `screenshots/ru/` | Actual phone screenshots in English and Russian, 1080×1920 |

Launcher PNGs and the adaptive foreground are copied from
[andre487/sms487](https://github.com/andre487/sms487/tree/d4aca0724c4d8c8cfcfe128c6df6cc93f64625f2/client/app/src/main).
The monitor, star, phone and indigo background preserve the original app identity.
The existing vector foreground also supplies the Android themed-icon mask; round launcher
resources and legacy notification icons are included. The app UI retains its purple Material theme.
The original artwork is distributed under the [sms487 MIT license](sms487-LICENSE).

The feature illustration was produced with the built-in `image_gen` tool using the old
Play Store icon as its reference, then exported with `sips -z 500 1024` as an opaque PNG.
The generation prompt is recorded in [feature-prompt.txt](feature-prompt.txt).
It extends the icon's device imagery with notification cards and workflow nodes and uses
no localized text or third-party service marks. It is promotional artwork, not an app screenshot.

For a Play listing, upload `message487-icon.png` as the app icon and `message487-feature.png`
as the feature graphic. The screenshots were captured from the debug app on the API 35 emulator with synthetic
local data, using Android app locales and a 1080×1920 display. Refresh them from the release
being submitted whenever its UI changes; do not use generated illustrations as screenshots. Follow the current
[Google Play asset requirements](https://support.google.com/googleplay/android-developer/answer/9866151?hl=en).
4 changes: 4 additions & 0 deletions assets/branding/feature-prompt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Tool: built-in image_gen
Reference: message487-icon.png (original sms487 Play Store icon)

Use case: ads-marketing. Create a finished Google Play feature graphic for the Android application Message487, precisely 1024 x 500 pixels, opaque RGB PNG. Reference image is the old sms487 icon: indigo background and a white outlined desktop monitor with a five-point star, next to a smartphone. Extend this visual language into a polished restrained flat Material Design editorial illustration about forwarding phone notifications and SMS to workflows. Do not simply enlarge or repeat the app icon: use a smaller smartphone with two abstract notification cards connected by graceful simple lines to a desktop monitor and three small workflow nodes. Keep the distinctive star on the monitor. Deep indigo and violet with soft lavender fields, white outlines, very subtle depth, clean geometry, ample empty margins. No text, letters, numbers, badges, logos of other services, people, photos, or fake screenshots. Keep all meaningful content within the central 75 percent width and central 70 percent height so nothing is cropped in store previews. Crisp professional composition, visually balanced, not crowded. This is final promotional illustration, not a mockup of a store page.
Binary file added assets/branding/message487-feature-master.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 assets/branding/message487-feature.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 assets/branding/message487-icon.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 assets/branding/screenshots/en/overview.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 assets/branding/screenshots/en/sources.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 assets/branding/screenshots/ru/overview.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 assets/branding/screenshots/ru/sources.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions assets/branding/sms487-LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Andrey Prokopyuk

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2 changes: 1 addition & 1 deletion docs/en/n8n-webhook.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Installing on a phone? See [APK installation and Android restrictions](apk-installation.md).

This guide targets the development version with Bearer authentication (not release 0.0.1). You need the n8n editor and an HTTPS endpoint
This guide targets Message487 0.0.2 and later, with Bearer authentication. You need the n8n editor and an HTTPS endpoint
reachable from your phone. For a local Android emulator, use the debug build and
[DevServer](../../DevServer/README.md). Release APKs reject HTTP endpoints.

Expand Down
54 changes: 45 additions & 9 deletions docs/en/releases.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
# Signed APK releases
# Signed APK and App Bundle releases

[English](../en/releases.md) | [Русский](../ru/releases.md)

Installing on a phone? See [APK installation and Android restrictions](apk-installation.md).

Run `bundle exec fastlane android release_artifacts` with JDK 21 and Android SDK 36. The lane runs
Android JVM/Compose tests, debug/release lint and a signed release build, then checks the certificate,
package ID, version and non-debuggable flag. Outputs are `dist/release/message487-<version>.apk`,
R8 `mapping.txt` and `SHA256SUMS`. Keep the mapping with its exact APK when diagnosing crashes.
Run `bundle exec fastlane android release_artifacts` with JDK 21 and Android SDK 36.
The lane runs Android JVM/Compose tests and debug/release lint, then builds signed APK and AAB
from the same release variant. It checks the APK certificate, package/version and non-debuggable
flag, and verifies the certificate and signature of every AAB payload entry.

| Output in `dist/release/` | Purpose |
| --- | --- |
| `message487-<version>.apk` | Versioned signed APK |
| `message487.apk` | Byte-identical APK with a stable download filename |
| `message487.aab` | Signed Android App Bundle for manual Play Console upload |
| `mapping.txt` | R8 mapping for this exact build |
| `SHA256SUMS` | Checksums for both APK names, AAB and mapping |

The [permanent APK link](https://github.com/andre487/AndroidMessage487/releases/latest/download/message487.apk)
follows GitHub's latest published release, not the current branch. Existing versioned URLs keep
working. A copy of the original 0.0.1 APK provides the stable filename for that release;
it does not acquire features added after its tag.

Signing follows MegaProxy's environment contract with the `MESSAGE487_` prefix:

Expand All @@ -23,7 +36,7 @@ Signing follows MegaProxy's environment contract with the `MESSAGE487_` prefix:

Gradle reads only the four signing environment variables (path, store password, alias and key
password). Partial signing configuration fails closed. The PR `checks` lane rejects signing inputs
and verifies an unsigned release APK. Passwords are not command-line arguments and must never be
and verifies unsigned release APK and AAB files. Passwords are not command-line arguments and must never be
printed, committed or passed through Gradle `-P` properties.

GitHub Secrets use the same names as MegaProxy: `ANDROID_SIGNING_KEY_BASE64`,
Expand All @@ -34,14 +47,37 @@ GitHub Release. PR workflows do not consume signing secrets.

## Verification and publication

- Push a `release-check/*` tag to build and verify a signed APK in GitHub Actions without publishing
a Release. The signed APK, checksums, mapping and test reports are available as Actions artifacts.
- Push a `release-check/*` tag to build and verify signed APK/AAB files in GitHub Actions without publishing
a Release. The signed APK/AAB files, checksums, mapping and test reports are available as Actions artifacts.
- After the workflow is merged into the default branch, manual dispatch also builds artifacts only.
- For publication, increment `versionCode`, set the intended `versionName` in `app/build.gradle.kts`,
and merge the reviewed change after all required PR checks pass. Push the matching `v<versionName>`
tag. The workflow requires the tag commit to be contained in `main` and rejects a version mismatch.
It publishes the verified APK, mapping and checksums to GitHub Releases. An existing Release is
It publishes the verified APKs, AAB, mapping and checksums to GitHub Releases. An existing Release is
not overwritten by a rerun.

The first configured version is `0.0.1` with `versionCode = 1`. Later releases must increase
`versionCode` to support Android upgrades. Keep using the same signing key for installed users.

## Upload to Google Play

1. Build release artifacts as above or download them from a verified release workflow run.
Use a new `versionCode` for each Play upload; increase it in `app/build.gradle.kts` before building.
2. Create/select the Play Console app for `life.andre.message487` and configure Play App Signing.
The AAB uses the same configured key as the GitHub APK. Confirm that Play accepts it as the
upload key. To allow updates between GitHub and Play installs, plan the **app signing key**
consistently; an upload key and a Play-generated app signing key are different roles.
See [Android signing guidance](https://developer.android.com/studio/publish/app-signing).
3. Create an internal-testing release and upload `message487.aab`. An AAB is a publishing
artifact; install the APK on phones, not the AAB. Keep `mapping.txt` with the build
(AGP also embeds the R8 mapping in the bundle).
4. Add the icon and feature graphic from [Branding](../../assets/branding/README.md), real app
screenshots, descriptions, privacy policy and Data safety answers. Complete Play's
[SMS permissions declaration/review](https://support.google.com/googleplay/android-developer/answer/10208820?hl=en)
for `RECEIVE_SMS`; a successful AAB build does not establish store eligibility.
5. Review the internal release in Play Console before rolling it out.

CI builds and verifies the artifacts; it does not upload to Google Play or require a Play service
account. Native symbol packaging from MegaProxy is unnecessary here: this app has no native core.
The bundle signature verifier also rejects unsigned added entries, modified entries, missing
required bundle entries and unexpected certificates; its regression fixtures run in Android CI.
Loading
Loading