Răsfoiți Sursa

Merge pull request #4064 from nextcloud/create_new_conversation

Create new conversation
Marcel Hibbe 9 luni în urmă
părinte
comite
38c1b6b007

+ 1 - 1
app/build.gradle

@@ -309,7 +309,7 @@ dependencies {
     //compose
     implementation(platform("androidx.compose:compose-bom:2024.09.00"))
     implementation("androidx.compose.ui:ui")
-    implementation 'androidx.compose.material3:material3'
+    implementation 'androidx.compose.material3:material3:1.2.1'
     implementation("androidx.compose.ui:ui-tooling-preview")
     implementation 'androidx.activity:activity-compose:1.9.2'
     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.5'

+ 3 - 0
app/src/main/AndroidManifest.xml

@@ -129,6 +129,9 @@
         <activity android:name=".contacts.ContactsActivityCompose"
             android:theme="@style/AppTheme"/>
 
+        <activity android:name=".conversationcreation.ConversationCreationActivity"
+            android:theme="@style/AppTheme"/>
+
         <activity
             android:name=".account.AccountVerificationActivity"
             android:theme="@style/AppTheme" />

+ 57 - 0
app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt

@@ -9,9 +9,15 @@ package com.nextcloud.talk.api
 
 import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
 import com.nextcloud.talk.models.json.conversations.RoomOverall
+import com.nextcloud.talk.models.json.generic.GenericOverall
+import com.nextcloud.talk.models.json.participants.AddParticipantOverall
+import retrofit2.http.DELETE
+import retrofit2.http.Field
+import retrofit2.http.FormUrlEncoded
 import retrofit2.http.GET
 import retrofit2.http.Header
 import retrofit2.http.POST
+import retrofit2.http.PUT
 import retrofit2.http.Query
 import retrofit2.http.QueryMap
 import retrofit2.http.Url
@@ -39,4 +45,55 @@ interface NcApiCoroutines {
         @Url url: String?,
         @QueryMap options: Map<String, String>?
     ): RoomOverall
+
+    /*
+        QueryMap items are as follows:
+            - "roomName" : "newName"
+
+        Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /room/roomToken
+     */
+    @FormUrlEncoded
+    @PUT
+    suspend fun renameRoom(
+        @Header("Authorization") authorization: String?,
+        @Url url: String,
+        @Field("roomName") roomName: String?
+    ): GenericOverall
+
+    @FormUrlEncoded
+    @PUT
+    suspend fun openConversation(
+        @Header("Authorization") authorization: String?,
+        @Url url: String,
+        @Field("scope") scope: Int
+    ): GenericOverall
+
+    @FormUrlEncoded
+    @PUT
+    suspend fun setConversationDescription(
+        @Header("Authorization") authorization: String?,
+        @Url url: String,
+        @Field("description") description: String?
+    ): GenericOverall
+
+    @POST
+    suspend fun addParticipant(
+        @Header("Authorization") authorization: String?,
+        @Url url: String?,
+        @QueryMap options: Map<String, String>?
+    ): AddParticipantOverall
+
+    @POST
+    suspend fun makeRoomPublic(@Header("Authorization") authorization: String?, @Url url: String): GenericOverall
+
+    @DELETE
+    suspend fun makeRoomPrivate(@Header("Authorization") authorization: String?, @Url url: String): GenericOverall
+
+    @FormUrlEncoded
+    @PUT
+    suspend fun setPassword(
+        @Header("Authorization") authorization: String?,
+        @Url url: String?,
+        @Field("password") password: String?
+    ): GenericOverall
 }

+ 267 - 139
app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt

@@ -11,17 +11,20 @@ import android.annotation.SuppressLint
 import android.app.Activity
 import android.content.Context
 import android.content.Intent
+import android.os.Build
 import android.os.Bundle
 import android.util.Log
 import androidx.activity.compose.setContent
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -46,17 +49,25 @@ import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBar
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.res.vectorResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import androidx.core.view.WindowCompat
 import androidx.lifecycle.ViewModelProvider
 import autodagger.AutoInjector
 import coil.compose.AsyncImage
@@ -64,6 +75,7 @@ import com.nextcloud.talk.R
 import com.nextcloud.talk.activities.BaseActivity
 import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.chat.ChatActivity
+import com.nextcloud.talk.conversationcreation.ConversationCreationActivity
 import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
 import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
 import com.nextcloud.talk.utils.bundle.BundleKeys
@@ -82,8 +94,31 @@ class ContactsActivityCompose : BaseActivity() {
         NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
         contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java]
         setContent {
+            val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false)
+            contactsViewModel.updateIsAddParticipants(isAddParticipants)
+            if (isAddParticipants) {
+                contactsViewModel.updateShareTypes(
+                    listOf(
+                        ShareType.Group.shareType,
+                        ShareType.Email.shareType,
+                        ShareType.Circle.shareType
+                    )
+                )
+                contactsViewModel.getContactsFromSearchParams()
+            }
             val colorScheme = viewThemeUtils.getColorScheme(this)
             val uiState = contactsViewModel.contactsViewState.collectAsState()
+            val selectedParticipants = remember {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                    intent.getParcelableArrayListExtra("selectedParticipants", AutocompleteUser::class.java)
+                        ?: emptyList()
+                } else {
+                    @Suppress("DEPRECATION")
+                    intent.getParcelableArrayListExtra("selectedParticipants") ?: emptyList()
+                }
+            }
+            val participants = selectedParticipants.toSet().toMutableList()
+            contactsViewModel.updateSelectedParticipants(participants)
             MaterialTheme(
                 colorScheme = colorScheme
             ) {
@@ -98,112 +133,75 @@ class ContactsActivityCompose : BaseActivity() {
                     },
                     content = {
                         Column(Modifier.padding(it)) {
-                            ConversationCreationOptions(context = context)
+                            ConversationCreationOptions(context = context, contactsViewModel = contactsViewModel)
                             ContactsList(
                                 contactsUiState = uiState.value,
                                 contactsViewModel = contactsViewModel,
-                                context = context
+                                context = context,
+                                selectedParticipants = selectedParticipants.toMutableList()
                             )
                         }
                     }
                 )
             }
-        }
-        setupSystemColors()
-    }
-}
 
-@Composable
-fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsViewModel, context: Context) {
-    when (contactsUiState) {
-        is ContactsUiState.None -> {
-        }
-        is ContactsUiState.Loading -> {
-            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                CircularProgressIndicator()
-            }
-        }
-        is ContactsUiState.Success -> {
-            val contacts = contactsUiState.contacts
-            Log.d(CompanionClass.TAG, "Contacts:$contacts")
-            if (contacts != null) {
-                ContactsItem(contacts, contactsViewModel, context)
-            }
-        }
-        is ContactsUiState.Error -> {
-            val errorMessage = contactsUiState.message
-            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                Text(text = "Error: $errorMessage", color = MaterialTheme.colorScheme.error)
-            }
+            SetStatusBarColor()
         }
     }
