From 964af9fe9da88dfd419e6556770395f1d3137816 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Thu, 24 Apr 2025 22:12:27 +0300 Subject: [PATCH 1/3] Started to work on icon tooltips --- .../kotlin/com/tutpro/baresip/MainActivity.kt | 92 +++++++++++++------ 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index cba806ee..86b96891 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -82,6 +82,7 @@ import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.outlined.Clear import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults @@ -92,17 +93,21 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.RichTooltip import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TextField +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator import androidx.compose.material3.pulltorefresh.pullToRefresh import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -111,6 +116,7 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf 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 @@ -147,6 +153,7 @@ import com.tutpro.baresip.CustomElements.SelectableAlertDialog import com.tutpro.baresip.CustomElements.Text import com.tutpro.baresip.CustomElements.verticalScrollbar import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import java.io.File import java.text.SimpleDateFormat import java.util.Date @@ -608,8 +615,7 @@ class MainActivity : ComponentActivity() { showCall(ua) } } - } - else if (offset > swipeThreshold) { + } else if (offset > swipeThreshold) { if (uas.value.isNotEmpty()) { val curPos = UserAgent.findAorIndex(viewModel.selectedAor.value) val newPos = when (curPos) { @@ -679,28 +685,58 @@ class MainActivity : ComponentActivity() { containerColor = LocalCustomColors.current.primary ), actions = { - IconButton( - modifier = Modifier.padding(end=12.dp), - onClick = { - if (Call.call("connected") == null) { - BaresipService.isRecOn = !BaresipService.isRecOn - recImage = if (BaresipService.isRecOn) { - Api.module_load("sndfile") - recOnImage - } - else { - Api.module_unload("sndfile") - recOffImage - } - } - else - Toast.makeText(ctx, R.string.rec_in_call, Toast.LENGTH_SHORT).show() - } + val scope = rememberCoroutineScope() + val tooltipState = rememberTooltipState() + TooltipBox( + positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(), + tooltip = { + RichTooltip( + title = { Text( + text = "Call Recording", + color = LocalCustomColors.current.alert, + fontSize = 18.sp, + modifier = Modifier.padding(bottom = 4.dp) + ) }, + text = { Text( + text = "If activated, new incoming or outgoing calls are recorded.", + color = LocalCustomColors.current.itemText, + fontSize = 16.sp + ) }, + caretSize = TooltipDefaults.caretSize, + shape = RoundedCornerShape(16.dp), + colors = TooltipDefaults.richTooltipColors( + containerColor = LocalCustomColors.current.cardBackground, + ), + ) + }, + enableUserInput = true, + state = tooltipState ) { - Icon(imageVector = recImage, - tint = Color.Unspecified, - contentDescription = null) + IconButton( + onClick = { + if (Call.call("connected") == null) { + BaresipService.isRecOn = !BaresipService.isRecOn + recImage = if (BaresipService.isRecOn) { + Api.module_load("sndfile") + recOnImage + } else { + Api.module_unload("sndfile") + recOffImage + } + } else + Toast.makeText(ctx, R.string.rec_in_call, Toast.LENGTH_SHORT).show() + }, + ) { + Icon( + imageVector = recImage, + tint = Color.Unspecified, + contentDescription = null + ) + } } + + Spacer(modifier = Modifier.width(16.dp)) + IconButton( modifier = Modifier.padding(end=12.dp), onClick = { @@ -721,6 +757,7 @@ class MainActivity : ComponentActivity() { tint = Color.Unspecified, contentDescription = null) } + IconButton( modifier = Modifier.padding(end=6.dp), onClick = { @@ -740,6 +777,7 @@ class MainActivity : ComponentActivity() { tint = Color.Unspecified, contentDescription = null) } + IconButton( onClick = { menuExpanded = !menuExpanded } ) { @@ -749,6 +787,7 @@ class MainActivity : ComponentActivity() { tint = LocalCustomColors.current.light ) } + DropdownMenu( expanded = menuExpanded, onDismissRequest = { menuExpanded = false }, @@ -1693,15 +1732,16 @@ class MainActivity : ComponentActivity() { showAlert.value = true return } - Utils.telToSip(uriText, ua.account) - } else { - Utils.uriComplete(uriText, aor) + Utils.telToSip(peerUri, ua.account) } + else + Utils.uriComplete(peerUri, aor) if (!Utils.checkUri(uri)) { alertTitle.value = getString(R.string.notice) alertMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), uri) showAlert.value = true - } else if (!BaresipService.requestAudioFocus(applicationContext)) { + } + else if (!BaresipService.requestAudioFocus(applicationContext)) { Toast.makeText(applicationContext, R.string.audio_focus_denied, Toast.LENGTH_SHORT).show() } else { From d19bf4db7882453af44a7a34a490684378b58fe6 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Sat, 26 Apr 2025 07:28:33 +0300 Subject: [PATCH 2/3] Added tooltips to mic and speakerphone icons --- .../kotlin/com/tutpro/baresip/MainActivity.kt | 126 ++++++++---------- libbaresip-android | 2 +- 2 files changed, 59 insertions(+), 69 deletions(-) diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index 86b96891..40dcdf77 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -82,7 +82,6 @@ import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.outlined.Clear import androidx.compose.material3.BasicAlertDialog -import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults @@ -93,21 +92,17 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults -import androidx.compose.material3.RichTooltip import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TextField -import androidx.compose.material3.TooltipBox -import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator import androidx.compose.material3.pulltorefresh.pullToRefresh import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState -import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -116,7 +111,6 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf 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 @@ -153,12 +147,10 @@ import com.tutpro.baresip.CustomElements.SelectableAlertDialog import com.tutpro.baresip.CustomElements.Text import com.tutpro.baresip.CustomElements.verticalScrollbar import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import java.io.File import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import kotlin.collections.set import kotlin.system.exitProcess class MainActivity : ComponentActivity() { @@ -685,34 +677,9 @@ class MainActivity : ComponentActivity() { containerColor = LocalCustomColors.current.primary ), actions = { - val scope = rememberCoroutineScope() - val tooltipState = rememberTooltipState() - TooltipBox( - positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(), - tooltip = { - RichTooltip( - title = { Text( - text = "Call Recording", - color = LocalCustomColors.current.alert, - fontSize = 18.sp, - modifier = Modifier.padding(bottom = 4.dp) - ) }, - text = { Text( - text = "If activated, new incoming or outgoing calls are recorded.", - color = LocalCustomColors.current.itemText, - fontSize = 16.sp - ) }, - caretSize = TooltipDefaults.caretSize, - shape = RoundedCornerShape(16.dp), - colors = TooltipDefaults.richTooltipColors( - containerColor = LocalCustomColors.current.cardBackground, - ), - ) - }, - enableUserInput = true, - state = tooltipState - ) { - IconButton( + + IconButton( + modifier = Modifier.combinedClickable( onClick = { if (Call.call("connected") == null) { BaresipService.isRecOn = !BaresipService.isRecOn @@ -726,31 +693,46 @@ class MainActivity : ComponentActivity() { } else Toast.makeText(ctx, R.string.rec_in_call, Toast.LENGTH_SHORT).show() }, - ) { - Icon( - imageVector = recImage, - tint = Color.Unspecified, - contentDescription = null - ) - } + onLongClick = { + alertTitle.value = "Call Recording" + alertMessage.value = "If activated, new incoming and outgoing calls will be recorded." + + "Recordings can be played on Call Details page" + showAlert.value = true + } + ), + onClick = {} + ) { + Icon( + imageVector = recImage, + tint = Color.Unspecified, + contentDescription = null + ) } Spacer(modifier = Modifier.width(16.dp)) IconButton( - modifier = Modifier.padding(end=12.dp), - onClick = { - if (Call.call("connected") != null) { - BaresipService.isMicMuted = !BaresipService.isMicMuted - if (BaresipService.isMicMuted) { - micIcon = R.drawable.mic_off - Api.calls_mute(true) - } else { - micIcon = R.drawable.mic_on - Api.calls_mute(false) - } - } - } + modifier = Modifier.padding(end=12.dp) + .combinedClickable( + onClick = { + if (Call.call("connected") != null) { + BaresipService.isMicMuted = !BaresipService.isMicMuted + if (BaresipService.isMicMuted) { + micIcon = R.drawable.mic_off + Api.calls_mute(true) + } else { + micIcon = R.drawable.mic_on + Api.calls_mute(false) + } + } + }, + onLongClick = { + alertTitle.value = "Microphone" + alertMessage.value = "If activated, microphone is muted." + showAlert.value = true + }, + ), + onClick = {} ) { Icon( imageVector = ImageVector.vectorResource(micIcon), @@ -759,18 +741,26 @@ class MainActivity : ComponentActivity() { } IconButton( - modifier = Modifier.padding(end=6.dp), - onClick = { - if (Build.VERSION.SDK_INT >= 31) - Log.d(TAG, "Toggling speakerphone when dev/mode is " + - "${am.communicationDevice!!.type}/${am.mode}" - ) - Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(ctx), am) - speakerIcon = if (Utils.isSpeakerPhoneOn(am)) - R.drawable.speaker_on - else - R.drawable.speaker_off - } + modifier = Modifier.padding(end=6.dp) + .combinedClickable( + onClick = { + if (Build.VERSION.SDK_INT >= 31) + Log.d(TAG, "Toggling speakerphone when dev/mode is " + + "${am.communicationDevice!!.type}/${am.mode}" + ) + Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(ctx), am) + speakerIcon = if (Utils.isSpeakerPhoneOn(am)) + R.drawable.speaker_on + else + R.drawable.speaker_off + }, + onLongClick = { + alertTitle.value = "Speakerphone" + alertMessage.value = "If activated, audio is player via speakerphone." + showAlert.value = true + }, + ), + onClick = {} ) { Icon( imageVector = ImageVector.vectorResource(speakerIcon), diff --git a/libbaresip-android b/libbaresip-android index 3826d8b6..735e7e03 160000 --- a/libbaresip-android +++ b/libbaresip-android @@ -1 +1 @@ -Subproject commit 3826d8b6371f23bed14eea5c0f4167810efde794 +Subproject commit 735e7e03c0d4cd421be01b44f3d3d2678e43536f From 04d5fa2bf1cb3868ef418f8c6e81ff8d1a794ca0 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Sat, 26 Apr 2025 10:46:47 +0300 Subject: [PATCH 3/3] Completed app bar icon tooltips --- app/build.gradle.kts | 4 +- .../kotlin/com/tutpro/baresip/MainActivity.kt | 116 ++++++++---------- 2 files changed, 56 insertions(+), 64 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7a36fd67..12a221f1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.tutpro.baresip" minSdk = 28 targetSdk = 35 - versionCode = 426 - versionName = "65.3.0" + versionCode = 427 + versionName = "66.0.0" externalNativeBuild { cmake { cFlags += "-DHAVE_INTTYPES_H -lstdc++" diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index 40dcdf77..ba753b7a 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -678,8 +678,9 @@ class MainActivity : ComponentActivity() { ), actions = { - IconButton( - modifier = Modifier.combinedClickable( + Icon( + imageVector = recImage, + modifier = Modifier.size(40.dp).combinedClickable( onClick = { if (Call.call("connected") == null) { BaresipService.isRecOn = !BaresipService.isRecOn @@ -696,77 +697,68 @@ class MainActivity : ComponentActivity() { onLongClick = { alertTitle.value = "Call Recording" alertMessage.value = "If activated, new incoming and outgoing calls will be recorded." + - "Recordings can be played on Call Details page" + " Recordings can be played on Call Details page" showAlert.value = true } ), - onClick = {} - ) { - Icon( - imageVector = recImage, - tint = Color.Unspecified, - contentDescription = null - ) - } + tint = Color.Unspecified, + contentDescription = null + ) - Spacer(modifier = Modifier.width(16.dp)) + Spacer(modifier = Modifier.width(22.dp)) - IconButton( - modifier = Modifier.padding(end=12.dp) - .combinedClickable( - onClick = { - if (Call.call("connected") != null) { - BaresipService.isMicMuted = !BaresipService.isMicMuted - if (BaresipService.isMicMuted) { - micIcon = R.drawable.mic_off - Api.calls_mute(true) - } else { - micIcon = R.drawable.mic_on - Api.calls_mute(false) - } - } - }, - onLongClick = { - alertTitle.value = "Microphone" - alertMessage.value = "If activated, microphone is muted." - showAlert.value = true - }, - ), - onClick = {} - ) { - Icon( - imageVector = ImageVector.vectorResource(micIcon), - tint = Color.Unspecified, - contentDescription = null) - } - - IconButton( - modifier = Modifier.padding(end=6.dp) - .combinedClickable( + Icon( + imageVector = ImageVector.vectorResource(micIcon), + modifier = Modifier.size(40.dp).combinedClickable( onClick = { - if (Build.VERSION.SDK_INT >= 31) - Log.d(TAG, "Toggling speakerphone when dev/mode is " + - "${am.communicationDevice!!.type}/${am.mode}" - ) - Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(ctx), am) - speakerIcon = if (Utils.isSpeakerPhoneOn(am)) - R.drawable.speaker_on - else - R.drawable.speaker_off + if (Call.call("connected") != null) { + BaresipService.isMicMuted = !BaresipService.isMicMuted + if (BaresipService.isMicMuted) { + micIcon = R.drawable.mic_off + Api.calls_mute(true) + } else { + micIcon = R.drawable.mic_on + Api.calls_mute(false) + } + } }, onLongClick = { - alertTitle.value = "Speakerphone" - alertMessage.value = "If activated, audio is player via speakerphone." + alertTitle.value = "Microphone" + alertMessage.value = "If activated during call, microphone is muted." showAlert.value = true }, ), - onClick = {} - ) { - Icon( - imageVector = ImageVector.vectorResource(speakerIcon), - tint = Color.Unspecified, - contentDescription = null) - } + tint = Color.Unspecified, + contentDescription = null + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Icon( + imageVector = ImageVector.vectorResource(speakerIcon), + modifier = Modifier.size(40.dp).combinedClickable( + onClick = { + if (Build.VERSION.SDK_INT >= 31) + Log.d(TAG, "Toggling speakerphone when dev/mode is " + + "${am.communicationDevice!!.type}/${am.mode}" + ) + Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(ctx), am) + speakerIcon = if (Utils.isSpeakerPhoneOn(am)) + R.drawable.speaker_on + else + R.drawable.speaker_off + }, + onLongClick = { + alertTitle.value = "Speakerphone" + alertMessage.value = "If activated, audio is player via device speakerphone." + showAlert.value = true + }, + ), + tint = Color.Unspecified, + contentDescription = null + ) + + Spacer(modifier = Modifier.width(8.dp)) IconButton( onClick = { menuExpanded = !menuExpanded }