From 5eb6efb36b90201178ed35ddf071214d5bcb074e Mon Sep 17 00:00:00 2001 From: b-dev-mobile Date: Tue, 7 Jul 2026 21:22:24 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=B2=D0=B0=D1=82=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=BA=D1=81?= =?UTF-8?q?=D0=BA=D1=80=D0=B8=D0=BD=D0=B5=20+=20=D0=BE=D1=82=D0=BA=D0=B0?= =?UTF-8?q?=D1=82=20=D0=B1=D0=B8=D0=BE=D0=BF=D1=80=D0=B8=D0=B2=D1=8F=D0=B7?= =?UTF-8?q?=D0=BA=D0=B8=20(=D1=82=D1=80=D0=B5=D0=B1=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=BB=D0=B0=D0=B4=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D1=86=D0=B0:=20=D0=B7=D0=B2=D0=BE=D0=BD=D0=BA=D0=B8/push=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Владелец: входящие звонки и push должны работать при заблокированном телефоне. - Биопривязка app password ОТКЛОНЕНА (вариант А): блокировала приём звонка без разблокировки. Откачена из main (сохранена на ветке wip/biometric-binding). Пароль остаётся под Keystore AES-256-GCM (п.3) — работает в фоне. - Приватность локскрина: сообщения и звонки → VISIBILITY_PRIVATE + generic public-версия ('Новое сообщение'/'Входящий звонок'); канал lockscreenVisibility=PRIVATE. Полный контент — после разблокировки. Push приходит plaintext, для показа/звонка пароль не нужен → звонки звонят и пуши приходят при блокировке. Проверено сборкой: assembleRelease BUILD SUCCESSFUL, APK 53 МБ. --- CHANGELOG.md | 1 + .../f7cloud/core/push/F7IncomingCallQueue.kt | 21 +++++++++++++++++-- .../core/push/F7NotificationChannels.kt | 5 ++++- .../core/push/F7PushNotificationHelper.kt | 17 +++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c96648..44dad8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Формат: `ГГГГ-ММ-ДД | версия | изменение | контракты | риск` +- 2026-07-07 | v0.5.114 (122) | Требование владельца: звонки/push ДОЛЖНЫ работать при заблокированном телефоне. (1) Биометрическая привязка app password ОТКЛОНЕНА (вариант А) — она неустранимо блокировала приём звонка без разблокировки; откачена из main, сохранена на ветке wip/biometric-binding. App password остаётся защищён шифрованием в покое (Keystore AES-256-GCM из п.3, работает в фоне → звонки/push/приём при блокировке ОК). (2) Приватность локскрина: уведомления сообщений и звонков → VISIBILITY_PRIVATE + generic public-версия («Новое сообщение»/«Входящий звонок»), канал lockscreenVisibility=PRIVATE. Полный контент (отправитель/текст/имя звонящего) — только после разблокировки. Push у приложения plaintext, пароль для показа/звонка НЕ нужен. Проверено сборкой: assembleRelease 53 МБ | контракты не менялись | низкий: только UX уведомлений; звонки/push в фоне работают как раньше - 2026-07-07 | v0.5.114 (122) | Этап 0 п.4b (GPL): восстановлены затёртые копирайты Nextcloud в vendor/talk-android — точной сверкой с апстримом v23.0.0 (скачан с GitHub). 102 файла: искажённые email реальных разработчиков NC (@f7cloud.com → настоящие), холдер «F7cloud»/«F7cloud and F7cloud contributors» → «Nextcloud GmbH and Nextcloud contributors». Название продукта в заголовках оставлено «F7cloud Talk» (ребренд форка, требование trademark-политики NC). Менялись ТОЛЬКО строки копирайта в комментах, код не тронут (103+/103−). Проверено сборкой: assembleRelease 53 МБ OK | лицензирование/атрибуция (не код) | нет: функционально нейтрально, снимает нарушение GPLv3 §4 (атрибуция) - 2026-07-07 | v0.5.114 (122) | Этап 0 п.4a (GPL): добавлен LICENSE (GPL-3.0-or-later) в корень репо — приложение производно от vendored Nextcloud Talk (GPL), значит весь APK под GPL; раздел «Лицензия» в README (обязательство corresponding source по тегу релиза). Восстановление затёртых копирайтов Nextcloud в vendor (п.4b) — отдельно, сверкой с апстримом v23.0.0 | лицензирование (не код) | нет - 2026-07-07 | v0.5.114 (122) | Этап 0 п.3 (безопасность-минимум): (1) trust-all TLS отключён в release — гейт `if(!BuildConfig.DEBUG)` в UnsafeSsl + обе WebView-точки (OfficeWebViewClient, TalkCallActivity), чекбокс «доверять сертификату» скрыт в release (buildConfig включён в core:network и feature:talk); (2) allowBackup=false (переопределяет vendor true, подтверждено в собранном манифесте); (3) app password шифруется в покое ключом AndroidKeystore AES-256-GCM (KeystoreCrypto) + одноразовая миграция legacy-plaintext при load; (4) ревокация app password при logout (AppPasswordRevoker: DELETE /ocs/v2.php/core/apppassword, best-effort). Биометрическая привязка ключа — отдельная задача (конфликтует с фоновыми push/звонками, нужен кэш в памяти). Проверено сборкой: assembleRelease 53 МБ, подпись v2+v3, allowBackup=false OK | НОВЫЙ вызов OCS: DELETE core/apppassword при logout (стандартный NC endpoint) | средний: миграция хранилища и logout затрагивают все сессии — проверить на устройстве вход/выход/перезапуск diff --git a/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7IncomingCallQueue.kt b/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7IncomingCallQueue.kt index 9779983..df1b34a 100644 --- a/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7IncomingCallQueue.kt +++ b/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7IncomingCallQueue.kt @@ -241,7 +241,8 @@ object F7IncomingCallQueue { .setStyle(callStyle) .setCategory(NotificationCompat.CATEGORY_CALL) .setPriority(NotificationCompat.PRIORITY_MAX) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) + .setPublicVersion(genericCallPublic(context, intents.preview)) .setOngoing(true) .setAutoCancel(false) .setOnlyAlertOnce(true) @@ -282,7 +283,8 @@ object F7IncomingCallQueue { .setStyle(NotificationCompat.BigTextStyle().bigText(body)) .setCategory(NotificationCompat.CATEGORY_CALL) .setPriority(NotificationCompat.PRIORITY_MAX) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) + .setPublicVersion(genericCallPublic(context, intents.preview)) .setOngoing(true) .setAutoCancel(false) .setOnlyAlertOnce(true) @@ -304,6 +306,21 @@ object F7IncomingCallQueue { ?: throw IllegalStateException("NotificationManager unavailable") } + /** + * Обезличенная (public) версия уведомления звонка для локскрина: показывает лишь «Входящий + * звонок» без имени звонящего/комнаты (минимум информации, решение владельца). Полное имя — + * в полноэкранном UI звонка/после разблокировки. Full-screen intent работает независимо от visibility. + */ + private fun genericCallPublic(context: Context, preview: PendingIntent): android.app.Notification = + NotificationCompat.Builder(context, F7NotificationChannels.CALLS) + .setSmallIcon(android.R.drawable.stat_sys_phone_call) + .setContentTitle("F7cloud") + .setContentText("Входящий звонок") + .setCategory(NotificationCompat.CATEGORY_CALL) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setContentIntent(preview) + .build() + private fun resolveJoinUrl(context: Context, call: PendingCall): String? { val raw = call.acceptUrl?.takeIf { it.isNotBlank() } ?: AuthStore(context).load()?.let { session -> diff --git a/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7NotificationChannels.kt b/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7NotificationChannels.kt index ec4f94d..699c574 100644 --- a/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7NotificationChannels.kt +++ b/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7NotificationChannels.kt @@ -48,7 +48,10 @@ object F7NotificationChannels { setSound(sound, soundAttrs) } setShowBadge(true) - lockscreenVisibility = android.app.Notification.VISIBILITY_PUBLIC + // Приватно на локскрине: система покажет public-версию (generic), а не полный + // контент (минимум информации, решение владельца). Public-версия задаётся + // per-notification в F7PushNotificationHelper / F7IncomingCallQueue. + lockscreenVisibility = android.app.Notification.VISIBILITY_PRIVATE }, ) } diff --git a/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7PushNotificationHelper.kt b/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7PushNotificationHelper.kt index b5235f7..08e837c 100644 --- a/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7PushNotificationHelper.kt +++ b/core/push/src/main/java/ru/forbion/f7cloud/core/push/F7PushNotificationHelper.kt @@ -49,6 +49,21 @@ object F7PushNotificationHelper { } val iconRes = context.applicationInfo.icon.takeIf { it != 0 } ?: android.R.drawable.stat_notify_chat + // На локскрине — ОБЕЗЛИЧЕННАЯ версия (минимум информации, решение владельца): отправитель + // и текст не утекают на заблокированном экране. Полный контент — после разблокировки. + val genericText = when (type) { + "chat", "room", "message" -> "Новое сообщение" + "call" -> "Входящий звонок" + else -> "Новое уведомление" + } + val publicVersion = NotificationCompat.Builder(context, channel) + .setSmallIcon(iconRes) + .setContentTitle("F7cloud") + .setContentText(genericText) + .setAutoCancel(true) + .setPriority(priority) + .setContentIntent(pending) + .build() val notification = NotificationCompat.Builder(context, channel) .setSmallIcon(iconRes) .setContentTitle(title) @@ -56,6 +71,8 @@ object F7PushNotificationHelper { .setStyle(NotificationCompat.BigTextStyle().bigText(body)) .setAutoCancel(true) .setPriority(priority) + .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) + .setPublicVersion(publicVersion) .setContentIntent(pending) .build() manager.notify((System.currentTimeMillis() % Int.MAX_VALUE).toInt(), notification)