-}
 
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-fun ContactsItem(contacts: List<AutocompleteUser>, contactsViewModel: ContactsViewModel, context: Context) {
-    val groupedContacts: Map<String, List<AutocompleteUser>> = contacts.groupBy { contact ->
-        (
-            if (contact.source == "users") {
-                contact.label?.first()?.uppercase()
-            } else {
-                contact.source?.replaceFirstChar { actorType ->
-                    actorType.uppercase()
-                }
-            }
-            ).toString()
-    }
-    LazyColumn(
-        modifier = Modifier
-            .padding(8.dp)
-            .fillMaxWidth(),
-        contentPadding = PaddingValues(all = 10.dp),
-        verticalArrangement = Arrangement.spacedBy(10.dp)
-    ) {
-        groupedContacts.forEach { (initial, contactsForInitial) ->
-            stickyHeader {
-                Column {
-                    Surface(Modifier.fillParentMaxWidth()) {
-                        Header(initial)
-                    }
-                    HorizontalDivider(thickness = 1.dp, color = MaterialTheme.colorScheme.outlineVariant)
-                }
-            }
-            items(contactsForInitial) { contact ->
-                ContactItemRow(contact = contact, contactsViewModel = contactsViewModel, context = context)
-                Log.d(CompanionClass.TAG, "Contacts:$contact")
+    @Composable
+    private fun SetStatusBarColor() {
+        val view = LocalView.current
+        val isDarkMod = isSystemInDarkTheme()
+
+        DisposableEffect(isDarkMod) {
+            val activity = view.context as Activity
+            activity.window.statusBarColor = resources.getColor(R.color.bg_default)
+
+            WindowCompat.getInsetsController(activity.window, activity.window.decorView).apply {
+                isAppearanceLightStatusBars = !isDarkMod
             }
+
+            onDispose { }
         }
     }
 }
 
 @Composable
-fun Header(header: String) {
-    Text(
-        text = header,
-        modifier = Modifier
-            .fillMaxSize()
-            .background(Color.Transparent)
-            .padding(start = 60.dp),
-        color = MaterialTheme.colorScheme.primary,
-        fontWeight = FontWeight.Bold
-    )
-}
-
-@Composable
-fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewModel, context: Context) {
+fun ContactItemRow(
+    contact: AutocompleteUser,
+    contactsViewModel: ContactsViewModel,
+    context: Context,
+    selectedContacts: MutableList<AutocompleteUser>
+) {
+    var isSelected by remember { mutableStateOf(selectedContacts.contains(contact)) }
     val roomUiState by contactsViewModel.roomViewState.collectAsState()
+    val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState()
     Row(
         modifier = Modifier
             .fillMaxWidth()
-            .clickable {
-                contactsViewModel.createRoom(
-                    CompanionClass.ROOM_TYPE_ONE_ONE,
-                    contact.source!!,
-                    contact.id!!,
-                    null
-                )
-            },
+            .clickable(
+                onClick = {
+                    if (!isAddParticipants.value) {
+                        contactsViewModel.createRoom(
+                            CompanionClass.ROOM_TYPE_ONE_ONE,
+                            contact.source!!,
+                            contact.id!!,
+                            null
+                        )
+                    } else {
+                        isSelected = !isSelected
+                        selectedContacts.apply {
+                            if (isSelected) {
+                                add(contact)
+                            } else {
+                                remove(contact)
+                            }
+                        }
+                        contactsViewModel.updateSelectedParticipants(selectedContacts)
+                    }
+                }
+            ),
         verticalAlignment = Alignment.CenterVertically
     ) {
         val imageUri = contact.id?.let { contactsViewModel.getImageUri(it, true) }
@@ -215,13 +213,23 @@ fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewMod
             modifier = Modifier.size(width = 45.dp, height = 45.dp)
         )
         Text(modifier = Modifier.padding(16.dp), text = contact.label!!)
+        if (isAddParticipants.value) {
+            if (isSelected) {
+                Spacer(modifier = Modifier.weight(1f))
+                Icon(
+                    imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle),
+                    contentDescription = "Selected",
+                    tint = Color.Blue,
+                    modifier = Modifier.padding(end = 8.dp)
+                )
+            }
+        }
     }
     when (roomUiState) {
         is RoomUiState.Success -> {
             val conversation = (roomUiState as RoomUiState.Success).conversation
             val bundle = Bundle()
             bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation?.token)
-            // bundle.putString(BundleKeys.KEY_ROOM_ID, conversation?.roomId)
             val chatIntent = Intent(context, ChatActivity::class.java)
             chatIntent.putExtras(bundle)
             chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
@@ -230,7 +238,7 @@ fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewMod
         is RoomUiState.Error -> {
             val errorMessage = (roomUiState as RoomUiState.Error).message
             Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-                Text(text = "Error: $errorMessage", color = MaterialTheme.colorScheme.error)
+                Text(text = "Error: $errorMessage", color = Color.Red)
             }
         }
         is RoomUiState.None -> {}
@@ -243,10 +251,10 @@ fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewMod
 fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) {
     val searchQuery by contactsViewModel.searchQuery.collectAsState()
     val searchState = contactsViewModel.searchState.collectAsState()
+    val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState()
 
     TopAppBar(
         title = { Text(text = title) },
-
         navigationIcon = {
             IconButton(onClick = {
                 (context as? Activity)?.finish()
@@ -260,72 +268,192 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel
             }) {
                 Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon))
             }
+            if (isAddParticipants.value) {
+                Text(
+                    text = stringResource(id = R.string.nc_contacts_done),
+                    modifier = Modifier.clickable {
+                        val resultIntent = Intent().apply {
+                            putParcelableArrayListExtra(
+                                "selectedParticipants",
+                                ArrayList(
+                                    contactsViewModel
+                                        .selectedParticipantsList.value
+                                )
+                            )
+                        }
+                        (context as? Activity)?.setResult(Activity.RESULT_OK, resultIntent)
+                        (context as? Activity)?.finish()
+                    }
+                )
+            }
         }
     )
     if (searchState.value) {
-        DisplaySearch(
-            text = searchQuery,
-            onTextChange = { searchQuery ->
-                contactsViewModel.updateSearchQuery(query = searchQuery)
-                contactsViewModel.getContactsFromSearchParams()
-            },
-            contactsViewModel = contactsViewModel
-        )
+        Row {
+            DisplaySearch(
+                text = searchQuery,
+                onTextChange = { searchQuery ->
+                    contactsViewModel.updateSearchQuery(query = searchQuery)
+                    contactsViewModel.getContactsFromSearchParams()
+                },
+                contactsViewModel = contactsViewModel
+            )
+        }
     }
 }
 
 @Composable
