From f55bb16bfa288e2115b1f1ed1953bbaa89d64e07 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 3 Nov 2022 18:02:53 +0100 Subject: [PATCH] Continue refinement of RoomList (and remove avatar library) --- features/roomlist/build.gradle.kts | 1 + .../x/features/roomlist/RoomListScreen.kt | 161 +++--------------- .../x/features/roomlist/RoomListViewModel.kt | 15 +- .../features/roomlist/components/RoomItem.kt | 157 +++++++++++++++++ .../roomlist/components/RoomListTopBar.kt | 48 ++++++ .../roomlist/model/RoomListRoomSummary.kt | 16 +- .../x/features/roomlist/model/stubbed.kt | 25 +++ gradle/libs.versions.toml | 1 + libraries/avatar/.gitignore | 1 - libraries/avatar/build.gradle.kts | 12 -- libraries/avatar/consumer-rules.pro | 0 libraries/avatar/proguard-rules.pro | 21 --- .../x/avatar/ExampleInstrumentedTest.kt | 24 --- libraries/avatar/src/main/AndroidManifest.xml | 4 - .../io/element/android/x/avatar/Avatar.kt | 33 ---- .../io/element/android/x/avatar/AvatarData.kt | 10 -- .../element/android/x/avatar/AvatarFetcher.kt | 41 ----- .../android/x/avatar/ExampleUnitTest.kt | 17 -- .../designsystem/components/avatar/Avatar.kt | 24 ++- .../components/avatar/AvatarData.kt | 4 +- .../components/avatar/AvatarSize.kt | 11 ++ settings.gradle.kts | 1 - 22 files changed, 305 insertions(+), 322 deletions(-) create mode 100644 features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomItem.kt create mode 100644 features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt create mode 100644 features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt delete mode 100644 libraries/avatar/.gitignore delete mode 100644 libraries/avatar/build.gradle.kts delete mode 100644 libraries/avatar/consumer-rules.pro delete mode 100644 libraries/avatar/proguard-rules.pro delete mode 100644 libraries/avatar/src/androidTest/java/io/element/android/x/avatar/ExampleInstrumentedTest.kt delete mode 100644 libraries/avatar/src/main/AndroidManifest.xml delete mode 100644 libraries/avatar/src/main/java/io/element/android/x/avatar/Avatar.kt delete mode 100644 libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarData.kt delete mode 100644 libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarFetcher.kt delete mode 100644 libraries/avatar/src/test/java/io/element/android/x/avatar/ExampleUnitTest.kt create mode 100644 libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarSize.kt diff --git a/features/roomlist/build.gradle.kts b/features/roomlist/build.gradle.kts index 6bb83ff0d5..a745356971 100644 --- a/features/roomlist/build.gradle.kts +++ b/features/roomlist/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(libs.mavericks.compose) implementation(libs.timber) implementation(libs.datetime) + implementation(libs.accompanist.placeholder) testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.3") androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt index c54c5e8bb2..53368503b3 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt @@ -2,39 +2,30 @@ package io.element.android.x.features.roomlist -import Avatar -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ExitToApp -import androidx.compose.material.ripple.rememberRipple -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.airbnb.mvrx.Success import com.airbnb.mvrx.compose.collectAsState import com.airbnb.mvrx.compose.mavericksViewModel import io.element.android.x.core.data.LogCompositions +import io.element.android.x.designsystem.ElementXTheme import io.element.android.x.designsystem.components.avatar.AvatarData +import io.element.android.x.features.roomlist.components.RoomItem +import io.element.android.x.features.roomlist.components.RoomListTopBar import io.element.android.x.features.roomlist.model.MatrixUser import io.element.android.x.features.roomlist.model.RoomListRoomSummary import io.element.android.x.features.roomlist.model.RoomListViewState +import io.element.android.x.features.roomlist.model.stubbedRoomSummaries import io.element.android.x.matrix.core.RoomId @Composable @@ -87,125 +78,29 @@ fun RoomListContent( } -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun RoomListTopBar( - matrixUser: MatrixUser?, - onLogoutClicked: () -> Unit, - scrollBehavior: TopAppBarScrollBehavior -) { - LogCompositions(tag = "RoomListScreen", msg = "TopBar") - if (matrixUser == null) return - MediumTopAppBar( - modifier = Modifier - .nestedScroll(scrollBehavior.nestedScrollConnection), - title = { - Text( - fontWeight = FontWeight.Bold, - text = "All Chats" - ) - }, - navigationIcon = { - IconButton(onClick = {}) { - Avatar(matrixUser.avatarData) - } - }, - actions = { - IconButton( - onClick = onLogoutClicked - ) { - Icon(Icons.Default.ExitToApp, contentDescription = "logout") - } - }, - scrollBehavior = scrollBehavior, - ) -} - +@Preview @Composable -private fun RoomItem( - modifier: Modifier = Modifier, - room: RoomListRoomSummary, - onClick: (RoomId) -> Unit -) { - if (room.isPlaceholder) { - return - } - Column( - modifier = modifier - .fillMaxWidth() - .height(72.dp) - .clickable( - onClick = { onClick(room.roomId) }, - indication = rememberRipple(), - interactionSource = remember { MutableInteractionSource() } - ), - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Avatar(room.avatarData) - Column( - modifier = Modifier - .padding(12.dp) - .weight(1f) - ) { - Text( - fontSize = 16.sp, - fontWeight = FontWeight.SemiBold, - text = room.name, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = room.lastMessage?.toString().orEmpty(), - color = MaterialTheme.colorScheme.secondary, - fontSize = 15.sp, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - Column( - ) { - Text( - fontSize = 12.sp, - text = room.timestamp ?: "", - color = MaterialTheme.colorScheme.secondary, - ) - Spacer(modifier.size(4.dp)) - val unreadIndicatorColor = - if (room.hasUnread) MaterialTheme.colorScheme.primary else Color.Transparent - Box( - modifier = Modifier - .size(12.dp) - .clip(CircleShape) - .background(unreadIndicatorColor) - .align(Alignment.End), - ) - } - } +private fun PreviewableRoomListContent() { + ElementXTheme(darkTheme = false) { + RoomListContent( + roomSummaries = stubbedRoomSummaries(), + matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")), + onRoomClicked = {}, + onLogoutClicked = {} + ) } } @Preview @Composable -private fun PreviewableRoomListContent() { - val roomSummaries = listOf( - RoomListRoomSummary( - name = "Room", - hasUnread = true, - timestamp = "14:18", - lastMessage = "A message", - avatarData = AvatarData("R"), - id = "roomId" +private fun PreviewableDarkRoomListContent() { + ElementXTheme(darkTheme = true) { + RoomListContent( + roomSummaries = stubbedRoomSummaries(), + matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")), + onRoomClicked = {}, + onLogoutClicked = {} ) - ) - RoomListContent( - roomSummaries = roomSummaries, - matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")), - onRoomClicked = {}, - onLogoutClicked = {} - ) -} \ No newline at end of file + } +} + diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt index 3798150ac0..098e55359a 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt @@ -1,12 +1,12 @@ package io.element.android.x.features.roomlist -import androidx.compose.ui.unit.dp import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MavericksViewModel import com.airbnb.mvrx.Success import io.element.android.x.core.data.parallelMap import io.element.android.x.designsystem.components.avatar.AvatarData +import io.element.android.x.designsystem.components.avatar.AvatarSize import io.element.android.x.features.roomlist.model.MatrixUser import io.element.android.x.features.roomlist.model.RoomListRoomSummary import io.element.android.x.features.roomlist.model.RoomListViewState @@ -47,7 +47,7 @@ class RoomListViewModel(initialState: RoomListViewState) : val userAvatarUrl = client.loadUserAvatarURLString().getOrNull() val userDisplayName = client.loadUserDisplayName().getOrNull() val avatarData = - loadAvatarData(client, userDisplayName ?: client.userId().value, userAvatarUrl, 32) + loadAvatarData(client, userDisplayName ?: client.userId().value, userAvatarUrl, AvatarSize.SMALL) MatrixUser( username = userDisplayName ?: client.userId().value, avatarUrl = userAvatarUrl, @@ -73,10 +73,7 @@ class RoomListViewModel(initialState: RoomListViewState) : ): List { return roomSummaries.parallelMap { roomSummary -> when (roomSummary) { - is RoomSummary.Empty -> RoomListRoomSummary( - id = roomSummary.identifier, - isPlaceholder = true - ) + is RoomSummary.Empty -> RoomListRoomSummary.placeholder(roomSummary.identifier) is RoomSummary.Filled -> { val avatarData = loadAvatarData( client, @@ -100,17 +97,17 @@ class RoomListViewModel(initialState: RoomListViewState) : client: MatrixClient, name: String, url: String?, - size: Long = 48 + size: AvatarSize = AvatarSize.MEDIUM ): AvatarData { val mediaContent = url?.let { val mediaSource = mediaSourceFromUrl(it) - client.loadMediaThumbnailForSource(mediaSource, size, size) + client.loadMediaThumbnailForSource(mediaSource, size.value.toLong(), size.value.toLong()) } return mediaContent?.fold( { it }, { null } ).let { model -> - AvatarData(name.first().toString(), model, size.toInt()) + AvatarData(name.first().uppercase(), model, size) } } diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomItem.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomItem.kt new file mode 100644 index 0000000000..451886c5ea --- /dev/null +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomItem.kt @@ -0,0 +1,157 @@ +package io.element.android.x.features.roomlist.components + +import Avatar +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.CenterVertically +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.google.accompanist.placeholder.material.placeholder +import io.element.android.x.features.roomlist.model.RoomListRoomSummary +import io.element.android.x.matrix.core.RoomId + +@Composable +internal fun RoomItem( + modifier: Modifier = Modifier, + room: RoomListRoomSummary, + onClick: (RoomId) -> Unit +) { + if (room.isPlaceholder) { + return PlaceholderRoomItem(modifier = modifier, room = room) + } + Column( + modifier = modifier + .fillMaxWidth() + .clickable( + onClick = { onClick(room.roomId) }, + indication = rememberRipple(), + interactionSource = remember { MutableInteractionSource() } + ), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + .height(IntrinsicSize.Min), + verticalAlignment = CenterVertically + ) { + Avatar(room.avatarData) + Column( + modifier = Modifier + .padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp) + .alignByBaseline() + .weight(1f) + ) { + // Name + Text( + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + text = room.name, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + // Last Message + Text( + text = room.lastMessage?.toString().orEmpty(), + color = MaterialTheme.colorScheme.secondary, + lineHeight = 20.sp, + fontSize = 15.sp, + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + } + // Timestamp and Unread + Column( + modifier = Modifier + .alignByBaseline(), + ) { + Text( + fontSize = 12.sp, + text = room.timestamp ?: "", + color = MaterialTheme.colorScheme.secondary, + ) + Spacer(modifier.size(4.dp)) + val unreadIndicatorColor = + if (room.hasUnread) MaterialTheme.colorScheme.primary else Color.Transparent + Box( + modifier = Modifier + .size(12.dp) + .clip(CircleShape) + .background(unreadIndicatorColor) + .align(Alignment.End), + ) + } + } + } +} + +@Composable +internal fun PlaceholderRoomItem( + modifier: Modifier = Modifier, + room: RoomListRoomSummary, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalAlignment = CenterVertically, + ) { + Text( + modifier = Modifier + .size(room.avatarData.size.dp) + .clip(CircleShape) + .placeholder(true), + text = "" + ) + Column( + modifier = Modifier + .padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp) + .weight(1f) + ) { + Text( + modifier = Modifier + .size(width = 80.dp, height = 12.dp) + .placeholder(visible = true), + text = "", + ) + Spacer(modifier = Modifier.size(4.dp)) + Text( + modifier = Modifier + .size(width = 160.dp, height = 12.dp) + .placeholder(visible = true), + text = "", + ) + } + Column { + Text( + modifier = Modifier + .size(width = 24.dp, height = 12.dp) + .placeholder(visible = true), + text = "", + color = MaterialTheme.colorScheme.secondary, + ) + Spacer(Modifier.size(4.dp)) + Box( + modifier = Modifier + .size(12.dp) + .clip(CircleShape) + .background(Color.Transparent) + .align(Alignment.End), + ) + } + } +} \ No newline at end of file diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt new file mode 100644 index 0000000000..70156f8220 --- /dev/null +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomListTopBar.kt @@ -0,0 +1,48 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + +package io.element.android.x.features.roomlist.components + +import Avatar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ExitToApp +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.text.font.FontWeight +import io.element.android.x.core.data.LogCompositions +import io.element.android.x.features.roomlist.model.MatrixUser + +@Composable +fun RoomListTopBar( + matrixUser: MatrixUser?, + onLogoutClicked: () -> Unit, + scrollBehavior: TopAppBarScrollBehavior +) { + LogCompositions(tag = "RoomListScreen", msg = "TopBar") + MediumTopAppBar( + modifier = Modifier + .nestedScroll(scrollBehavior.nestedScrollConnection), + title = { + Text( + fontWeight = FontWeight.Bold, + text = "All Chats" + ) + }, + navigationIcon = { + if (matrixUser != null) { + IconButton(onClick = {}) { + Avatar(matrixUser.avatarData) + } + } + }, + actions = { + IconButton( + onClick = onLogoutClicked + ) { + Icon(Icons.Default.ExitToApp, contentDescription = "logout") + } + }, + scrollBehavior = scrollBehavior, + ) +} \ No newline at end of file diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt index ab54f5b1be..551f06fb6c 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/RoomListRoomSummary.kt @@ -12,4 +12,18 @@ data class RoomListRoomSummary( val lastMessage: CharSequence? = null, val avatarData: AvatarData = AvatarData(), val isPlaceholder: Boolean = false, -) \ No newline at end of file +) { + + companion object { + fun placeholder(id: String): RoomListRoomSummary { + return RoomListRoomSummary( + id = id, + isPlaceholder = true, + name = "Short name", + timestamp = "hh:mm", + lastMessage = "Last message for placeholder", + avatarData = AvatarData("S") + ) + } + } +} \ No newline at end of file diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt new file mode 100644 index 0000000000..55a68ab18e --- /dev/null +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/model/stubbed.kt @@ -0,0 +1,25 @@ +package io.element.android.x.features.roomlist.model + +import io.element.android.x.designsystem.components.avatar.AvatarData + +internal fun stubbedRoomSummaries(): List { + return listOf( + RoomListRoomSummary( + name = "Room", + hasUnread = true, + timestamp = "14:18", + lastMessage = "A very very very very long message which suites on two lines", + avatarData = AvatarData("R"), + id = "roomId" + ), + RoomListRoomSummary( + name = "Room#2", + hasUnread = false, + timestamp = "14:16", + lastMessage = "A short message", + avatarData = AvatarData("Z"), + id = "roomId2" + ), + RoomListRoomSummary.placeholder("roomId2") + ) +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6ca1660ecf..0273d23149 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,6 +57,7 @@ accompanist_animation = { module = "com.google.accompanist:accompanist-navigatio accompanist_permission = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" } accompanist_material = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanist" } accompanist_systemui = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } +accompanist_placeholder = { module = "com.google.accompanist:accompanist-placeholder-material", version.ref = "accompanist" } # Test test_junit = { module = "junit:junit", version.ref = "test_junit" } diff --git a/libraries/avatar/.gitignore b/libraries/avatar/.gitignore deleted file mode 100644 index 42afabfd2a..0000000000 --- a/libraries/avatar/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/libraries/avatar/build.gradle.kts b/libraries/avatar/build.gradle.kts deleted file mode 100644 index 9808ef7ba6..0000000000 --- a/libraries/avatar/build.gradle.kts +++ /dev/null @@ -1,12 +0,0 @@ -plugins { - id("io.element.android-compose") -} - -android { - namespace = "io.element.android.x.libraries.avatar" -} - -dependencies { - implementation(project(":libraries:matrix")) - implementation(libs.coil.compose) -} \ No newline at end of file diff --git a/libraries/avatar/consumer-rules.pro b/libraries/avatar/consumer-rules.pro deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libraries/avatar/proguard-rules.pro b/libraries/avatar/proguard-rules.pro deleted file mode 100644 index 481bb43481..0000000000 --- a/libraries/avatar/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/libraries/avatar/src/androidTest/java/io/element/android/x/avatar/ExampleInstrumentedTest.kt b/libraries/avatar/src/androidTest/java/io/element/android/x/avatar/ExampleInstrumentedTest.kt deleted file mode 100644 index 2ab7adcb23..0000000000 --- a/libraries/avatar/src/androidTest/java/io/element/android/x/avatar/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.element.android.x.avatar - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("io.element.android.x.avatar.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/libraries/avatar/src/main/AndroidManifest.xml b/libraries/avatar/src/main/AndroidManifest.xml deleted file mode 100644 index a5918e68ab..0000000000 --- a/libraries/avatar/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/libraries/avatar/src/main/java/io/element/android/x/avatar/Avatar.kt b/libraries/avatar/src/main/java/io/element/android/x/avatar/Avatar.kt deleted file mode 100644 index ec71819247..0000000000 --- a/libraries/avatar/src/main/java/io/element/android/x/avatar/Avatar.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.element.android.x.avatar - -import android.util.Log -import androidx.compose.foundation.Image -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp -import coil.compose.rememberAsyncImagePainter - -/** - * TODO fallback Avatar - */ -@Composable -fun Avatar(avatarData: AvatarData) { - Image( - painter = rememberAsyncImagePainter( - model = avatarData.url, - onError = { - Log.e("TAG", "Error $it\n${it.result}", it.result.throwable) - }), - contentDescription = null, - modifier = Modifier - .size(avatarData.size) - .clip(CircleShape) - .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) - ) -} - diff --git a/libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarData.kt b/libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarData.kt deleted file mode 100644 index c5004e6666..0000000000 --- a/libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarData.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.element.android.x.avatar - -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -data class AvatarData( - val url: String, - val size: Dp = 48.dp -) - diff --git a/libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarFetcher.kt b/libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarFetcher.kt deleted file mode 100644 index bf88e161b6..0000000000 --- a/libraries/avatar/src/main/java/io/element/android/x/avatar/AvatarFetcher.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.element.android.x.avatar - -import coil.ImageLoader -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.request.Options -import io.element.android.x.matrix.MatrixClient -import org.matrix.rustcomponents.sdk.mediaSourceFromUrl - -class AvatarFetcher( - private val matrixClient: MatrixClient, - private val avatarData: AvatarData, - private val options: Options, - private val imageLoader: ImageLoader -) : - Fetcher { - - override suspend fun fetch(): FetchResult? { - val mediaSource = mediaSourceFromUrl(avatarData.url) - val mediaContent = matrixClient.loadMediaContentForSource(mediaSource) - return mediaContent.fold( - { mediaContent -> - val byteArray = mediaContent.toUByteArray().toByteArray() - val fetcher = imageLoader.components.newFetcher(byteArray, options, imageLoader) - fetcher?.first?.fetch() - }, - {null} - ) - } - - class Factory(private val matrixClient: MatrixClient) : Fetcher.Factory { - - override fun create( - data: AvatarData, - options: Options, - imageLoader: ImageLoader - ): Fetcher? { - return AvatarFetcher(matrixClient, data, options, imageLoader) - } - } -} diff --git a/libraries/avatar/src/test/java/io/element/android/x/avatar/ExampleUnitTest.kt b/libraries/avatar/src/test/java/io/element/android/x/avatar/ExampleUnitTest.kt deleted file mode 100644 index 79be6a0b85..0000000000 --- a/libraries/avatar/src/test/java/io/element/android/x/avatar/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.element.android.x.avatar - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/Avatar.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/Avatar.kt index bbbe50a035..4dbcd857a5 100644 --- a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/Avatar.kt +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/Avatar.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale @@ -20,18 +21,17 @@ import io.element.android.x.designsystem.components.avatar.AvatarData @Composable fun Avatar(avatarData: AvatarData, modifier: Modifier = Modifier) { + val commonModifier = modifier + .size(avatarData.size.dp) + .clip(CircleShape) if (avatarData.model == null) { InitialsAvatar( - modifier = modifier - .size(avatarData.size.dp) - .clip(CircleShape), + modifier = commonModifier, initials = avatarData.initials ) } else { ImageAvatar( - modifier = modifier - .size(avatarData.size.dp) - .clip(CircleShape), + modifier = commonModifier, avatarData = avatarData ) } @@ -50,8 +50,6 @@ private fun ImageAvatar( contentDescription = null, contentScale = ContentScale.Crop, modifier = modifier - .size(avatarData.size.dp) - .clip(CircleShape) ) } @@ -65,15 +63,15 @@ private fun InitialsAvatar( listOf( AvatarGradientStart, AvatarGradientEnd, - ) + ), + start = Offset(0.0f, 100f), + end = Offset(100f, 0f) ) Box( - modifier - .background(brush = initialsGradient) + modifier.background(brush = initialsGradient) ) { Text( - modifier = Modifier - .align(Alignment.Center), + modifier = Modifier.align(Alignment.Center), text = initials, fontSize = 24.sp, color = Color.White, diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarData.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarData.kt index 2d4e6f34ef..9f2350cefa 100644 --- a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarData.kt +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarData.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.Stable data class AvatarData( val initials: String = "", val model: ByteArray? = null, - val size: Int = 0 + val size: AvatarSize = AvatarSize.MEDIUM ) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -27,7 +27,7 @@ data class AvatarData( override fun hashCode(): Int { var result = initials.hashCode() result = 31 * result + (model?.contentHashCode() ?: 0) - result = 31 * result + size + result = 31 * result + size.value return result } diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarSize.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarSize.kt new file mode 100644 index 0000000000..764b547dca --- /dev/null +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/avatar/AvatarSize.kt @@ -0,0 +1,11 @@ +package io.element.android.x.designsystem.components.avatar + +import androidx.compose.ui.unit.dp + +enum class AvatarSize(val value: Int) { + SMALL(32), + MEDIUM(40), + BIG(48); + + val dp = value.dp +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 865db142fc..e64417e0aa 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,4 +24,3 @@ include(":features:login") include(":features:roomlist") include(":features:messages") include(":libraries:designsystem") -include(":libraries:avatar")