Repository: wireapp/wire-android
Pull request #3911: feat(channels): dynamically generate channel avatars (WPB-15857)
Author: login: ohassine
Labels: name: size/S
Created: 2025-03-11T09:15:30Z | Merged: 2025-03-18T09:49:29Z


<!--do not remove this marker, its needed to replace info when ticket title is updated -->
<!--jira-description-action-hidden-marker-start-->

<table>
<td>
  <a href="https://wearezeta.atlassian.net/browse/WPB-15857" title="WPB-15857" target="_blank"><img alt="Task" src="https://wearezeta.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10818?size=medium" />WPB-15857</a>  [Android] Add Channel avatars
  </td></table>
  <br />
 

<!--jira-description-action-hidden-marker-end-->

----
#### PR Submission Checklist for internal contributors

- The **PR Title**
    - [ ] conforms to the style of semantic commits messages¹ supported in Wire's Github Workflow²
    - [ ] contains a reference JIRA issue number like `SQPIT-764`
    - [ ] answers the question: _If merged, this PR will: ..._ ³

- The **PR Description**
    - [ ] is free of optional paragraphs and you have filled the relevant parts to the best of your ability
----

# What's new in this PR?

### Issues

A follow up PR of https://github.com/wireapp/wire-android/pull/3897, it contains some changes of it

Generate channel avatars based on conversationId, so it's the same on all platforms

Needs releases with:

- [ ] GitHub link to other pull request

### Testing

#### Test Coverage (Optional)

- [ ] I have added automated test to this contribution

### Attachments (Optional)