-fun ConversationCreationOptions(context: Context) {
-    Column {
-        Row(
-            modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp),
-            verticalAlignment = Alignment.CenterVertically
-        ) {
-            Icon(
-                painter = painterResource(id = R.drawable.baseline_chat_bubble_outline_24),
+fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsViewModel) {
+    val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsState()
+    if (!isAddParticipants) {
+        Column {
+            Row(
                 modifier = Modifier
-                    .width(40.dp)
-                    .height(40.dp)
-                    .padding(8.dp),
-                contentDescription = null
-            )
-            Text(
+                    .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
+                    .clickable {
+                        val intent = Intent(context, ConversationCreationActivity::class.java)
+                        context.startActivity(intent)
+                    },
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                Icon(
+                    painter = painterResource(id = R.drawable.baseline_chat_bubble_outline_24),
+                    modifier = Modifier
+                        .width(40.dp)
+                        .height(40.dp)
+                        .padding(8.dp),
+                    contentDescription = null
+                )
+                Text(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .wrapContentHeight(),
+                    text = stringResource(R.string.nc_create_new_conversation),
+                    maxLines = 1,
+                    fontSize = 16.sp
+                )
+            }
+            Row(
                 modifier = Modifier
-                    .fillMaxWidth()
-                    .wrapContentHeight(),
-                text = stringResource(R.string.nc_create_new_conversation),
-                maxLines = 1,
-                fontSize = 16.sp
-            )
+                    .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp)
+                    .clickable {
+                        val intent = Intent(context, ListOpenConversationsActivity::class.java)
+                        context.startActivity(intent)
+                    },
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                Icon(
+                    Icons.AutoMirrored.Filled.List,
+                    modifier = Modifier
+                        .width(40.dp)
+                        .height(40.dp)
+                        .padding(8.dp),
+                    contentDescription = null
+                )
+                Text(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .wrapContentHeight(),
+                    text = stringResource(R.string.nc_join_open_conversations),
+                    fontSize = 16.sp
+                )
+            }
         }
-        Row(
-            modifier = Modifier
-                .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp)
-                .clickable {
-                    val intent = Intent(context, ListOpenConversationsActivity::class.java)
-                    context.startActivity(intent)
-                },
-            verticalAlignment = Alignment.CenterVertically
-        ) {
-            Icon(
-                Icons.AutoMirrored.Filled.List,
-                modifier = Modifier
-                    .width(40.dp)
-                    .height(40.dp)
-                    .padding(8.dp),
-                contentDescription = null
-            )
-            Text(
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .wrapContentHeight(),
-                text = stringResource(R.string.nc_join_open_conversations),
-                fontSize = 16.sp
-            )
+    }
+}
+
+@Composable
+fun ContactsList(
+    contactsUiState: ContactsUiState,
+    contactsViewModel: ContactsViewModel,
+    context: Context,
+    selectedParticipants: MutableList<AutocompleteUser>
+) {
+    when (contactsUiState) {
+        is ContactsUiState.None -> {
+        }
+        is ContactsUiState.Loading -> {
+            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                CircularProgressIndicator()
+            }
+        }
+        is ContactsUiState.Success -> {
+            val contacts = contactsUiState.contacts
+            Log.d(CompanionClass.TAG, "Contacts:$contacts")
+            if (contacts != null) {
+                ContactsItem(contacts, contactsViewModel, context, selectedParticipants)
+            }
+        }
+        is ContactsUiState.Error -> {
+            val errorMessage = contactsUiState.message
+            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+                Text(text = "Error: $errorMessage", color = Color.Red)
+            }
         }
     }
 }
 
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun ContactsItem(
+    contacts: List<AutocompleteUser>,
+    contactsViewModel: ContactsViewModel,
+    context: Context,
+    selectedParticipants: MutableList<AutocompleteUser>
+) {
+    val groupedContacts: Map<String, List<AutocompleteUser>> = contacts.groupBy { contact ->
+        (
+            if (contact.source == "users") {
+                contact.label?.first()?.uppercase()
+            } else {
+                contact.source?.replaceFirstChar { actorType ->
+                    actorType.uppercase()
+                }
+            }
+            ).toString()
+    }
+    LazyColumn(
+        modifier = Modifier
+            .padding(8.dp)
+            .fillMaxWidth(),
+        contentPadding = PaddingValues(all = 10.dp),
+        verticalArrangement = Arrangement.spacedBy(10.dp)
+    ) {
+        groupedContacts.forEach { (initial, contactsForInitial) ->
+            stickyHeader {
+                Column {
+                    Surface(Modifier.fillParentMaxWidth()) {
+                        Header(initial)
+                    }
+                    HorizontalDivider(thickness = 0.1.dp, color = Color.Black)
+                }
+            }
+            items(contactsForInitial) { contact ->
+                ContactItemRow(
+                    contact = contact,
+                    contactsViewModel = contactsViewModel,
+                    context = context,
+                    selectedContacts = selectedParticipants
+                )
+                Log.d(CompanionClass.TAG, "Contacts:$contact")
+            }
+        }
+    }
+}
+
+@Composable
+fun Header(header: String) {
+    Text(
+        text = header,
+        modifier = Modifier
+            .fillMaxSize()
+            .background(Color.Transparent)
+            .padding(start = 60.dp),
+        color = Color.Blue,
+        fontWeight = FontWeight.Bold
+    )
+}
+
 class CompanionClass {
     companion object {
         internal val TAG = ContactsActivityCompose::class.simpleName

+ 13 - 3
app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt

@@ -30,6 +30,10 @@ class ContactsViewModel @Inject constructor(
     val shareTypeList: List<String> = shareTypes
     private val _searchState = MutableStateFlow(false)
     val searchState: StateFlow<Boolean> = _searchState
+    private val selectedParticipants = MutableStateFlow<List<AutocompleteUser>>(emptyList())
+    val selectedParticipantsList: StateFlow<List<AutocompleteUser>> = selectedParticipants
+    private val _isAddParticipantsView = MutableStateFlow(false)
+    val isAddParticipantsView: StateFlow<Boolean> = _isAddParticipantsView
 
     init {
         getContactsFromSearchParams()
@@ -39,12 +43,19 @@ class ContactsViewModel @Inject constructor(
         _searchQuery.value = query
     }
 
+    fun updateSelectedParticipants(participants: List<AutocompleteUser>) {
+        selectedParticipants.value = participants
+    }
     fun updateSearchState(searchState: Boolean) {
         _searchState.value = searchState
     }
 
-    fun updateShareTypes(value: String) {
-        shareTypes.add(value)
+    fun updateShareTypes(value: List<String>) {
+        shareTypes.addAll(value)
+    }
+
+    fun updateIsAddParticipants(value: Boolean) {
+        _isAddParticipantsView.value = value
     }
 
     fun getContactsFromSearchParams() {
@@ -62,7 +73,6 @@ class ContactsViewModel @Inject constructor(
             }
         }
     }
-
     fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) {
         viewModelScope.launch {
             try {

+ 2 - 1
app/src/main/java/com/nextcloud/talk/contacts/SearchComponent.kt

@@ -19,6 +19,7 @@ import androidx.compose.material3.IconButton
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextField
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalSoftwareKeyboardController
 import androidx.compose.ui.res.stringResource
@@ -30,6 +31,7 @@ import com.nextcloud.talk.R
 
 @Composable
 fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewModel: ContactsViewModel) {
+    val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState()
     val keyboardController = LocalSoftwareKeyboardController.current
     TextField(
         modifier = Modifier
@@ -42,7 +44,6 @@ fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewMode
                 text = stringResource(R.string.nc_search)
             )
         },
-
         textStyle = TextStyle(
             fontSize = 16.sp
         ),

+ 3 - 3
app/src/main/java/com/nextcloud/talk/contacts/ShareType.kt

@@ -10,7 +10,7 @@ package com.nextcloud.talk.contacts
 enum class ShareType(val shareType: String) {
     User("0"),
     Group("1"),
-    Email(""),
-    Circle(""),
-    Federated("")
+    Email("4"),
+    Remote("5"),
+    Circle("7")
 }

+ 535 - 0
app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt

@@ -0,0 +1,535 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+@file:Suppress("DEPRECATION")
+
+package com.nextcloud.talk.conversationcreation
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.compose.ManagedActivityResultLauncher
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.compose.setContent
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.core.view.WindowCompat
+import androidx.lifecycle.ViewModelProvider
+import autodagger.AutoInjector
+import coil.compose.AsyncImage
+import com.nextcloud.talk.R
+import com.nextcloud.talk.activities.BaseActivity
+import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.chat.ChatActivity
+import com.nextcloud.talk.contacts.ContactsActivityCompose
+import com.nextcloud.talk.contacts.loadImage
+import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
+import com.nextcloud.talk.utils.bundle.BundleKeys
+import javax.inject.Inject
+
+@AutoInjector(NextcloudTalkApplication::class)
+class ConversationCreationActivity : BaseActivity() {
+    @Inject
+    lateinit var viewModelFactory: ViewModelProvider.Factory
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
+        val conversationCreationViewModel = ViewModelProvider(
+            this,
+            viewModelFactory
+        )[ConversationCreationViewModel::class.java]
+        setContent {
+            val colorScheme = viewThemeUtils.getColorScheme(this)
+            val context = LocalContext.current
+            MaterialTheme(
+                colorScheme = colorScheme
+            ) {
+                ConversationCreationScreen(conversationCreationViewModel, context)
+            }
+            SetStatusBarColor()
+        }
+    }
+}
+
+@Composable
+private fun SetStatusBarColor() {
+    val view = LocalView.current
+    val isDarkMod = isSystemInDarkTheme()
+
+    DisposableEffect(isDarkMod) {
+        val activity = view.context as Activity
+        activity.window.statusBarColor = activity.getColor(R.color.bg_default)
+
+        WindowCompat.getInsetsController(activity.window, activity.window.decorView).apply {
+            isAppearanceLightStatusBars = !isDarkMod
+        }
+
+        onDispose { }
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ConversationCreationScreen(conversationCreationViewModel: ConversationCreationViewModel, context: Context) {
+    val launcher = rememberLauncherForActivityResult(
+        contract = ActivityResultContracts.StartActivityForResult(),
+
+        onResult = { result ->
+            if (result.resultCode == Activity.RESULT_OK) {
+                val data = result.data
+                val selectedParticipants = data?.getParcelableArrayListExtra<AutocompleteUser>("selectedParticipants")
+                    ?: emptyList()
+                val participants = selectedParticipants.toMutableList()
+                conversationCreationViewModel.updateSelectedParticipants(participants)
+            }
+        }
+    )
+
+    Scaffold(
+        topBar = {
+            TopAppBar(
+                title = { Text(text = stringResource(id = R.string.nc_new_conversation)) },
+                navigationIcon = {
+                    IconButton(onClick = {
+                        (context as? Activity)?.finish()
+                    }) {
+                        Icon(
+                            Icons.AutoMirrored.Filled.ArrowBack,
+                            contentDescription = stringResource(id = R.string.back_button)
+                        )
+                    }
+                }
+            )
+        },
+        content = { paddingValues ->
+            Column(
+                modifier = Modifier
+                    .padding(paddingValues)
+                    .verticalScroll(rememberScrollState())
+            ) {
+                DefaultUserAvatar()
+                UploadAvatar()
+                ConversationNameAndDescription(conversationCreationViewModel)
+                AddParticipants(launcher, context, conversationCreationViewModel)
+                RoomCreationOptions(conversationCreationViewModel)
+                CreateConversation(conversationCreationViewModel, context)
+            }
+        }
+    )
+}
+
+@Composable
+fun DefaultUserAvatar() {
+    Box(
+        modifier = Modifier.fillMaxWidth(),
+        contentAlignment = Alignment.Center
+    ) {
+        AsyncImage(
+            model = R.drawable.ic_circular_group,
+            contentDescription = stringResource(id = R.string.user_avatar),
+            modifier = Modifier
+                .size(width = 84.dp, height = 84.dp)
+                .padding(top = 8.dp)
+        )
+    }
+}
+
+@Composable
+fun UploadAvatar() {
+    Row(
+        modifier = Modifier
+            .fillMaxWidth()
+            .padding(16.dp),
+        horizontalArrangement = Arrangement.Center
+    ) {
+        IconButton(onClick = {
+        }) {
+            Icon(
+                painter = painterResource(id = R.drawable.ic_baseline_photo_camera_24),
+                contentDescription = null,
+                modifier = Modifier.size(24.dp)
+            )
+        }
+
+        IconButton(onClick = {
+        }) {
+            Icon(
+                painter = painterResource(id = R.drawable.ic_folder_multiple_image),
+                contentDescription = null,
+                modifier = Modifier.size(24.dp)
+            )
+        }
+
+        IconButton(onClick = {
+        }) {
+            Icon(
+                painter = painterResource(id = R.drawable.baseline_tag_faces_24),
+                contentDescription = null,
+                modifier = Modifier.size(24.dp)
+            )
+        }
+
+        IconButton(onClick = {
+        }) {
+            Icon(
+                painter = painterResource(id = R.drawable.ic_delete_grey600_24dp),
+                contentDescription = null,
+                modifier = Modifier.size(24.dp)
+            )
+        }
+    }
+}
+
+@Composable
+fun ConversationNameAndDescription(conversationCreationViewModel: ConversationCreationViewModel) {
+    val conversationRoomName = conversationCreationViewModel.roomName.collectAsState()
+    val conversationDescription = conversationCreationViewModel.conversationDescription.collectAsState()
+    OutlinedTextField(
+        value = conversationRoomName.value,
+        onValueChange = {
+            conversationCreationViewModel.updateRoomName(it)
+        },
+        label = { Text(text = stringResource(id = R.string.nc_call_name)) },
+        modifier = Modifier
+            .padding(start = 16.dp, end = 16.dp)
+            .fillMaxWidth()
+    )
+    OutlinedTextField(
+        value = conversationDescription.value,
+        onValueChange = {
+            conversationCreationViewModel.updateConversationDescription(it)
+        },
+        label = { Text(text = stringResource(id = R.string.nc_conversation_description)) },
+        modifier = Modifier
+            .padding(top = 8.dp, start = 16.dp, end = 16.dp)
+            .fillMaxWidth()
+    )
+}
+
+@SuppressLint("SuspiciousIndentation")
+@Composable
+fun AddParticipants(
+    launcher: ManagedActivityResultLauncher<Intent, ActivityResult>,
+    context: Context,
+    conversationCreationViewModel: ConversationCreationViewModel
+) {
+    val participants = conversationCreationViewModel.selectedParticipants.collectAsState().value
+
+    Column(
+        modifier = Modifier
+            .fillMaxHeight()
+            .padding(start = 16.dp, end = 16.dp, top = 16.dp)
+    ) {
+        Row {
+            Text(
+                text = stringResource(id = R.string.nc_participants).uppercase(),
+                fontSize = 14.sp,
+                modifier = Modifier.padding(start = 0.dp, bottom = 16.dp)
+            )
+            Spacer(modifier = Modifier.weight(1f))
+            if (participants.isNotEmpty()) {
+                Text(
+                    text = stringResource(id = R.string.nc_edit),
+                    fontSize = 12.sp,
+                    modifier = Modifier
+                        .padding(start = 16.dp, bottom = 16.dp)
+                        .clickable {
+                            val intent = Intent(context, ContactsActivityCompose::class.java)
+                            intent.putParcelableArrayListExtra(
+                                "selectedParticipants",
+                                participants as ArrayList<AutocompleteUser>
+                            )
+                            intent.putExtra("isAddParticipants", true)
+                            intent.putExtra("isAddParticipantsEdit", true)
+                            launcher.launch(intent)
+                        },
+                    textAlign = TextAlign.Right
+                )
+            }
+        }
+        participants.toSet().forEach { participant ->
+            Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+                val imageUri = participant.id?.let { conversationCreationViewModel.getImageUri(it, true) }
+                val errorPlaceholderImage: Int = R.drawable.account_circle_96dp
+                val loadedImage = loadImage(imageUri, context, errorPlaceholderImage)
+                AsyncImage(
+                    model = loadedImage,
+                    contentDescription = stringResource(id = R.string.user_avatar),
+                    modifier = Modifier.size(width = 32.dp, height = 32.dp)
+                )
+                participant.label?.let {
+                    Text(
+                        text = it,
+                        modifier = Modifier.padding(all = 16.dp),
+                        fontSize = 15.sp
+                    )
+                }
+            }
+            HorizontalDivider(thickness = 0.1.dp, color = Color.Black)
+        }
+
+        Row(
+            modifier = Modifier
+                .fillMaxWidth()
+                .clickable {
+                    val intent = Intent(context, ContactsActivityCompose::class.java)
+                    intent.putExtra("isAddParticipants", true)
+                    launcher.launch(intent)
+                },
+            verticalAlignment = Alignment.CenterVertically
+        ) {
+            if (participants.isEmpty()) {
+                Icon(
+                    painter = painterResource(id = R.drawable.ic_account_plus),
+                    contentDescription = null,
+                    modifier = Modifier.size(24.dp)
+                )
+                Text(
+                    text = stringResource(id = R.string.nc_add_participants),
+                    modifier = Modifier.padding(start = 16.dp)
+                )
+            }
+        }
+    }
+}
+
+@Composable
+fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewModel) {
+    val isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed.value
+    val isConversationAvailableForRegisteredUsers = conversationCreationViewModel
+        .isConversationAvailableForRegisteredUsers.value
+    val isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value
+
+    Text(
+        text = stringResource(id = R.string.nc_new_conversation_visibility).uppercase(),
+        fontSize = 14.sp,
+        modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp)
+    )
+    ConversationOptions(
+        icon = R.drawable.ic_avatar_link,
+        text = R.string.nc_guest_access_allow_title,
+        switch = {
+            Switch(
+                checked = isGuestsAllowed,
+                onCheckedChange = {
+                    conversationCreationViewModel.isGuestsAllowed.value = it
+                }
+            )
+        },
+        showDialog = false,
+        conversationCreationViewModel = conversationCreationViewModel
+    )
+
+    if (isGuestsAllowed) {
+        ConversationOptions(
+            icon = R.drawable.ic_lock_grey600_24px,
+            text = R.string.nc_set_password,
+            showDialog = true,
+            conversationCreationViewModel = conversationCreationViewModel
+        )
+    }
+
+    ConversationOptions(
+        icon = R.drawable.baseline_format_list_bulleted_24,
+        text = R.string.nc_open_conversation_to_registered_users,
+        switch = {
+            Switch(
+                checked = isConversationAvailableForRegisteredUsers,
+                onCheckedChange = {
+                    conversationCreationViewModel.isConversationAvailableForRegisteredUsers.value = it
+                }
+            )
+        },
+        showDialog = false,
+        conversationCreationViewModel = conversationCreationViewModel
+    )
+
+    if (isConversationAvailableForRegisteredUsers) {
+        ConversationOptions(
+            text = R.string.nc_open_to_guest_app_users,
+            switch = {
+                Switch(
+                    checked = isOpenForGuestAppUsers,
+                    onCheckedChange = {
+                        conversationCreationViewModel.openForGuestAppUsers.value = it
+                    }
+                )
+            },
+            showDialog = false,
+            conversationCreationViewModel = conversationCreationViewModel
+        )
+    }
+}
+
+@Composable
+fun ConversationOptions(
+    icon: Int? = null,
+    text: Int,
+    switch: @Composable (() -> Unit)? = null,
+    showDialog: Boolean,
+    conversationCreationViewModel: ConversationCreationViewModel
+) {
+    var showPasswordDialog by remember { mutableStateOf(false) }
+    Row(
+        modifier = Modifier
+            .fillMaxWidth()
+            .padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
+            .then(
+                if (showDialog) {
+                    Modifier.clickable {
+                        showPasswordDialog = true
+                    }
+                } else {
+                    Modifier
+                }
+            ),
+        horizontalArrangement = Arrangement.SpaceBetween,
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        if (icon != null) {
+            Icon(
+                painter = painterResource(id = icon),
+                contentDescription = null,
+                modifier = Modifier.size(24.dp)
+            )
+            Spacer(modifier = Modifier.width(16.dp))
+        } else {
+            Spacer(modifier = Modifier.width(40.dp))
+        }
+        Text(
+            text = stringResource(id = text),
+            modifier = Modifier.weight(1f)
+        )
+        if (switch != null) {
+            switch()
+        }
+        if (showPasswordDialog) {
+            ShowPasswordDialog(
+                onDismiss = { showPasswordDialog = false },
+                conversationCreationViewModel = conversationCreationViewModel
+            )
+        }
+    }
+}
+
+@Composable
+fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) {
+    var password by remember { mutableStateOf("") }
+
+    AlertDialog(
+        onDismissRequest = onDismiss,
+        confirmButton = {
+            Button(onClick = {
+                conversationCreationViewModel.updatePassword(password)
+                onDismiss()
+            }) {
+                Text(text = stringResource(id = R.string.save))
+            }
+        },
+        title = { Text(text = stringResource(id = R.string.nc_set_password)) },
+        text = {
+            TextField(
+                value = password,
+                onValueChange = {
+                    password = it
+                },
+                label = { Text(text = stringResource(id = R.string.nc_guest_access_password_dialog_hint)) }
+            )
+        },
+        dismissButton = {
+            Button(onClick = { onDismiss() }) {
+                Text(text = stringResource(id = R.string.nc_cancel))
+            }
+        }
+    )
+}
+
+@Composable
+fun CreateConversation(conversationCreationViewModel: ConversationCreationViewModel, context: Context) {
+    val selectedParticipants by conversationCreationViewModel.selectedParticipants.collectAsState()
+    Box(
+        modifier = Modifier
+            .fillMaxWidth()
+            .padding(all = 16.dp),
+        contentAlignment = Alignment.Center
+    ) {
+        Button(
+            onClick = {
+                conversationCreationViewModel.createRoomAndAddParticipants(
+                    roomType = CompanionClass.ROOM_TYPE_GROUP,
+                    conversationName = conversationCreationViewModel.roomName.value,
+                    participants = selectedParticipants.toSet()
+                ) { roomToken ->
+                    val bundle = Bundle()
+                    bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
+                    val chatIntent = Intent(context, ChatActivity::class.java)
+                    chatIntent.putExtras(bundle)
+                    chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                    context.startActivity(chatIntent)
+                }
+            }
+        ) {
+            Text(text = stringResource(id = R.string.create_conversation))
+        }
+    }
+}
+class CompanionClass {
+    companion object {
+        internal val TAG = ConversationCreationActivity::class.simpleName
+        internal const val ROOM_TYPE_GROUP = "2"
+    }
+}

+ 24 - 0
app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepository.kt

@@ -0,0 +1,24 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package com.nextcloud.talk.conversationcreation
+
+import com.nextcloud.talk.models.json.conversations.RoomOverall
+import com.nextcloud.talk.models.json.generic.GenericOverall
+import com.nextcloud.talk.models.json.participants.AddParticipantOverall
+
+interface ConversationCreationRepository {
+
+    suspend fun allowGuests(token: String, allow: Boolean): GenericOverall
+    suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall
+    suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall
+    suspend fun openConversation(roomToken: String, scope: Int): GenericOverall
+    suspend fun addParticipants(conversationToken: String?, userId: String, sourceType: String): AddParticipantOverall
+    suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall
+    fun getImageUri(avatarId: String, requestBigSize: Boolean): String
+    suspend fun setPassword(roomToken: String, password: String): GenericOverall
+}

+ 150 - 0
app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationRepositoryImpl.kt

@@ -0,0 +1,150 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package com.nextcloud.talk.conversationcreation
+
+import com.nextcloud.talk.api.NcApiCoroutines
+import com.nextcloud.talk.data.user.model.User
+import com.nextcloud.talk.models.RetrofitBucket
+import com.nextcloud.talk.models.json.conversations.RoomOverall
+import com.nextcloud.talk.models.json.generic.GenericOverall
+import com.nextcloud.talk.models.json.participants.AddParticipantOverall
+import com.nextcloud.talk.users.UserManager
+import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipant
+import com.nextcloud.talk.utils.ApiUtils.getRetrofitBucketForAddParticipantWithSource
+
+class ConversationCreationRepositoryImpl(
+    private val ncApiCoroutines: NcApiCoroutines,
+    private val userManager: UserManager
+) : ConversationCreationRepository {
+    private val _currentUser = userManager.currentUser.blockingGet()
+    val currentUser: User = _currentUser
+    val credentials = ApiUtils.getCredentials(_currentUser.username, _currentUser.token)
+    val apiVersion = ApiUtils.getConversationApiVersion(_currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
+
+    override suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall {
+        return ncApiCoroutines.renameRoom(
+            credentials,
+            ApiUtils.getUrlForRoom(
+                apiVersion,
+                _currentUser.baseUrl,
+                roomToken
+            ),
+            roomNameNew
+        )
+    }
+
+    override suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall {
+        return ncApiCoroutines.setConversationDescription(
+            credentials,
+            ApiUtils.getUrlForConversationDescription(
+                apiVersion,
+                _currentUser.baseUrl,
+                roomToken
+            ),
+            description
+        )
+    }
+
+    override suspend fun openConversation(roomToken: String, scope: Int): GenericOverall {
+        return ncApiCoroutines.openConversation(
+            credentials,
+            ApiUtils.getUrlForOpeningConversations(
+                apiVersion,
+                _currentUser.baseUrl,
+                roomToken
+            ),
+            scope
+        )
+    }
+
+    override suspend fun addParticipants(
+        conversationToken: String?,
+        userId: String,
+        sourceType: String
+    ): AddParticipantOverall {
+        val retrofitBucket: RetrofitBucket = if (sourceType == "users") {
+            getRetrofitBucketForAddParticipant(
+                apiVersion,
+                _currentUser.baseUrl,
+                conversationToken,
+                userId
+            )
+        } else {
+            getRetrofitBucketForAddParticipantWithSource(
+                apiVersion,
+                _currentUser.baseUrl,
+                conversationToken,
+                sourceType,
+                userId
+            )
+        }
+        val participants = ncApiCoroutines.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap)
+        return participants
+    }
+
+    override suspend fun createRoom(roomType: String, conversationName: String?): RoomOverall {
+        val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
+            apiVersion,
+            _currentUser.baseUrl,
+            roomType,
+            null,
+            null,
+            conversationName
+        )
+        val response = ncApiCoroutines.createRoom(
+            credentials,
+            retrofitBucket.url,
+            retrofitBucket.queryMap
+        )
+        return response
+    }
+
+    override fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
+        return ApiUtils.getUrlForAvatar(
+            _currentUser.baseUrl,
+            avatarId,
+            requestBigSize
+        )
+    }
+
+    override suspend fun setPassword(roomToken: String, password: String): GenericOverall {
+        val result = ncApiCoroutines.setPassword(
+            credentials,
+            ApiUtils.getUrlForRoomPassword(
+                apiVersion,
+                _currentUser.baseUrl!!,
+                roomToken
+            ),
+            password
+        )
+        return result
+    }
+
+    override suspend fun allowGuests(token: String, allow: Boolean): GenericOverall {
+        val url = ApiUtils.getUrlForRoomPublic(
+            apiVersion,
+            _currentUser.baseUrl!!,
+            token
+        )
+
+        val result: GenericOverall = if (allow) {
+            ncApiCoroutines.makeRoomPublic(
+                credentials,
+                url
+            )
+        } else {
+            ncApiCoroutines.makeRoomPrivate(
+                credentials,
+                url
+            )
+        }
+
+        return result
+    }
+}

