Merge branch 'master' into master-android

This commit is contained in:
Evgeny Poberezkin
2025-06-01 22:52:08 +01:00
71 changed files with 1364 additions and 416 deletions
@@ -101,13 +101,13 @@ actual fun GrayU8.toImageBitmap(): ImageBitmap = ConvertBitmap.grayToBitmap(this
actual fun ImageBitmap.hasAlpha(): Boolean = hasAlpha
actual fun ImageBitmap.addLogo(): ImageBitmap = asAndroidBitmap().applyCanvas {
val radius = (width * 0.16f) / 2
actual fun ImageBitmap.addLogo(size: Float): ImageBitmap = asAndroidBitmap().applyCanvas {
val radius = (width * size) / 2
val paint = android.graphics.Paint()
paint.color = android.graphics.Color.WHITE
drawCircle(width / 2f, height / 2f, radius, paint)
val logo = androidAppContext.resources.getDrawable(R.drawable.icon_foreground_android_common, null).toBitmap()
val logoSize = (width * 0.24).toInt()
val logoSize = (width * size * 1.5).toInt()
translate((width - logoSize) / 2f, (height - logoSize) / 2f)
drawBitmap(logo, null, android.graphics.Rect(0, 0, logoSize, logoSize), null)
}.asImageBitmap()
@@ -1570,6 +1570,16 @@ object ChatController {
return null
}
suspend fun apiAddMyAddressShortLink(rh: Long?): UserContactLinkRec? {
val userId = kotlin.runCatching { currentUserId("apiAddMyAddressShortLink") }.getOrElse { return null }
val r = sendCmd(rh, CC.ApiAddMyAddressShortLink(userId))
if (r is API.Result && r.res is CR.UserContactLink) return r.res.contactLink
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiAddMyAddressShortLink", generalGetString(MR.strings.error_creating_address), r)
}
return null
}
suspend fun userAddressAutoAccept(rh: Long?, autoAccept: AutoAccept?): UserContactLinkRec? {
val userId = kotlin.runCatching { currentUserId("userAddressAutoAccept") }.getOrElse { return null }
val r = sendCmd(rh, CC.ApiAddressAutoAccept(userId, autoAccept))
@@ -2016,6 +2026,15 @@ object ChatController {
return null
}
suspend fun apiAddGroupShortLink(rh: Long?, groupId: Long): Pair<CreatedConnLink, GroupMemberRole>? {
val r = sendCmd(rh, CC.ApiAddGroupShortLink(groupId))
if (r is API.Result && r.res is CR.GroupLink) return r.res.connLinkContact to r.res.memberRole
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiAddGroupShortLink", generalGetString(MR.strings.error_creating_link_for_group), r)
}
return null
}
suspend fun apiCreateMemberContact(rh: Long?, groupId: Long, groupMemberId: Long): Contact? {
val r = sendCmd(rh, CC.APICreateMemberContact(groupId, groupMemberId))
if (r is API.Result && r.res is CR.NewMemberContact) return r.res.contact
@@ -3368,6 +3387,7 @@ sealed class CC {
class APIGroupLinkMemberRole(val groupId: Long, val memberRole: GroupMemberRole): CC()
class APIDeleteGroupLink(val groupId: Long): CC()
class APIGetGroupLink(val groupId: Long): CC()
class ApiAddGroupShortLink(val groupId: Long): CC()
class APICreateMemberContact(val groupId: Long, val groupMemberId: Long): CC()
class APISendMemberContactInvitation(val contactId: Long, val mc: MsgContent): CC()
class APITestProtoServer(val userId: Long, val server: String): CC()
@@ -3422,6 +3442,7 @@ sealed class CC {
class ApiCreateMyAddress(val userId: Long, val short: Boolean): CC()
class ApiDeleteMyAddress(val userId: Long): CC()
class ApiShowMyAddress(val userId: Long): CC()
class ApiAddMyAddressShortLink(val userId: Long): CC()
class ApiSetProfileAddress(val userId: Long, val on: Boolean): CC()
class ApiAddressAutoAccept(val userId: Long, val autoAccept: AutoAccept?): CC()
class ApiGetCallInvitations: CC()
@@ -3555,6 +3576,7 @@ sealed class CC {
is APIGroupLinkMemberRole -> "/_set link role #$groupId ${memberRole.name.lowercase()}"
is APIDeleteGroupLink -> "/_delete link #$groupId"
is APIGetGroupLink -> "/_get link #$groupId"
is ApiAddGroupShortLink -> "/_short link #$groupId"
is APICreateMemberContact -> "/_create member contact #$groupId $groupMemberId"
is APISendMemberContactInvitation -> "/_invite member contact @$contactId ${mc.cmdString}"
is APITestProtoServer -> "/_server test $userId $server"
@@ -3609,6 +3631,7 @@ sealed class CC {
is ApiCreateMyAddress -> "/_address $userId short=${onOff(short)}"
is ApiDeleteMyAddress -> "/_delete_address $userId"
is ApiShowMyAddress -> "/_show_address $userId"
is ApiAddMyAddressShortLink -> "/_short_link_address $userId"
is ApiSetProfileAddress -> "/_profile_address $userId ${onOff(on)}"
is ApiAddressAutoAccept -> "/_auto_accept $userId ${AutoAccept.cmdString(autoAccept)}"
is ApiAcceptContact -> "/_accept incognito=${onOff(incognito)} $contactReqId"
@@ -3720,6 +3743,7 @@ sealed class CC {
is APIGroupLinkMemberRole -> "apiGroupLinkMemberRole"
is APIDeleteGroupLink -> "apiDeleteGroupLink"
is APIGetGroupLink -> "apiGetGroupLink"
is ApiAddGroupShortLink -> "apiAddGroupShortLink"
is APICreateMemberContact -> "apiCreateMemberContact"
is APISendMemberContactInvitation -> "apiSendMemberContactInvitation"
is APITestProtoServer -> "testProtoServer"
@@ -3774,6 +3798,7 @@ sealed class CC {
is ApiCreateMyAddress -> "apiCreateMyAddress"
is ApiDeleteMyAddress -> "apiDeleteMyAddress"
is ApiShowMyAddress -> "apiShowMyAddress"
is ApiAddMyAddressShortLink -> "apiAddMyAddressShortLink"
is ApiSetProfileAddress -> "apiSetProfileAddress"
is ApiAddressAutoAccept -> "apiAddressAutoAccept"
is ApiAcceptContact -> "apiAcceptContact"
@@ -16,7 +16,7 @@ expect fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOut
expect fun GrayU8.toImageBitmap(): ImageBitmap
expect fun ImageBitmap.hasAlpha(): Boolean
expect fun ImageBitmap.addLogo(): ImageBitmap
expect fun ImageBitmap.addLogo(size: Float): ImageBitmap
expect fun ImageBitmap.scale(width: Int, height: Int): ImageBitmap
expect fun isImage(uri: URI): Boolean
@@ -68,7 +68,7 @@ private fun VerifyCodeLayout(
}
}
QRCode(connectionCode, padding = PaddingValues(vertical = DEFAULT_PADDING_HALF))
QRCode(connectionCode, small = true, padding = PaddingValues(vertical = DEFAULT_PADDING_HALF))
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Spacer(Modifier.weight(2f))
@@ -49,6 +49,18 @@ fun GroupLinkView(
creatingLink = false
}
}
fun addShortLink() {
creatingLink = true
withBGApi {
val link = chatModel.controller.apiAddGroupShortLink(rhId, groupInfo.groupId)
if (link != null) {
groupLink = link.first
groupLinkMemberRole.value = link.second
onGroupLinkUpdated?.invoke(link)
}
creatingLink = false
}
}
LaunchedEffect(Unit) {
if (groupLink == null && !creatingLink) {
createLink()
@@ -60,6 +72,7 @@ fun GroupLinkView(
groupLinkMemberRole,
creatingLink,
createLink = ::createLink,
addShortLink = ::addShortLink,
updateLink = {
val role = groupLinkMemberRole.value
if (role != null) {
@@ -105,6 +118,7 @@ fun GroupLinkLayout(
groupLinkMemberRole: MutableState<GroupMemberRole?>,
creatingLink: Boolean,
createLink: () -> Unit,
addShortLink: () -> Unit,
updateLink: () -> Unit,
deleteLink: () -> Unit,
creatingGroup: Boolean = false,
@@ -182,12 +196,26 @@ fun GroupLinkLayout(
)
}
}
if (groupLink.connShortLink == null && appPreferences.privacyShortLinks.get()) {
AddShortLinkButton(addShortLink)
}
}
}
SectionBottomSpacer()
}
}
@Composable
private fun AddShortLinkButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(MR.images.ic_add),
stringResource(MR.strings.add_short_link),
onClick,
iconColor = MaterialTheme.colors.primary,
textColor = MaterialTheme.colors.primary,
)
}
@Composable
private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<GroupMemberRole?>, enabled: Boolean = true) {
Row(
@@ -31,6 +31,7 @@ fun SimpleXCreatedLinkQRCode(
) {
QRCode(
connLink.simplexChatUri(short),
small = short && connLink.connShortLink != null,
modifier,
padding,
tintColor,
@@ -50,6 +51,7 @@ fun SimpleXLinkQRCode(
) {
QRCode(
simplexChatLink(connReq),
small = connReq.count() < 200,
modifier,
padding,
tintColor,
@@ -61,6 +63,7 @@ fun SimpleXLinkQRCode(
@Composable
fun QRCode(
connReq: String,
small: Boolean = false,
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = DEFAULT_PADDING * 2f, vertical = DEFAULT_PADDING_HALF),
tintColor: Color = Color(0xff062d56),
@@ -68,9 +71,11 @@ fun QRCode(
onShare: (() -> Unit)? = null,
) {
val scope = rememberCoroutineScope()
val logoSize = if (small) 0.21f else 0.16f
val errorLevel = if (small) QrCode.ErrorLevel.M else QrCode.ErrorLevel.L
val qr = remember(connReq, tintColor, withLogo) {
qrCodeBitmap(connReq, 1024).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo() else it }
qrCodeBitmap(connReq, 1024, errorLevel).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo(logoSize) else it }
}
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Image(
@@ -79,12 +84,13 @@ fun QRCode(
Modifier
.padding(padding)
.widthIn(max = 400.dp)
.fillMaxWidth(if (small) 0.63f else 1f)
.aspectRatio(1f)
.then(modifier)
.clickable {
scope.launch {
val image = qrCodeBitmap(connReq, 1024).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo() else it }
val image = qrCodeBitmap(connReq, 1024, errorLevel).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo(logoSize) else it }
val file = saveTempImageUncompressed(image, true)
if (file != null) {
shareFile("", CryptoFile.plain(file.absolutePath))
@@ -96,8 +102,8 @@ fun QRCode(
}
}
fun qrCodeBitmap(content: String, size: Int = 1024): ImageBitmap {
val qrCode = QrCodeEncoder().addAutomatic(content).setError(QrCode.ErrorLevel.L).fixate()
fun qrCodeBitmap(content: String, size: Int = 1024, errorLevel: QrCode.ErrorLevel): ImageBitmap {
val qrCode = QrCodeEncoder().addAutomatic(content).setError(errorLevel).fixate()
/** See [QrCodeGeneratorImage.initialize] and [FiducialImageEngine.configure] for size calculation */
val numModules = QrCode.totalModules(qrCode.version)
// Hide border on light themes to make it fit to the same place as camera in QRCodeScanner.
@@ -43,6 +43,7 @@ fun UserAddressView(
KeyChangeEffect(user.value?.remoteHostId, user.value?.userId) {
close()
}
fun setProfileAddress(on: Boolean) {
progressIndicator = true
withBGApi {
@@ -81,6 +82,17 @@ fun UserAddressView(
}
}
fun addShortLink() {
withBGApi {
progressIndicator = true
val userAddress = chatModel.controller.apiAddMyAddressShortLink(user.value?.remoteHostId)
if (userAddress != null) {
chatModel.userAddress.value = userAddress
}
progressIndicator = false
}
}
LaunchedEffect(autoCreateAddress) {
if (chatModel.userAddress.value == null && autoCreateAddress) {
createAddress()
@@ -95,6 +107,7 @@ fun UserAddressView(
userAddress = userAddress.value,
shareViaProfile,
createAddress = { createAddress() },
addShortLink = { addShortLink() },
learnMore = {
ModalManager.start.showModal {
UserAddressLearnMore()
@@ -169,6 +182,7 @@ private fun UserAddressLayout(
userAddress: UserContactLinkRec?,
shareViaProfile: MutableState<Boolean>,
createAddress: () -> Unit,
addShortLink: () -> Unit,
learnMore: () -> Unit,
share: (String) -> Unit,
sendEmail: (UserContactLinkRec) -> Unit,
@@ -211,6 +225,9 @@ private fun UserAddressLayout(
// ShareViaEmailButton { sendEmail(userAddress) }
BusinessAddressToggle(autoAcceptState) { saveAas(autoAcceptState.value, autoAcceptStateSaved) }
AddressSettingsButton(user, userAddress, shareViaProfile, setProfileAddress, saveAas)
if (userAddress.connLinkContact.connShortLink == null && appPreferences.privacyShortLinks.get()) {
AddShortLinkButton(addShortLink)
}
if (autoAcceptState.value.business) {
SectionTextFooter(stringResource(MR.strings.add_your_team_members_to_conversations))
@@ -248,6 +265,17 @@ private fun CreateAddressButton(onClick: () -> Unit) {
)
}
@Composable
private fun AddShortLinkButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(MR.images.ic_add),
stringResource(MR.strings.add_short_link),
onClick,
iconColor = MaterialTheme.colors.primary,
textColor = MaterialTheme.colors.primary,
)
}
@Composable
private fun CreateOneTimeLinkButton() {
val closeAll = { ModalManager.start.closeModals() }
@@ -559,6 +587,7 @@ fun PreviewUserAddressLayoutNoAddress() {
user = User.sampleData,
userAddress = null,
createAddress = {},
addShortLink = {},
share = { _ -> },
deleteAddress = {},
saveAas = { _, _ -> },
@@ -592,6 +621,7 @@ fun PreviewUserAddressLayoutAddressCreated() {
user = User.sampleData,
userAddress = UserContactLinkRec(CreatedConnLink("https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D", null)),
createAddress = {},
addShortLink = {},
share = { _ -> },
deleteAddress = {},
saveAas = { _, _ -> },
@@ -189,7 +189,7 @@ fun CustomServer(
if (valid.value) {
SectionDividerSpaced()
SectionView(stringResource(MR.strings.smp_servers_add_to_another_device).uppercase()) {
QRCode(serverAddress.value)
QRCode(serverAddress.value, small = true)
}
}
}
@@ -1058,6 +1058,7 @@
<string name="address_settings">Address settings</string>
<string name="business_address">Business address</string>
<string name="add_your_team_members_to_conversations">Add your team members to the conversations.</string>
<string name="add_short_link">Add short link</string>
<!-- CreateSimpleXAddress.kt -->
<string name="continue_to_next_step">Continue</string>
@@ -1014,7 +1014,7 @@
<string name="moderate_verb">Moderieren</string>
<string name="moderate_message_will_be_marked_warning">Diese Nachricht wird für alle Mitglieder als moderiert gekennzeichnet.</string>
<string name="you_are_observer">Sie sind Beobachter</string>
<string name="observer_cant_send_message_title">Sie können keine Nachrichten versenden!</string>
<string name="observer_cant_send_message_title">Sie sind Beobachter</string>
<string name="group_member_role_observer">Beobachter</string>
<string name="initial_member_role">Anfängliche Rolle</string>
<string name="delete_member_message__question">Nachricht des Mitglieds löschen\?</string>
@@ -2466,7 +2466,7 @@
<string name="group_new_support_chats_short">%d Chat(s)</string>
<string name="report_sent_alert_title">Meldung wurde an die Moderatoren gesendet</string>
<string name="snd_group_event_member_accepted">Sie haben dieses Mitglied übernommen</string>
<string name="admission_stage_review_descr">Überprüfung der Mitglieder vor der Aufnahme (Anklopfen).</string>
<string name="admission_stage_review_descr">Überprüfung der Mitglieder vor der Aufnahme (\"Anklopfen\").</string>
<string name="admission_stage_review">Überprüfung der Mitglieder</string>
<string name="member_criteria_all">alle</string>
<string name="member_criteria_off">Aus</string>
@@ -2489,10 +2489,25 @@
<string name="button_support_chat">Chat mit Administratoren</string>
<string name="rcv_group_event_member_accepted">%1$s übernommen</string>
<string name="accept_pending_member_alert_confirmation_as_member">Als Mitglied übernehmen</string>
<string name="error_accepting_member">Fehler beim Übernehmen eines Mitglieds</string>
<string name="error_accepting_member">Fehler beim Übernehmen des Mitglieds</string>
<string name="member_admission">Aufnahme von Mitgliedern</string>
<string name="group_member_status_pending_review">Ausstehende Überprüfung</string>
<string name="group_new_support_chat_one">Chat mit einem Mitglied</string>
<string name="accept_pending_member_button">Übernehmen</string>
<string name="snd_group_event_user_pending_review">Bitte warten Sie auf die Überprüfung Ihrer Anfrage durch die Gruppen-Moderatoren, um der Gruppe beitreten zu können.</string>
<string name="cant_send_message_group_deleted">Gruppe wird gelöscht</string>
<string name="cant_send_message_rejected">Beitrittsanfrage abgelehnt</string>
<string name="cant_send_message_mem_removed">Von der Gruppe entfernt</string>
<string name="cant_send_message_you_left">Sie haben die Gruppe verlassen</string>
<string name="cant_send_message_contact_disabled">Kontakt deaktiviert</string>
<string name="cant_send_message_contact_not_synchronized">Nicht synchronisiert</string>
<string name="error_deleting_member_support_chat">Fehler beim Löschen des Chats mit dem Mitglied</string>
<string name="cant_send_message_contact_not_ready">Kontakt nicht bereit</string>
<string name="cant_send_message_alert_title">Sie können keine Nachrichten senden!</string>
<string name="delete_member_support_chat_button">Chat löschen</string>
<string name="delete_member_support_chat_alert_title">Chat mit dem Mitglied löschen?</string>
<string name="reject_pending_member_alert_title">Mitglied ablehnen?</string>
<string name="cant_send_message_generic">Es können keine Nachrichten gesendet werden</string>
<string name="cant_send_message_contact_deleted">Kontakt gelöscht</string>
<string name="cant_send_message_member_has_old_version">Das Mitglied hat eine alte App-Version</string>
</resources>
@@ -940,7 +940,7 @@
<string name="error_updating_link_for_group">Errore nell\'aggiornamento del link del gruppo</string>
<string name="group_member_role_observer">osservatore</string>
<string name="observer_cant_send_message_desc">Contatta l\'amministratore del gruppo.</string>
<string name="observer_cant_send_message_title">Non puoi inviare messaggi!</string>
<string name="observer_cant_send_message_title">sei un osservatore</string>
<string name="language_system">Sistema</string>
<string name="button_add_welcome_message">Aggiungi messaggio di benvenuto</string>
<string name="button_welcome_message">Messaggio di benvenuto</string>
@@ -2414,14 +2414,29 @@
<string name="accept_pending_member_alert_title">Accetta membro</string>
<string name="button_support_chat">Chatta con gli amministratori</string>
<string name="accept_pending_member_alert_confirmation_as_observer">Accetta come osservatore</string>
<string name="rcv_group_event_new_member_pending_review">Il nuovo membro vuole entrare nel gruppo.</string>
<string name="rcv_group_event_new_member_pending_review">Un nuovo membro vuole entrare nel gruppo.</string>
<string name="member_criteria_all">tutti</string>
<string name="button_support_chat_member">Chatta con il membro</string>
<string name="member_support">Chat con membri</string>
<string name="error_accepting_member">Errore di accettazione del membro</string>
<string name="accept_pending_member_alert_confirmation_as_member">Accetta come membro</string>
<string name="set_member_admission">Imposta l\'ammissione del membro</string>
<string name="set_member_admission">Imposta l\'ammissione dei membri</string>
<string name="report_sent_alert_title">Segnalazione inviata ai moderatori</string>
<string name="group_member_status_pending_review">in attesa di revisione</string>
<string name="report_sent_alert_msg_view_in_support_chat">Puoi vedere i tuoi resoconti nella chat con gli amministratori.</string>
<string name="report_sent_alert_msg_view_in_support_chat">Puoi vedere le tue segnalazioni nella chat con gli amministratori.</string>
<string name="cant_send_message_alert_title">Non puoi inviare messaggi!</string>
<string name="cant_send_message_contact_not_ready">contatto non pronto</string>
<string name="cant_send_message_contact_deleted">contatto eliminato</string>
<string name="cant_send_message_contact_disabled">contatto disattivato</string>
<string name="cant_send_message_contact_not_synchronized">non sincronizzato</string>
<string name="cant_send_message_rejected">richiesta di entrare rifiutata</string>
<string name="cant_send_message_generic">impossibile inviare messaggi</string>
<string name="cant_send_message_group_deleted">il gruppo è eliminato</string>
<string name="cant_send_message_member_has_old_version">il membro ha una versione vecchia</string>
<string name="cant_send_message_mem_removed">rimosso dal gruppo</string>
<string name="cant_send_message_you_left">sei uscito/a</string>
<string name="delete_member_support_chat_alert_title">Eliminare la chat con il membro?</string>
<string name="reject_pending_member_alert_title">Rifiutare il membro?</string>
<string name="delete_member_support_chat_button">Elimina chat</string>
<string name="error_deleting_member_support_chat">Errore di eliminazione della chat con il membro</string>
</resources>
@@ -1019,7 +1019,7 @@
<string name="moderate_message_will_be_deleted_warning">Сообщение будет удалено для всех членов группы.</string>
<string name="moderate_message_will_be_marked_warning">Сообщение будет помечено как удаленное для всех членов группы.</string>
<string name="observer_cant_send_message_desc">Пожалуйста, свяжитесь с админом группы.</string>
<string name="observer_cant_send_message_title">Вы не можете отправлять сообщения!</string>
<string name="observer_cant_send_message_title">Вы \"читатель\"</string>
<string name="you_are_observer">только чтение сообщений</string>
<string name="group_member_role_observer">читатель</string>
<string name="initial_member_role">Роль при вступлении</string>
@@ -1132,7 +1132,7 @@
<string name="decryption_error">Ошибка расшифровки</string>
<string name="lock_not_enabled">Блокировка SimpleX не включена!</string>
<string name="alert_title_msg_bad_hash">Ошибка хэш сообщения</string>
<string name="alert_text_msg_bad_hash">Хэш предыдущего сообщения отличается\"</string>
<string name="alert_text_msg_bad_hash">Хэш предыдущего сообщения отличается.</string>
<string name="confirm_passcode">Подтвердить код</string>
<string name="incorrect_passcode">Неправильный код</string>
<string name="lock_after">Заблокировать через</string>
@@ -2467,4 +2467,58 @@
<string name="onboarding_conditions_private_chats_not_accessible">Частные разговоры, группы и Ваши контакты недоступны для операторов серверов.</string>
<string name="onboarding_conditions_configure_server_operators">Настроить операторов серверов</string>
<string name="onboarding_conditions_privacy_policy_and_conditions_of_use">Политика конфиденциальности и условия использования.</string>
<string name="member_criteria_all">все</string>
<string name="accept_pending_member_button">Принять</string>
<string name="accept_pending_member_alert_question">Участник хочет присоединиться к группе. Принять?</string>
<string name="cant_send_message_group_deleted">группа удалена</string>
<string name="cant_send_message_mem_removed">удален из группы</string>
<string name="group_new_support_chats_short">%d чата(ов)</string>
<string name="cant_send_message_contact_not_ready">контакт не готов</string>
<string name="cant_send_message_contact_deleted">контакт удален</string>
<string name="cant_send_message_contact_not_synchronized">не синхронизирован</string>
<string name="cant_send_message_rejected">запрос на вступление отклонён</string>
<string name="rcv_group_event_new_member_pending_review">Новый участник хочет присоединиться к группе.</string>
<string name="snd_group_event_user_pending_review">Пожалуйста, подождите, пока модераторы группы рассмотрят ваш запрос на вступление.</string>
<string name="group_member_status_pending_review">ожидает одобрения</string>
<string name="reject_pending_member_button">Отклонить</string>
<string name="reject_pending_member_alert_title">Отклонить участника?</string>
<string name="error_deleting_member_support_chat">Ошибка при удалении чата с членом группы</string>
<string name="full_link_button_text">Полная ссылка</string>
<string name="error_accepting_member">Ошибка вступления члена группы</string>
<string name="unsupported_connection_link">Ссылка не поддерживается</string>
<string name="link_requires_newer_app_version_please_upgrade">Эта ссылка требует новую версию. Обновите приложение или попросите Ваш контакт прислать совместимую ссылку.</string>
<string name="group_new_support_messages">%d сообщений</string>
<string name="report_sent_alert_msg_view_in_support_chat">Вы можете найти Ваши жалобы в Чате с админами.</string>
<string name="button_support_chat">Чат с админами</string>
<string name="button_support_chat_member">Чат с членом группы</string>
<string name="member_criteria_off">выключено</string>
<string name="admission_stage_review">Одобрять членов</string>
<string name="member_support">Чаты с членами группы</string>
<string name="member_admission">Приём членов в группу</string>
<string name="admission_stage_review_descr">Одобрять членов для вступления в группу.</string>
<string name="no_support_chats">Нет чатов с членами группы</string>
<string name="accept_pending_member_alert_confirmation_as_observer">Принять как читателя</string>
<string name="accept_pending_member_alert_confirmation_as_member">Принять в группу</string>
<string name="accept_pending_member_alert_title">Принять члена</string>
<string name="reviewed_by_admins">одобрен админами</string>
<string name="report_sent_alert_title">Жалоба отправлена модераторам</string>
<string name="cant_send_message_you_left">Вы вышли</string>
<string name="cant_send_message_generic">нельзя отправлять</string>
<string name="group_new_support_chats">%d чатов с членами группы</string>
<string name="cant_send_message_contact_disabled">контакт выключен</string>
<string name="cant_send_message_member_has_old_version">член имеет старую версию</string>
<string name="cant_send_message_alert_title">Вы не можете отправлять сообщения!</string>
<string name="short_link_button_text">Короткая ссылка</string>
<string name="save_admission_question">Сохранить настройки вступления?</string>
<string name="snd_group_event_member_accepted">Вы приняли этого члена</string>
<string name="group_member_status_pending_review_short">рассмотрение</string>
<string name="set_member_admission">Установить вступление в группу</string>
<string name="delete_member_support_chat_alert_title">Удалить чат с членом группы?</string>
<string name="delete_member_support_chat_button">Удалить разговор</string>
<string name="rcv_group_event_member_accepted">принят %1$s</string>
<string name="support_chat">Чат с админами</string>
<string name="rcv_group_event_user_accepted">Вы приняты</string>
<string name="group_new_support_chat_one">1 чат с членом группы</string>
<string name="simplex_link_channel">SimpleX ссылка канала</string>
<string name="privacy_short_links">Короткие ссылки (БЕТА)</string>
</resources>
@@ -929,7 +929,7 @@
<string name="group_member_role_observer">观察员</string>
<string name="you_are_observer">你是观察者</string>
<string name="error_updating_link_for_group">更新群链接错误</string>
<string name="observer_cant_send_message_title">无法发送消息!</string>
<string name="observer_cant_send_message_title">是观察员</string>
<string name="initial_member_role">初始角色</string>
<string name="observer_cant_send_message_desc">请联系群管理员。</string>
<string name="language_system">系统</string>
@@ -2408,4 +2408,19 @@
<string name="report_sent_alert_msg_view_in_support_chat">你可以在和管理员和聊天中查看你的举报。</string>
<string name="accept_pending_member_alert_confirmation_as_observer">接受为观察员</string>
<string name="group_new_support_chat_one">和一名成员的一个聊天</string>
<string name="cant_send_message_generic">无法发送消息</string>
<string name="cant_send_message_you_left">你离开了</string>
<string name="error_deleting_member_support_chat">删除和成员的聊天出错</string>
<string name="cant_send_message_alert_title">你无法发送消息!</string>
<string name="cant_send_message_contact_disabled">禁用了联系人</string>
<string name="cant_send_message_group_deleted">群被删除了</string>
<string name="cant_send_message_mem_removed">从群被删除了</string>
<string name="cant_send_message_rejected">加入请求被拒绝</string>
<string name="delete_member_support_chat_button">删除聊天</string>
<string name="delete_member_support_chat_alert_title">删除和成员的聊天吗?</string>
<string name="cant_send_message_contact_not_synchronized">未同步</string>
<string name="cant_send_message_member_has_old_version">成员有旧版本</string>
<string name="cant_send_message_contact_deleted">删除了联系人</string>
<string name="cant_send_message_contact_not_ready">联系人未就绪</string>
<string name="reject_pending_member_alert_title">拒绝成员?</string>
</resources>
@@ -133,9 +133,9 @@ actual fun ImageBitmap.hasAlpha(): Boolean {
return false
}
actual fun ImageBitmap.addLogo(): ImageBitmap {
val radius = (width * 0.16f).toInt()
val logoSize = (width * 0.24).toInt()
actual fun ImageBitmap.addLogo(size: Float): ImageBitmap {
val radius = (width * size).toInt()
val logoSize = (width * size * 1.5).toInt()
val logo: BufferedImage = MR.images.icon_foreground_common.image
val original = toAwtImage()
val withLogo = BufferedImage(width, height, original.type)