Restore new message after pause/resume

This commit is contained in:
Juha Heinanen
2025-04-30 13:20:41 +03:00
parent 990bb0fba4
commit b8c2f80414
2 changed files with 105 additions and 60 deletions

View File

@ -61,25 +61,31 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer
import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.EventListener
import com.tutpro.baresip.CustomElements.LabelText
import com.tutpro.baresip.CustomElements.verticalScrollbar
import kotlinx.coroutines.launch
@ -99,7 +105,6 @@ class ChatActivity : ComponentActivity() {
private var chatMessages : List<Message> by _chatMessages
private var focus = false
private var lastCall: Long = 0
private var unsentMessage = ""
private var keyboardController: SoftwareKeyboardController? = null
private var backInvokedCallback: OnBackInvokedCallback? = null
@ -267,7 +272,9 @@ class ChatActivity : ComponentActivity() {
aor.split(":")[1]
Text(
text = headerText,
modifier = Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 8.dp),
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp, bottom = 8.dp),
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
textAlign = TextAlign.Center
@ -406,8 +413,10 @@ class ChatActivity : ComponentActivity() {
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(start = if (message.direction == MESSAGE_DOWN) 0.dp else 24.dp,
end = if (message.direction == MESSAGE_DOWN) 24.dp else 0.dp)
.padding(
start = if (message.direction == MESSAGE_DOWN) 0.dp else 24.dp,
end = if (message.direction == MESSAGE_DOWN) 24.dp else 0.dp
)
) {
Column {
Row {
@ -449,6 +458,36 @@ class ChatActivity : ComponentActivity() {
@Composable
fun NewMessage(ctx: Context, peerUri: String) {
var newMessage = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(""))
}
var textFieldLoaded by remember { mutableStateOf(false) }
EventListener {
when (it) {
Lifecycle.Event.ON_RESUME -> {
val chatText = BaresipService.chatTexts["$aor::$peerUri"]
if (chatText != null) {
Log.d(TAG, "Restoring newMessage '$chatText' for $aor::$peerUri")
newMessage.value = TextFieldValue(
text = chatText,
selection = TextRange(chatText.length)
)
BaresipService.chatTexts.remove("$aor::$peerUri")
}
_chatMessages.value = uaPeerMessages(aor, peerUri)
}
Lifecycle.Event.ON_PAUSE -> {
if (newMessage.value != TextFieldValue("")) {
Log.d(TAG, "Saving newMessage '${newMessage.value.text}' for $aor::$peerUri")
BaresipService.chatTexts["$aor::$peerUri"] = newMessage.value.text
}
MainActivity.activityAor = aor
}
else -> {}
}
}
val showDialog = remember { mutableStateOf(false) }
val dialogMessage = remember { mutableStateOf("") }
@ -459,7 +498,6 @@ class ChatActivity : ComponentActivity() {
positiveButtonText = stringResource(R.string.ok),
)
var newMessage by remember { mutableStateOf("") }
Row(modifier = Modifier
.fillMaxWidth()
.navigationBarsPadding()
@ -468,16 +506,27 @@ class ChatActivity : ComponentActivity() {
) {
val keyboardController = LocalSoftwareKeyboardController.current
OutlinedTextField(
value = newMessage,
value = newMessage.value.text,
placeholder = { Text(stringResource(R.string.new_message)) },
onValueChange = { newMessage = it },
onValueChange = {
newMessage.value = newMessage.value.copy(text = it)
},
modifier = Modifier
.weight(1f)
.padding(end = 8.dp)
.verticalScroll(rememberScrollState())
.onGloballyPositioned {
if (!textFieldLoaded) {
textFieldLoaded = true
//val textLength = newMessage.value.text.length
//newMessage.value = newMessage.value.copy(
// selection = TextRange(textLength)
//)
}
}
.combinedClickable(
onClick = {
val msgText = newMessage
val msgText = newMessage.value.text
if (msgText.isNotEmpty()) {
keyboardController?.hide()
val time = System.currentTimeMillis()
@ -498,7 +547,8 @@ class ChatActivity : ComponentActivity() {
if (ua.account.telProvider == "") {
dialogMessage.value = String.format(
getString(R.string.no_telephony_provider),
Utils.plainAor(aor))
Utils.plainAor(aor)
)
showDialog.value = true
} else {
msgUri = Utils.telToSip(peerUri, ua.account)
@ -513,12 +563,14 @@ class ChatActivity : ComponentActivity() {
time.toString()
) != 0
) {
Toast.makeText(ctx, "${getString(R.string.message_failed)}!", Toast.LENGTH_SHORT).show()
Toast.makeText(
ctx, "${getString(R.string.message_failed)}!",
Toast.LENGTH_SHORT
).show()
msg.direction = MESSAGE_UP_FAIL
msg.responseReason = getString(R.string.message_failed)
} else {
newMessage = ""
unsentMessage = ""
newMessage.value = TextFieldValue("")
keyboardController?.hide()
BaresipService.chatTexts.remove("$aor::$peerUri")
}
@ -531,7 +583,7 @@ class ChatActivity : ComponentActivity() {
val clipData = clipboardManager.primaryClip
if (clipData != null && clipData.itemCount > 0) {
val pastedText = clipData.getItemAt(0).text.toString()
newMessage = pastedText
newMessage.value = TextFieldValue(pastedText)
} else {
Toast.makeText(ctx, "Nothing to paste", Toast.LENGTH_SHORT).show()
}
@ -539,11 +591,11 @@ class ChatActivity : ComponentActivity() {
),
singleLine = false,
trailingIcon = {
if (newMessage.isNotEmpty()) {
if (newMessage.value.text.isNotEmpty()) {
Icon(
Icons.Outlined.Clear,
contentDescription = "Clear",
modifier = Modifier.clickable { newMessage = "" }
modifier = Modifier.clickable { newMessage.value = TextFieldValue("") }
)
} },
label = { LabelText(stringResource(R.string.new_message)) },
@ -554,26 +606,21 @@ class ChatActivity : ComponentActivity() {
autoCorrectEnabled = true
)
)
LaunchedEffect(Unit) {
}
Image(
painter = painterResource(id = R.drawable.send),
contentDescription = "Send",
contentScale = ContentScale.Crop,
modifier = Modifier.size(36.dp)
modifier = Modifier
.size(36.dp)
.clickable {
val msgText = newMessage
val msgText = newMessage.value.text
if (msgText.isNotEmpty()) {
keyboardController?.hide()
val time = System.currentTimeMillis()
val msg = Message(
aor,
peerUri,
msgText,
time,
MESSAGE_UP_WAIT,
0,
"",
true
)
val msg =
Message(aor, peerUri, msgText, time, MESSAGE_UP_WAIT, 0, "", true)
msg.add()
var msgUri = ""
_chatMessages.value += msg
@ -581,7 +628,8 @@ class ChatActivity : ComponentActivity() {
if (ua.account.telProvider == "") {
dialogMessage.value = String.format(
getString(R.string.no_telephony_provider),
Utils.plainAor(aor))
Utils.plainAor(aor)
)
showDialog.value = true
} else {
msgUri = Utils.telToSip(peerUri, ua.account)
@ -589,19 +637,16 @@ class ChatActivity : ComponentActivity() {
else
msgUri = peerUri
if (msgUri != "")
if (Api.message_send(
ua.uap,
msgUri,
msgText,
time.toString()
) != 0
if (Api.message_send(ua.uap, msgUri, msgText, time.toString()) != 0
) {
Toast.makeText(ctx, "${getString(R.string.message_failed)}!", Toast.LENGTH_SHORT).show()
Toast.makeText(
ctx, "${getString(R.string.message_failed)}!",
Toast.LENGTH_SHORT
).show()
msg.direction = MESSAGE_UP_FAIL
msg.responseReason = getString(R.string.message_failed)
} else {
newMessage = ""
unsentMessage = ""
newMessage.value = TextFieldValue("")
keyboardController?.hide()
BaresipService.chatTexts.remove("$aor::$peerUri")
}
@ -611,27 +656,6 @@ class ChatActivity : ComponentActivity() {
}
}
override fun onResume() {
super.onResume()
val chatText = BaresipService.chatTexts["$aor::$peerUri"]
if (chatText != null) {
Log.d(TAG, "Restoring newMessage $chatText for $aor::$peerUri")
unsentMessage = chatText
//newMessage.requestFocus()
BaresipService.chatTexts.remove("$aor::$peerUri")
}
_chatMessages.value = uaPeerMessages(aor, peerUri)
}
override fun onPause() {
super.onPause()
if (unsentMessage != "") {
Log.d(TAG, "Saving newMessage $unsentMessage for $aor::$peerUri")
BaresipService.chatTexts["$aor::$peerUri"] = unsentMessage
}
MainActivity.activityAor = aor
}
override fun onDestroy() {
if (Build.VERSION.SDK_INT >= 33) {
if (backInvokedCallback != null)

View File

@ -44,11 +44,13 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -82,6 +84,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.DialogProperties
import androidx.core.content.ContextCompat.getString
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
object CustomElements {
@ -609,4 +614,20 @@ object CustomElements {
}
}
@Composable
fun EventListener(onEvent : (event: Lifecycle.Event) -> Unit) {
val eventHandler = rememberUpdatedState(newValue = onEvent)
val lifecycleOwner = rememberUpdatedState(newValue = LocalLifecycleOwner.current)
DisposableEffect(lifecycleOwner.value ){
val lifecycle = lifecycleOwner.value.lifecycle
val observer = LifecycleEventObserver{source, event ->
eventHandler.value(event)
}
lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer)
}
}
}
}