+ 156 - 0
app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt

@@ -0,0 +1,156 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package com.nextcloud.talk.conversationcreation
+
+import android.util.Log
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
+import com.nextcloud.talk.models.json.conversations.Conversation
+import com.nextcloud.talk.models.json.generic.GenericMeta
+import com.nextcloud.talk.repositories.conversations.ConversationsRepositoryImpl.Companion.STATUS_CODE_OK
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+class ConversationCreationViewModel @Inject constructor(
+    private val repository: ConversationCreationRepository
+) : ViewModel() {
+    private val _selectedParticipants = MutableStateFlow<List<AutocompleteUser>>(emptyList())
+    val selectedParticipants: StateFlow<List<AutocompleteUser>> = _selectedParticipants
+    private val roomViewState = MutableStateFlow<RoomUIState>(RoomUIState.None)
+
+    fun updateSelectedParticipants(participants: List<AutocompleteUser>) {
+        _selectedParticipants.value = participants
+    }
+
+    private val _roomName = MutableStateFlow("")
+    val roomName: StateFlow<String> = _roomName
+    private val _password = MutableStateFlow("")
+    val password: StateFlow<String> = _password
+    private val _conversationDescription = MutableStateFlow("")
+    val conversationDescription: StateFlow<String> = _conversationDescription
+    var isGuestsAllowed = mutableStateOf(false)
+    var isConversationAvailableForRegisteredUsers = mutableStateOf(false)
+    var openForGuestAppUsers = mutableStateOf(false)
+    private val addParticipantsViewState = MutableStateFlow<AddParticipantsUiState>(AddParticipantsUiState.None)
+    private val allowGuestsResult = MutableStateFlow<AllowGuestsUiState>(AllowGuestsUiState.None)
+    fun updateRoomName(roomName: String) {
+        _roomName.value = roomName
+    }
+
+    fun updatePassword(password: String) {
+        _password.value = password
+    }
+
+    fun updateConversationDescription(conversationDescription: String) {
+        _conversationDescription.value = conversationDescription
+    }
+
+    fun createRoomAndAddParticipants(
+        roomType: String,
+        conversationName: String,
+        participants: Set<AutocompleteUser>,
+        onRoomCreated: (String) -> Unit
+    ) {
+        val scope = when {
+            isConversationAvailableForRegisteredUsers.value && !openForGuestAppUsers.value -> 1
+            isConversationAvailableForRegisteredUsers.value && openForGuestAppUsers.value -> 2
+            else -> 0
+        }
+        viewModelScope.launch {
+            roomViewState.value = RoomUIState.None
+            try {
+                val roomResult = repository.createRoom(roomType, conversationName)
+                val conversation = roomResult.ocs?.data
+
+                if (conversation != null) {
+                    val token = conversation.token
+                    if (token != null) {
+                        try {
+                            repository.setConversationDescription(
+                                token,
+                                _conversationDescription.value
+                            )
+                            val allowGuestResultOverall = repository.allowGuests(token, isGuestsAllowed.value)
+                            val statusCode: GenericMeta? = allowGuestResultOverall.ocs?.meta
+                            val result = (statusCode?.statusCode == STATUS_CODE_OK)
+                            if (result) {
+                                allowGuestsResult.value = AllowGuestsUiState.Success(result)
+                                for (participant in participants) {
+                                    if (participant.id != null) {
+                                        val participantOverall = repository.addParticipants(
+                                            token,
+                                            participant.id!!,
+                                            participant.source!!
+                                        ).ocs?.data
+                                        addParticipantsViewState.value =
+                                            AddParticipantsUiState.Success(participantOverall)
+                                    }
+                                }
+                            }
+                            if (_password.value.isNotEmpty()) {
+                                repository.setPassword(token, _password.value)
+                            }
+                            repository.openConversation(token, scope)
+                            onRoomCreated(token)
+                        } catch (exception: Exception) {
+                            allowGuestsResult.value = AllowGuestsUiState.Error(exception.message ?: "")
+                        }
+                    }
+                    roomViewState.value = RoomUIState.Success(conversation)
+                } else {
+                    roomViewState.value = RoomUIState.Error("Conversation is null")
+                }
+            } catch (e: Exception) {
+                roomViewState.value = RoomUIState.Error(e.message ?: "Unknown error")
+                Log.e("ConversationCreationViewModel", "Error - ${e.message}")
+            }
+        }
+    }
+
+    fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
+        return repository.getImageUri(avatarId, requestBigSize)
+    }
+
+    fun createRoom(roomType: String, conversationName: String?) {
+        viewModelScope.launch {
+            try {
+                val room = repository.createRoom(
+                    roomType,
+                    conversationName
+                )
+
+                val conversation: Conversation? = room.ocs?.data
+                roomViewState.value = RoomUIState.Success(conversation)
+            } catch (exception: Exception) {
+                roomViewState.value = RoomUIState.Error(exception.message ?: "")
+            }
+        }
+    }
+}
+
+sealed class AllowGuestsUiState {
+    data object None : AllowGuestsUiState()
+    data class Success(val result: Boolean) : AllowGuestsUiState()
+    data class Error(val message: String) : AllowGuestsUiState()
+}
+
+sealed class RoomUIState {
+    data object None : RoomUIState()
+    data class Success(val conversation: Conversation?) : RoomUIState()
+    data class Error(val message: String) : RoomUIState()
+}
+
+sealed class AddParticipantsUiState {
+    data object None : AddParticipantsUiState()
+    data class Success(val participants: List<Conversation>?) : AddParticipantsUiState()
+    data class Error(val message: String) : AddParticipantsUiState()
+}

