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

Fixes #241 #259

Open
wants to merge 27 commits into
base: sc
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9888273
#7761 Added ring when starting jitsi call
Jul 13, 2024
42fbd15
#7761 Added ring notification for jitsi calls, and handled decline event
Aug 18, 2024
596ca51
#7761 Added displaying notification from background
Aug 27, 2024
74a0b18
#7761 Fixed case when event info is absent
Sep 1, 2024
14dfbdb
#7761 Added feature flag
Sep 1, 2024
4f9cc81
Merge D:\AndroidStudioProjects\element-android into develop
Sep 1, 2024
e4a98e6
#7761 Removed unnecessary code
Sep 1, 2024
9743c72
#7761 Removed unnecessary code
Sep 2, 2024
aa58ede
#7761 Added changelog file
Sep 2, 2024
1b88471
Added ring for group calls
Sep 4, 2024
259087a
Updated strings_sc, fixed resources
Sep 9, 2024
aa38093
Fixed cancel notification
Sep 10, 2024
00668f8
Removed redundant string resources
Sep 11, 2024
b348531
Merge branch 'SchildiChat:sc' into sc
Turubaev Sep 25, 2024
be9fa3b
Fixed code style
Sep 25, 2024
9fa6013
Removed russian translation. Fixed gradle. Deleted unnecessary file
Sep 29, 2024
b63af09
Disabled settings_labs_enable_jitsi_call_notifications_default by def…
Oct 5, 2024
b00181f
Added formatting name and type to lowercase
Oct 7, 2024
d5566d1
Merge remote-tracking branch 'refs/remotes/upstream/sc' into sc
GlebMikhailov Dec 20, 2024
f0ff573
Fix: Code review comments -divide the utils class into smaller classe…
GlebMikhailov Dec 21, 2024
6532c85
Chore: Add argument for JVM `-XX:MaxPermSize=2048m`
GlebMikhailov Dec 21, 2024
a5e08fa
Chore: Resolve MR code-review.
GlebMikhailov Dec 23, 2024
c907b8a
Merge pull request #1 from NoCloud-today/chore/resolve-notifications-mr
GlebMikhailov Dec 23, 2024
2895c31
Chore: Fix FakeNotificationFactory
GlebMikhailov Dec 24, 2024
e274ce1
Merge remote-tracking branch 'refs/remotes/upstream/sc' into chore/re…
GlebMikhailov Dec 24, 2024
1cdfd3d
Chore: Remove jitsiNotificationsUtils from the SingletonEntryPoint
GlebMikhailov Dec 24, 2024
bc5415b
Merge pull request #2 from NoCloud-today/chore/resolve-notifications-mr
GlebMikhailov Dec 24, 2024
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
2 changes: 1 addition & 1 deletion 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,4 @@
<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>
</resources>
</resources>
2 changes: 2 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 @@ -239,6 +239,8 @@
<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>

<string name="labs_enable_group_call_notifications_summary">Ring for group calls</string>
<string name="call_notification_open_app_action">Open App</string>
<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>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
import org.matrix.android.sdk.api.session.events.model.EventType.IS_JITSI_CALL
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.room.model.JitsiEventContent
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
Expand Down Expand Up @@ -498,6 +500,9 @@ fun Event.getPollContent(): MessagePollContent? {
return getClearContent().toModel<MessagePollContent>()
}

fun Event.isJitsiEvent() = content?.toModel<JitsiEventContent>()?.type?.lowercase() == IS_JITSI_CALL &&
content.toModel<JitsiEventContent>()?.name?.lowercase() == IS_JITSI_CALL

