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
@@ -0,0 +1,18 @@
/*
* 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.sessionstorage.api"
}
dependencies {
implementation(libs.coroutines.core)
}
@@ -0,0 +1,20 @@
/*
* 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.sessionstorage.api
import androidx.compose.runtime.Immutable
@Immutable
sealed interface LoggedInState {
data object NotLoggedIn : LoggedInState
data class LoggedIn(
val sessionId: String,
val isTokenValid: Boolean,
) : LoggedInState
}
@@ -0,0 +1,34 @@
/*
* 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.sessionstorage.api
// Imported from Element Android, to be able to migrate from EA to EXA.
enum class LoginType {
PASSWORD,
OIDC,
SSO,
UNSUPPORTED,
CUSTOM,
DIRECT,
UNKNOWN,
QR;
companion object {
fun fromName(name: String) = when (name) {
PASSWORD.name -> PASSWORD
OIDC.name -> OIDC
SSO.name -> SSO
UNSUPPORTED.name -> UNSUPPORTED
CUSTOM.name -> CUSTOM
DIRECT.name -> DIRECT
QR.name -> QR
else -> UNKNOWN
}
}
}
@@ -0,0 +1,49 @@
/*
* 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.sessionstorage.api
import java.util.Date
/**
* Data class representing the session data to store locally.
*/
data class SessionData(
/** The user ID of the logged in user. */
val userId: String,
/** The device ID of the session. */
val deviceId: String,
/** The current access token of the session. */
val accessToken: String,
/** The optional current refresh token of the session. */
val refreshToken: String?,
/** The homeserver URL of the session. */
val homeserverUrl: String,
/** The Open ID Connect info for this session, if any. */
val oidcData: String?,
/** The timestamp of the last login. May be `null` in very old sessions. */
val loginTimestamp: Date?,
/** Whether the [accessToken] is valid or not. */
val isTokenValid: Boolean,
/** The login type used to authenticate the session. */
val loginType: LoginType,
/** The optional passphrase used to encrypt data in the SDK local store. */
val passphrase: String?,
/** The paths to the session data stored in the filesystem. */
val sessionPath: String,
/** The path to the cache data stored for the session in the filesystem. */
val cachePath: String,
/** The position, to be able to order account. */
val position: Long,
/** The index of the last date of session usage. */
val lastUsageIndex: Long,
/** The optional display name of the user. */
val userDisplayName: String?,
/** The optional avatar URL of the user. */
val userAvatarUrl: String?,
)
@@ -0,0 +1,93 @@
/*
* 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.sessionstorage.api
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
interface SessionStore {
/**
* A flow emitting the current logged in state.
* If there is at least one session, the state is [LoggedInState.LoggedIn] with the latest used session.
* If there is no session, the state is [LoggedInState.NotLoggedIn].
*/
fun loggedInStateFlow(): Flow<LoggedInState>
/**
* Return a flow of all sessions ordered by last usage descending.
*/
fun sessionsFlow(): Flow<List<SessionData>>
/**
* Add a new session. If other sessions exist, the new one will be set as the latest used one, and
* the added session position will be set to a value higher than the other session positions.
*/
suspend fun addSession(sessionData: SessionData)
/**
* Will update the session data matching the userId, except the value of loginTimestamp.
* No op if userId is not found in DB.
*/
suspend fun updateData(sessionData: SessionData)
/**
* Update the user profile info of the session matching the userId.
*/
suspend fun updateUserProfile(sessionId: String, displayName: String?, avatarUrl: String?)
/**
* Get the session data matching the userId, or null if not found.
*/
suspend fun getSession(sessionId: String): SessionData?
/**
* Get all sessions ordered by last usage descending.
*/
suspend fun getAllSessions(): List<SessionData>
/**
* Get the number of sessions.
*/
suspend fun numberOfSessions(): Int
/**
* Get the latest session, or null if no session exists.
*/
suspend fun getLatestSession(): SessionData?
/**
* Set the session with [sessionId] as the latest used one.
*/
suspend fun setLatestSession(sessionId: String)
/**
* Remove the session matching the sessionId.
*/
suspend fun removeSession(sessionId: String)
}
fun List<SessionData>.toUserList(): List<String> {
return map { it.userId }
}
fun Flow<List<SessionData>>.toUserListFlow(): Flow<List<String>> {
return map { it.toUserList() }
}
/**
* @return a flow emitting the sessionId of the latest session if logged in, null otherwise.
*/
fun SessionStore.sessionIdFlow(): Flow<String?> {
return loggedInStateFlow().map {
when (it) {
is LoggedInState.LoggedIn -> it.sessionId
is LoggedInState.NotLoggedIn -> null
}
}
}
@@ -0,0 +1,14 @@
/*
* 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.sessionstorage.api.observer
interface SessionListener {
suspend fun onSessionCreated(userId: String) {}
suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {}
}
@@ -0,0 +1,14 @@
/*
* 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.sessionstorage.api.observer
interface SessionObserver {
fun addListener(listener: SessionListener)
fun removeListener(listener: SessionListener)
}