Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#241 Group call notifications #250

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 21 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
1 change: 1 addition & 0 deletions changelog.d/7761.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ring for group calls
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this file for downstream changes

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this file

2 changes: 2 additions & 0 deletions library/ui-strings/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@
</plurals>
<string name="title_activity_settings">Настройки</string>
<string name="call_notification_answer">Принять</string>
<string name="call_notification_open_app">Открыть приложение</string>
<string name="call_notification_reject">Отклонить</string>
<string name="call_notification_hangup">Завершить звонок</string>
<string name="ok">OK</string>
Expand Down Expand Up @@ -2875,6 +2876,7 @@
<string name="home_empty_no_rooms_message">Универсальное безопасное приложение для переписок с командами, друзьями и организациями. Создайте переписку или присоеденитесь к уже существующей, чтобы начать.</string>
<string name="home_empty_space_no_rooms_message">Пространства — новый способ групировать комнаты и людей. Добавьте существующую комнату или создайте новую, используя кнопку слева снизу.</string>
<string name="labs_enable_voice_broadcast_summary">Возможность записывать и отправлять голосовые трансляции в ленту комнаты.</string>
<string name="labs_enable_jitsi_call_notifications_summary">Включить уведомление для групповых звонков</string>
<string name="labs_enable_session_manager_summary">Получите лучший надзор и контроль над всеми вашими сессиями.</string>
<string name="device_manager_learn_more_sessions_verified_description">Заверенные сеансы есть везде, где вы используете эту учётную запись после ввода своей мнемонической фразы или подтверждения своей личности с помощью другого заверенного сеанса.
\n
Expand Down
2 changes: 2 additions & 0 deletions library/ui-strings/src/main/res/values-ru/strings_sc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,6 @@
<string name="settings_clear_highlight_on_scroll">Очистить выделение при прокрутке</string>
<string name="settings_call_ringtone_use_default_stun_title">Разрешить резервный сервер звонков</string>
<string name="settings_integrations_scalar_warning">⚠️ Эта настройка по умолчанию (если не изменена конфигурацией Вашего домашнего сервера) включает доступ к \"Scalar\", менеджеру интеграций от Element. К сожалению, он является проприетарным, т.е. его исходый код не открытый и не может быть проверен пользователями или разработчиками SchildiChat.</string>
<string name="call_notification_open_app_action">Открыть приложение</string>
<string name="labs_enable_group_call_notifications_summary">Включить уведомление для групповых звонков</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if weblate will be happy for manually added translations. Maybe it works but safer path would probably merging without translation first, then add translations via weblate later. 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed manually added translation for Russian

</resources>
2 changes: 2 additions & 0 deletions library/ui-strings/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@
<string name="done">Done</string>

<string name="call_notification_answer">Accept</string>
<string name="call_notification_open_app">Open App</string>
<string name="call_notification_reject">Decline</string>
<string name="call_notification_hangup">Hang Up</string>

Expand Down Expand Up @@ -3493,6 +3494,7 @@
<string name="labs_enable_client_info_recording_summary">Record the client name, version, and url to recognise sessions more easily in session manager.</string>
<string name="labs_enable_voice_broadcast_title">Enable voice broadcast</string>
<string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline.</string>
<string name="labs_enable_jitsi_call_notifications_summary">Ring for group calls.</string>

<!-- Note to translators: %s will be replaces with selected space name -->
<string name="home_empty_space_no_rooms_title">%s\nis looking a little empty.</string>
Expand Down
3 changes: 3 additions & 0 deletions library/ui-strings/src/main/res/values/strings_sc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@
<!-- SC-TMP, until merged upstream: https://github.com/vector-im/element-android/pull/5781 -->
<string name="settings_call_ringtone_use_default_stun_title">Allow fallback call assist server</string>
<string name="settings_call_ringtone_use_default_stun_summary">Will use %s as assist when your homeserver does not offer one (your IP address will be seen by the stun server during a call

<string name="call_notification_open_app_action">Open App</string>
<string name="labs_enable_group_call_notifications_summary">Ring for group calls</string>
)</string>
SpiritCroc marked this conversation as resolved.
Show resolved Hide resolved

<string name="settings_integrations_scalar_warning">⚠️ This setting by default (unless overridden by your homeserver\'s configuration) enables access to \"scalar\", Element\'s integration manager which is unfortunately proprietary, i.e. its source code is not open and can not be checked by the public or the SchildiChat developers.</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ fun Event.getPollContent(): MessagePollContent? {
return getClearContent().toModel<MessagePollContent>()
}

fun Event.isJitsiEvent() = this.getClearType() == EventType.STATE_ROOM_WIDGET_LEGACY
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks too generic to me, won't that trigger for all kinds of widgets, not just jitsi?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated detecting function to more accurate one


