android: Voice messages UI changes (#1422)
* android: Voice messages UI changes * Alert for groups Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
60fedbf5d2
commit
f8cf35879f
@@ -1052,7 +1052,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
|||||||
val file = cItem.file
|
val file = cItem.file
|
||||||
if (cItem.content.msgContent is MsgContent.MCImage && file != null && file.fileSize <= MAX_IMAGE_SIZE_AUTO_RCV && appPrefs.privacyAcceptImages.get()) {
|
if (cItem.content.msgContent is MsgContent.MCImage && file != null && file.fileSize <= MAX_IMAGE_SIZE_AUTO_RCV && appPrefs.privacyAcceptImages.get()) {
|
||||||
withApi { receiveFile(file.fileId) }
|
withApi { receiveFile(file.fileId) }
|
||||||
} else if (cItem.content.msgContent is MsgContent.MCVoice && file != null && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV && appPrefs.privacyAcceptImages.get()) {
|
} else if (cItem.content.msgContent is MsgContent.MCVoice && file != null && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV && file.fileSize > MAX_VOICE_SIZE_FOR_SENDING && appPrefs.privacyAcceptImages.get()) {
|
||||||
withApi { receiveFile(file.fileId) } // TODO check inlineFileMode != IFMSent
|
withApi { receiveFile(file.fileId) } // TODO check inlineFileMode != IFMSent
|
||||||
}
|
}
|
||||||
if (!cItem.chatDir.sent && !cItem.isCall && !cItem.isMutedMemberEvent && (!isAppOnForeground(appContext) || chatModel.chatId.value != cInfo.id)) {
|
if (!cItem.chatDir.sent && !cItem.isCall && !cItem.isMutedMemberEvent && (!isAppOnForeground(appContext) || chatModel.chatId.value != cInfo.id)) {
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ fun TerminalLayout(
|
|||||||
topBar = { CloseSheetBar(close) },
|
topBar = { CloseSheetBar(close) },
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
Box(Modifier.padding(horizontal = 8.dp)) {
|
Box(Modifier.padding(horizontal = 8.dp)) {
|
||||||
SendMsgView(composeState, false, sendCommand, ::onMessageChange, { _, _, _ -> }, textStyle)
|
SendMsgView(composeState, false, false, false, sendCommand, ::onMessageChange, { _, _, _ -> }, {}, {}, textStyle)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.navigationBarsWithImePadding()
|
modifier = Modifier.navigationBarsWithImePadding()
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ fun ChatInfoView(
|
|||||||
},
|
},
|
||||||
openPreferences = {
|
openPreferences = {
|
||||||
ModalManager.shared.showModal(true) {
|
ModalManager.shared.showModal(true) {
|
||||||
ContactPreferencesView(chatModel, chatModel.currentUser.value ?: return@showModal, contact)
|
ContactPreferencesView(chatModel, chatModel.currentUser.value ?: return@showModal, contact.contactId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteContact = { deleteContactDialog(chat.chatInfo, chatModel, close) },
|
deleteContact = { deleteContactDialog(chat.chatInfo, chatModel, close) },
|
||||||
|
|||||||
@@ -455,6 +455,29 @@ fun ComposeView(
|
|||||||
composeState.value = composeState.value.copy(preview = ComposePreview.VoicePreview(filePath, durationMs, finished))
|
composeState.value = composeState.value.copy(preview = ComposePreview.VoicePreview(filePath, durationMs, finished))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun allowVoiceToContact() {
|
||||||
|
val contact = (chat.chatInfo as ChatInfo.Direct?)?.contact ?: return
|
||||||
|
val featuresAllowed = contactUserPrefsToFeaturesAllowed(contact.mergedPreferences)
|
||||||
|
withApi {
|
||||||
|
val prefs = contactFeaturesAllowedToPrefs(featuresAllowed).copy(voice = ChatPreference(FeatureAllowed.YES))
|
||||||
|
val toContact = chatModel.controller.apiSetContactPrefs(contact.contactId, prefs)
|
||||||
|
if (toContact != null) {
|
||||||
|
chatModel.updateContact(toContact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun showDisabledVoiceAlert() {
|
||||||
|
AlertManager.shared.showAlertMsg(
|
||||||
|
title = generalGetString(R.string.voice_messages_prohibited),
|
||||||
|
text = generalGetString(
|
||||||
|
if (chat.chatInfo is ChatInfo.Direct)
|
||||||
|
R.string.ask_your_contact_to_enable_voice
|
||||||
|
else
|
||||||
|
R.string.only_group_owners_can_enable_voice
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun cancelLinkPreview() {
|
fun cancelLinkPreview() {
|
||||||
val uri = composeState.value.linkPreview?.uri
|
val uri = composeState.value.linkPreview?.uri
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
@@ -549,15 +572,35 @@ fun ComposeView(
|
|||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val allowedVoiceByPrefs = remember(chat.chatInfo) {
|
||||||
|
when (chat.chatInfo) {
|
||||||
|
is ChatInfo.Direct -> chat.chatInfo.contact.mergedPreferences.voice.enabled.forUser
|
||||||
|
is ChatInfo.Group -> chat.chatInfo.groupInfo.fullGroupPreferences.voice.enable == GroupFeatureEnabled.ON
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val needToAllowVoiceToContact = remember(chat.chatInfo) {
|
||||||
|
when (chat.chatInfo) {
|
||||||
|
is ChatInfo.Direct -> with(chat.chatInfo.contact.mergedPreferences.voice) {
|
||||||
|
((userPreference as? ContactUserPref.User)?.preference?.allow == FeatureAllowed.NO || (userPreference as? ContactUserPref.Contact)?.preference?.allow == FeatureAllowed.NO) &&
|
||||||
|
contactPreference.allow == FeatureAllowed.YES
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
SendMsgView(
|
SendMsgView(
|
||||||
composeState,
|
composeState,
|
||||||
allowVoiceRecord = true,
|
showVoiceRecordIcon = true,
|
||||||
|
allowedVoiceByPrefs = allowedVoiceByPrefs,
|
||||||
|
needToAllowVoiceToContact = needToAllowVoiceToContact,
|
||||||
sendMessage = {
|
sendMessage = {
|
||||||
sendMessage()
|
sendMessage()
|
||||||
resetLinkPreview()
|
resetLinkPreview()
|
||||||
},
|
},
|
||||||
::onMessageChange,
|
::onMessageChange,
|
||||||
::onAudioAdded,
|
::onAudioAdded,
|
||||||
|
::allowVoiceToContact,
|
||||||
|
::showDisabledVoiceAlert,
|
||||||
textStyle
|
textStyle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ fun ComposeVoiceView(
|
|||||||
Modifier
|
Modifier
|
||||||
.requiredWidth(progressBarWidth.value.dp)
|
.requiredWidth(progressBarWidth.value.dp)
|
||||||
.padding(top = 58.dp)
|
.padding(top = 58.dp)
|
||||||
.height(2.dp)
|
.height(3.dp)
|
||||||
.background(MaterialTheme.colors.primary)
|
.background(MaterialTheme.colors.primary)
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
|
|||||||
@@ -24,15 +24,17 @@ import chat.simplex.app.views.helpers.*
|
|||||||
fun ContactPreferencesView(
|
fun ContactPreferencesView(
|
||||||
m: ChatModel,
|
m: ChatModel,
|
||||||
user: User,
|
user: User,
|
||||||
contact: Contact,
|
contactId: Long,
|
||||||
) {
|
) {
|
||||||
var featuresAllowed by remember { mutableStateOf(contactUserPrefsToFeaturesAllowed(contact.mergedPreferences)) }
|
val contact = remember { derivedStateOf { (m.getContactChat(contactId)?.chatInfo as? ChatInfo.Direct)?.contact } }
|
||||||
var currentFeaturesAllowed by remember { mutableStateOf(featuresAllowed) }
|
val ct = contact.value ?: return
|
||||||
|
var featuresAllowed by remember(ct) { mutableStateOf(contactUserPrefsToFeaturesAllowed(ct.mergedPreferences)) }
|
||||||
|
var currentFeaturesAllowed by remember(ct) { mutableStateOf(featuresAllowed) }
|
||||||
ContactPreferencesLayout(
|
ContactPreferencesLayout(
|
||||||
featuresAllowed,
|
featuresAllowed,
|
||||||
currentFeaturesAllowed,
|
currentFeaturesAllowed,
|
||||||
user,
|
user,
|
||||||
contact,
|
ct,
|
||||||
applyPrefs = { prefs ->
|
applyPrefs = { prefs ->
|
||||||
featuresAllowed = prefs
|
featuresAllowed = prefs
|
||||||
},
|
},
|
||||||
@@ -42,7 +44,7 @@ fun ContactPreferencesView(
|
|||||||
savePrefs = {
|
savePrefs = {
|
||||||
withApi {
|
withApi {
|
||||||
val prefs = contactFeaturesAllowedToPrefs(featuresAllowed)
|
val prefs = contactFeaturesAllowedToPrefs(featuresAllowed)
|
||||||
val toContact = m.controller.apiSetContactPrefs(contact.contactId, prefs)
|
val toContact = m.controller.apiSetContactPrefs(ct.contactId, prefs)
|
||||||
if (toContact != null) {
|
if (toContact != null) {
|
||||||
m.updateContact(toContact)
|
m.updateContact(toContact)
|
||||||
currentFeaturesAllowed = featuresAllowed
|
currentFeaturesAllowed = featuresAllowed
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import androidx.compose.material.*
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material.icons.outlined.*
|
import androidx.compose.material.icons.outlined.*
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.*
|
import androidx.compose.ui.*
|
||||||
@@ -25,7 +24,6 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.*
|
import androidx.compose.ui.graphics.*
|
||||||
import androidx.compose.ui.platform.*
|
import androidx.compose.ui.platform.*
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@@ -48,10 +46,14 @@ import java.io.*
|
|||||||
@Composable
|
@Composable
|
||||||
fun SendMsgView(
|
fun SendMsgView(
|
||||||
composeState: MutableState<ComposeState>,
|
composeState: MutableState<ComposeState>,
|
||||||
allowVoiceRecord: Boolean,
|
showVoiceRecordIcon: Boolean,
|
||||||
|
allowedVoiceByPrefs: Boolean,
|
||||||
|
needToAllowVoiceToContact: Boolean,
|
||||||
sendMessage: () -> Unit,
|
sendMessage: () -> Unit,
|
||||||
onMessageChange: (String) -> Unit,
|
onMessageChange: (String) -> Unit,
|
||||||
onAudioAdded: (String, Int, Boolean) -> Unit,
|
onAudioAdded: (String, Int, Boolean) -> Unit,
|
||||||
|
allowVoiceToContact: () -> Unit,
|
||||||
|
showDisabledVoiceAlert: () -> Unit,
|
||||||
textStyle: MutableState<TextStyle>
|
textStyle: MutableState<TextStyle>
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(vertical = 8.dp)) {
|
Column(Modifier.padding(vertical = 8.dp)) {
|
||||||
@@ -60,7 +62,7 @@ fun SendMsgView(
|
|||||||
val attachEnabled = !composeState.value.editing
|
val attachEnabled = !composeState.value.editing
|
||||||
val filePath = rememberSaveable { mutableStateOf(null as String?) }
|
val filePath = rememberSaveable { mutableStateOf(null as String?) }
|
||||||
var recordingTimeRange by rememberSaveable(saver = LongRange.saver) { mutableStateOf(0L..0L) } // since..to
|
var recordingTimeRange by rememberSaveable(saver = LongRange.saver) { mutableStateOf(0L..0L) } // since..to
|
||||||
val showVoiceButton = ((cs.message.isEmpty() || recordingTimeRange.first > 0L) && allowVoiceRecord && attachEnabled && cs.preview is ComposePreview.NoPreview) || filePath.value != null
|
val showVoiceButton = ((cs.message.isEmpty() || recordingTimeRange.first > 0L) && showVoiceRecordIcon && attachEnabled && cs.preview is ComposePreview.NoPreview) || filePath.value != null
|
||||||
Box(if (recordingTimeRange.first == 0L)
|
Box(if (recordingTimeRange.first == 0L)
|
||||||
Modifier
|
Modifier
|
||||||
else
|
else
|
||||||
@@ -112,6 +114,16 @@ fun SendMsgView(
|
|||||||
val startStopRecording: () -> Unit = {
|
val startStopRecording: () -> Unit = {
|
||||||
when {
|
when {
|
||||||
!permissionsState.allPermissionsGranted -> permissionsState.launchMultiplePermissionRequest()
|
!permissionsState.allPermissionsGranted -> permissionsState.launchMultiplePermissionRequest()
|
||||||
|
needToAllowVoiceToContact -> {
|
||||||
|
AlertManager.shared.showAlertDialog(
|
||||||
|
title = generalGetString(R.string.allow_voice_messages_question),
|
||||||
|
text = generalGetString(R.string.you_need_to_allow_to_send_voice),
|
||||||
|
confirmText = generalGetString(R.string.allow_verb),
|
||||||
|
dismissText = generalGetString(R.string.cancel_verb),
|
||||||
|
onConfirm = allowVoiceToContact,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
!allowedVoiceByPrefs -> showDisabledVoiceAlert()
|
||||||
recordingInProgress.value -> stopRecordingAndAddAudio()
|
recordingInProgress.value -> stopRecordingAndAddAudio()
|
||||||
filePath.value == null -> {
|
filePath.value == null -> {
|
||||||
recordingTimeRange = System.currentTimeMillis()..0L
|
recordingTimeRange = System.currentTimeMillis()..0L
|
||||||
@@ -149,10 +161,15 @@ fun SendMsgView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val interactionSource = interactionSourceWithTapDetection(
|
val interactionSource = interactionSourceWithTapDetection(
|
||||||
|
// It's just a key for triggering dropping a state in the compose function. Without it
|
||||||
|
// nothing will react on changed params like needToAllowVoiceToContact or allowedVoiceByPrefs
|
||||||
|
needToAllowVoiceToContact.toString() + allowedVoiceByPrefs.toString(),
|
||||||
onPress = {
|
onPress = {
|
||||||
if (filePath.value == null) startStopRecording()
|
if (filePath.value == null) startStopRecording()
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
|
// Voice not allowed or not granted voice record permission for the app
|
||||||
|
if (!allowedVoiceByPrefs || !permissionsState.allPermissionsGranted) return@interactionSourceWithTapDetection
|
||||||
if (!recordingInProgress.value && filePath.value != null) {
|
if (!recordingInProgress.value && filePath.value != null) {
|
||||||
sendMessage()
|
sendMessage()
|
||||||
cleanUp(false)
|
cleanUp(false)
|
||||||
@@ -173,9 +190,19 @@ fun SendMsgView(
|
|||||||
Modifier
|
Modifier
|
||||||
IconButton({}, Modifier.size(36.dp), enabled = !cs.inProgress, interactionSource = interactionSource) {
|
IconButton({}, Modifier.size(36.dp), enabled = !cs.inProgress, interactionSource = interactionSource) {
|
||||||
Icon(
|
Icon(
|
||||||
if (recordingTimeRange.last != 0L) Icons.Outlined.ArrowUpward else if (stopRecOnNextClick) Icons.Default.Stop else Icons.Default.Mic,
|
when {
|
||||||
|
recordingTimeRange.last != 0L -> Icons.Outlined.ArrowUpward
|
||||||
|
stopRecOnNextClick -> Icons.Filled.Stop
|
||||||
|
allowedVoiceByPrefs -> Icons.Filled.KeyboardVoice
|
||||||
|
else -> Icons.Outlined.KeyboardVoice
|
||||||
|
},
|
||||||
stringResource(R.string.icon_descr_record_voice_message),
|
stringResource(R.string.icon_descr_record_voice_message),
|
||||||
tint = if (recordingTimeRange.last != 0L) Color.White else if (!cs.inProgress) MaterialTheme.colors.primary else HighOrLowlight,
|
tint = when {
|
||||||
|
recordingTimeRange.last != 0L -> Color.White
|
||||||
|
stopRecOnNextClick -> MaterialTheme.colors.primary
|
||||||
|
allowedVoiceByPrefs -> MaterialTheme.colors.primary
|
||||||
|
else -> HighOrLowlight
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
.size(36.dp)
|
||||||
.padding(4.dp)
|
.padding(4.dp)
|
||||||
@@ -301,10 +328,14 @@ fun PreviewSendMsgView() {
|
|||||||
SimpleXTheme {
|
SimpleXTheme {
|
||||||
SendMsgView(
|
SendMsgView(
|
||||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||||
allowVoiceRecord = false,
|
showVoiceRecordIcon = false,
|
||||||
|
allowedVoiceByPrefs = false,
|
||||||
|
needToAllowVoiceToContact = false,
|
||||||
sendMessage = {},
|
sendMessage = {},
|
||||||
onMessageChange = { _ -> },
|
onMessageChange = { _ -> },
|
||||||
onAudioAdded = { _, _, _ -> },
|
onAudioAdded = { _, _, _ -> },
|
||||||
|
allowVoiceToContact = {},
|
||||||
|
showDisabledVoiceAlert = {},
|
||||||
textStyle = textStyle
|
textStyle = textStyle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -324,10 +355,14 @@ fun PreviewSendMsgViewEditing() {
|
|||||||
SimpleXTheme {
|
SimpleXTheme {
|
||||||
SendMsgView(
|
SendMsgView(
|
||||||
composeState = remember { mutableStateOf(composeStateEditing) },
|
composeState = remember { mutableStateOf(composeStateEditing) },
|
||||||
allowVoiceRecord = false,
|
showVoiceRecordIcon = false,
|
||||||
|
allowedVoiceByPrefs = false,
|
||||||
|
needToAllowVoiceToContact = false,
|
||||||
sendMessage = {},
|
sendMessage = {},
|
||||||
onMessageChange = { _ -> },
|
onMessageChange = { _ -> },
|
||||||
onAudioAdded = { _, _, _ -> },
|
onAudioAdded = { _, _, _ -> },
|
||||||
|
allowVoiceToContact = {},
|
||||||
|
showDisabledVoiceAlert = {},
|
||||||
textStyle = textStyle
|
textStyle = textStyle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -347,10 +382,14 @@ fun PreviewSendMsgViewInProgress() {
|
|||||||
SimpleXTheme {
|
SimpleXTheme {
|
||||||
SendMsgView(
|
SendMsgView(
|
||||||
composeState = remember { mutableStateOf(composeStateInProgress) },
|
composeState = remember { mutableStateOf(composeStateInProgress) },
|
||||||
allowVoiceRecord = false,
|
showVoiceRecordIcon = false,
|
||||||
|
allowedVoiceByPrefs = false,
|
||||||
|
needToAllowVoiceToContact = false,
|
||||||
sendMessage = {},
|
sendMessage = {},
|
||||||
onMessageChange = { _ -> },
|
onMessageChange = { _ -> },
|
||||||
onAudioAdded = { _, _, _ -> },
|
onAudioAdded = { _, _, _ -> },
|
||||||
|
allowVoiceToContact = {},
|
||||||
|
showDisabledVoiceAlert = {},
|
||||||
textStyle = textStyle
|
textStyle = textStyle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.InsertDriveFile
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -95,7 +94,7 @@ fun FramedItemView(
|
|||||||
ciQuotedMsgView(qi)
|
ciQuotedMsgView(qi)
|
||||||
}
|
}
|
||||||
Icon(
|
Icon(
|
||||||
if (qi.content is MsgContent.MCFile) Icons.Filled.InsertDriveFile else Icons.Filled.PlayArrow,
|
if (qi.content is MsgContent.MCFile) Icons.Filled.InsertDriveFile else Icons.Filled.Mic,
|
||||||
if (qi.content is MsgContent.MCFile) stringResource(R.string.icon_descr_file) else stringResource(R.string.voice_message),
|
if (qi.content is MsgContent.MCFile) stringResource(R.string.icon_descr_file) else stringResource(R.string.voice_message),
|
||||||
Modifier
|
Modifier
|
||||||
.padding(top = 6.dp, end = 4.dp)
|
.padding(top = 6.dp, end = 4.dp)
|
||||||
|
|||||||
@@ -214,8 +214,8 @@ fun interactionSourceWithDetection(onClick: () -> Unit, onLongClick: () -> Unit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun interactionSourceWithTapDetection(onPress: () -> Unit, onClick: () -> Unit, onCancel: () -> Unit, onRelease: ()-> Unit): MutableInteractionSource {
|
fun interactionSourceWithTapDetection(key: Any, onPress: () -> Unit, onClick: () -> Unit, onCancel: () -> Unit, onRelease: ()-> Unit): MutableInteractionSource {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember(key) { MutableInteractionSource() }
|
||||||
LaunchedEffect(interactionSource) {
|
LaunchedEffect(interactionSource) {
|
||||||
var firstTapTime = 0L
|
var firstTapTime = 0L
|
||||||
interactionSource.interactions.collect { interaction ->
|
interactionSource.interactions.collect { interaction ->
|
||||||
|
|||||||
@@ -167,6 +167,7 @@
|
|||||||
<string name="save_verb">Speichern</string>
|
<string name="save_verb">Speichern</string>
|
||||||
<string name="edit_verb">Bearbeiten</string>
|
<string name="edit_verb">Bearbeiten</string>
|
||||||
<string name="delete_verb">Löschen</string>
|
<string name="delete_verb">Löschen</string>
|
||||||
|
<string name="allow_verb">***Allow</string>
|
||||||
<string name="delete_message__question">Die Nachricht löschen?</string>
|
<string name="delete_message__question">Die Nachricht löschen?</string>
|
||||||
<string name="delete_message_cannot_be_undone_warning">Nachricht wird gelöscht - dies kann nicht rückgängig gemacht werden!</string>
|
<string name="delete_message_cannot_be_undone_warning">Nachricht wird gelöscht - dies kann nicht rückgängig gemacht werden!</string>
|
||||||
<string name="for_me_only">Nur für mich</string>
|
<string name="for_me_only">Nur für mich</string>
|
||||||
@@ -249,6 +250,11 @@
|
|||||||
<!-- Message Actions - SendMsgView.kt -->
|
<!-- Message Actions - SendMsgView.kt -->
|
||||||
<string name="icon_descr_send_message">Nachricht senden</string>
|
<string name="icon_descr_send_message">Nachricht senden</string>
|
||||||
<string name="icon_descr_record_voice_message">***Record voice message</string>
|
<string name="icon_descr_record_voice_message">***Record voice message</string>
|
||||||
|
<string name="allow_voice_messages_question">***Allow voice messages?</string>
|
||||||
|
<string name="you_need_to_allow_to_send_voice">***You need to allow your contact to send voice messages to be able to send them.</string>
|
||||||
|
<string name="voice_messages_prohibited">***Voice messages prohibited!</string>
|
||||||
|
<string name="ask_your_contact_to_enable_voice">***Please ask your contact to enable sending voice messages.</string>
|
||||||
|
<string name="only_group_owners_can_enable_voice">***Only group owners can enable voice messages.</string>
|
||||||
|
|
||||||
<!-- General Actions / Responses -->
|
<!-- General Actions / Responses -->
|
||||||
<string name="back">Zurück</string>
|
<string name="back">Zurück</string>
|
||||||
|
|||||||
@@ -167,6 +167,7 @@
|
|||||||
<string name="save_verb">Сохранить</string>
|
<string name="save_verb">Сохранить</string>
|
||||||
<string name="edit_verb">Редактировать</string>
|
<string name="edit_verb">Редактировать</string>
|
||||||
<string name="delete_verb">Удалить</string>
|
<string name="delete_verb">Удалить</string>
|
||||||
|
<string name="allow_verb">Разрешить</string>
|
||||||
<string name="delete_message__question">Удалить сообщение?</string>
|
<string name="delete_message__question">Удалить сообщение?</string>
|
||||||
<string name="delete_message_cannot_be_undone_warning">Сообщение будет удалено – это действие нельзя отменить!</string>
|
<string name="delete_message_cannot_be_undone_warning">Сообщение будет удалено – это действие нельзя отменить!</string>
|
||||||
<string name="for_me_only">Только для меня</string>
|
<string name="for_me_only">Только для меня</string>
|
||||||
@@ -249,6 +250,11 @@
|
|||||||
<!-- Message Actions - SendMsgView.kt -->
|
<!-- Message Actions - SendMsgView.kt -->
|
||||||
<string name="icon_descr_send_message">Отправить сообщение</string>
|
<string name="icon_descr_send_message">Отправить сообщение</string>
|
||||||
<string name="icon_descr_record_voice_message">Записать голосовое сообщение</string>
|
<string name="icon_descr_record_voice_message">Записать голосовое сообщение</string>
|
||||||
|
<string name="allow_voice_messages_question">Разрешить голосовые сообщения?</string>
|
||||||
|
<string name="you_need_to_allow_to_send_voice">Чтобы включить отправку голосовых сообщений, разрешите их вашему контакту.</string>
|
||||||
|
<string name="voice_messages_prohibited">Голосовые сообщения запрещены!</string>
|
||||||
|
<string name="ask_your_contact_to_enable_voice">Попросите вашего контакта разрешить отправку голосовых сообщений.</string>
|
||||||
|
<string name="only_group_owners_can_enable_voice">Только владельцы группы могут разрешить голосовые сообщения.</string>
|
||||||
|
|
||||||
<!-- General Actions / Responses -->
|
<!-- General Actions / Responses -->
|
||||||
<string name="back">Назад</string>
|
<string name="back">Назад</string>
|
||||||
|
|||||||
@@ -167,6 +167,7 @@
|
|||||||
<string name="save_verb">Save</string>
|
<string name="save_verb">Save</string>
|
||||||
<string name="edit_verb">Edit</string>
|
<string name="edit_verb">Edit</string>
|
||||||
<string name="delete_verb">Delete</string>
|
<string name="delete_verb">Delete</string>
|
||||||
|
<string name="allow_verb">Allow</string>
|
||||||
<string name="delete_message__question">Delete message?</string>
|
<string name="delete_message__question">Delete message?</string>
|
||||||
<string name="delete_message_cannot_be_undone_warning">Message will be deleted - this cannot be undone!</string>
|
<string name="delete_message_cannot_be_undone_warning">Message will be deleted - this cannot be undone!</string>
|
||||||
<string name="for_me_only">For me only</string>
|
<string name="for_me_only">For me only</string>
|
||||||
@@ -249,6 +250,11 @@
|
|||||||
<!-- Message Actions - SendMsgView.kt -->
|
<!-- Message Actions - SendMsgView.kt -->
|
||||||
<string name="icon_descr_send_message">Send Message</string>
|
<string name="icon_descr_send_message">Send Message</string>
|
||||||
<string name="icon_descr_record_voice_message">Record voice message</string>
|
<string name="icon_descr_record_voice_message">Record voice message</string>
|
||||||
|
<string name="allow_voice_messages_question">Allow voice messages?</string>
|
||||||
|
<string name="you_need_to_allow_to_send_voice">You need to allow your contact to send voice messages to be able to send them.</string>
|
||||||
|
<string name="voice_messages_prohibited">Voice messages prohibited!</string>
|
||||||
|
<string name="ask_your_contact_to_enable_voice">Please ask your contact to enable sending voice messages.</string>
|
||||||
|
<string name="only_group_owners_can_enable_voice">Only group owners can enable voice messages.</string>
|
||||||
|
|
||||||
<!-- General Actions / Responses -->
|
<!-- General Actions / Responses -->
|
||||||
<string name="back">Back</string>
|
<string name="back">Back</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user