diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinDigit.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinDigit.kt index aa3c45e02e..f3af07294a 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinDigit.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/model/PinDigit.kt @@ -16,6 +16,9 @@ package io.element.android.features.lockscreen.impl.pin.model +import androidx.compose.runtime.Immutable + +@Immutable sealed interface PinDigit { data object Empty : PinDigit data class Filled(val value: Char) : PinDigit diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/ExpandableBottomSheetScaffold.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/ExpandableBottomSheetScaffold.kt index 215559b334..5323531f97 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/ExpandableBottomSheetScaffold.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/ExpandableBottomSheetScaffold.kt @@ -170,9 +170,8 @@ private fun CustomSheetState.getIntOffset(): Int? = try { null } -private sealed class Slot { - data class SheetContent(val key: Int?) : Slot() - data object DragHandle : Slot() - data object Scaffold : Slot() +private sealed interface Slot { + data class SheetContent(val key: Int?) : Slot + data object DragHandle : Slot + data object Scaffold : Slot } - diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index b66decd81e..c4b631e4df 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -20,6 +20,7 @@ import android.Manifest import android.annotation.SuppressLint import android.net.Uri import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue @@ -409,6 +410,7 @@ class MessageComposerPresenter @Inject constructor( } } +@Immutable sealed interface RoomMemberSuggestion { data object Room : RoomMemberSuggestion data class Member(val roomMember: RoomMember) : RoomMemberSuggestion diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 85a9dd0c24..357c9b3c20 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -103,11 +104,12 @@ fun MessagesReactionButton( } } -sealed class MessagesReactionsButtonContent { - data class Text(val text: String) : MessagesReactionsButtonContent() - data class Icon(@DrawableRes val resourceId: Int) : MessagesReactionsButtonContent() +@Immutable +sealed interface MessagesReactionsButtonContent { + data class Text(val text: String) : MessagesReactionsButtonContent + data class Icon(@DrawableRes val resourceId: Int) : MessagesReactionsButtonContent - data class Reaction(val reaction: AggregatedReaction) : MessagesReactionsButtonContent() + data class Reaction(val reaction: AggregatedReaction) : MessagesReactionsButtonContent val isHighlighted get() = this is Reaction && reaction.isHighlighted } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateContent.kt index b136a602b2..7f9bc7973a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateContent.kt @@ -16,6 +16,9 @@ package io.element.android.features.messages.impl.timeline.model.event +import androidx.compose.runtime.Immutable + +@Immutable sealed interface TimelineItemStateContent : TimelineItemEventContent { val body: String } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt index 10fca53261..5d5f121b75 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemTextBasedContent.kt @@ -16,8 +16,10 @@ package io.element.android.features.messages.impl.timeline.model.event +import androidx.compose.runtime.Immutable import org.jsoup.nodes.Document +@Immutable sealed interface TimelineItemTextBasedContent : TimelineItemEventContent { val body: String val htmlDocument: Document? diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/throttler/FirstThrottler.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/throttler/FirstThrottler.kt index f537ddcd4b..dc1de094d7 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/throttler/FirstThrottler.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/throttler/FirstThrottler.kt @@ -24,9 +24,9 @@ import android.os.SystemClock class FirstThrottler(private val minimumInterval: Long = 800) { private var lastDate = 0L - sealed class CanHandleResult { - data object Yes : CanHandleResult() - data class No(val shouldWaitMillis: Long) : CanHandleResult() + sealed interface CanHandleResult { + data object Yes : CanHandleResult + data class No(val shouldWaitMillis: Long) : CanHandleResult fun waitMillis(): Long { return when (this) { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt index 24f3989f66..3da88c49a4 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt @@ -18,14 +18,16 @@ package io.element.android.libraries.designsystem.components.button import androidx.compose.material3.Icon import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.TextButton /** - * A sealed class that represents the different visual styles that a button can have. + * A sealed interface that represents the different visual styles that a button can have. */ +@Immutable sealed interface ButtonVisuals { val action: () -> Unit diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt index cac8b557ee..a229aba378 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.designsystem.components.list import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.DpSize @@ -34,6 +35,7 @@ import io.element.android.libraries.designsystem.theme.components.Text as TextCo /** * This is a helper to set default leading and trailing content for [ListItem]s. */ +@Immutable sealed interface ListItemContent { /** * Default Switch content for [ListItem]. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt index 5ce528c814..30734f3f12 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt @@ -37,6 +37,7 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -238,6 +239,7 @@ private fun ButtonInternal( } } +@Immutable sealed interface IconSource { val contentDescription: String? diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt index 528aa3cfec..0b0584da98 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListItem.kt @@ -23,6 +23,7 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.Immutable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -134,6 +135,7 @@ fun ListItem( /** * The style to use for a [ListItem]. */ +@Immutable sealed interface ListItemStyle { data object Default : ListItemStyle data object Primary : ListItemStyle diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt index f76562f0cd..d71073b604 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSupportingText.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.ExperimentalTextApi @@ -82,6 +83,7 @@ fun ListSupportingText( object ListSupportingTextDefaults { /** Specifies the padding to use for the supporting text. */ + @Immutable sealed interface Padding { /** No padding. */ data object None : Padding diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt index 65d156fbbc..19b73bf46a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/SearchBar.kt @@ -30,6 +30,7 @@ import androidx.compose.material3.SearchBarColors import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -180,6 +181,7 @@ object ElementSearchBarDefaults { ) } +@Immutable sealed interface SearchBarResultState { /** No search results are available yet (e.g. because the user hasn't entered a search term). */ class NotSearching : SearchBarResultState diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts index b52f201458..fd59941ac3 100644 --- a/libraries/matrix/api/build.gradle.kts +++ b/libraries/matrix/api/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") id("kotlin-parcelize") alias(libs.plugins.anvil) kotlin("plugin.serialization") version "1.9.10" diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt index 0a70055e4f..72caa711cb 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkData.kt @@ -22,14 +22,14 @@ import android.net.Uri * This sealed class represents all the permalink cases. * You don't have to instantiate yourself but should use [PermalinkParser] instead. */ -sealed class PermalinkData { +sealed interface PermalinkData { data class RoomLink( val roomIdOrAlias: String, val isRoomAlias: Boolean, val eventId: String?, val viaParameters: List - ) : PermalinkData() + ) : PermalinkData /* * &room_name=Team2 @@ -47,9 +47,9 @@ sealed class PermalinkData { val token: String, val privateKey: String, val roomType: String? - ) : PermalinkData() + ) : PermalinkData - data class UserLink(val userId: String) : PermalinkData() + data class UserLink(val userId: String) : PermalinkData - data class FallbackLink(val uri: Uri, val isLegacyGroupLink: Boolean = false) : PermalinkData() + data class FallbackLink(val uri: Uri, val isLegacyGroupLink: Boolean = false) : PermalinkData } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt index 38ce7a03d3..5597aaf1c5 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomMembersState.kt @@ -16,6 +16,9 @@ package io.element.android.libraries.matrix.api.room +import androidx.compose.runtime.Immutable + +@Immutable sealed interface MatrixRoomMembersState { data object Unknown : MatrixRoomMembersState data class Pending(val prevRoomMembers: List? = null) : MatrixRoomMembersState diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt index c3dd6330b5..3d429766b0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt @@ -28,9 +28,9 @@ import kotlin.time.Duration * Can be retrieved from [RoomListService] methods. */ interface RoomList { - sealed class LoadingState { - data object NotLoaded : LoadingState() - data class Loaded(val numberOfRooms: Int) : LoadingState() + sealed interface LoadingState { + data object NotLoaded : LoadingState + data class Loaded(val numberOfRooms: Int) : LoadingState } /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt index 8b85b3fe38..3bd445a282 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt @@ -25,16 +25,16 @@ import kotlinx.coroutines.flow.StateFlow */ interface RoomListService { - sealed class State { - data object Idle : State() - data object Running : State() - data object Error : State() - data object Terminated : State() + sealed interface State { + data object Idle : State + data object Running : State + data object Error : State + data object Terminated : State } - sealed class SyncIndicator { - data object Show : SyncIndicator() - data object Hide : SyncIndicator() + sealed interface SyncIndicator { + data object Show : SyncIndicator + data object Hide : SyncIndicator } /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt index 01aeb208ca..27b378846d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.matrix.api.tracing -sealed class WriteToFilesConfiguration { - data object Disabled : WriteToFilesConfiguration() - data class Enabled(val directory: String, val filenamePrefix: String) : WriteToFilesConfiguration() +sealed interface WriteToFilesConfiguration { + data object Disabled : WriteToFilesConfiguration + data class Enabled(val directory: String, val filenamePrefix: String) : WriteToFilesConfiguration } diff --git a/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerType.kt b/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerType.kt index de07450eec..9a20421a16 100644 --- a/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerType.kt +++ b/libraries/mediapickers/api/src/main/kotlin/io/element/android/libraries/mediapickers/api/PickerType.kt @@ -20,8 +20,10 @@ import android.net.Uri import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContract import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Immutable import io.element.android.libraries.core.mimetype.MimeTypes +@Immutable sealed interface PickerType { fun getContract(): ActivityResultContract fun getDefaultRequest(): Input diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt index 34ab1641f2..9abf6bc52e 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt @@ -17,11 +17,13 @@ package io.element.android.libraries.textcomposer.model import android.os.Parcelable +import androidx.compose.runtime.Immutable import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo import kotlinx.parcelize.Parcelize +@Immutable sealed interface MessageComposerMode : Parcelable { @Parcelize data object Normal: MessageComposerMode diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt index 96dff11cad..340540886d 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt @@ -16,8 +16,8 @@ package io.element.android.libraries.textcomposer.model -sealed class PressEvent { - data object PressStart: PressEvent() - data object Tapped: PressEvent() - data object LongPressEnd: PressEvent() +sealed interface PressEvent { + data object PressStart: PressEvent + data object Tapped: PressEvent + data object LongPressEnd: PressEvent } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessagePlayerEvent.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessagePlayerEvent.kt index 7f827caef7..193c72969b 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessagePlayerEvent.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessagePlayerEvent.kt @@ -16,11 +16,11 @@ package io.element.android.libraries.textcomposer.model -sealed class VoiceMessagePlayerEvent { - data object Play: VoiceMessagePlayerEvent() - data object Pause: VoiceMessagePlayerEvent() +sealed interface VoiceMessagePlayerEvent { + data object Play: VoiceMessagePlayerEvent + data object Pause: VoiceMessagePlayerEvent data class Seek( val position: Float - ): VoiceMessagePlayerEvent() + ): VoiceMessagePlayerEvent } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt index 6cf3166f68..94a854a491 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt @@ -16,11 +16,13 @@ package io.element.android.libraries.textcomposer.model +import androidx.compose.runtime.Immutable import kotlinx.collections.immutable.ImmutableList import kotlin.time.Duration -sealed class VoiceMessageState { - data object Idle: VoiceMessageState() +@Immutable +sealed interface VoiceMessageState { + data object Idle: VoiceMessageState data class Preview( val isSending: Boolean, @@ -29,10 +31,10 @@ sealed class VoiceMessageState { val playbackProgress: Float, val time: Duration, val waveform: ImmutableList, - ): VoiceMessageState() + ): VoiceMessageState data class Recording( val duration: Duration, val levels: ImmutableList, - ): VoiceMessageState() + ): VoiceMessageState } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt index 714581feb1..50df6d591c 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt @@ -19,13 +19,13 @@ package io.element.android.libraries.textcomposer.utils /** * State of a press gesture. */ -internal sealed class PressState { +internal sealed interface PressState { data class Idle( val lastPress: Pressing? - ) : PressState() + ) : PressState - sealed class Pressing : PressState() - data object Tapping : Pressing() - data object LongPressing : Pressing() + sealed interface Pressing : PressState + data object Tapping : Pressing + data object LongPressing : Pressing } diff --git a/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt b/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt index 04c532e86b..42035615d5 100644 --- a/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt +++ b/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt @@ -19,11 +19,11 @@ package io.element.android.libraries.voicerecorder.api import java.io.File import kotlin.time.Duration -sealed class VoiceRecorderState { +sealed interface VoiceRecorderState { /** * The recorder is idle and not recording. */ - data object Idle : VoiceRecorderState() + data object Idle : VoiceRecorderState /** * The recorder is currently recording. @@ -31,7 +31,7 @@ sealed class VoiceRecorderState { * @property elapsedTime The elapsed time since the recording started. * @property levels The current audio levels of the recording as a fraction of 1. */ - data class Recording(val elapsedTime: Duration, val levels: List) : VoiceRecorderState() + data class Recording(val elapsedTime: Duration, val levels: List) : VoiceRecorderState /** * The recorder has finished recording. @@ -46,5 +46,5 @@ sealed class VoiceRecorderState { val mimeType: String, val waveform: List, val duration: Duration, - ) : VoiceRecorderState() + ) : VoiceRecorderState } diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/Audio.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/Audio.kt index 3e51d615f4..463c2ce8a1 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/Audio.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/Audio.kt @@ -16,13 +16,31 @@ package io.element.android.libraries.voicerecorder.impl.audio -sealed class Audio { - class Data( +sealed interface Audio { + data class Data( val readSize: Int, val buffer: ShortArray, - ) : Audio() + ) : Audio { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Data + + if (readSize != other.readSize) return false + if (!buffer.contentEquals(other.buffer)) return false + + return true + } + + override fun hashCode(): Int { + var result = readSize + result = 31 * result + buffer.contentHashCode() + return result + } + } data class Error( val audioRecordErrorCode: Int - ) : Audio() + ) : Audio } diff --git a/services/apperror/api/build.gradle.kts b/services/apperror/api/build.gradle.kts index 94970d9774..7c776881bf 100644 --- a/services/apperror/api/build.gradle.kts +++ b/services/apperror/api/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") } android { diff --git a/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorState.kt b/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorState.kt index fb5fb9fd76..f370aefa2e 100644 --- a/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorState.kt +++ b/services/apperror/api/src/main/kotlin/io/element/android/services/apperror/api/AppErrorState.kt @@ -16,6 +16,9 @@ package io.element.android.services.apperror.api +import androidx.compose.runtime.Immutable + +@Immutable sealed interface AppErrorState { data object NoError : AppErrorState diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt index 6ed58b61e8..21625d50a5 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistArchitectureTest.kt @@ -16,12 +16,19 @@ package io.element.android.tests.konsist +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.constructors import com.lemonappdev.konsist.api.ext.list.modifierprovider.withSealedModifier import com.lemonappdev.konsist.api.ext.list.parameters +import com.lemonappdev.konsist.api.ext.list.withAnnotationOf import com.lemonappdev.konsist.api.ext.list.withNameEndingWith +import com.lemonappdev.konsist.api.ext.list.withoutAnnotationOf +import com.lemonappdev.konsist.api.ext.list.withoutConstructors import com.lemonappdev.konsist.api.ext.list.withoutName +import com.lemonappdev.konsist.api.ext.list.withoutParents import com.lemonappdev.konsist.api.verify.assertEmpty import com.lemonappdev.konsist.api.verify.assertTrue import org.junit.Test @@ -55,4 +62,33 @@ class KonsistArchitectureTest { .withNameEndingWith("Events") .assertEmpty(additionalMessage = "Events class MUST be sealed interface") } + + @Test + fun `Sealed class without constructor and without parent MUST be sealed interface`() { + Konsist.scopeFromProject() + .classes() + .withSealedModifier() + .withoutConstructors() + .withoutParents() + .assertEmpty(additionalMessage = "Sealed class without constructor MUST be sealed interface") + } + + @Test + fun `Sealed interface used in Composable MUST be Immutable or Stable`() { + // List all sealed interface without Immutable nor Stable annotation in the project + val forbiddenInterfacesForComposableParameter = Konsist.scopeFromProject() + .interfaces() + .withSealedModifier() + .withoutAnnotationOf(Immutable::class, Stable::class) + .map { it.fullyQualifiedName } + + Konsist.scopeFromProject() + .functions() + .withAnnotationOf(Composable::class) + .assertTrue(additionalMessage = "Consider adding the @Immutable or @Stable annotation to the sealed interface") { + it.parameters.all { param -> + param.type.fullyQualifiedName !in forbiddenInterfacesForComposableParameter + } + } + } }