| Dark | Light |
| ----------- | ------------ |
| ![Screenshot_20250307_112258](https://github.com/user-attachments/assets/10d7d39f-ff5f-4ff5-8e75-fbbd1fd35811) | ![Screenshot_20250310_160418](https://github.com/user-attachments/assets/2a2c3a9a-2b72-465c-a3bd-bce38deb464d) |

----
#### PR Post Submission Checklist for internal contributors (Optional)

- [ ] Wire's Github Workflow has automatically linked the PR to a JIRA issue
----
#### PR Post Merge Checklist for internal contributors

- [ ] If any soft of configuration variable was introduced by this PR, it has been added to the relevant documents and the CI jobs have been updated.
----
##### References
1. https://sparkbox.com/foundry/semantic_commit_messages
1. https://github.com/wireapp/.github#usage
1. E.g. `feat(conversation-list): Sort conversations by most emojis in the title #SQPIT-764`.



Diff:
diff --git a/app/src/main/kotlin/com/wire/android/ui/common/AppThemeExt.kt b/app/src/main/kotlin/com/wire/android/ui/common/AppThemeExt.kt
index 3ac8bdcf820..bcd757dca9f 100644
--- a/app/src/main/kotlin/com/wire/android/ui/common/AppThemeExt.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/common/AppThemeExt.kt
@@ -19,13 +19,20 @@
 package com.wire.android.ui.common
 
 import androidx.compose.runtime.Composable
+import com.wire.android.ui.theme.ChannelAvatarColors
 import com.wire.android.ui.theme.GroupAvatarColors
 import com.wire.android.ui.theme.WireColorScheme
 import com.wire.kalium.logic.data.id.ConversationId
 import kotlin.math.absoluteValue
 
 @Composable
-internal fun WireColorScheme.conversationColor(id: ConversationId): GroupAvatarColors {
+internal fun WireColorScheme.channelConversationColor(id: ConversationId): ChannelAvatarColors {
+    val colors = this.channelAvatarColors
+    return colors[(id.hashCode() % colors.size).absoluteValue]
+}
+
+@Composable
+internal fun WireColorScheme.groupConversationColor(id: ConversationId): GroupAvatarColors {
     val colors = this.groupAvatarColors
     return colors[(id.hashCode() % colors.size).absoluteValue]
 }
diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ChannelConversationAvatar.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ChannelConversationAvatar.kt
new file mode 100644
index 00000000000..983370d7591
--- /dev/null
+++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/ChannelConversationAvatar.kt
@@ -0,0 +1,116 @@
+/*
+ * Wire
+ * Copyright (C) 2024 Wire Swiss GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+package com.wire.android.ui.home.conversationslist.common
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+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.res.painterResource
+import androidx.compose.ui.unit.Dp
+import com.wire.android.R
+import com.wire.android.ui.common.channelConversationColor
+import com.wire.android.ui.common.colorsScheme
+import com.wire.android.ui.common.dimensions
+import com.wire.android.ui.common.preview.MultipleThemePreviews
+import com.wire.android.ui.theme.WireTheme
+import com.wire.android.ui.theme.wireDimensions
+import com.wire.kalium.logic.data.id.ConversationId
+
+@Composable
+fun ChannelConversationAvatar(
+    conversationId: ConversationId,
+    modifier: Modifier = Modifier,
+    isPrivateChannel: Boolean = false,
+    size: Dp = MaterialTheme.wireDimensions.avatarDefaultSize,
+    cornerRadius: Dp = MaterialTheme.wireDimensions.groupAvatarCornerRadius,
+    padding: Dp = MaterialTheme.wireDimensions.avatarClickablePadding,
+    borderWidth: Dp = dimensions().avatarBorderWidth,
+) {
+    Box {
+        val colors = colorsScheme().channelConversationColor(conversationId)
+
+        Box(
+            contentAlignment = Alignment.Center,
+            modifier = modifier
+                .padding(padding)
+                .size(size + borderWidth * 2)
+                .border(color = colors.border, width = borderWidth, shape = RoundedCornerShape(cornerRadius + borderWidth))
+                .padding(borderWidth)
+                .background(color = colors.background, shape = RoundedCornerShape(cornerRadius))
+        ) {
+            Icon(
+                painter = painterResource(id = R.drawable.ic_channel),
+                contentDescription = null,
+                tint = colors.icon
+            )
+        }
+        if (isPrivateChannel) {
+            ChannelLock(Modifier.offset(x = size, y = size))
+        }
+    }
+}
+
+@Composable
+private fun ChannelLock(modifier: Modifier = Modifier) {
+    Column(
+        modifier = modifier
+            .size(dimensions().spacing14x)
+            .clip(RoundedCornerShape(dimensions().spacing3x)) // Apply rounded corners with a radius of 16.dp
+            .background(color = colorsScheme().inverseSurface)
+    ) {
+        Icon(
+            painter = painterResource(id = R.drawable.ic_lock),
+            contentDescription = null,
+            modifier = Modifier
+                .size(dimensions().spacing14x)
+                .padding(dimensions().spacing2x),
+            tint = colorsScheme().inverseOnSurface
+        )
+    }
+}
+
+@MultipleThemePreviews
+@Composable
+fun PreviewChannelConversationAvatar() {
+    WireTheme {
+        ChannelConversationAvatar(
+            isPrivateChannel = true,
+            conversationId = ConversationId("value", "domain")
+        )
+    }
+}
+
+@MultipleThemePreviews
+@Composable
+fun PreviewChannelLock() {
+    WireTheme {
+        ChannelLock()
+    }
+}
diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/GroupConversationAvatar.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/GroupConversationAvatar.kt
index 655d2a58ec5..52d18eeaf1f 100644
--- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/GroupConversationAvatar.kt
+++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/GroupConversationAvatar.kt
@@ -33,7 +33,7 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.Dp
 import com.wire.android.ui.common.colorsScheme
-import com.wire.android.ui.common.conversationColor
+import com.wire.android.ui.common.groupConversationColor
 import com.wire.android.ui.common.dimensions
 import com.wire.android.ui.theme.WireTheme
 import com.wire.android.ui.theme.wireDimensions
@@ -59,7 +59,7 @@ fun GroupConversationAvatar(
             .padding(borderWidth)
             .background(color = colorsScheme().surface, shape = RoundedCornerShape(cornerRadius))
     ) {
-        val colors = colorsScheme().conversationColor(id = conversationId)
+        val colors = colorsScheme().groupConversationColor(id = conversationId)
         CustomGroupAvatarDrawing(
             modifier = Modifier.padding(dimensions().spacing4x),
             leftSideShapeColor = colors.left,
diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml
new file mode 100644
index 00000000000..9f0eec95ecc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_lock.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="10dp"
+    android:height="10dp"
+    android:viewportWidth="10"
+    android:viewportHeight="10">
+  <path
+      android:pathData="M4.086,2.208C4.321,1.991 4.649,1.862 5,1.862C5.351,1.862 5.679,1.991 5.914,2.208C6.147,2.424 6.267,2.705 6.267,2.987V4.375H3.733V2.987C3.733,2.705 3.853,2.424 4.086,2.208ZM2.5,4.375V2.987C2.5,2.344 2.776,1.738 3.25,1.3C3.722,0.863 4.352,0.625 5,0.625C5.648,0.625 6.278,0.863 6.75,1.3C7.224,1.738 7.5,2.344 7.5,2.987V4.375H7.837C8.53,4.375 9.063,4.905 9.063,5.525V8.225C9.063,8.845 8.53,9.375 7.837,9.375H2.163C1.47,9.375 0.938,8.845 0.938,8.225V5.525C0.938,4.905 1.47,4.375 2.163,4.375H2.5Z"
+      android:fillColor="#ffffff"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/GroupAvatarColors.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/AvatarColors.kt
similarity index 90%
rename from core/ui-common/src/main/kotlin/com/wire/android/ui/theme/GroupAvatarColors.kt
rename to core/ui-common/src/main/kotlin/com/wire/android/ui/theme/AvatarColors.kt
index 5156c3ed0b9..bfb49322903 100644
--- a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/GroupAvatarColors.kt
+++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/AvatarColors.kt
@@ -23,3 +23,5 @@ package com.wire.android.ui.theme
 import androidx.compose.ui.graphics.Color
 
 data class GroupAvatarColors(val left: Color, val middle: Color, val right: Color)
+
+data class ChannelAvatarColors(val border: Color, val background: Color, val icon: Color)
diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt
index 73b4d04721e..2c849dc0fcb 100644
--- a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt
+++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt
@@ -23,6 +23,14 @@ package com.wire.android.ui.theme
 import androidx.compose.material3.ColorScheme
 import androidx.compose.runtime.Immutable
 import androidx.compose.ui.graphics.Color
+import com.wire.android.ui.theme.WireColorPalette.Gray30
+import com.wire.android.ui.theme.WireColorPalette.Gray80
+import com.wire.android.ui.theme.WireColorPalette.LightAmber50
+import com.wire.android.ui.theme.WireColorPalette.LightBlue50
+import com.wire.android.ui.theme.WireColorPalette.LightGreen50
+import com.wire.android.ui.theme.WireColorPalette.LightPetrol50
+import com.wire.android.ui.theme.WireColorPalette.LightPurple50
+import com.wire.android.ui.theme.WireColorPalette.LightRed50
 import io.github.esentsov.PackagePrivate
 
 @Suppress("LongParameterList")
@@ -80,12 +88,12 @@ class WireColorScheme(
 
     // accents
     val groupAvatarColors: List<GroupAvatarColors>,
+    val channelAvatarColors: List<ChannelAvatarColors>,
     val wireAccentColors: WireAccentColors,
 
     val emojiBackgroundColor: Color,
 
-    val avatarBorderColor: Color
-) {
+    ) {
     fun toColorScheme(): ColorScheme = ColorScheme(
         primary = primary, onPrimary = onPrimary,
         primaryContainer = primaryVariant, onPrimaryContainer = onPrimaryVariant,
@@ -183,6 +191,15 @@ private val LightWireColorScheme = WireColorScheme(
         GroupAvatarColors(WireColorPalette.LightGreen500, WireColorPalette.LightBlue500, WireColorPalette.LightAmber500),
         GroupAvatarColors(WireColorPalette.LightGreen500, WireColorPalette.LightAmber500, WireColorPalette.LightPurple500)
     ),
+    channelAvatarColors = listOf(
+        ChannelAvatarColors(WireColorPalette.LightBlue100, LightBlue50, WireColorPalette.LightBlue500),
+        ChannelAvatarColors(WireColorPalette.LightPurple100, LightPurple50, WireColorPalette.LightPurple500),
+        ChannelAvatarColors(WireColorPalette.LightRed100, LightRed50, WireColorPalette.LightRed500),
+        ChannelAvatarColors(WireColorPalette.LightGreen100, LightGreen50, WireColorPalette.LightGreen500),
+        ChannelAvatarColors(WireColorPalette.LightAmber100, LightAmber50, WireColorPalette.LightAmber500),
+        ChannelAvatarColors(WireColorPalette.LightPetrol100, LightPetrol50, WireColorPalette.LightPetrol500),
+        ChannelAvatarColors(WireColorPalette.Gray40, Gray30, Gray80)
+    ),
     wireAccentColors = WireAccentColors {
         when (it) {
             Accent.Amber -> WireColorPalette.LightAmber500
@@ -195,7 +212,6 @@ private val LightWireColorScheme = WireColorScheme(
         }
     },
     emojiBackgroundColor = Color.White,
-    avatarBorderColor = WireColorPalette.Gray40
 )
 
 // Dark WireColorScheme
@@ -268,6 +284,15 @@ private val DarkWireColorScheme = WireColorScheme(
         GroupAvatarColors(WireColorPalette.DarkGreen500, WireColorPalette.DarkBlue500, WireColorPalette.DarkAmber500),
         GroupAvatarColors(WireColorPalette.DarkGreen500, WireColorPalette.DarkAmber500, WireColorPalette.DarkPurple500)
     ),
+    channelAvatarColors = listOf(
+        ChannelAvatarColors(WireColorPalette.DarkBlue900, WireColorPalette.DarkBlue800, WireColorPalette.DarkBlue500),
+        ChannelAvatarColors(WireColorPalette.DarkPurple900, WireColorPalette.DarkPurple800, WireColorPalette.DarkPurple500),
+        ChannelAvatarColors(WireColorPalette.DarkRed900, WireColorPalette.DarkRed800, WireColorPalette.DarkRed500),
+        ChannelAvatarColors(WireColorPalette.DarkGreen900, WireColorPalette.DarkGreen800, WireColorPalette.DarkGreen500),
+        ChannelAvatarColors(WireColorPalette.DarkAmber900, WireColorPalette.DarkAmber800, WireColorPalette.DarkAmber500),
+        ChannelAvatarColors(WireColorPalette.DarkPetrol900, WireColorPalette.DarkPetrol800, WireColorPalette.DarkPetrol500),
+        ChannelAvatarColors(WireColorPalette.Gray90, Gray80, WireColorPalette.Gray40)
+    ),
     wireAccentColors = WireAccentColors {
         when (it) {
             Accent.Amber -> WireColorPalette.DarkAmber500
@@ -280,7 +305,6 @@ private val DarkWireColorScheme = WireColorScheme(
         }
     },
     emojiBackgroundColor = Color.Black,
-    avatarBorderColor = WireColorPalette.Gray90
 )
 
 @PackagePrivate
diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireDimensions.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireDimensions.kt
index edfab24c0fc..815051c069d 100644
--- a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireDimensions.kt
+++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireDimensions.kt
@@ -231,7 +231,7 @@ private val DefaultPhonePortraitWireDimensions: WireDimensions = WireDimensions(
     avatarTemporaryUserBorderWidth = 2.dp,
     avatarBigTemporaryUserBorderWidth = 4.dp,
     avatarLegalHoldIndicatorBorderWidth = 4.dp,
-    groupAvatarCornerRadius = 10.dp,
+    groupAvatarCornerRadius = 8.dp,
     avatarConversationTopBarSize = 24.dp,
     groupAvatarConversationTopBarCornerRadius = 8.dp,
     groupAvatarConversationDetailsTopBarSize = 64.dp,


Reviews:
user: login: vitorhugods
body: Nicely done. Just a couple of small comments.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T11:31:48Z
user: login: vitorhugods
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T07:03:03Z
user: login: ohassine
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T08:49:29Z
user: login: saleniuk
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T09:10:46Z

Review comments:
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Wire
+ * Copyright (C) 2025 Wire Swiss GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+package com.wire.android.ui.home.conversationslist.common
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.unit.dp
+import com.wire.android.util.ui.PreviewMultipleThemes
+
+@Suppress("ArgumentListWrapping")
+@Composable
+fun CustomGroupAvatarDrawing(
path: app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/CustomGroupAvatarDrawing.kt
user: login: vitorhugods
body: Might be annoying to maintain if the design changes, but TBH I can't find anything simpler without adding a 3rd party library just for these icons.

For the sake of documentation... how did you "convert" the paths in the SVG to the canvas code below?
Just reading it and rewriting in order?

If there was any tool or anything like that, it would be nice to document in code, in case someone ever needs to change it.
created_at: 2025-03-13T11:29:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -164,14 +175,31 @@ private val LightWireColorScheme = WireColorScheme(
     scrim = WireColorPalette.BlackAlpha55,
 
     // accents
-    groupAvatarColors = listOf(
-        WireColorPalette.LightRed300, WireColorPalette.LightRed500, WireColorPalette.LightRed700,
-        WireColorPalette.LightGreen300, WireColorPalette.LightGreen500, WireColorPalette.LightGreen700,
-        WireColorPalette.LightBlue300, WireColorPalette.LightBlue500, WireColorPalette.LightBlue700,
-        WireColorPalette.LightPurple300, WireColorPalette.LightPurple500, WireColorPalette.LightPurple700,
-        WireColorPalette.LightAmber300, WireColorPalette.LightAmber500, WireColorPalette.LightAmber700,
-        WireColorPalette.LightPetrol300, WireColorPalette.LightPetrol500, WireColorPalette.LightPetrol700,
-        WireColorPalette.Gray30, WireColorPalette.Gray50, WireColorPalette.Gray70,
+    groupAvatarColors = listOf( // left, middle, right
+        Triple(WireColorPalette.LightGreen500, WireColorPalette.LightPurple500, WireColorPalette.LightRed500),
path: core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt
user: login: vitorhugods
body: Instead of triples, it would be nicer to actually create a class so it's easier to use and we reduce the possibility of using the wrong values.

```kotlin
data class GroupAvatarColors(val left: Color, val middle: Color, val right: Color)

data class ChannelAvatarColors(val border: Color, val background: Color, val icon: Color)
```
created_at: 2025-03-13T11:31:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Wire
+ * Copyright (C) 2025 Wire Swiss GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+package com.wire.android.ui.home.conversationslist.common
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.unit.dp
+import com.wire.android.util.ui.PreviewMultipleThemes
+
+@Suppress("ArgumentListWrapping")
+@Composable
+fun CustomGroupAvatarDrawing(
path: app/src/main/kotlin/com/wire/android/ui/home/conversationslist/common/CustomGroupAvatarDrawing.kt
user: login: ohassine
body: I think it's not worth it creating a library for just one usage, could be the case if we want to use it for other platforms.
For the shape creation, I converted the SVG to a vector using Android studio and then you can make a Kotlin path  using any AI tool to speedup.

Or, you you can do it manually, it's just copy paste of values.
For example this path
```
  <path
      android:pathData="M10.283,16.982C9.212,16.982 8.314,16.028 8.314,14.823C8.308,13.639 9.218,12.702 10.283,12.702C11.349,12.702 12.259,13.623 12.259,14.817C12.259,16.028 11.355,16.982 10.283,16.982ZM7.231,22.066C6.45,22.066 6.165,21.731 6.165,21.145C6.165,19.655 7.806,17.83 10.283,17.83C11.266,17.83 12.069,18.115 12.677,18.5C11.383,19.527 10.769,21.206 11.372,22.066H7.231Z"
      android:fillColor="#1D7833"/>
```
`M` will be `moveTo` and `C` will be `cubicTo`, and if you find another M you `close` and you `moveTo`
```
val path = Path().apply {
    moveTo(10.283f, 16.982f)
    cubicTo(9.212f, 16.982f, 8.314f, 16.028f, 8.314f, 14.823f)
    cubicTo(8.308f, 13.639f, 9.218f, 12.702f, 10.283f, 12.702f)
    cubicTo(11.349f, 12.702f, 12.259f, 13.623f, 12.259f, 14.817f)
    cubicTo(12.259f, 16.028f, 11.355f, 16.982f, 10.283f, 16.982f)
    close()

    moveTo(7.231f, 22.066f)
    cubicTo(6.45f, 22.066f, 6.165f, 21.731f, 6.165f, 21.145f)
    ...
}
```

The scale is added to increase the size of the shape
created_at: 2025-03-18T08:49:29Z
author_association: MEMBER

PR comments:
user: login: codecov-commenter
created_at: 2025-03-11T09:34:31Z
body: ## [Codecov](https://app.codecov.io/gh/wireapp/wire-android/pull/3911?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 45.59%. Comparing base [(`00f744e`)](https://app.codecov.io/gh/wireapp/wire-android/commit/00f744e29b10446e7abc267e1ee2834b125e1005?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp) to head [(`891aea9`)](https://app.codecov.io/gh/wireapp/wire-android/commit/891aea9165264ca0a9464bb6de756eeb60ef7aca?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff            @@
##           develop    #3911   +/-   ##
========================================
  Coverage    45.59%   45.59%           
========================================
  Files          491      491           
  Lines        17087    17087           
  Branches      2851     2851           
========================================
  Hits          7790     7790           
  Misses        8505     8505           
  Partials       792      792           
```

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/wireapp/wire-android/pull/3911?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/wireapp/wire-android/pull/3911?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp). Last update [00f744e...891aea9](https://app.codecov.io/gh/wireapp/wire-android/pull/3911?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wireapp).

</details>
<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-11T09:55:59Z
body: Built [wire-android-staging-compat-pr-3911.apk]() is available for download
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-11T09:56:10Z
body: Built [wire-android-dev-debug-pr-3911.apk]() is available for download
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-11T13:34:23Z
body: Built [wire-android-staging-compat-pr-3911.apk]() is available for download
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-11T13:34:38Z
body: Built [wire-android-dev-debug-pr-3911.apk]() is available for download
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-03-17T16:50:39Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=wireapp_wire-android&pullRequest=3911) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=wireapp_wire-android&pullRequest=3911&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=wireapp_wire-android&pullRequest=3911&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=wireapp_wire-android&pullRequest=3911&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=wireapp_wire-android&pullRequest=3911&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=wireapp_wire-android&pullRequest=3911&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=wireapp_wire-android&pullRequest=3911)


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-17T17:38:10Z
body: Built [wire-android-staging-compat-pr-3911.apk]() is available for download
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-17T17:38:23Z
body: Built [wire-android-dev-debug-pr-3911.apk]() is available for download
author_association: CONTRIBUTOR