First Commit

This commit is contained in:
2025-12-18 16:28:50 +07:00
commit 8c3e4f491f
9974 changed files with 396488 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
id("io.element.android-compose-library")
}
android {
namespace = "io.element.android.libraries.push.api"
}
dependencies {
implementation(libs.androidx.corektx)
implementation(libs.coroutines.core)
implementation(libs.coil.compose)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrixmedia.api)
implementation(projects.libraries.pushproviders.api)
}
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api
import io.element.android.libraries.matrix.api.core.SessionId
interface GetCurrentPushProvider {
suspend fun getCurrentPushProvider(sessionId: SessionId): String?
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.push.api.history.PushHistoryItem
import io.element.android.libraries.pushproviders.api.Distributor
import io.element.android.libraries.pushproviders.api.PushProvider
import kotlinx.coroutines.flow.Flow
interface PushService {
/**
* Return the current push provider, or null if none.
*/
suspend fun getCurrentPushProvider(sessionId: SessionId): PushProvider?
/**
* Return the list of push providers, available at compile time, sorted by index.
*/
fun getAvailablePushProviders(): List<PushProvider>
/**
* Will unregister any previous pusher and register a new one with the provided [PushProvider].
*
* The method has effect only if the [PushProvider] is different than the current one.
*/
suspend fun registerWith(
matrixClient: MatrixClient,
pushProvider: PushProvider,
distributor: Distributor,
): Result<Unit>
/**
* Ensure that the pusher with the current push provider and distributor is registered.
* If there is no current config, the default push provider with the default distributor will be used.
* Error can be [PusherRegistrationFailure].
*/
suspend fun ensurePusherIsRegistered(
matrixClient: MatrixClient,
): Result<Unit>
/**
* Store the given push provider as the current one, but do not register.
* To be used when there is no distributor available.
*/
suspend fun selectPushProvider(
sessionId: SessionId,
pushProvider: PushProvider,
)
fun ignoreRegistrationError(sessionId: SessionId): Flow<Boolean>
suspend fun setIgnoreRegistrationError(sessionId: SessionId, ignore: Boolean)
/**
* Return false in case of early error.
*/
suspend fun testPush(sessionId: SessionId): Boolean
/**
* Get a flow of total number of received Push.
*/
val pushCounter: Flow<Int>
/**
* Get a flow of list of [PushHistoryItem].
*/
fun getPushHistoryItemsFlow(): Flow<List<PushHistoryItem>>
/**
* Reset the push history, including the push counter.
*/
suspend fun resetPushHistory()
/**
* Reset the battery optimization state.
*/
suspend fun resetBatteryOptimizationState()
/**
* Notify the user that the service is un-registered.
*/
suspend fun onServiceUnregistered(userId: UserId)
}
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api
import io.element.android.libraries.matrix.api.exception.ClientException
sealed class PusherRegistrationFailure : Exception() {
class AccountNotVerified : PusherRegistrationFailure()
class NoProvidersAvailable : PusherRegistrationFailure()
class NoDistributorsAvailable : PusherRegistrationFailure()
/**
* @param clientException the failure that occurred.
* @param isRegisteringAgain true if the server should already have a the same pusher registered.
*/
class RegistrationFailure(
val clientException: ClientException,
val isRegisteringAgain: Boolean,
) : PusherRegistrationFailure()
}
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.battery
sealed interface BatteryOptimizationEvents {
data object Dismiss : BatteryOptimizationEvents
data object RequestDisableOptimizations : BatteryOptimizationEvents
}
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.battery
data class BatteryOptimizationState(
val shouldDisplayBanner: Boolean,
val eventSink: (BatteryOptimizationEvents) -> Unit,
)
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.battery
fun aBatteryOptimizationState(
shouldDisplayBanner: Boolean = false,
eventSink: (BatteryOptimizationEvents) -> Unit = {},
) = BatteryOptimizationState(
shouldDisplayBanner = shouldDisplayBanner,
eventSink = eventSink,
)
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.gateway
sealed class PushGatewayFailure : Exception() {
class PusherRejected : PushGatewayFailure()
}
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.history
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
/**
* Data class representing a push history item.
* @property pushDate Date (timestamp).
* @property formattedDate Formatted date.
* @property providerInfo Push provider name / info
* @property eventId EventId from the push, can be null if the received data are not correct.
* @property roomId RoomId from the push, can be null if the received data are not correct.
* @property sessionId The session Id, can be null if the session cannot be retrieved
* @property hasBeenResolved Result of resolving the event
* @property comment Comment. Can contains an error message if the event could not be resolved, or other any information.
*/
data class PushHistoryItem(
val pushDate: Long,
val formattedDate: String,
val providerInfo: String,
val eventId: EventId?,
val roomId: RoomId?,
val sessionId: SessionId?,
val hasBeenResolved: Boolean,
val comment: String?,
)
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.notifications
import android.graphics.Bitmap
import androidx.core.graphics.drawable.IconCompat
import coil3.ImageLoader
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.ui.media.AVATAR_THUMBNAIL_SIZE_IN_PIXEL
interface NotificationBitmapLoader {
/**
* Get icon of a room.
* @param avatarData the data related to the Avatar
* @param imageLoader Coil image loader
* @param targetSize The size we want the bitmap to be resized to
*/
suspend fun getRoomBitmap(
avatarData: AvatarData,
imageLoader: ImageLoader,
targetSize: Long = AVATAR_THUMBNAIL_SIZE_IN_PIXEL,
): Bitmap?
/**
* Get icon of a user.
* Before Android P, this does nothing because the icon won't be used
* @param avatarData the data related to the Avatar
* @param imageLoader Coil image loader
*/
suspend fun getUserIcon(
avatarData: AvatarData,
imageLoader: ImageLoader,
): IconCompat?
}
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.notifications
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
interface NotificationCleaner {
fun clearAllMessagesEvents(sessionId: SessionId)
fun clearMessagesForRoom(sessionId: SessionId, roomId: RoomId)
fun clearMessagesForThread(sessionId: SessionId, roomId: RoomId, threadId: ThreadId)
fun clearEvent(sessionId: SessionId, eventId: EventId)
fun clearMembershipNotificationForSession(sessionId: SessionId)
fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId)
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.notifications
import io.element.android.libraries.matrix.api.core.SessionId
import kotlin.math.abs
object NotificationIdProvider {
fun getSummaryNotificationId(sessionId: SessionId): Int {
return getOffset(sessionId) + SUMMARY_NOTIFICATION_ID
}
fun getRoomMessagesNotificationId(sessionId: SessionId): Int {
return getOffset(sessionId) + ROOM_MESSAGES_NOTIFICATION_ID
}
fun getRoomEventNotificationId(sessionId: SessionId): Int {
return getOffset(sessionId) + ROOM_EVENT_NOTIFICATION_ID
}
fun getRoomInvitationNotificationId(sessionId: SessionId): Int {
return getOffset(sessionId) + ROOM_INVITATION_NOTIFICATION_ID
}
fun getFallbackNotificationId(sessionId: SessionId): Int {
return getOffset(sessionId) + FALLBACK_NOTIFICATION_ID
}
fun getForegroundServiceNotificationId(type: ForegroundServiceType): Int {
return type.ordinal * 10 + FOREGROUND_SERVICE_NOTIFICATION_ID
}
private fun getOffset(sessionId: SessionId): Int {
// Compute a int from a string with a low risk of collision.
return abs(sessionId.value.hashCode() % 100_000) * 10
}
private const val FALLBACK_NOTIFICATION_ID = -1
private const val SUMMARY_NOTIFICATION_ID = 0
private const val ROOM_MESSAGES_NOTIFICATION_ID = 1
private const val ROOM_EVENT_NOTIFICATION_ID = 2
private const val ROOM_INVITATION_NOTIFICATION_ID = 3
private const val FOREGROUND_SERVICE_NOTIFICATION_ID = 4
}
enum class ForegroundServiceType {
INCOMING_CALL,
ONGOING_CALL,
}
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.notifications
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
/**
* Handles missed calls by creating a new notification.
*/
interface OnMissedCallNotificationHandler {
/**
* Adds a missed call notification.
*/
suspend fun addMissedCallNotification(
sessionId: SessionId,
roomId: RoomId,
eventId: EventId,
)
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.notifications.conversations
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
/**
* Service to handle conversation-related notifications.
*/
interface NotificationConversationService {
/**
* Called when a new message is received in a room.
* It should create a new conversation shortcut for this room.
*/
suspend fun onSendMessage(
sessionId: SessionId,
roomId: RoomId,
roomName: String,
roomIsDirect: Boolean,
roomAvatarUrl: String?,
)
/**
* Called when a room is left.
* It should remove the conversation shortcut for this room.
*/
suspend fun onLeftRoom(sessionId: SessionId, roomId: RoomId)
/**
* Called when the list of available rooms changes.
* It should update the conversation shortcuts accordingly, removing shortcuts for no longer joined rooms.
*/
suspend fun onAvailableRoomsChanged(sessionId: SessionId, roomIds: Set<RoomId>)
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.push
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
data class NotificationEventRequest(
val sessionId: SessionId,
val roomId: RoomId,
val eventId: EventId,
val providerInfo: String,
)
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.push.api.push
fun interface SyncOnNotifiableEvent {
suspend operator fun invoke(requests: List<NotificationEventRequest>)
}