Browse Source

ConversationCreationActivity UI

Signed-off-by: sowjanyakch <sowjanya.kch@gmail.com>
sowjanyakch 9 months ago
parent
commit
cd01c80d71

+ 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" />

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

@@ -9,9 +9,13 @@ 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 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 +43,26 @@ 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
+    fun renameRoom(
+        @Header("Authorization") authorization: String?,
+        @Url url: String,
+        @Field("roomName") roomName: String?
+    ): GenericOverall
+
+    @FormUrlEncoded
+    @PUT
+    fun setConversationDescription(
+        @Header("Authorization") authorization: String?,
+        @Url url: String,
+        @Field("description") description: String?
+    ): GenericOverall
 }

+ 8 - 1
app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt

@@ -64,6 +64,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
@@ -278,7 +279,11 @@ fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel
 fun ConversationCreationOptions(context: Context) {
     Column {
         Row(
-            modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp),
+            modifier = Modifier.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(
@@ -326,6 +331,8 @@ fun ConversationCreationOptions(context: Context) {
     }
 }
 
+
+
 class CompanionClass {
     companion object {
         internal val TAG = ContactsActivityCompose::class.simpleName

+ 104 - 0
app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityViewModel.kt

@@ -0,0 +1,104 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package com.nextcloud.talk.contacts
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.nextcloud.talk.data.user.model.User
+import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
+import com.nextcloud.talk.models.json.conversations.Conversation
+import com.nextcloud.talk.users.UserManager
+import com.nextcloud.talk.utils.ApiUtils
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+class ContactsActivityViewModel @Inject constructor(
+    private val repository: ContactsRepository,
+    private val userManager: UserManager
+) : ViewModel() {
+
+    private val _contactsViewState = MutableStateFlow<ContactsUiState>(ContactsUiState.None)
+    val contactsViewState: StateFlow<ContactsUiState> = _contactsViewState
+    private val _roomViewState = MutableStateFlow<RoomUiState>(RoomUiState.None)
+    val roomViewState: StateFlow<RoomUiState> = _roomViewState
+    private val _currentUser = userManager.currentUser.blockingGet()
+    val currentUser: User = _currentUser
+    private val _searchQuery = MutableStateFlow("")
+    val searchQuery: StateFlow<String> = _searchQuery
+    private val shareTypes: MutableList<String> = mutableListOf(ShareType.User.shareType)
+    val shareTypeList: List<String> = shareTypes
+
+    init {
+        getContactsFromSearchParams()
+    }
+
+    fun updateSearchQuery(query: String) {
+        _searchQuery.value = query
+    }
+
+    fun updateShareTypes(value: String) {
+        shareTypes.add(value)
+    }
+
+    fun getContactsFromSearchParams() {
+        _contactsViewState.value = ContactsUiState.Loading
+        viewModelScope.launch {
+            try {
+                val contacts = repository.getContacts(
+                    searchQuery.value,
+                    shareTypeList
+                )
+                val contactsList: List<AutocompleteUser>? = contacts.ocs!!.data
+                _contactsViewState.value = ContactsUiState.Success(contactsList)
+            } catch (exception: Exception) {
+                _contactsViewState.value = ContactsUiState.Error(exception.message ?: "")
+            }
+        }
+    }
+
+    fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) {
+        viewModelScope.launch {
+            try {
+                val room = repository.createRoom(
+                    roomType,
+                    sourceType,
+                    userId,
+                    conversationName
+                )
+
+                val conversation: Conversation? = room.ocs?.data
+                _roomViewState.value = RoomUiState.Success(conversation)
+            } catch (exception: Exception) {
+                _roomViewState.value = RoomUiState.Error(exception.message ?: "")
+            }
+        }
+    }
+
+    fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
+        return ApiUtils.getUrlForAvatar(
+            _currentUser.baseUrl,
+            avatarId,
+            requestBigSize
+        )
+    }
+}
+
+sealed class ContactsUiState {
+    data object None : ContactsUiState()
+    data object Loading : ContactsUiState()
+    data class Success(val contacts: List<AutocompleteUser>?) : ContactsUiState()
+    data class Error(val message: String) : ContactsUiState()
+}
+
+sealed class RoomUiState {
+    data object None : RoomUiState()
+    data class Success(val conversation: Conversation?) : RoomUiState()
+    data class Error(val message: String) : RoomUiState()
+}

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

@@ -0,0 +1,246 @@
+/*
+ * 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.app.Activity
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.clickable
+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.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.ExperimentalMaterial3Api
+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.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+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.contacts.ContactsViewModel
+import javax.inject.Inject
+
+@AutoInjector(NextcloudTalkApplication::class)
+class ConversationCreationActivity : BaseActivity() {
+    @Inject
+    lateinit var viewModelFactory: ViewModelProvider.Factory
+    private lateinit var conversationCreationViewModel: ConversationCreationViewModel
+    private lateinit var contactsViewModel: ContactsViewModel
+
+    @OptIn(ExperimentalMaterial3Api::class)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
+        conversationCreationViewModel =
+            ViewModelProvider(this, viewModelFactory)[ConversationCreationViewModel::class.java]
+        contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java]
+        setContent {
+            val colorScheme = viewThemeUtils.getColorScheme(this)
+            MaterialTheme(
+                colorScheme = colorScheme
+            ) {
+                val context = LocalContext.current
+                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(R.string.back_button)
+                                    )
+                                }
+                            }
+                        )
+                    },
+                    content = {
+                        Column(Modifier.padding(it)) {
+                            DefaultUserAvatar()
+                            UploadAvatar()
+                            ConversationNameAndDescription(conversationCreationViewModel)
+                            AddParticipants(contactsViewModel)
+                            RoomCreationOptions(conversationCreationViewModel)
+                        }
+                    }
+                )
+            }
+        }
+    }
+}
+
+@Composable
+fun DefaultUserAvatar() {
+    Box(
+        modifier = Modifier.fillMaxWidth(),
+        contentAlignment = Alignment.Center
+    ) {
+        AsyncImage(
+            model = R.drawable.ic_circular_group,
+            contentDescription = "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.updateRoomName(it)
+        },
+        label = { Text(text = stringResource(id = R.string.nc_conversation_description)) },
+        modifier = Modifier
+            .padding(top = 8.dp, start = 16.dp, end = 16.dp)
+            .fillMaxWidth()
+    )
+}
+
+@Composable
+fun AddParticipants(contactsViewModel: ContactsViewModel) {
+    Text(
+        text = stringResource(id = R.string.nc_participants).uppercase(),
+        fontSize = 14.sp,
+        modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp)
+    )
+
+    Row(
+        modifier = Modifier
+            .padding(start = 16.dp, end = 16.dp)
+            .clickable {
+                // Show contacts
+            },
+        verticalAlignment = Alignment
+            .CenterVertically
+    ) {
+        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 isChecked = conversationCreationViewModel.isGuestsAllowed.value
+    Text(
+        text = stringResource(id = R.string.nc_visible).uppercase(),
+        fontSize = 14.sp,
+        modifier = Modifier.padding(top = 24.dp, start = 16.dp, end = 16.dp)
+    )
+    Row(
+        modifier = Modifier.padding(start = 16.dp, top = 16.dp, end = 16.dp).fillMaxWidth(),
+        verticalAlignment = Alignment
+            .CenterVertically
+    ) {
+        Icon(
+            painter = painterResource(id = R.drawable.ic_avatar_link),
+            contentDescription = null,
+            modifier = Modifier.size(24.dp)
+        )
+        Text(text = stringResource(id = R.string.nc_guest_access_allow_title))
+
+        Spacer(modifier = Modifier.weight(1f))
+        Switch(
+            checked = isChecked,
+            onCheckedChange = {
+                conversationCreationViewModel.isGuestsAllowed.value = it
+            }
+        )
+    }
+}

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

@@ -0,0 +1,15 @@
+/*
+ * 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.generic.GenericOverall
+
+interface ConversationCreationRepository {
+    suspend fun renameConversation(roomToken: String, roomNameNew: String?): GenericOverall
+    suspend fun setConversationDescription(roomToken: String, description: String?): GenericOverall
+}

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

@@ -0,0 +1,48 @@
+/*
+ * 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.json.generic.GenericOverall
+import com.nextcloud.talk.users.UserManager
+import com.nextcloud.talk.utils.ApiUtils
+
+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
+        )
+    }
+}

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

@@ -0,0 +1,57 @@
+/*
+ * 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 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 _roomName = MutableStateFlow("")
+    val roomName: StateFlow<String> = _roomName
+    private val _conversationDescription = MutableStateFlow("")
+    val conversationDescription: StateFlow<String> = _conversationDescription
+    var isGuestsAllowed = mutableStateOf(false)
+    var isConversationAvailableForRegisteredUsers = mutableStateOf(false)
+    var openForGuestAppUsers = mutableStateOf(false)
+
+    fun updateRoomName(roomName: String) {
+        _roomName.value = roomName
+    }
+
+    fun updateConversationDescription(conversationDescription: String) {
+        _conversationDescription.value = conversationDescription
+    }
+
+    fun renameConversation(roomToken: String) {
+        viewModelScope.launch {
+            try {
+                repository.renameConversation(roomToken, roomName.value)
+            } catch (e: Exception) {
+                Log.d("ConversationCreationViewModel", "${e.message}")
+            }
+        }
+    }
+    fun setConversationDescription(roomToken: String) {
+        viewModelScope.launch {
+            try {
+                repository.setConversationDescription(roomToken, conversationDescription.value)
+            } catch (e: Exception) {
+                Log.d("ConversationCreationViewModel", "${e.message}")
+            }
+        }
+    }
+}

+ 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
 }

+ 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>

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

@@ -386,6 +386,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>

+ 4 - 0
app/src/test/java/com/nextcloud/talk/contacts/repository/FakeRepositorySuccess.kt

@@ -29,4 +29,8 @@ class FakeRepositorySuccess : ContactsRepository {
     override fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
         return "https://mydomain.com/index.php/avatar/$avatarId/512"
     }
+
+    override fun getImageUri(avatarId: String, requestBigSize: Boolean): String {
+        TODO("Not yet implemented")
+    }
 }

+ 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"/>