diff --git a/README.md b/README.md index 55a6884..f0ad63f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Android 8+](https://img.shields.io/badge/Android-8.0%2B-3DDC84?logo=android&logoColor=white)](https://developer.android.com/about/versions/oreo)

- Message487 app icon + Message487 app icon

Message487 connects selected Android notifications and incoming SMS to your n8n workflows. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cc72932..293ac2e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,6 +13,16 @@ if (signingInputs.any { it != null } && signingInputs.any { it.isNullOrBlank() } throw GradleException("Release signing environment is incomplete") } +val gitCommitHash = providers.environmentVariable("GITHUB_SHA") + .orElse(providers.environmentVariable("MESSAGE487_GIT_COMMIT")) + .orElse(providers.exec { + workingDir(rootProject.projectDir) + commandLine("git", "rev-parse", "HEAD") + isIgnoreExitValue = true + }.standardOutput.asText) + .map { value -> value.trim().take(8).takeIf { it.matches(Regex("[0-9a-fA-F]{8}")) } ?: "unknown" } + .get() + android { namespace = "life.andre.message487" compileSdk = 36 @@ -21,8 +31,9 @@ android { applicationId = "life.andre.message487" minSdk = 26 targetSdk = 36 - versionCode = 2 - versionName = "0.0.2" + versionCode = 3 + versionName = "0.0.3" + buildConfigField("String", "GIT_COMMIT_HASH", "\"$gitCommitHash\"") } signingConfigs { if (signingInputs.all { !it.isNullOrBlank() }) { @@ -50,6 +61,10 @@ android { compose = true buildConfig = true } + dependenciesInfo { + includeInApk = false + includeInBundle = false + } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 diff --git a/app/src/main/java/life/andre/message487/AppInfo.kt b/app/src/main/java/life/andre/message487/AppInfo.kt new file mode 100644 index 0000000..53b2a76 --- /dev/null +++ b/app/src/main/java/life/andre/message487/AppInfo.kt @@ -0,0 +1,35 @@ +package life.andre.message487 + +import android.content.ActivityNotFoundException +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource + +@Composable +internal fun AppInfo() { + val context = LocalContext.current + var browserError by remember { mutableStateOf(false) } + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + SupportingText(stringResource(R.string.app_version, BuildConfig.VERSION_NAME, BuildConfig.GIT_COMMIT_HASH)) + TextButton(onClick = { + browserError = false + try { + context.startActivity(Intent(Intent.ACTION_VIEW, + Uri.parse("https://github.com/andre487/AndroidMessage487/blob/main/PRIVACY.md"))) + } catch (_: ActivityNotFoundException) { + browserError = true + } catch (_: SecurityException) { + browserError = true + } + }, modifier = Modifier.fillMaxWidth()) { + Text(stringResource(R.string.privacy_policy)) + } + if (browserError) Text(stringResource(R.string.no_browser), color = MaterialTheme.colorScheme.error) + } +} diff --git a/app/src/main/java/life/andre/message487/ConnectionScreen.kt b/app/src/main/java/life/andre/message487/ConnectionScreen.kt index d55c040..3558a4c 100644 --- a/app/src/main/java/life/andre/message487/ConnectionScreen.kt +++ b/app/src/main/java/life/andre/message487/ConnectionScreen.kt @@ -68,5 +68,6 @@ internal fun ConnectionScreen(state: ConnectionState, model: ConnectionViewModel } } item { SupportingText(stringResource(R.string.destination_note)) } + item { AppInfo() } } } diff --git a/app/src/main/java/life/andre/message487/OverviewScreen.kt b/app/src/main/java/life/andre/message487/OverviewScreen.kt index 107395f..b3a4258 100644 --- a/app/src/main/java/life/andre/message487/OverviewScreen.kt +++ b/app/src/main/java/life/andre/message487/OverviewScreen.kt @@ -116,6 +116,7 @@ internal fun OverviewScreen(settings: ForwardingSettings, permissions: Permissio } } } + item { AppInfo() } } } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0b5387d..3dd17ed 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -120,4 +120,7 @@ Токен веб-хука Введите токен без префикса Bearer. Хранится на устройстве в зашифрованном виде. Введите токен без пробелов и префикса Bearer. + Версия %1$s · %2$s + Политика конфиденциальности + Не удалось открыть политику конфиденциальности: браузер недоступен. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 37ac61a..33f2ff8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -120,4 +120,7 @@ Webhook token Enter the token without the Bearer prefix. Stored encrypted on this device. Enter a token without spaces or the Bearer prefix. + Version %1$s · %2$s + Privacy policy + No browser is available to open the privacy policy. diff --git a/app/src/test/java/life/andre/message487/ScreenInteractionTest.kt b/app/src/test/java/life/andre/message487/ScreenInteractionTest.kt index 7e94406..c03fc56 100644 --- a/app/src/test/java/life/andre/message487/ScreenInteractionTest.kt +++ b/app/src/test/java/life/andre/message487/ScreenInteractionTest.kt @@ -71,6 +71,21 @@ class ScreenInteractionTest { node(R.string.connection_heading).assertIsDisplayed() } + @Test fun `overview and connection show version and open the public privacy policy`() { + for (connection in listOf(false, true)) { + if (connection) compose.onNode(hasText(application.getString(R.string.connection_nav)) and hasClickAction()).performClick() + compose.onNode(hasScrollToIndexAction()).performScrollToNode(hasText(application.getString(R.string.privacy_policy))) + compose.onNodeWithText(application.getString(R.string.app_version, BuildConfig.VERSION_NAME, BuildConfig.GIT_COMMIT_HASH)).assertIsDisplayed() + node(R.string.privacy_policy).performClick() + compose.runOnIdle { + val intent = org.robolectric.Shadows.shadowOf(compose.activity).nextStartedActivity + assertNotNull(intent) + assertEquals(android.content.Intent.ACTION_VIEW, intent.action) + assertEquals("https://github.com/andre487/AndroidMessage487/blob/main/PRIVACY.md", intent.dataString) + } + } + } + @Test fun `invalid URL is rejected then valid connection persists across store recreation`() { node(R.string.connection_nav).performClick() node(R.string.webhook_url).performTextReplacement("not a URL") diff --git a/assets/branding/README.md b/assets/branding/README.md index 8a23e86..74ed4a8 100644 --- a/assets/branding/README.md +++ b/assets/branding/README.md @@ -2,10 +2,10 @@ | 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 | +| [`icon.png`](../../fastlane/metadata/android/en-US/images/icon.png) | Google Play app icon, 512×512 RGBA PNG; also used in README | +| [`featureGraphic.png`](../../fastlane/metadata/android/en-US/images/featureGraphic.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 | +| `fastlane/metadata/android/{en-US,ru-RU}/images/phoneScreenshots/` | 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). @@ -20,7 +20,10 @@ 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` +Current store assets and localized descriptions have one canonical location under +[`fastlane/metadata/android`](../../fastlane/metadata/android), shared by F-Droid and Google Play. + +For a Play listing, upload [`icon.png`](../../fastlane/metadata/android/en-US/images/icon.png) as the app icon and [`featureGraphic.png`](../../fastlane/metadata/android/en-US/images/featureGraphic.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 diff --git a/assets/branding/screenshots/en/overview.png b/assets/branding/screenshots/en/overview.png deleted file mode 100644 index 3731158..0000000 Binary files a/assets/branding/screenshots/en/overview.png and /dev/null differ diff --git a/assets/branding/screenshots/en/sources.png b/assets/branding/screenshots/en/sources.png deleted file mode 100644 index 98b4a1d..0000000 Binary files a/assets/branding/screenshots/en/sources.png and /dev/null differ diff --git a/assets/branding/screenshots/ru/overview.png b/assets/branding/screenshots/ru/overview.png deleted file mode 100644 index 09140ad..0000000 Binary files a/assets/branding/screenshots/ru/overview.png and /dev/null differ diff --git a/assets/branding/screenshots/ru/sources.png b/assets/branding/screenshots/ru/sources.png deleted file mode 100644 index 5e4ba2a..0000000 Binary files a/assets/branding/screenshots/ru/sources.png and /dev/null differ diff --git a/docs/en/releases.md b/docs/en/releases.md index 0ca6bc4..14cec0a 100644 --- a/docs/en/releases.md +++ b/docs/en/releases.md @@ -81,3 +81,14 @@ CI builds and verifies the artifacts; it does not upload to Google Play or requi 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. + +## F-Droid + +Store descriptions, changelogs and artwork live in +[`fastlane/metadata/android`](../../fastlane/metadata/android). Update both locales +before tagging a release. The submission recipe is maintained in `fdroid/fdroiddata`, +not duplicated in this repository. Pin each build to the full release commit SHA, +use JDK 21, and compare against the versioned GitHub release APK with the expected +signing certificate. Keep dependency metadata disabled for both APKs and bundles. +A successful GitHub build alone does not establish reproducibility: the F-Droid +build and binary comparison must pass before marking that verification complete. diff --git a/docs/ru/releases.md b/docs/ru/releases.md index 129d25b..9fc8d65 100644 --- a/docs/ru/releases.md +++ b/docs/ru/releases.md @@ -83,3 +83,14 @@ Play не настроены. Архив нативных символов из нативного ядра. Проверка подписи бандла отвергает добавленные неподписанные файлы, изменённые файлы, отсутствие обязательных частей и чужой сертификат; регрессионные тесты этой проверки входят в Android CI. + +## F-Droid + +Описания, списки изменений и графика магазина находятся в +[`fastlane/metadata/android`](../../fastlane/metadata/android). Обновляйте обе локали +перед созданием релизного тега. Рецепт заявки поддерживается в `fdroid/fdroiddata`, +без дублирования в этом репозитории. Для сборки указывайте полный SHA релизного +коммита и JDK 21; сравнивайте результат с APK конкретной версии из GitHub Releases +с проверкой ожидаемого сертификата подписи. Метаданные зависимостей должны оставаться +выключенными для APK и App Bundle. Успешной GitHub-сборки недостаточно для заявления +о воспроизводимости: должны пройти сборка F-Droid и сравнение бинарных файлов. diff --git a/fastlane/metadata/android/en-US/changelogs/3.txt b/fastlane/metadata/android/en-US/changelogs/3.txt new file mode 100644 index 0000000..71fa2ab --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3.txt @@ -0,0 +1,2 @@ +Prepare Message487 for F-Droid with localized store descriptions and artwork. Disable AGP dependency metadata in APKs and App Bundles. Update the n8n Telegram guide with a shared formatter, separate header lines and configurable event time formatting. +Show the app version with its commit hash and a privacy policy link on the overview and connection screens. diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt new file mode 100644 index 0000000..74fd492 --- /dev/null +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -0,0 +1,14 @@ +Message487 forwards selected Android notifications and new incoming SMS to an HTTPS webhook you configure. Connect your phone to n8n workflows or another webhook service; no n8n account is required when using a different server. + +• Choose which applications can forward notifications, or select all visible launcher apps. +• Enable SMS forwarding separately. Existing SMS history is not read. +• Authenticate requests with a Bearer token and optionally require an event acknowledgement. +• Keep events in an encrypted local outbox and retry temporary delivery failures. +• Pause forwarding, review delivery status, retry or delete queued events. +• Identify your phone using a custom device code. +• Use light or dark themes and English or Russian interfaces. +• View rotating local diagnostics and optionally share a report by email. + +Notification and SMS capture are off by default. Android notification access and SMS permission are requested for the corresponding features. Android may hide sensitive notification content. The app lists visible launcher apps rather than requesting access to the entire installed-app inventory. + +You need your own HTTPS endpoint and token. The app has no advertising, analytics or automatic crash uploads. Your endpoint and downstream services receive forwarded message contents and apply their own retention policies. Retried requests can produce duplicates; configure deduplication on your server if needed. diff --git a/assets/branding/message487-feature.png b/fastlane/metadata/android/en-US/images/featureGraphic.png similarity index 99% rename from assets/branding/message487-feature.png rename to fastlane/metadata/android/en-US/images/featureGraphic.png index 58cdea4..14be112 100644 Binary files a/assets/branding/message487-feature.png and b/fastlane/metadata/android/en-US/images/featureGraphic.png differ diff --git a/assets/branding/message487-icon.png b/fastlane/metadata/android/en-US/images/icon.png similarity index 100% rename from assets/branding/message487-icon.png rename to fastlane/metadata/android/en-US/images/icon.png diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png new file mode 100644 index 0000000..a3b504b Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png new file mode 100644 index 0000000..6d97b1d Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png new file mode 100644 index 0000000..a7a19f4 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt new file mode 100644 index 0000000..daf86da --- /dev/null +++ b/fastlane/metadata/android/en-US/short_description.txt @@ -0,0 +1 @@ +Forward notifications and SMS to n8n or your own webhook diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt new file mode 100644 index 0000000..0b0ec87 --- /dev/null +++ b/fastlane/metadata/android/en-US/title.txt @@ -0,0 +1 @@ +Message487 diff --git a/fastlane/metadata/android/ru-RU/changelogs/3.txt b/fastlane/metadata/android/ru-RU/changelogs/3.txt new file mode 100644 index 0000000..b4fedf4 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/3.txt @@ -0,0 +1,2 @@ +Подготовка к F-Droid: добавлены описания и графика магазина на двух языках, отключены метаданные зависимостей AGP в APK и App Bundle. Инструкция n8n использует общий скрипт форматирования Telegram с отдельными строками заголовка и настройкой времени события. +На главной и в настройках подключения показаны версия приложения с хешем коммита и ссылка на политику конфиденциальности. diff --git a/fastlane/metadata/android/ru-RU/full_description.txt b/fastlane/metadata/android/ru-RU/full_description.txt new file mode 100644 index 0000000..ecec82b --- /dev/null +++ b/fastlane/metadata/android/ru-RU/full_description.txt @@ -0,0 +1,14 @@ +Message487 пересылает выбранные уведомления Android и новые входящие SMS на указанный вами HTTPS-веб-хук. Подключите телефон к сценариям n8n или другому серверу; при использовании другого сервера аккаунт n8n не нужен. + +• Выберите приложения для пересылки уведомлений или все видимые приложения с иконкой запуска. +• Отдельно включите пересылку SMS. История SMS не читается. +• Используйте Bearer-токен и при необходимости подтверждение приёма события. +• Храните события в зашифрованной локальной очереди с повторными попытками при временных сбоях. +• Приостанавливайте пересылку, проверяйте статусы, повторяйте или удаляйте события. +• Задайте собственное обозначение телефона. +• Выберите светлую или тёмную тему, русский или английский интерфейс. +• Просматривайте ротируемые локальные логи и по желанию отправляйте отчёт по почте. + +Сбор уведомлений и SMS изначально выключен. Для соответствующих функций нужны доступ к уведомлениям и разрешение на получение SMS. Android может скрывать чувствительное содержимое уведомлений. Приложение показывает видимые приложения с иконкой запуска, не запрашивая доступ ко всему списку установленных пакетов. + +Нужны ваш HTTPS-сервер и токен. В приложении нет рекламы, аналитики и автоматической отправки отчётов о сбоях. Настроенный сервер и последующие сервисы получают содержимое сообщений и хранят его по своим правилам. Повторные попытки могут создавать дубликаты; при необходимости настройте дедупликацию на сервере. diff --git a/fastlane/metadata/android/ru-RU/images/phoneScreenshots/1.png b/fastlane/metadata/android/ru-RU/images/phoneScreenshots/1.png new file mode 100644 index 0000000..1e97d37 Binary files /dev/null and b/fastlane/metadata/android/ru-RU/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/ru-RU/images/phoneScreenshots/2.png b/fastlane/metadata/android/ru-RU/images/phoneScreenshots/2.png new file mode 100644 index 0000000..0f932a8 Binary files /dev/null and b/fastlane/metadata/android/ru-RU/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/ru-RU/images/phoneScreenshots/3.png b/fastlane/metadata/android/ru-RU/images/phoneScreenshots/3.png new file mode 100644 index 0000000..37ba934 Binary files /dev/null and b/fastlane/metadata/android/ru-RU/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/ru-RU/short_description.txt b/fastlane/metadata/android/ru-RU/short_description.txt new file mode 100644 index 0000000..82b6de4 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/short_description.txt @@ -0,0 +1 @@ +Пересылка уведомлений и SMS в n8n или ваш веб-хук diff --git a/fastlane/metadata/android/ru-RU/title.txt b/fastlane/metadata/android/ru-RU/title.txt new file mode 100644 index 0000000..0b0ec87 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/title.txt @@ -0,0 +1 @@ +Message487