mirror of
https://github.com/nextcloud/talk-android
synced 2025-03-10 08:00:57 +00:00
Merge pull request #4064 from nextcloud/create_new_conversation
Create new conversation
This commit is contained in:
commit
38c1b6b007
@ -309,7 +309,7 @@ dependencies {
|
|||||||
//compose
|
//compose
|
||||||
implementation(platform("androidx.compose:compose-bom:2024.09.00"))
|
implementation(platform("androidx.compose:compose-bom:2024.09.00"))
|
||||||
implementation("androidx.compose.ui:ui")
|
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.compose.ui:ui-tooling-preview")
|
||||||
implementation 'androidx.activity:activity-compose:1.9.2'
|
implementation 'androidx.activity:activity-compose:1.9.2'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.5'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.5'
|
||||||
|
@ -129,6 +129,9 @@
|
|||||||
<activity android:name=".contacts.ContactsActivityCompose"
|
<activity android:name=".contacts.ContactsActivityCompose"
|
||||||
android:theme="@style/AppTheme"/>
|
android:theme="@style/AppTheme"/>
|
||||||
|
|
||||||
|
<activity android:name=".conversationcreation.ConversationCreationActivity"
|
||||||
|
android:theme="@style/AppTheme"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".account.AccountVerificationActivity"
|
android:name=".account.AccountVerificationActivity"
|
||||||
android:theme="@style/AppTheme" />
|
android:theme="@style/AppTheme" />
|
||||||
|
@ -9,9 +9,15 @@ package com.nextcloud.talk.api
|
|||||||
|
|
||||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
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.GET
|
||||||
import retrofit2.http.Header
|
import retrofit2.http.Header
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.PUT
|
||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
import retrofit2.http.QueryMap
|
import retrofit2.http.QueryMap
|
||||||
import retrofit2.http.Url
|
import retrofit2.http.Url
|
||||||
@ -39,4 +45,55 @@ interface NcApiCoroutines {
|
|||||||
@Url url: String?,
|
@Url url: String?,
|
||||||
@QueryMap options: Map<String, String>?
|
@QueryMap options: Map<String, String>?
|
||||||
): RoomOverall
|
): 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
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,20 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@ -46,17 +49,25 @@ import androidx.compose.material3.Surface
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
@ -64,6 +75,7 @@ import com.nextcloud.talk.R
|
|||||||
import com.nextcloud.talk.activities.BaseActivity
|
import com.nextcloud.talk.activities.BaseActivity
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.chat.ChatActivity
|
import com.nextcloud.talk.chat.ChatActivity
|
||||||
|
import com.nextcloud.talk.conversationcreation.ConversationCreationActivity
|
||||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||||
import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
|
import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
@ -82,8 +94,31 @@ class ContactsActivityCompose : BaseActivity() {
|
|||||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||||
contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java]
|
contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java]
|
||||||
setContent {
|
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 colorScheme = viewThemeUtils.getColorScheme(this)
|
||||||
val uiState = contactsViewModel.contactsViewState.collectAsState()
|
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(
|
MaterialTheme(
|
||||||
colorScheme = colorScheme
|
colorScheme = colorScheme
|
||||||
) {
|
) {
|
||||||
@ -98,23 +133,242 @@ class ContactsActivityCompose : BaseActivity() {
|
|||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
Column(Modifier.padding(it)) {
|
Column(Modifier.padding(it)) {
|
||||||
ConversationCreationOptions(context = context)
|
ConversationCreationOptions(context = context, contactsViewModel = contactsViewModel)
|
||||||
ContactsList(
|
ContactsList(
|
||||||
contactsUiState = uiState.value,
|
contactsUiState = uiState.value,
|
||||||
contactsViewModel = contactsViewModel,
|
contactsViewModel = contactsViewModel,
|
||||||
context = context
|
context = context,
|
||||||
|
selectedParticipants = selectedParticipants.toMutableList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetStatusBarColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 { }
|
||||||
}
|
}
|
||||||
setupSystemColors()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContactsList(contactsUiState: ContactsUiState, 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(
|
||||||
|
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) }
|
||||||
|
val errorPlaceholderImage: Int = R.drawable.account_circle_96dp
|
||||||
|
val loadedImage = loadImage(imageUri, context, errorPlaceholderImage)
|
||||||
|
AsyncImage(
|
||||||
|
model = loadedImage,
|
||||||
|
contentDescription = stringResource(R.string.user_avatar),
|
||||||
|
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)
|
||||||
|
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||||
|
chatIntent.putExtras(bundle)
|
||||||
|
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
context.startActivity(chatIntent)
|
||||||
|
}
|
||||||
|
is RoomUiState.Error -> {
|
||||||
|
val errorMessage = (roomUiState as RoomUiState.Error).message
|
||||||
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
Text(text = "Error: $errorMessage", color = Color.Red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RoomUiState.None -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnrememberedMutableState")
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
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()
|
||||||
|
}) {
|
||||||
|
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_button))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
IconButton(onClick = {
|
||||||
|
contactsViewModel.updateSearchState(true)
|
||||||
|
}) {
|
||||||
|
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) {
|
||||||
|
Row {
|
||||||
|
DisplaySearch(
|
||||||
|
text = searchQuery,
|
||||||
|
onTextChange = { searchQuery ->
|
||||||
|
contactsViewModel.updateSearchQuery(query = searchQuery)
|
||||||
|
contactsViewModel.getContactsFromSearchParams()
|
||||||
|
},
|
||||||
|
contactsViewModel = contactsViewModel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsViewModel) {
|
||||||
|
val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsState()
|
||||||
|
if (!isAddParticipants) {
|
||||||
|
Column {
|
||||||
|
Row(
|
||||||
|
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(
|
||||||
|
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
|
||||||
|
.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) {
|
when (contactsUiState) {
|
||||||
is ContactsUiState.None -> {
|
is ContactsUiState.None -> {
|
||||||
}
|
}
|
||||||
@ -127,13 +381,13 @@ fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsVi
|
|||||||
val contacts = contactsUiState.contacts
|
val contacts = contactsUiState.contacts
|
||||||
Log.d(CompanionClass.TAG, "Contacts:$contacts")
|
Log.d(CompanionClass.TAG, "Contacts:$contacts")
|
||||||
if (contacts != null) {
|
if (contacts != null) {
|
||||||
ContactsItem(contacts, contactsViewModel, context)
|
ContactsItem(contacts, contactsViewModel, context, selectedParticipants)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ContactsUiState.Error -> {
|
is ContactsUiState.Error -> {
|
||||||
val errorMessage = contactsUiState.message
|
val errorMessage = contactsUiState.message
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
Text(text = "Error: $errorMessage", color = MaterialTheme.colorScheme.error)
|
Text(text = "Error: $errorMessage", color = Color.Red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +395,12 @@ fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsVi
|
|||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ContactsItem(contacts: List<AutocompleteUser>, contactsViewModel: ContactsViewModel, context: Context) {
|
fun ContactsItem(
|
||||||
|
contacts: List<AutocompleteUser>,
|
||||||
|
contactsViewModel: ContactsViewModel,
|
||||||
|
context: Context,
|
||||||
|
selectedParticipants: MutableList<AutocompleteUser>
|
||||||
|
) {
|
||||||
val groupedContacts: Map<String, List<AutocompleteUser>> = contacts.groupBy { contact ->
|
val groupedContacts: Map<String, List<AutocompleteUser>> = contacts.groupBy { contact ->
|
||||||
(
|
(
|
||||||
if (contact.source == "users") {
|
if (contact.source == "users") {
|
||||||
@ -166,11 +425,16 @@ fun ContactsItem(contacts: List<AutocompleteUser>, contactsViewModel: ContactsVi
|
|||||||
Surface(Modifier.fillParentMaxWidth()) {
|
Surface(Modifier.fillParentMaxWidth()) {
|
||||||
Header(initial)
|
Header(initial)
|
||||||
}
|
}
|
||||||
HorizontalDivider(thickness = 1.dp, color = MaterialTheme.colorScheme.outlineVariant)
|
HorizontalDivider(thickness = 0.1.dp, color = Color.Black)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
items(contactsForInitial) { contact ->
|
items(contactsForInitial) { contact ->
|
||||||
ContactItemRow(contact = contact, contactsViewModel = contactsViewModel, context = context)
|
ContactItemRow(
|
||||||
|
contact = contact,
|
||||||
|
contactsViewModel = contactsViewModel,
|
||||||
|
context = context,
|
||||||
|
selectedContacts = selectedParticipants
|
||||||
|
)
|
||||||
Log.d(CompanionClass.TAG, "Contacts:$contact")
|
Log.d(CompanionClass.TAG, "Contacts:$contact")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,147 +449,11 @@ fun Header(header: String) {
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(Color.Transparent)
|
.background(Color.Transparent)
|
||||||
.padding(start = 60.dp),
|
.padding(start = 60.dp),
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = Color.Blue,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewModel, context: Context) {
|
|
||||||
val roomUiState by contactsViewModel.roomViewState.collectAsState()
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable {
|
|
||||||
contactsViewModel.createRoom(
|
|
||||||
CompanionClass.ROOM_TYPE_ONE_ONE,
|
|
||||||
contact.source!!,
|
|
||||||
contact.id!!,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
},
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
val imageUri = contact.id?.let { contactsViewModel.getImageUri(it, true) }
|
|
||||||
val errorPlaceholderImage: Int = R.drawable.account_circle_96dp
|
|
||||||
val loadedImage = loadImage(imageUri, context, errorPlaceholderImage)
|
|
||||||
AsyncImage(
|
|
||||||
model = loadedImage,
|
|
||||||
contentDescription = stringResource(R.string.user_avatar),
|
|
||||||
modifier = Modifier.size(width = 45.dp, height = 45.dp)
|
|
||||||
)
|
|
||||||
Text(modifier = Modifier.padding(16.dp), text = contact.label!!)
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
context.startActivity(chatIntent)
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RoomUiState.None -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnrememberedMutableState")
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun AppBar(title: String, context: Context, contactsViewModel: ContactsViewModel) {
|
|
||||||
val searchQuery by contactsViewModel.searchQuery.collectAsState()
|
|
||||||
val searchState = contactsViewModel.searchState.collectAsState()
|
|
||||||
|
|
||||||
TopAppBar(
|
|
||||||
title = { Text(text = title) },
|
|
||||||
|
|
||||||
navigationIcon = {
|
|
||||||
IconButton(onClick = {
|
|
||||||
(context as? Activity)?.finish()
|
|
||||||
}) {
|
|
||||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_button))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
IconButton(onClick = {
|
|
||||||
contactsViewModel.updateSearchState(true)
|
|
||||||
}) {
|
|
||||||
Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (searchState.value) {
|
|
||||||
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),
|
|
||||||
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
|
|
||||||
.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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CompanionClass {
|
class CompanionClass {
|
||||||
companion object {
|
companion object {
|
||||||
internal val TAG = ContactsActivityCompose::class.simpleName
|
internal val TAG = ContactsActivityCompose::class.simpleName
|
||||||
|
@ -30,6 +30,10 @@ class ContactsViewModel @Inject constructor(
|
|||||||
val shareTypeList: List<String> = shareTypes
|
val shareTypeList: List<String> = shareTypes
|
||||||
private val _searchState = MutableStateFlow(false)
|
private val _searchState = MutableStateFlow(false)
|
||||||
val searchState: StateFlow<Boolean> = _searchState
|
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 {
|
init {
|
||||||
getContactsFromSearchParams()
|
getContactsFromSearchParams()
|
||||||
@ -39,12 +43,19 @@ class ContactsViewModel @Inject constructor(
|
|||||||
_searchQuery.value = query
|
_searchQuery.value = query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateSelectedParticipants(participants: List<AutocompleteUser>) {
|
||||||
|
selectedParticipants.value = participants
|
||||||
|
}
|
||||||
fun updateSearchState(searchState: Boolean) {
|
fun updateSearchState(searchState: Boolean) {
|
||||||
_searchState.value = searchState
|
_searchState.value = searchState
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateShareTypes(value: String) {
|
fun updateShareTypes(value: List<String>) {
|
||||||
shareTypes.add(value)
|
shareTypes.addAll(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateIsAddParticipants(value: Boolean) {
|
||||||
|
_isAddParticipantsView.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getContactsFromSearchParams() {
|
fun getContactsFromSearchParams() {
|
||||||
@ -62,7 +73,6 @@ class ContactsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) {
|
fun createRoom(roomType: String, sourceType: String, userId: String, conversationName: String?) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
|
@ -19,6 +19,7 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -30,6 +31,7 @@ import com.nextcloud.talk.R
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewModel: ContactsViewModel) {
|
fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewModel: ContactsViewModel) {
|
||||||
|
val isAddParticipants = contactsViewModel.isAddParticipantsView.collectAsState()
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
TextField(
|
TextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -42,7 +44,6 @@ fun DisplaySearch(text: String, onTextChange: (String) -> Unit, contactsViewMode
|
|||||||
text = stringResource(R.string.nc_search)
|
text = stringResource(R.string.nc_search)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
textStyle = TextStyle(
|
textStyle = TextStyle(
|
||||||
fontSize = 16.sp
|
fontSize = 16.sp
|
||||||
),
|
),
|
||||||
|
@ -10,7 +10,7 @@ package com.nextcloud.talk.contacts
|
|||||||
enum class ShareType(val shareType: String) {
|
enum class ShareType(val shareType: String) {
|
||||||
User("0"),
|
User("0"),
|
||||||
Group("1"),
|
Group("1"),
|
||||||
Email(""),
|
Email("4"),
|
||||||
Circle(""),
|
Remote("5"),
|
||||||
Federated("")
|
Circle("7")
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
@ -19,6 +19,8 @@ import com.nextcloud.talk.contacts.ContactsRepository
|
|||||||
import com.nextcloud.talk.contacts.ContactsRepositoryImpl
|
import com.nextcloud.talk.contacts.ContactsRepositoryImpl
|
||||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
||||||
import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
|
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.ConversationInfoEditRepository
|
||||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepositoryImpl
|
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepositoryImpl
|
||||||
import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
|
import com.nextcloud.talk.conversationlist.data.OfflineConversationsRepository
|
||||||
@ -208,4 +210,12 @@ class RepositoryModule {
|
|||||||
fun provideContactsRepository(ncApiCoroutines: NcApiCoroutines, userManager: UserManager): ContactsRepository {
|
fun provideContactsRepository(ncApiCoroutines: NcApiCoroutines, userManager: UserManager): ContactsRepository {
|
||||||
return ContactsRepositoryImpl(ncApiCoroutines, userManager)
|
return ContactsRepositoryImpl(ncApiCoroutines, userManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideConversationCreationRepository(
|
||||||
|
ncApiCoroutines: NcApiCoroutines,
|
||||||
|
userManager: UserManager
|
||||||
|
): ConversationCreationRepository {
|
||||||
|
return ConversationCreationRepositoryImpl(ncApiCoroutines, userManager)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import com.nextcloud.talk.contacts.ContactsViewModel
|
|||||||
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
|
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
|
||||||
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
||||||
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
|
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
|
||||||
|
import com.nextcloud.talk.conversationcreation.ConversationCreationViewModel
|
||||||
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
|
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
|
||||||
import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
|
import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
|
||||||
import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel
|
import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel
|
||||||
@ -156,4 +157,9 @@ abstract class ViewModelModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@ViewModelKey(ContactsViewModel::class)
|
@ViewModelKey(ContactsViewModel::class)
|
||||||
abstract fun contactsViewModel(viewModel: ContactsViewModel): ViewModel
|
abstract fun contactsViewModel(viewModel: ContactsViewModel): ViewModel
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ViewModelKey(ConversationCreationViewModel::class)
|
||||||
|
abstract fun conversationCreationViewModel(viewModel: ConversationCreationViewModel): ViewModel
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.data.database.mappers
|
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.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) =
|
fun ChatMessageJson.asEntity(accountId: Long) =
|
||||||
ChatMessageEntity(
|
ChatMessageEntity(
|
||||||
|
@ -539,6 +539,10 @@ object ApiUtils {
|
|||||||
return getUrlForRoom(version, baseUrl, token) + "/description"
|
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 {
|
fun getUrlForTranslation(baseUrl: String): String {
|
||||||
return "$baseUrl$OCS_API_VERSION/translation/translate"
|
return "$baseUrl$OCS_API_VERSION/translation/translate"
|
||||||
}
|
}
|
||||||
|
18
app/src/main/res/drawable/baseline_tag_faces_24.xml
Normal file
18
app/src/main/res/drawable/baseline_tag_faces_24.xml
Normal file
@ -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>
|
@ -242,6 +242,9 @@ How to translate with transifex:
|
|||||||
<string name="nc_remove_from_favorites">Remove from favorites</string>
|
<string name="nc_remove_from_favorites">Remove from favorites</string>
|
||||||
<string name="nc_create_new_conversation">Create a new conversation</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_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="added_to_favorites">Added conversation %1$s to favorites</string>
|
||||||
<string name="removed_from_favorites">Removed conversation %1$s from 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="close_icon">Close Icon</string>
|
||||||
<string name="nc_refresh">Refresh</string>
|
<string name="nc_refresh">Refresh</string>
|
||||||
<string name="nc_check_your_internet">Please check your internet connection</string>
|
<string name="nc_check_your_internet">Please check your internet connection</string>
|
||||||
|
<string name="nc_visible">Visible</string>
|
||||||
|
|
||||||
<!-- Chat -->
|
<!-- Chat -->
|
||||||
<string name="nc_hint_enter_a_message">Enter a message …</string>
|
<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_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_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_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_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_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>
|
<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="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_chat_via">Chat via %s</string>
|
||||||
<string name="nc_phone_book_integration_account_not_found">Account not found</string>
|
<string name="nc_phone_book_integration_account_not_found">Account not found</string>
|
||||||
|
<string name= "nc_edit">Edit</string>
|
||||||
|
|
||||||
<!-- save feature -->
|
<!-- save feature -->
|
||||||
<string name="nc_save_message">Save</string>
|
<string name="nc_save_message">Save</string>
|
||||||
|
@ -81,7 +81,7 @@ class ContactsViewModelTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `update shareTypes`() {
|
fun `update shareTypes`() {
|
||||||
viewModel.updateShareTypes(ShareType.Group.shareType)
|
viewModel.updateShareTypes(listOf(ShareType.Group.shareType))
|
||||||
assert(viewModel.shareTypeList.contains(ShareType.Group.shareType))
|
assert(viewModel.shareTypeList.contains(ShareType.Group.shareType))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
<verify-metadata>true</verify-metadata>
|
<verify-metadata>true</verify-metadata>
|
||||||
<verify-signatures>true</verify-signatures>
|
<verify-signatures>true</verify-signatures>
|
||||||
<trusted-artifacts>
|
<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.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.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=".*-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>
|
</trusted-artifacts>
|
||||||
<ignored-keys>
|
<ignored-keys>
|
||||||
<ignored-key id="0AA3E5C3D232E79B" reason="Key couldn't be downloaded from any key server"/>
|
<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.annotation"/>
|
||||||
<trusting group="androidx.camera"/>
|
<trusting group="androidx.camera"/>
|
||||||
<trusting group="androidx.collection"/>
|
<trusting group="androidx.collection"/>
|
||||||
<trusting group="androidx.compose.foundation"/>
|
|
||||||
<trusting group="androidx.compose.material3"/>
|
<trusting group="androidx.compose.material3"/>
|
||||||
<trusting group="androidx.core"/>
|
<trusting group="androidx.core"/>
|
||||||
<trusting group="androidx.emoji2"/>
|
<trusting group="androidx.emoji2"/>
|
||||||
@ -157,6 +156,7 @@
|
|||||||
<trusting group="androidx.sqlite"/>
|
<trusting group="androidx.sqlite"/>
|
||||||
<trusting group="androidx.webkit"/>
|
<trusting group="androidx.webkit"/>
|
||||||
<trusting group="androidx.work"/>
|
<trusting group="androidx.work"/>
|
||||||
|
<trusting group="androidx.compose.foundation"/>
|
||||||
</trusted-key>
|
</trusted-key>
|
||||||
<trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1">
|
<trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1">
|
||||||
<trusting group="org.apache" name="apache"/>
|
<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="E4AC7874F3479A0F1F8ECF9960BB45F36B649F22" group="fr.dudie" name="nominatim-api" version="3.4"/>
|
||||||
<trusted-key id="E77417AC194160A3FABD04969A259C7EE636C5ED" group="^com[.]google($|([.].*))" regex="true"/>
|
<trusted-key id="E77417AC194160A3FABD04969A259C7EE636C5ED" group="^com[.]google($|([.].*))" regex="true"/>
|
||||||
<trusted-key id="E7DC75FC24FB3C8DFE8086AD3D5839A2262CBBFB" group="org.jetbrains.kotlinx"/>
|
<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="E82D2EAF2E83830CE1F7F6BE571A5291E827E1C7" group="net.java" name="jvnet-parent" version="3"/>
|
||||||
<trusted-key id="E85AED155021AF8A6C6B7A4A7C7D8456294423BA" group="org.objenesis"/>
|
<trusted-key id="E85AED155021AF8A6C6B7A4A7C7D8456294423BA" group="org.objenesis"/>
|
||||||
<trusted-key id="EAA526B91DD83BA3E1B9636FA730529CA355A63E" group="org.ccil.cowan.tagsoup" name="tagsoup" version="1.2.1"/>
|
<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"/>
|
<sha256 value="9516c2ae44284ea0bd3d0eade0ee638879b708cbe31e3af92ba96c300604ebc3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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">
|
<component group="androidx.annotation" name="annotation" version="1.5.0">
|
||||||
<artifact name="annotation-1.5.0.jar">
|
<artifact name="annotation-1.5.0.jar">
|
||||||
<sha256 value="261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<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"/>
|
<sha256 value="fbc64f5c44a7added8b6eab517cf7d70555e25153bf5d44a6ed9b0e5312f7de9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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">
|
<component group="androidx.annotation" name="annotation-experimental" version="1.0.0">
|
||||||
<artifact name="annotation-experimental-1.0.0.pom">
|
<artifact name="annotation-experimental-1.0.0.pom">
|
||||||
<sha256 value="6b73ff6608f4b1d6cbab620b65708a382d0b39901cf4e6b0d16f84a1b04d7732" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<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"/>
|
<sha256 value="0361d1526a4d7501255e19779e09e93cdbd07fee0e2f5c50b7a137432d510119" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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">
|
<component group="androidx.annotation" name="annotation-experimental" version="1.1.0-rc01">
|
||||||
<artifact name="annotation-experimental-1.1.0-rc01.module">
|
<artifact name="annotation-experimental-1.1.0-rc01.module">
|
||||||
<sha256 value="d45ac493e84d968aabb2bea2b7744031a98cf5074447c0f3b862d600fc44b55c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<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"/>
|
<sha256 value="9b6974a7dfe26d3c209dd63e16f8ee2461b57a091789160ca1eb492bb1bf3f84" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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">
|
<component group="androidx.appcompat" name="appcompat" version="1.1.0">
|
||||||
<artifact name="appcompat-1.1.0.pom">
|
<artifact name="appcompat-1.1.0.pom">
|
||||||
<sha256 value="340d617121f8ef8e02a6680c8f357aa3e542276d0c8a1cdcb6fd98984b2cb7b9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
<sha256 value="340d617121f8ef8e02a6680c8f357aa3e542276d0c8a1cdcb6fd98984b2cb7b9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user