+ 10 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt

@@ -19,6 +19,8 @@ import com.nextcloud.talk.contacts.ContactsRepository
 import com.nextcloud.talk.contacts.ContactsRepositoryImpl
 import com.nextcloud.talk.conversation.repository.ConversationRepository
 import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
+import com.nextcloud.talk.conversationcreation.ConversationCreationRepository
+import com.nextcloud.talk.conversationcreation.ConversationCreationRepositoryImpl
 import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
 import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepositoryImpl
 import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
@@ -208,4 +210,12 @@ class RepositoryModule {
     fun provideContactsRepository(ncApiCoroutines: NcApiCoroutines, userManager: UserManager): ContactsRepository {
         return ContactsRepositoryImpl(ncApiCoroutines, userManager)
     }
+
+    @Provides
+    fun provideConversationCreationRepository(
+        ncApiCoroutines: NcApiCoroutines,
+        userManager: UserManager
+    ): ConversationCreationRepository {
+        return ConversationCreationRepositoryImpl(ncApiCoroutines, userManager)
+    }
 }

+ 6 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt

@@ -14,6 +14,7 @@ import com.nextcloud.talk.contacts.ContactsViewModel
 import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
 import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
 import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
+import com.nextcloud.talk.conversationcreation.ConversationCreationViewModel
 import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
 import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
 import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel
