Приватные уведомления на локскрине + откат биопривязки (требование владельца: звонки/push при блокировке)
Владелец: входящие звонки и 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 МБ.
This commit is contained in:
@@ -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 затрагивают все сессии — проверить на устройстве вход/выход/перезапуск
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user