fun Event.supportsNotification() =
this.getClearType() in EventType.MESSAGE +
EventType.POLL_START.values +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ object EventType {
// Relation Events
const val REACTION = "m.reaction"

// Jitsi call
const val IS_JITSI_CALL = "jitsi"

// Poll
val POLL_START = StableUnstableId(stable = "m.poll.start", unstable = "org.matrix.msc3381.poll.start")
val POLL_RESPONSE = StableUnstableId(stable = "m.poll.response", unstable = "org.matrix.msc3381.poll.response")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.api.session.room.model

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

/**
* Class representing the Jitsi call state event.
*/
@JsonClass(generateAdapter = true)
data class JitsiEventContent(
@Json(name = "type") val type: String? = null,
@Json(name = "name") val name: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,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 @@ -70,6 +70,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 @@ -134,4 +137,5 @@ object DebugFeatureKeys {
val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled")
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">false</bool>
<!-- Level 1: Advanced settings -->

<!-- Level 1: Help and about -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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 @@ -32,6 +33,8 @@ interface SingletonEntryPoint {

fun avatarRenderer(): AvatarRenderer

fun notificationUtils(): NotificationUtils

fun activeSessionHolder(): ActiveSessionHolder

fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog
Expand Down
29 changes: 29 additions & 0 deletions vector/src/main/java/im/vector/app/features/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,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 @@ -79,7 +80,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 @@ -110,6 +113,20 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
}
}

fun getCallIntent(
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 @@ -128,6 +145,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 @@ -195,6 +213,17 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
setResult(RESULT_OK)
finish()
} else if (intent.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
startSyncing()
val roomId = intent.getStringExtra(EXTRA_ROOM_ID)
if (roomId?.isNotEmpty() == true) {
navigator.openRoom(this, roomId, trigger = ViewRoom.Trigger.Shortcut)
}
finish()
} 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) {
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 @@ -32,6 +32,7 @@ interface VectorFeatures {
*/
fun isNewAppLayoutFeatureEnabled(): Boolean
fun isVoiceBroadcastEnabled(): Boolean
fun isJitsiCallNotificationEnabled(): Boolean
fun isUnverifiedSessionsAlertEnabled(): Boolean
}

Expand All @@ -49,5 +50,6 @@ class DefaultVectorFeatures : VectorFeatures {
override fun forceUsageOfOpusEncoder(): Boolean = false
override fun isNewAppLayoutFeatureEnabled(): Boolean = true
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 @@ -11,7 +11,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 @@ -24,9 +26,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
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2024 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 android.app.Notification
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat
import im.vector.app.R
import im.vector.app.core.platform.PendingIntentCompat
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.MainActivity
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.notifications.NotificationUtils.Companion.CALL_NOTIFICATION_CHANNEL_ID
import im.vector.app.features.notifications.NotificationUtils.Companion.SILENT_NOTIFICATION_CHANNEL_ID
import im.vector.app.features.themes.ThemeUtils
import im.vector.lib.core.utils.timer.Clock
import im.vector.lib.strings.CommonStrings
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class JitsiNotificationsUtils @Inject constructor(
private val context: Context,
private val stringProvider: StringProvider,
private val notificationUtils: NotificationUtils,
private val clock: Clock,
) {
/**
* Build an incoming jitsi call notification.
* This notification starts the VectorHomeActivity which is in charge of centralizing the incoming call flow.
*
* @param callId id of the jitsi call
* @param signalingRoomId id of the room
* @param title title of the notification
* @param fromBg true if the app is in background when posting the notification
* @return the call notification.
*/
fun buildIncomingJitsiCallNotification(
callId: String,
signalingRoomId: String,
title: String,
fromBg: Boolean,
): Notification {
val accentColor = ContextCompat.getColor(context, im.vector.lib.ui.styles.R.color.notification_accent_color)
val notificationChannel = if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID
val builder = NotificationCompat.Builder(context, notificationChannel)
.setContentTitle(notificationUtils.ensureTitleNotEmpty(title))
.apply {
setContentText(stringProvider.getString(CommonStrings.incoming_video_call))
setSmallIcon(R.drawable.ic_call_answer_video)
}
.setCategory(NotificationCompat.CATEGORY_CALL)
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
.setLights(accentColor, 500, 500)
.setOngoing(true)

val contentIntent = MainActivity.getCallIntent(
context = context,
roomId = signalingRoomId,
callId = callId,
)

val contentPendingIntent = PendingIntent.getActivity(
context,
clock.epochMillis().toInt(),
contentIntent,
PendingIntentCompat.FLAG_IMMUTABLE
)

val answerCallPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context, firstStartMainActivity = false))
.addNextIntent(contentIntent)
.getPendingIntent(clock.epochMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)

val rejectCallPendingIntent = notificationUtils.buildRejectCallPendingIntent(callId)

builder.addAction(
NotificationCompat.Action(
IconCompat.createWithResource(context, R.drawable.ic_call_hangup)
.setTint(ThemeUtils.getColor(context, android.R.attr.colorError)),
notificationUtils.getActionText(CommonStrings.call_notification_reject, android.R.attr.colorError),
rejectCallPendingIntent
)
)

builder.addAction(
NotificationCompat.Action(
R.drawable.ic_call_answer,
notificationUtils.getActionText(CommonStrings.call_notification_open_app_action, android.R.attr.colorPrimary),
answerCallPendingIntent
)
)
if (fromBg) {
// Compat: Display the incoming call notification on the lock screen
builder.priority = NotificationCompat.PRIORITY_HIGH
builder.setFullScreenIntent(contentPendingIntent, true)
}
return builder.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,29 @@ class NotifiableEventProcessor @Inject constructor(
.also { Timber.d("notification message removed due to being read") }
else -> KEEP
}
is NotifiableJitsiEvent -> {
if (it.isReceived != true) {
KEEP
} else {
REMOVE
}
}
is SimpleNotifiableEvent -> when (it.type) {
EventType.REDACTION -> REMOVE
else -> KEEP
}
}
ProcessedEvent(type, it)

val updatedEvent = if (it is NotifiableJitsiEvent) it.updateReceivedStatus() else it
ProcessedEvent(type, updatedEvent)
}

val removedEventsDiff = renderedEvents.filter { renderedEvent ->
queuedEvents.none { it.eventId == renderedEvent.event.eventId }
}.map { ProcessedEvent(REMOVE, it.event) }
}.map {
val updatedEvent = if (it.event is NotifiableJitsiEvent) it.event.updateReceivedStatus() else it.event
ProcessedEvent(REMOVE, updatedEvent)
}

return removedEventsDiff + processedEvents
}
Expand Down
Loading