@@ -156,4 +157,9 @@ abstract class ViewModelModule {
     @IntoMap
     @ViewModelKey(ContactsViewModel::class)
     abstract fun contactsViewModel(viewModel: ContactsViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(ConversationCreationViewModel::class)
+    abstract fun conversationCreationViewModel(viewModel: ConversationCreationViewModel): ViewModel
 }

+ 2 - 2
app/src/main/java/com/nextcloud/talk/data/database/mappers/ChatMessageMapUtils.kt

@@ -7,9 +7,9 @@
 
 package com.nextcloud.talk.data.database.mappers
 
-import com.nextcloud.talk.chat.data.model.ChatMessage
-import com.nextcloud.talk.data.database.model.ChatMessageEntity
 import com.nextcloud.talk.models.json.chat.ChatMessageJson
+import com.nextcloud.talk.data.database.model.ChatMessageEntity
+import com.nextcloud.talk.chat.data.model.ChatMessage
 
 fun ChatMessageJson.asEntity(accountId: Long) =
     ChatMessageEntity(

+ 4 - 0
app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt

@@ -539,6 +539,10 @@ object ApiUtils {
         return getUrlForRoom(version, baseUrl, token) + "/description"
     }
 
+    fun getUrlForOpeningConversations(version: Int, baseUrl: String?, token: String): String {
+        return getUrlForRoom(version, baseUrl, token) + "/listable"
+    }
+
     fun getUrlForTranslation(baseUrl: String): String {
         return "$baseUrl$OCS_API_VERSION/translation/translate"
     }

+ 18 - 0
app/src/main/res/drawable/baseline_tag_faces_24.xml

@@ -0,0 +1,18 @@
+<!--
+  ~ Nextcloud Talk - Android Client
+  ~
+  ~ SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
+  ~ SPDX-License-Identifier: GPL-3.0-or-later
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:tint="#000000"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp">
+      
+    <path android:fillColor="@android:color/white"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,17.5c2.33,0 4.31,-1.46 5.11,-3.5L6.89,14c0.8,2.04 2.78,3.5 5.11,3.5z"/>
+    
+</vector>

+ 6 - 0
app/src/main/res/values/strings.xml

@@ -242,6 +242,9 @@ How to translate with transifex:
     <string name="nc_remove_from_favorites">Remove from favorites</string>
     <string name="nc_create_new_conversation">Create a new conversation</string>
     <string name="nc_join_open_conversations">Join open conversations</string>
+    <string name="nc_open_conversation_to_registered_users">Open conversation to registered users</string>
+    <string name="nc_open_to_guest_app_users">Also open to guest app users</string>
+    <string name="nc_new_conversation_visibility">Visibility</string>
 
     <string name="added_to_favorites">Added conversation %1$s to favorites</string>
     <string name="removed_from_favorites">Removed conversation %1$s from favorites</string>
@@ -386,6 +389,7 @@ How to translate with transifex:
     <string name="close_icon">Close Icon</string>
     <string name="nc_refresh">Refresh</string>
     <string name="nc_check_your_internet">Please check your internet connection</string>
+    <string name="nc_visible">Visible</string>
 
     <!-- Chat -->
     <string name="nc_hint_enter_a_message">Enter a message …</string>
@@ -429,6 +433,7 @@ How to translate with transifex:
     <string name="nc_guest_access_allow_title">Allow guests</string>
     <string name="nc_guest_access_allow_summary">Allow guests to share a public link to join this conversation.</string>
     <string name="nc_guest_access_allow_failed">Cannot enable/disable guest access.</string>
+    <string name="nc_set_password">Set Password</string>
     <string name="nc_guest_access_password_title">Password protection</string>
     <string name="nc_guest_access_password_summary">Set a password to restrict who can use the public link.</string>
     <string name="nc_guest_access_password_dialog_title">Guest access password</string>
@@ -563,6 +568,7 @@ How to translate with transifex:
     <string name="no_phone_book_integration_due_to_permissions">No phone number integration due to missing permissions</string>
     <string name="nc_phone_book_integration_chat_via">Chat via %s</string>
     <string name="nc_phone_book_integration_account_not_found">Account not found</string>
+    <string name= "nc_edit">Edit</string>
 
     <!--  save feature -->
     <string name="nc_save_message">Save</string>

+ 1 - 1
app/src/test/java/com/nextcloud/talk/contacts/ContactsViewModelTest.kt

@@ -81,7 +81,7 @@ class ContactsViewModelTest {
 
     @Test
     fun `update shareTypes`() {
-        viewModel.updateShareTypes(ShareType.Group.shareType)
+        viewModel.updateShareTypes(listOf(ShareType.Group.shareType))
         assert(viewModel.shareTypeList.contains(ShareType.Group.shareType))
     }
 

+ 133 - 5
gradle/verification-metadata.xml

@@ -4,13 +4,13 @@
       <verify-metadata>true</verify-metadata>
       <verify-signatures>true</verify-signatures>
       <trusted-artifacts>
-         <trust file="tensorflow-lite-metadata-0.1.0-rc2.pom" reason="differing hash on every CI run - temp global trust"/>
-         <trust group="androidx.fragment"/>
          <trust group="com.android.tools.build" name="aapt2" version="8.4.1-11315950" reason="ships OS specific artifacts (win/linux) - temp global trust"/>
          <trust group="com.github.nextcloud-deps" name="android-talk-webrtc" version="110.5481.0" reason="ships OS specific artifacts (win/linux) - temp global trust"/>
-         <trust group="com.google.dagger"/>
-         <trust group="org.javassist" name="javassist" version="3.26.0-GA" reason="java assist"/>
          <trust file=".*-sources[.]jar" regex="true"/>
+		 <trust file="tensorflow-lite-metadata-0.1.0-rc2.pom" reason="differing hash on every CI run - temp global trust"/>
+          <trust group="com.google.dagger" />
+          <trust group="org.javassist" name="javassist" version="3.26.0-GA" reason="java assist"/>
+          <trust group="androidx.fragment"/>
       </trusted-artifacts>
       <ignored-keys>
          <ignored-key id="0AA3E5C3D232E79B" reason="Key couldn't be downloaded from any key server"/>
@@ -145,7 +145,6 @@
             <trusting group="androidx.annotation"/>
             <trusting group="androidx.camera"/>
             <trusting group="androidx.collection"/>
-            <trusting group="androidx.compose.foundation"/>
             <trusting group="androidx.compose.material3"/>
             <trusting group="androidx.core"/>
             <trusting group="androidx.emoji2"/>
@@ -157,6 +156,7 @@
             <trusting group="androidx.sqlite"/>
             <trusting group="androidx.webkit"/>
             <trusting group="androidx.work"/>
+             <trusting group="androidx.compose.foundation"/>
          </trusted-key>
          <trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1">
             <trusting group="org.apache" name="apache"/>
@@ -246,6 +246,7 @@
          <trusted-key id="E4AC7874F3479A0F1F8ECF9960BB45F36B649F22" group="fr.dudie" name="nominatim-api" version="3.4"/>
          <trusted-key id="E77417AC194160A3FABD04969A259C7EE636C5ED" group="^com[.]google($|([.].*))" regex="true"/>
          <trusted-key id="E7DC75FC24FB3C8DFE8086AD3D5839A2262CBBFB" group="org.jetbrains.kotlinx"/>
+          <trusted-key id="64B9B09F164AA0BF88742EB61188B69F6D6259CA" group="com.google.accompanist"/>
          <trusted-key id="E82D2EAF2E83830CE1F7F6BE571A5291E827E1C7" group="net.java" name="jvnet-parent" version="3"/>
          <trusted-key id="E85AED155021AF8A6C6B7A4A7C7D8456294423BA" group="org.objenesis"/>
          <trusted-key id="EAA526B91DD83BA3E1B9636FA730529CA355A63E" group="org.ccil.cowan.tagsoup" name="tagsoup" version="1.2.1"/>
@@ -343,6 +344,50 @@
             <sha256 value="9516c2ae44284ea0bd3d0eade0ee638879b708cbe31e3af92ba96c300604ebc3" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+       <component group="androidx.exifinterface" name="exifinterface" version="1.3.6">
+           <artifact name="exifinterface-1.3.6.aar">
+               <sha256 value="1804105e9e05fdd8f760413bad5de498c381aa329f4f9d94c851bc891ac654c6" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+           <artifact name="exifinterface-1.3.6.module">
+               <sha256 value="5e9fd84ca3fd3b7706f6856fa4383107de8676bf7c42b7d4b8108949414d6201" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.core" name="core" version="1.1.0">
+           <artifact name="core-1.1.0.pom">
+               <sha256 value="dae46132cdcd46b798425f7cb78fd65890869b6d26101ccdcd43461a4f51754c" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.core" name="core" version="1.3.2">
+           <artifact name="core-1.3.2.pom">
+               <sha256 value="afb5ea494dd083ed404cd51f580d218e37362f8ae326e893bee521290ed34920" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.test.ext" name="junit" version="1.1.5">
+           <artifact name="junit-1.1.5.aar">
+               <sha256 value="4307c0e60f5d701db9c59bcd9115af705113c36a9132fa3dbad58db1294e9bfd" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+           <artifact name="junit-1.1.5.pom">
+               <sha256 value="4cff0df04cae25831e821ef2f9129245783460e98d0fd67d8f6824065a134c4e" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.core" name="core-ktx" version="1.8.0">
+           <artifact name="core-ktx-1.8.0.module">
+               <sha256 value="a91bc3e02f209f643dd8275345a9e3003ce20d64fc0760eccf479c1709842f72" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.annotation" name="annotation-experimental" version="1.3.0">
+           <artifact name="annotation-experimental-1.3.0.aar">
+               <sha256 value="abfd29c8556e5bd0325a9f769ab9e9d154ff4a5515c476cdd5a2a8285b1b19dc" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+           <artifact name="annotation-experimental-1.3.0.module">
+               <sha256 value="5eebeaff01d042e06dcf292abf8964ad391e4b0159f0090f16253d6045d38da0" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.annotation" name="annotation-experimental" version="1.1.0-rc01">
+           <artifact name="annotation-experimental-1.1.0-rc01.module">
+               <sha256 value="d45ac493e84d968aabb2bea2b7744031a98cf5074447c0f3b862d600fc44b55c" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
       <component group="androidx.annotation" name="annotation" version="1.5.0">
          <artifact name="annotation-1.5.0.jar">
             <sha256 value="261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a" origin="Generated by Gradle" reason="Artifact is not signed"/>
@@ -359,6 +404,14 @@
             <sha256 value="fbc64f5c44a7added8b6eab517cf7d70555e25153bf5d44a6ed9b0e5312f7de9" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+       <component group="androidx.exifinterface" name="exifinterface" version="1.3.2">
+           <artifact name="exifinterface-1.3.2.aar">
+               <sha256 value="8770c180103e0b8c04a07eb4c59153af639b09eca25deae9bdcdaf869d1e5b6b" origin="Generated by Gradle"/>
+           </artifact>
+           <artifact name="exifinterface-1.3.2.module">
+               <sha256 value="10ba5b5cbea7f5c8758be4fdaec60a3545e891a1130d830a442b88cf5336a885" origin="Generated by Gradle"/>
+           </artifact>
+       </component>
       <component group="androidx.annotation" name="annotation-experimental" version="1.0.0">
          <artifact name="annotation-experimental-1.0.0.pom">
             <sha256 value="6b73ff6608f4b1d6cbab620b65708a382d0b39901cf4e6b0d16f84a1b04d7732" origin="Generated by Gradle" reason="Artifact is not signed"/>
@@ -372,6 +425,11 @@
             <sha256 value="0361d1526a4d7501255e19779e09e93cdbd07fee0e2f5c50b7a137432d510119" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+       <component group="androidx.annotation" name="annotation-experimental" version="1.1.0-rc01">
+       <artifact name="annotation-experimental-1.1.0-rc01.module">
+           <sha256 value="d45ac493e84d968aabb2bea2b7744031a98cf5074447c0f3b862d600fc44b55c" origin="Generated by Gradle" reason="Artifact is not signed"/>
+       </artifact>
+       </component>
       <component group="androidx.annotation" name="annotation-experimental" version="1.1.0-rc01">
          <artifact name="annotation-experimental-1.1.0-rc01.module">
             <sha256 value="d45ac493e84d968aabb2bea2b7744031a98cf5074447c0f3b862d600fc44b55c" origin="Generated by Gradle" reason="Artifact is not signed"/>
@@ -393,6 +451,76 @@
             <sha256 value="9b6974a7dfe26d3c209dd63e16f8ee2461b57a091789160ca1eb492bb1bf3f84" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+       <component group="androidx.annotation" name="annotation-experimental" version="1.3.0">
+           <artifact name="annotation-experimental-1.3.0.aar">
+               <sha256 value="abfd29c8556e5bd0325a9f769ab9e9d154ff4a5515c476cdd5a2a8285b1b19dc" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+           <artifact name="annotation-experimental-1.3.0.module">
+               <sha256 value="5eebeaff01d042e06dcf292abf8964ad391e4b0159f0090f16253d6045d38da0" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.activity" name="activity-compose" version="1.7.0">
+           <artifact name="activity-compose-1.7.0.aar">
+               <sha256 value="caa72885d1ce7979c1d6c59a8b255c6097b770780d4d4da95d56979a348646cd" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+           <artifact name="activity-compose-1.7.0.module">
+               <sha256 value="f7a29bcba338575dcf89a553cff9cfad3f140340eaf2b56fd0193244da602c0a" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose.runtime" name="runtime" version="1.0.1">
+           <artifact name="runtime-1.0.1.module">
+               <sha256 value="2543a8c7edc16bde91f140286b4fd3773d7204a283a4ec99f6e5e286aa92c0c3" origin="Generated by Gradle"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose.runtime" name="runtime-saveable" version="1.0.1">
+           <artifact name="runtime-saveable-1.0.1.module">
+               <sha256 value="c0d6f142542d8d74f65481ef6526d2be265f01f812a112948fcde87a458f4fb6" origin="Generated by Gradle"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose.ui" name="ui" version="1.0.1">
+           <artifact name="ui-1.0.1.aar">
+               <sha256 value="1943daa4a3412861b9a2bdc1a7c8c2ff05d9b8191c1d3e56ebb223d2eb4a8526" origin="Generated by Gradle"/>
+           </artifact>
+           <artifact name="ui-1.0.1.module">
+               <sha256 value="57031a6ac9b60e5b56792ebf5cde6e16812ff566ed9190cbd188b00b46c13779" origin="Generated by Gradle"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose" name="compose-bom" version="2024.06.00">
+           <artifact name="compose-bom-2024.06.00.pom">
+               <sha256 value="1b391a969ff81c0bb43b3711e92d977e8bfa72457a11d8a37910a7051bdc3045" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.activity" name="activity-compose" version="1.7.0">
+           <artifact name="activity-compose-1.7.0.aar">
+               <sha256 value="caa72885d1ce7979c1d6c59a8b255c6097b770780d4d4da95d56979a348646cd" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+           <artifact name="activity-compose-1.7.0.module">
+               <sha256 value="f7a29bcba338575dcf89a553cff9cfad3f140340eaf2b56fd0193244da602c0a" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose.runtime" name="runtime" version="1.0.1">
+           <artifact name="runtime-1.0.1.module">
+               <sha256 value="2543a8c7edc16bde91f140286b4fd3773d7204a283a4ec99f6e5e286aa92c0c3" origin="Generated by Gradle"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose.runtime" name="runtime-saveable" version="1.0.1">
+           <artifact name="runtime-saveable-1.0.1.module">
+               <sha256 value="c0d6f142542d8d74f65481ef6526d2be265f01f812a112948fcde87a458f4fb6" origin="Generated by Gradle"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose.ui" name="ui" version="1.0.1">
+           <artifact name="ui-1.0.1.aar">
+               <sha256 value="1943daa4a3412861b9a2bdc1a7c8c2ff05d9b8191c1d3e56ebb223d2eb4a8526" origin="Generated by Gradle"/>
+           </artifact>
+           <artifact name="ui-1.0.1.module">
+               <sha256 value="57031a6ac9b60e5b56792ebf5cde6e16812ff566ed9190cbd188b00b46c13779" origin="Generated by Gradle"/>
+           </artifact>
+       </component>
+       <component group="androidx.compose" name="compose-bom" version="2024.06.00">
+           <artifact name="compose-bom-2024.06.00.pom">
+               <sha256 value="1b391a969ff81c0bb43b3711e92d977e8bfa72457a11d8a37910a7051bdc3045" origin="Generated by Gradle" reason="Artifact is not signed"/>
+           </artifact>
+       </component>
       <component group="androidx.appcompat" name="appcompat" version="1.1.0">
          <artifact name="appcompat-1.1.0.pom">
             <sha256 value="340d617121f8ef8e02a6680c8f357aa3e542276d0c8a1cdcb6fd98984b2cb7b9" origin="Generated by Gradle" reason="Artifact is not signed"/>