fun Event.supportsNotification() =
this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor(
EventType.MESSAGE,
EventType.REDACTION,
EventType.ENCRYPTED,
EventType.STATE_ROOM_WIDGET_LEGACY,
EventType.STATE_ROOM_MEMBER -> true
else -> false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class DebugVectorFeatures(
override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
?: vectorFeatures.isVoiceBroadcastEnabled()

override fun isJitsiCallNotificationEnabled(): Boolean = read(DebugFeatureKeys.jitsiCallNotificationsEnabled)
?: vectorFeatures.isJitsiCallNotificationEnabled()

override fun isUnverifiedSessionsAlertEnabled(): Boolean = read(DebugFeatureKeys.unverifiedSessionsAlertEnabled)
?: vectorFeatures.isUnverifiedSessionsAlertEnabled()

Expand Down Expand Up @@ -155,4 +158,5 @@ object DebugFeatureKeys {
val reciprocateQrCodeLogin = booleanPreferencesKey("reciprocate-qr-code-login")
val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
val unverifiedSessionsAlertEnabled = booleanPreferencesKey("unverified-sessions-alert-enabled")
val jitsiCallNotificationsEnabled = booleanPreferencesKey("jitsi-call-notifications-enabled")
}
1 change: 1 addition & 0 deletions vector-config/src/main/res/values/config-settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<bool name="settings_labs_rich_text_editor_default">false</bool>
<bool name="settings_labs_enable_voice_broadcast_visible">true</bool>
<bool name="settings_labs_enable_voice_broadcast_default">false</bool>
<bool name="settings_labs_enable_jitsi_call_notifications_default">true</bool>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be false

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabled by default

<!-- Level 1: Advanced settings -->

<!-- Level 1: Help and about -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.session.SessionListener
Expand All @@ -41,6 +42,8 @@ interface SingletonEntryPoint {

fun avatarRenderer(): AvatarRenderer

fun notificationUtils(): NotificationUtils

fun activeSessionHolder(): ActiveSessionHolder

fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog
Expand Down
32 changes: 31 additions & 1 deletion vector/src/main/java/im/vector/app/features/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.threads.ThreadsActivity
import im.vector.app.features.location.live.map.LiveLocationMapViewActivity
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.pin.UnlockedActivity
import im.vector.app.features.pin.lockscreen.crypto.LockScreenKeyRepository
import im.vector.app.features.pin.lockscreen.pincode.PinCodeHelper
Expand Down Expand Up @@ -88,7 +89,9 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
private const val EXTRA_NEXT_INTENT = "EXTRA_NEXT_INTENT"
private const val EXTRA_INIT_SESSION = "EXTRA_INIT_SESSION"
private const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
private const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT"
private const val ACTION_ROOM_DETAILS_JITSI_CALL = "ROOM_DETAILS_JITSI_CALL"

// Special action to clear cache and/or clear credentials
fun restartApp(activity: Activity, args: MainActivityArgs) {
Expand Down Expand Up @@ -119,6 +122,20 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
}
}

fun jitsiCallIntent(
context: Context,
roomId: String,
callId: String,
): Intent {
return Intent(context, MainActivity::class.java).apply {
action = ACTION_ROOM_DETAILS_JITSI_CALL
flags = Intent.FLAG_ACTIVITY_NEW_TASK

putExtra(EXTRA_ROOM_ID, roomId)
putExtra(EXTRA_CALL_ID, callId)
}
}

val allowList = listOf(
HomeActivity::class.java.name,
MainActivity::class.java.name,
Expand All @@ -137,6 +154,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
private lateinit var args: MainActivityArgs

@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var uiStateRepository: UiStateRepository
@Inject lateinit var shortcutsHandler: ShortcutsHandler
@Inject lateinit var pinCodeHelper: PinCodeHelper
Expand Down Expand Up @@ -210,7 +228,19 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
navigator.openRoom(this, roomId, trigger = ViewRoom.Trigger.Shortcut)
}
finish()
} else {
} else if (intent.action == ACTION_ROOM_DETAILS_JITSI_CALL) {
val callId = intent.getStringExtra(EXTRA_CALL_ID).orEmpty()

notificationUtils.cancelNotificationMessage(callId, NotificationDrawerManager.JITSI_CALL_NOTIFICATION_ID)

startSyncing()
val roomId = intent.getStringExtra(EXTRA_ROOM_ID)
if (roomId?.isNotEmpty() == true) {
navigator.openRoom(this, roomId, trigger = ViewRoom.Trigger.Shortcut)
}
finish()
}
else {
SpiritCroc marked this conversation as resolved.
Show resolved Hide resolved
args = parseArgs()
if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) {
clearNotifications()
Expand Down
2 changes: 2 additions & 0 deletions vector/src/main/java/im/vector/app/features/VectorFeatures.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ interface VectorFeatures {
fun isQrCodeLoginForAllServers(): Boolean
fun isReciprocateQrCodeLogin(): Boolean
fun isVoiceBroadcastEnabled(): Boolean
fun isJitsiCallNotificationEnabled(): Boolean
fun isUnverifiedSessionsAlertEnabled(): Boolean
}

Expand All @@ -64,5 +65,6 @@ class DefaultVectorFeatures : VectorFeatures {
override fun isQrCodeLoginForAllServers(): Boolean = false
override fun isReciprocateQrCodeLogin(): Boolean = false
override fun isVoiceBroadcastEnabled(): Boolean = true
override fun isJitsiCallNotificationEnabled(): Boolean = true
override fun isUnverifiedSessionsAlertEnabled(): Boolean = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.services.CallAndroidService
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.notifications.NotificationDrawerManager
import timber.log.Timber

class CallHeadsUpActionReceiver : BroadcastReceiver() {
Expand All @@ -33,9 +35,11 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent?) {
val webRtcCallManager = context.singletonEntryPoint().webRtcCallManager()
val notificationUtils = context.singletonEntryPoint().notificationUtils()
when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) {
CALL_ACTION_REJECT -> {
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return
notificationUtils.cancelNotificationMessage(callId, NotificationDrawerManager.JITSI_CALL_NOTIFICATION_ID)
onCallRejectClicked(webRtcCallManager, callId)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class NotifiableEventProcessor @Inject constructor(
.also { Timber.d("notification message removed due to being read") }
else -> KEEP
}
is NotifiableJitsiEvent -> KEEP
is SimpleNotifiableEvent -> when (it.type) {
EventType.REDACTION -> REMOVE
else -> KEEP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId
import org.matrix.android.sdk.api.session.events.model.isEdition
import org.matrix.android.sdk.api.session.events.model.isImageMessage
import org.matrix.android.sdk.api.session.events.model.isJitsiEvent
import org.matrix.android.sdk.api.session.events.model.supportsNotification
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom
Expand Down Expand Up @@ -80,6 +81,9 @@ class NotifiableEventResolver @Inject constructor(
event.supportsNotification() || event.type == EventType.ENCRYPTED -> {
resolveMessageEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy)
}
event.isJitsiEvent() -> {
resolveJitsiEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy)
}
else -> {
// If the event can be displayed, display it as is
Timber.w("NotifiableEventResolver Received an unsupported event matching a bing rule")
Expand Down Expand Up @@ -133,6 +137,50 @@ class NotifiableEventResolver @Inject constructor(
}
}

private fun resolveJitsiEvent(event: TimelineEvent, session: Session, canBeReplaced: Boolean, isNoisy: Boolean): NotifiableJitsiEvent?{
// The event only contains an eventId, and roomId (type is m.room.*) , we need to get the displayable content (names, avatar, text, etc...)
val room = session.getRoom(event.root.roomId!! /*roomID cannot be null*/)

return if (room != null) {
val body = displayableEventFormatter.format(event, isDm = room.roomSummary()?.isDirect.orFalse(), appendAuthor = false).toString()
val roomName = room.roomSummary()?.displayName.orEmpty()
val senderDisplayName = event.senderInfo.disambiguatedDisplayName

NotifiableJitsiEvent(
eventId = event.root.eventId.orEmpty(),
editedEventId = event.getEditedEventId(),
canBeReplaced = canBeReplaced,
timestamp = event.root.originServerTs ?: 0,
noisy = isNoisy,
senderName = senderDisplayName,
senderId = event.root.senderId,
body = body,
roomId = event.root.roomId!!,
threadId = event.root.getRootThreadEventId(),
roomName = roomName,
roomIsDirect = room.roomSummary()?.isDirect ?: false,
roomAvatarPath = session.contentUrlResolver()
.resolveThumbnail(
room.roomSummary()?.avatarUrl,
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE
),
senderAvatarPath = session.contentUrlResolver()
.resolveThumbnail(
event.senderInfo.avatarUrl,
250,
250,
ContentUrlResolver.ThumbnailMethod.SCALE
),
matrixID = session.myUserId,
soundName = null
)
} else {
null
}
}

fun TimelineEvent.getCaption(): String? = (getLastMessageContent() as? MessageWithAttachmentContent)?.getCaption()
fun TimelineEvent.getFilename(): String? = (getLastMessageContent() as? MessageWithAttachmentContent)?.getFileName()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.notifications

import org.matrix.android.sdk.api.session.events.model.EventType

data class NotifiableJitsiEvent(
override val eventId: String,
override val editedEventId: String?,
override val canBeReplaced: Boolean,
val noisy: Boolean,
val timestamp: Long,
val senderName: String?,
val senderId: String?,
val body: String?,
val roomId: String,
val threadId: String?,
val roomName: String?,
val roomIsDirect: Boolean = false,
val roomAvatarPath: String? = null,
val senderAvatarPath: String? = null,
val matrixID: String? = null,
val soundName: String? = null,
// This is used for >N notification, as the result of a smart reply
val outGoingMessage: Boolean = false,
val outGoingMessageFailed: Boolean = false,
override val isRedacted: Boolean = false,
override val isUpdated: Boolean = false
) : NotifiableEvent {

val type: String = EventType.MESSAGE
val description: String = body ?: ""
val title: String = senderName ?: ""
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,5 +240,6 @@ class NotificationDrawerManager @Inject constructor(
const val ROOM_MESSAGES_NOTIFICATION_ID = 1
const val ROOM_EVENT_NOTIFICATION_ID = 2
const val ROOM_INVITATION_NOTIFICATION_ID = 3
const val JITSI_CALL_NOTIFICATION_ID = 4
}
}
Loading