diff --git a/app/src/main/kotlin/com/tutpro/baresip/AboutScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/AboutScreen.kt index 8ff651e9..99001d39 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AboutScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AboutScreen.kt @@ -2,13 +2,13 @@ package com.tutpro.baresip import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -56,17 +56,11 @@ fun AboutScreen(onBack: () -> Unit) { } Scaffold( - modifier = Modifier.fillMaxSize().imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { Text( diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt index 06ee6378..17d0fa98 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt @@ -8,14 +8,15 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -137,9 +138,8 @@ private fun AccountScreen( DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver { _, event -> - if (event == Lifecycle.Event.ON_RESUME) { + if (event == Lifecycle.Event.ON_RESUME) resumeToggle = System.currentTimeMillis() - } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { @@ -164,23 +164,14 @@ private fun AccountScreen( } Scaffold( - modifier = Modifier.fillMaxSize().imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { - Text( - text = acc.text(), - fontWeight = FontWeight.Bold - ) + Text(text = acc.text(), fontWeight = FontWeight.Bold) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, @@ -199,10 +190,7 @@ private fun AccountScreen( windowInsets = WindowInsets(0, 0, 0, 0), actions = { IconButton(onClick = checkOnClick) { - Icon( - imageVector = Icons.Filled.Check, - contentDescription = "Check" - ) + Icon(imageVector = Icons.Filled.Check, contentDescription = "Check") } }, ) @@ -253,10 +241,7 @@ private fun AccountContent( modifier = Modifier.fillMaxWidth(), textStyle = TextStyle(fontSize = 18.sp), label = { - Text( - text = label, - fontWeight = FontWeight.Bold - ) + Text(text = label, fontWeight = FontWeight.Bold) }, colors = OutlinedTextFieldDefaults.colors( disabledTextColor = MaterialTheme.colorScheme.onSurface, @@ -271,9 +256,7 @@ private fun AccountContent( @Composable fun AoR(toggle: Long) { Column( - Modifier - .fillMaxWidth() - .padding(top = 8.dp, end = 10.dp), + Modifier.fillMaxWidth().padding(top = 8.dp, end = 10.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { if (ua.account.isMobile && android.os.Build.VERSION.SDK_INT >= 29) { @@ -294,7 +277,8 @@ private fun AccountContent( value = if (smsNumber != null) "tel:$smsNumber" else stringResource(R.string.not_available), label = stringResource(R.string.tel_uri_messages) ) - } else { + } + else { val currentSubId = remember(toggle) { if (voiceSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) voiceSubId @@ -312,12 +296,12 @@ private fun AccountContent( label = stringResource(R.string.tel_uri) ) } - } else { + } + else AoRField( value = if (ua.account.isMobile) stringResource(R.string.not_available) else ua.account.luri, label = stringResource(if (ua.account.isMobile) R.string.tel_uri else R.string.sip_uri) ) - } } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountsScreen.kt index f68dc9ea..c7908175 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountsScreen.kt @@ -7,8 +7,8 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding @@ -16,7 +16,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -69,23 +69,14 @@ fun NavGraphBuilder.accountsScreenRoute(navController: NavController) { @Composable fun AccountsScreen(navController: NavController) { Scaffold( - modifier = Modifier.fillMaxSize().imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { - Text( - text = stringResource(R.string.accounts), - fontWeight = FontWeight.Bold - ) + Text(text = stringResource(R.string.accounts), fontWeight = FontWeight.Bold) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, diff --git a/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt index 3583cd3f..45013d84 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AudioScreen.kt @@ -17,13 +17,13 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -101,23 +101,14 @@ private fun AudioScreen( checkOnClick: () -> Unit, ) { Scaffold( - modifier = Modifier.fillMaxSize().imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { - Text( - text = stringResource(R.string.audio_settings), - fontWeight = FontWeight.Bold - ) + Text(text = stringResource(R.string.audio_settings), fontWeight = FontWeight.Bold) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, @@ -136,10 +127,7 @@ private fun AudioScreen( windowInsets = WindowInsets(0, 0, 0, 0), actions = { IconButton(onClick = checkOnClick) { - Icon( - imageVector = Icons.Filled.Check, - contentDescription = "Check" - ) + Icon(imageVector = Icons.Filled.Check, contentDescription = "Check") } }, ) @@ -319,9 +307,8 @@ private fun ToneCountry() { isDropDownExpanded.value = false }) { countryNames.forEachIndexed { index, name -> - DropdownMenuItem(text = { - Text(text = name) - }, + DropdownMenuItem( + text = { Text(text = name) }, onClick = { isDropDownExpanded.value = false itemPosition.intValue = index @@ -355,8 +342,7 @@ private fun SpeakerPhone() { var speakerPhone by remember { mutableStateOf(oldSpeakerPhone) } Switch( checked = speakerPhone, - onCheckedChange = { - speakerPhone = it + onCheckedChange = { speakerPhone = it newSpeakerPhone = speakerPhone } ) @@ -409,9 +395,8 @@ private fun CallVolume() { isDropDownExpanded.value = false }) { volNames.forEachIndexed { index, vol -> - DropdownMenuItem(text = { - Text(text = vol) - }, + DropdownMenuItem( + text = { Text(text = vol) }, onClick = { isDropDownExpanded.value = false itemPosition.intValue = index @@ -440,8 +425,7 @@ private fun MicGain() { OutlinedTextField( value = micGain, placeholder = { Text(microphoneGainTitle) }, - onValueChange = { - micGain = it + onValueChange = { micGain = it newMicGain = micGain }, modifier = Modifier @@ -626,7 +610,8 @@ private fun checkOnClick(ctx: Context): Result { Api.module_unload("augain") Config.removeVariableValue("module", "augain.so") Config.replaceVariable("augain", "1.0") - } else { + } + else { if (oldMicGain == "1.0") { if (Api.module_load("augain") != 0) { alertTitle.value = ctx.getString(R.string.error) diff --git a/app/src/main/kotlin/com/tutpro/baresip/BlockedScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/BlockedScreen.kt index be8d4d01..10ba5db9 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BlockedScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BlockedScreen.kt @@ -11,12 +11,12 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -108,17 +108,11 @@ private fun BlockedScreen(navController: NavController, request: String, aor: St } Scaffold( - modifier = Modifier - .fillMaxSize() - .imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding(top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar(navController, account, request, blocked) } }, @@ -187,10 +181,7 @@ private fun TopAppBar( IconButton( onClick = { expanded = !expanded } ) { - Icon( - imageVector = Icons.Filled.Menu, - contentDescription = "Menu", - ) + Icon(imageVector = Icons.Filled.Menu, contentDescription = "Menu") } CustomElements.DropdownMenu( expanded, @@ -237,9 +228,7 @@ private fun BlockedContent( private fun Account(account: Account) { Text( text = stringResource(R.string.account) + " " + account.text(), - modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp), + modifier = Modifier.fillMaxWidth().padding(top = 8.dp), fontSize = 18.sp, fontWeight = FontWeight.SemiBold, textAlign = TextAlign.Center @@ -292,18 +281,17 @@ private fun Blocked(ctx: Context, navController: NavController, blocked: Mutable } ) ) { - Text(text = "\u2022", + Text( + text = "\u2022", modifier = Modifier.padding(start = 8.dp, end = 4.dp), fontSize = 18.sp) - - Text(text = peerUri.replace("sip:", ""), + Text( + text = peerUri.replace("sip:", ""), fontSize = 18.sp, maxLines = 1, overflow = TextOverflow.Ellipsis ) - Spacer(modifier = Modifier.weight(1f)) - val calendar = GregorianCalendar() calendar.timeInMillis = blocked.timeStamp Text( @@ -324,9 +312,8 @@ private fun loadBlocked(request: String, aor: String): MutableList { val res = mutableListOf() for (i in BaresipService.blocked.indices.reversed()) { val b = BaresipService.blocked[i] - if (b.aor == aor && b.request == request) { + if (b.aor == aor && b.request == request) res.add(Blocked("", b.peerUri, "", b.timeStamp)) - } } Log.d(TAG, "Loaded ${res.size} blocked $request requests") return res diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallDetailsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/CallDetailsScreen.kt index 33667dee..9dbbe42d 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallDetailsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallDetailsScreen.kt @@ -18,13 +18,13 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -80,6 +80,7 @@ import java.io.File import java.io.FileInputStream import java.text.DateFormat import java.util.GregorianCalendar +import kotlin.time.Duration.Companion.milliseconds fun NavGraphBuilder.callDetailsScreenRoute(navController: NavController, viewModel: ViewModel) { composable("call_details") { _ -> @@ -93,15 +94,11 @@ fun NavGraphBuilder.callDetailsScreenRoute(navController: NavController, viewMod private fun CallDetailsScreen(navController: NavController, callRow: CallRow) { val detailsState = remember { callRow.details.toMutableStateList() } Scaffold( - modifier = Modifier.fillMaxSize().imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding(top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { Text( @@ -252,22 +249,26 @@ private fun startTime(detail: Details, onDelete: (Details) -> Unit): String { val stopText = if (DateUtils.isToday(stopTime.timeInMillis)) { val fmt = DateFormat.getTimeInstance(DateFormat.MEDIUM) stringResource(R.string.today) + " " + fmt.format(stopTime.time) - } else { + } + else { val fmt = DateFormat.getDateTimeInstance() fmt.format(stopTime.time) } if (startTime == GregorianCalendar(0, 0, 0)) { startTimeText = stopText durationText = "?" - } else { + } + else { if (startTime == null || detail.direction == CALL_DOWN_BLUE) { startTimeText = stopText durationText = "" - } else { + } + else { val startText = if (DateUtils.isToday(startTime.timeInMillis)) { val fmt = DateFormat.getTimeInstance(DateFormat.MEDIUM) stringResource(R.string.today) + " " + fmt.format(startTime.time) - } else { + } + else { val fmt = DateFormat.getDateTimeInstance() fmt.format(startTime.time) } @@ -294,9 +295,7 @@ private fun startTime(detail: Details, onDelete: (Details) -> Unit): String { text = startTimeText, modifier = Modifier.combinedClickable( onClick = {}, - onLongClick = { - showDialog.value = true - } + onLongClick = { showDialog.value = true } ) ) @@ -313,7 +312,7 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { val mediaPlayer = remember { MediaPlayer() } val scope = rememberCoroutineScope() - // 1. Setup the File Saver Launcher + // 1. Set up the File Saver Launcher val saveLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument("audio/x-wav") ) { uri -> @@ -335,12 +334,12 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { Toast.LENGTH_SHORT ).show() } - } else { + } + else withContext(Dispatchers.Main) { Toast.makeText(ctx, "Source file not found", Toast.LENGTH_SHORT) .show() } - } } } catch (e: Exception) { Log.e(TAG, "Failed to save file: $e") @@ -381,16 +380,14 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { ) val hasRecording = detail.recording.isNotEmpty() && detail.recording[0].isNotEmpty() - if (hasRecording) { + if (hasRecording) Text( text = durationText, color = MaterialTheme.colorScheme.error, modifier = Modifier .padding(end = 12.dp) .combinedClickable( - onLongClick = { - showDownloadDialog.value = true - }, + onLongClick = { showDownloadDialog.value = true }, onClick = { if (!mediaPlayer.isPlaying) { mediaPlayer.reset() @@ -425,15 +422,15 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { ) finalFile = fallbackMerged // Update state to match reality by creating a new List - detail.recording = - listOf(fallbackMerged.absolutePath, "") - } else { + detail.recording = listOf(fallbackMerged.absolutePath, "") + } + else Log.e( TAG, "Raw file missing and fallback not found: ${currentRecording[0]}" ) - } - } else { + } + else { // Normal Raw processing val mergedFileName = "merged_${fileIn.nameWithoutExtension}_${fileOut.nameWithoutExtension}.wav" @@ -442,20 +439,16 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { mergedFileName ) if (mergedFile.exists()) { - Log.d( - TAG, - "Using already merged file: ${mergedFile.name}" - ) + Log.d(TAG, "Using already merged file: ${mergedFile.name}") finalFile = mergedFile - } else { + } + else if (Utils.mergeWavFiles(fileIn, fileOut, mergedFile)) finalFile = mergedFile - } // If merge successful, update state and delete originals if (finalFile != null && finalFile.exists()) { - detail.recording = - listOf(finalFile.absolutePath, "") + detail.recording = listOf(finalFile.absolutePath, "") try { if (fileIn.exists()) fileIn.delete() if (fileOut.exists()) fileOut.delete() @@ -468,21 +461,27 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { } } } - } else if (currentIsMerged) { - val f = File(currentRecording[0]) - if (f.exists()) { - Log.d(TAG, "Using already merged file: ${currentRecording[0]}") - finalFile = f - } else { - Log.e( - TAG, - "Merged file record exists but file is missing: ${currentRecording[0]}" - ) + } + else { + if (currentIsMerged) { + val f = File(currentRecording[0]) + if (f.exists()) { + Log.d( + TAG, + "Using already merged file: ${currentRecording[0]}" + ) + finalFile = f + } + else + Log.e( + TAG, + "Merged file record exists but file is missing: ${currentRecording[0]}" + ) } } withContext(Dispatchers.Main) { - if (finalFile != null && finalFile.exists()) { + if (finalFile != null && finalFile.exists()) try { mediaPlayer.apply { setAudioAttributes( @@ -511,15 +510,15 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { Toast.LENGTH_SHORT ).show() } - } else { + else Toast.makeText( ctx, "Failed to process audio file", Toast.LENGTH_SHORT ).show() - } } } - } else { + } + else { mediaPlayer.stop() mediaPlayer.reset() showPlaybackDialog.value = false @@ -527,9 +526,8 @@ private fun Duration(ctx: Context, detail: Details, durationText: String) { } ) ) - } else { + else Text(text = durationText, modifier = Modifier.padding(end = 12.dp)) - } } @Composable @@ -549,24 +547,18 @@ private fun PlaybackDialog( if (mediaPlayer.isPlaying) { val duration = mediaPlayer.duration totalDurationText = DateUtils.formatElapsedTime(duration / 1000L) - while (mediaPlayer.isPlaying) { val current = mediaPlayer.currentPosition currentProgress = if (duration > 0) current.toFloat() / duration.toFloat() else 0f currentPositionText = DateUtils.formatElapsedTime(current / 1000L) - delay(100) // Poll every 100ms + delay(100.milliseconds) // Poll every 100ms } } } AlertDialog( - onDismissRequest = { - // If user clicks outside, stop playback - onStop() - }, - title = { - Text(text = stringResource(R.string.playing_recording)) - }, + onDismissRequest = { onStop() }, // If user clicks outside, stop playback + title = { Text(text = stringResource(R.string.playing_recording)) }, text = { Column( modifier = Modifier.fillMaxWidth(), @@ -587,9 +579,7 @@ private fun PlaybackDialog( } }, confirmButton = { - TextButton( - onClick = { onStop() } - ) { + TextButton(onClick = { onStop() }) { Text(stringResource(R.string.stop)) } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt index 8148a375..b49942d1 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt @@ -14,13 +14,13 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -121,15 +121,11 @@ private fun CallsScreen(navController: NavController, viewModel: ViewModel, aor: } Scaffold( - modifier = Modifier.fillMaxSize().imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding(top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar(navController, ua, callHistory) } }, @@ -203,10 +199,7 @@ private fun TopAppBar(navController: NavController, ua: UserAgent, callHistory: IconButton( onClick = { expanded = !expanded } ) { - Icon( - imageVector = Icons.Filled.Menu, - contentDescription = "Menu", - ) + Icon(imageVector = Icons.Filled.Menu, contentDescription = "Menu") } CustomElements.DropdownMenu( expanded, @@ -353,15 +346,12 @@ private fun Calls( ) secondButtonText.value = ctx.getString(R.string.call) secondAction.value = { - if (ua.account.isMobile && ua.status != circleGreen.getValue( - colorblind - ) - ) { + if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) { alertTitle.value = ctx.getString(R.string.notice) - alertMessage.value = - ctx.getString(R.string.airplane_mode) + alertMessage.value = ctx.getString(R.string.airplane_mode) showAlert.value = true - } else { + } + else { handleIntent(ctx, viewModel, intent, "call") navController.navigate("main") { popUpTo("main") @@ -377,16 +367,20 @@ private fun Calls( alertMessage.value = ctx.getString(R.string.airplane_mode) showAlert.value = true - } else if (!Utils.isDefaultSmsApp(ctx)) { - alertTitle.value = ctx.getString(R.string.notice) - alertMessage.value = - ctx.getString(R.string.enable_default_messaging) - showAlert.value = true - } else { + } + else + if (!Utils.isDefaultSmsApp(ctx)) { + alertTitle.value = ctx.getString(R.string.notice) + alertMessage.value = + ctx.getString(R.string.enable_default_messaging) + showAlert.value = true + } + else { handleIntent(ctx, viewModel, intent, "message") navController.navigateUp() } - } else { + } + else { handleIntent(ctx, viewModel, intent, "message") navController.navigateUp() } @@ -400,13 +394,11 @@ private fun Calls( try { ctx.startActivity(emailIntent) } catch (e: Exception) { - Log.e( - TAG, - "Failed to start email activity: ${e.message}" - ) + Log.e(TAG, "Failed to start email activity: ${e.message}") } } - } else + } + else lastButtonText.value = "" showDialog.value = true } @@ -426,13 +418,9 @@ private fun Calls( ) secondButtonText.value = "" thirdButtonText.value = ctx.getString(R.string.copy_uri) - thirdAction.value = { - Utils.copyToClipboard(ctx, peerUri) - } + thirdAction.value = { Utils.copyToClipboard(ctx, peerUri) } lastButtonText.value = ctx.getString(R.string.delete) - lastAction.value = { - removeFromHistory(callHistory, callRow) - } + lastAction.value = { removeFromHistory(callHistory, callRow) } } else { message.value = String.format( @@ -446,13 +434,10 @@ private fun Calls( navController.navigate("contact/$uri/new") } thirdButtonText.value = ctx.getString(R.string.copy_uri) - thirdAction.value = { - Utils.copyToClipboard(ctx, peerUri) + thirdAction.value = { Utils.copyToClipboard(ctx, peerUri) } lastButtonText.value = ctx.getString(R.string.delete) - lastAction.value = { - removeFromHistory(callHistory, callRow) - } + lastAction.value = { removeFromHistory(callHistory, callRow) } } showDialog.value = true } @@ -509,7 +494,8 @@ private fun Calls( } if (count > 3) Text("...", color = MaterialTheme.colorScheme.onBackground) - Text(text = Utils.friendlyUri(ctx, peerUri, ua.account), + Text( + text = Utils.friendlyUri(ctx, peerUri, ua.account), modifier = Modifier.padding(start = 8.dp), fontSize = 18.sp, maxLines = 1, diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt index 43aea709..7203f83b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding @@ -22,7 +21,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -136,14 +135,10 @@ private fun ChatScreen( } } lifecycleOwner.lifecycle.addObserver(observer) - onDispose { - lifecycleOwner.lifecycle.removeObserver(observer) - } + onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } - var areMessagesLoaded by remember(aor, peerUri) { - mutableStateOf(false) - } + var areMessagesLoaded by remember(aor, peerUri) { mutableStateOf(false) } val reloadMessages = { Log.d(TAG, "Reloading messages for $aor peer $peerUri") @@ -175,16 +170,11 @@ private fun ChatScreen( } Scaffold( - modifier = Modifier - .fillMaxSize() - .imePadding(), + modifier = Modifier.fillMaxSize().imePadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column(modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding(top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar(ctx, navController, viewModel, account, peerUri) } }, @@ -199,7 +189,8 @@ private fun ChatScreen( }, content = { contentPadding -> if (areMessagesLoaded) - ChatContent(ctx, + ChatContent( + ctx, navController, contentPadding, account, peerUri, @@ -223,8 +214,7 @@ private fun TopAppBar( TopAppBar( title = { Text( - text = format(ctx.getString(R.string.chat_with), - Utils.friendlyUri(ctx, peerUri, account)), + text = format(ctx.getString(R.string.chat_with), Utils.friendlyUri(ctx, peerUri, account)), fontSize = 22.sp, fontWeight = FontWeight.Bold ) @@ -237,10 +227,7 @@ private fun TopAppBar( ), navigationIcon = { IconButton(onClick = { backAction(navController, account, peerUri) }) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back", - ) + Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, windowInsets = WindowInsets(0, 0, 0, 0), @@ -290,10 +277,7 @@ private fun TopAppBar( Log.w(TAG, "Call button onClick listener did not find UA for $aor") } ) { - Icon( - imageVector = Icons.Outlined.Email, - contentDescription = "Email", - ) + Icon(imageVector = Icons.Outlined.Email, contentDescription = "Email") } } ) @@ -310,9 +294,7 @@ private fun ChatContent( onMessageDeleted: () -> Unit ) { Column( - modifier = Modifier - .fillMaxWidth() - .padding(contentPadding), + modifier = Modifier.fillMaxWidth().padding(contentPadding), verticalArrangement = Arrangement.Bottom ) { Account(ctx, account) @@ -325,9 +307,7 @@ private fun ChatContent( private fun Account(ctx: Context, account: Account) { Text( text = ctx.getString(R.string.account) + " " + account.text(), - 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 @@ -368,11 +348,8 @@ private fun Messages( LaunchedEffect(messages) { // Scroll to the bottom when new messages are added - if (messages.isNotEmpty()) { - coroutineScope.launch { - lazyListState.scrollToItem(0) - } - } + if (messages.isNotEmpty()) + coroutineScope.launch { lazyListState.scrollToItem(0) } } LazyColumn( @@ -420,9 +397,7 @@ private fun Messages( peerUri ) secondButtonText.value = ctx.getString(R.string.add_contact) - secondAction.value = { - navController.navigate("contact/$peerUri/new") - } + secondAction.value = { navController.navigate("contact/$peerUri/new") } lastButtonText.value = ctx.getString(R.string.delete) lastAction.value = { message.delete() @@ -588,14 +563,16 @@ private fun NewMessage( if (ua.status != circleGreen.getValue(colorblind)) { dialogMessage.value = ctx.getString(R.string.airplane_mode) showDialog.value = true - } else { + } + else { val destination = Utils.uriUserPart(peerUri) if (Utils.sendSms(ctx, destination, msgText)) { msg.direction = MESSAGE_UP newMessage.value = TextFieldValue("") viewModel.updateAorPeerMessage(aor, peerUri, "") keyboardController?.hide() - } else { + } + else { Toast.makeText( ctx, "${ctx.getString(R.string.message_failed)}!", Toast.LENGTH_SHORT @@ -604,38 +581,36 @@ private fun NewMessage( msg.responseReason = ctx.getString(R.string.message_failed) } } - } else { - if (Utils.isTelUri(peerUri)) + } + else { + if (Utils.isTelUri(peerUri)) { if (ua.account.telProvider == "") { dialogMessage.value = String.format( ctx.getString(R.string.no_telephony_provider), Utils.plainAor(aor) ) showDialog.value = true - } else { - msgUri = Utils.telToSip(peerUri, ua.account) } + else + msgUri = Utils.telToSip(peerUri, ua.account) + } else msgUri = peerUri - if (msgUri != "") - if (Api.message_send( - ua.uap, - msgUri, - msgText, - time.toString() - ) != 0 - ) { + if (msgUri != "") { + if (Api.message_send(ua.uap, msgUri, msgText, time.toString()) != 0) { Toast.makeText( ctx, "${ctx.getString(R.string.message_failed)}!", Toast.LENGTH_SHORT ).show() msg.direction = MESSAGE_UP_FAIL msg.responseReason = ctx.getString(R.string.message_failed) - } else { + } + else { newMessage.value = TextFieldValue("") viewModel.updateAorPeerMessage(aor, peerUri, "") keyboardController?.hide() } + } } } }, diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt index 37556786..422edba1 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -23,7 +22,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn @@ -127,23 +126,15 @@ private fun ChatsScreen(navController: NavController, aor: String) { refreshTrigger++ } lifecycleOwner.lifecycle.addObserver(observer) - onDispose { - lifecycleOwner.lifecycle.removeObserver(observer) - } + onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } Scaffold( - modifier = Modifier - .fillMaxSize() - .imePadding(), + modifier = Modifier.fillMaxSize().imePadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding(top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar(navController, account, uaMessages) } }, @@ -188,10 +179,7 @@ private fun TopAppBar( ), navigationIcon = { IconButton(onClick = { navController.navigateUp() }) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = null, - ) + Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, windowInsets = WindowInsets(0, 0, 0, 0), @@ -199,10 +187,7 @@ private fun TopAppBar( IconButton( onClick = { menuExpanded = !menuExpanded } ) { - Icon( - imageVector = Icons.Filled.Menu, - contentDescription = "Menu", - ) + Icon(imageVector = Icons.Filled.Menu, contentDescription = "Menu") } DropdownMenu ( expanded = menuExpanded, @@ -218,9 +203,8 @@ private fun TopAppBar( } showDialog.value = true } - blocked -> { + blocked -> navController.navigate("blocked/message/${account.aor}") - } } } ) @@ -237,9 +221,7 @@ private fun ChatsContent( uaMessages: MutableState> ) { Column( - modifier = Modifier - .fillMaxWidth() - .padding(contentPadding), + modifier = Modifier.fillMaxWidth().padding(contentPadding), verticalArrangement = Arrangement.Top ) { Account(account) @@ -308,7 +290,6 @@ private fun Chats( else CustomElements.TextAvatar(contact.name, contact.color) } - is Contact.AndroidContact -> { val thumbNailUri = contact.thumbnailUri if (thumbNailUri != null) @@ -321,7 +302,6 @@ private fun Chats( else CustomElements.TextAvatar(contact.name, contact.color) } - null -> { Icon( imageVector = Icons.Filled.AccountCircle, @@ -332,20 +312,16 @@ private fun Chats( } } Spacer(modifier = Modifier.width(6.dp)) - val buttonShape = if (message.direction == MESSAGE_DOWN) { + val buttonShape = if (message.direction == MESSAGE_DOWN) RoundedCornerShape(50.dp, 20.dp, 20.dp, 10.dp) - } else { + else RoundedCornerShape(20.dp, 10.dp, 50.dp, 20.dp) - } - val borderStroke = if (account.unreadMessages && Message.unreadMessagesFromPeer(aor, message.peerUri)) { + val borderStroke = if (account.unreadMessages && Message.unreadMessagesFromPeer(aor, message.peerUri)) BorderStroke(width = 2.dp, color = MaterialTheme.colorScheme.error) - } else { + else null - } CustomElements.Button( - onClick = { - navController.navigate("chat/${aor}/${message.peerUri}") - }, + onClick = { navController.navigate("chat/${aor}/${message.peerUri}") }, onLongClick = { val peerName = Utils.friendlyUri(ctx, message.peerUri, account, includeLabel = false) val peerNameWithLabel = Utils.friendlyUri(ctx, message.peerUri, account) @@ -361,20 +337,17 @@ private fun Chats( lastAction.value = { deleteMessages(uaMessages, account, message.peerUri) } - } else { + } + else { dialogMessage.value = String.format( ctx.getString(R.string.long_chat_question), peerName ) secondButtonText.value = ctx.getString(R.string.delete) - secondAction.value = { - deleteMessages(uaMessages, account, message.peerUri) - } + secondAction.value = { deleteMessages(uaMessages, account, message.peerUri) } lastButtonText.value = ctx.getString(R.string.add_contact) - lastAction.value = { - navController.navigate("contact/${message.peerUri}/new") - } + lastAction.value = { navController.navigate("contact/${message.peerUri}/new") } } showDialog.value = true }, @@ -497,10 +470,7 @@ private fun NewChatPeer(ctx: Context, navController: NavController, account: Acc verticalAlignment = Alignment.CenterVertically, ) { var newPeer by remember { mutableStateOf("") } - Column( - horizontalAlignment = Alignment.Start, - modifier = Modifier.weight(1f) - ) { + Column(horizontalAlignment = Alignment.Start, modifier = Modifier.weight(1f)) { if (showSuggestions && filteredSuggestions.isNotEmpty()) { Column( modifier = Modifier @@ -511,8 +481,7 @@ private fun NewChatPeer(ctx: Context, navController: NavController, account: Acc ) .animateContentSize() ) { - Box(modifier = Modifier.fillMaxWidth().heightIn(max = 150.dp) - ) { + Box(modifier = Modifier.fillMaxWidth().heightIn(max = 150.dp)) { LazyColumn( modifier = Modifier .fillMaxWidth() @@ -553,9 +522,9 @@ private fun NewChatPeer(ctx: Context, navController: NavController, account: Acc onValueChange = { newPeer = it showSuggestions = newPeer.length > 1 - filteredSuggestions = if (it.isEmpty()) { + filteredSuggestions = if (it.isEmpty()) emptyList() - } else { + else { val normalizedInput = Utils.unaccent(it) suggestions .filter { suggestion -> @@ -609,7 +578,8 @@ private fun NewChatPeer(ctx: Context, navController: NavController, account: Acc } else { makeChat(ctx, navController, account, peerText) } - } else if (uris.size == 1) + } + else if (uris.size == 1) makeChat(ctx, navController, account, uris[0].uri) else { items.value = uris.map { it.label.ifEmpty { it.uri.substringAfter(":") } } @@ -656,9 +626,7 @@ private fun loadMessages(account: Account) : List { private fun deleteMessages(uaMessages: MutableState>, account: Account, peerUri: String) { val updatedMessages = BaresipService.messages.toMutableList() - updatedMessages.removeAll { - it.aor == account.aor && (peerUri == "" || it.peerUri == peerUri) - } + updatedMessages.removeAll { it.aor == account.aor && (peerUri == "" || it.peerUri == peerUri) } BaresipService.messages = updatedMessages.toList() Message.save() uaMessages.value = loadMessages(account) diff --git a/app/src/main/kotlin/com/tutpro/baresip/CodecsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/CodecsScreen.kt index 6dcca923..ad52eaf3 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CodecsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CodecsScreen.kt @@ -5,13 +5,14 @@ import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack @@ -98,16 +99,14 @@ private fun CodecsScreen( val codecs = remember { mutableStateListOf() } LaunchedEffect(acc, media) { - val allCodecs: List = if (media == "audio") { + val allCodecs: List = if (media == "audio") Api.audio_codecs().split(",") - } else { + else Api.video_codecs().split(",").distinct() - } - val accCodecs: List = if (media == "audio") { + val accCodecs: List = if (media == "audio") acc.audioCodec - } else { + else acc.videoCodec - } val currentCodecs = mutableListOf() for (codec in accCodecs) currentCodecs.add(Codec(codec, mutableStateOf(true))) @@ -119,17 +118,11 @@ private fun CodecsScreen( } Scaffold( - modifier = Modifier.fillMaxSize().imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { Text( @@ -146,7 +139,6 @@ private fun CodecsScreen( titleContentColor = MaterialTheme.colorScheme.onPrimary, actionIconContentColor = MaterialTheme.colorScheme.onPrimary ), - windowInsets = WindowInsets(0, 0, 0, 0), navigationIcon = { IconButton(onClick = onBack) { Icon( @@ -155,24 +147,18 @@ private fun CodecsScreen( ) } }, + windowInsets = WindowInsets(0, 0, 0, 0), actions = { - IconButton(onClick = { - checkOnClick(codecs) - }) { - Icon( - imageVector = Icons.Filled.Check, - contentDescription = "Check" - ) + IconButton( + onClick = { checkOnClick(codecs) }) { + Icon(imageVector = Icons.Filled.Check, contentDescription = "Check") } } ) } }, content = { contentPadding -> - CodecsContent( - contentPadding, - codecs - ) + CodecsContent(contentPadding, codecs) }, ) } @@ -204,9 +190,7 @@ private fun Codecs(codecs: SnapshotStateList) { ) LazyColumn( - modifier = Modifier - .padding(end = 4.dp) - .verticalScrollbar(state = draggableState.listState), + modifier = Modifier.padding(end = 4.dp).verticalScrollbar(state = draggableState.listState), state = draggableState.listState, contentPadding = PaddingValues(start = 12.dp, end = 12.dp), ) { @@ -236,7 +220,8 @@ private fun Codecs(codecs: SnapshotStateList) { val index = codecs.indexOf(item) codecs.removeAt(index) codecs.add(0, item) - } else { + } + else { val index = codecs.indexOf(item) codecs.removeAt(index) codecs.add(item) @@ -247,10 +232,7 @@ private fun Codecs(codecs: SnapshotStateList) { }, trailingContent = { Icon( - modifier = Modifier.dragHandle( - state = draggableState, - key = item.name - ), + modifier = Modifier.dragHandle(state = draggableState, key = item.name), imageVector =Icons.Filled.Reorder, contentDescription = null ) diff --git a/app/src/main/kotlin/com/tutpro/baresip/ContactScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactScreen.kt index 30451462..0dec9c9c 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ContactScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactScreen.kt @@ -26,14 +26,15 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.KeyboardOptions @@ -170,7 +171,8 @@ private fun ContactScreen( isLoading = false, isBaresipContact = true ) - } else { + } + else { val baresipContact = Contact.baresipContact(uriOrNameArg) val androidContact = Contact.androidContact(uriOrNameArg) val contact = baresipContact ?: androidContact @@ -226,7 +228,8 @@ private fun ContactScreen( null, tmpAvatarFile = null ) - } else { + } + else { screenState.tmpAvatarFile?.let { tempFile -> if (tempFile.exists()) { Log.d(TAG, "Back pressed, deleting temp avatar: ${tempFile.name}") @@ -238,16 +241,13 @@ private fun ContactScreen( } val onCheck: () -> Unit = { - val result = checkOnClick( - ctx = ctx, - currentState = screenState, - uriOrNameArg = uriOrNameArg, - ) + val result = checkOnClick(ctx = ctx, currentState = screenState, uriOrNameArg = uriOrNameArg) if (result) { if (screenState.new) { navController.previousBackStackEntry?.savedStateHandle?.set("scrollToContact", screenState.name) navController.navigateUp() - } else { + } + else { // Update UI state with saved values val contact = Contact.baresipContact(screenState.name)!! val avatarFile = File(BaresipService.filesPath, "${contact.id}.png") @@ -270,26 +270,16 @@ private fun ContactScreen( } } - val onEdit: () -> Unit = { - screenState = screenState.copy(isEditing = true) - } + val onEdit: () -> Unit = { screenState = screenState.copy(isEditing = true) } - BackHandler(enabled = true) { - onBack() - } + BackHandler(enabled = true) { onBack() } Scaffold( - modifier = Modifier - .fillMaxSize() - .imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding(top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = title, isEditing = screenState.isEditing, @@ -301,7 +291,7 @@ private fun ContactScreen( } }, content = { contentPadding -> - if (!screenState.isLoading) { + if (!screenState.isLoading) ContactContent( ctx = ctx, viewModel = viewModel, @@ -310,11 +300,10 @@ private fun ContactScreen( screenState = screenState, onStateChange = { newState -> screenState = newState } ) - } else { + else Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } - } } ) } @@ -340,28 +329,21 @@ private fun TopAppBar( windowInsets = WindowInsets(0, 0, 0, 0), navigationIcon = { IconButton(onClick = onBack) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back", - ) + Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, actions = { - if (isEditing) { + if (isEditing) IconButton(onClick = onCheck) { - Icon( - imageVector = Icons.Filled.Check, - contentDescription = "Save" - ) + Icon(imageVector = Icons.Filled.Check, contentDescription = "Save") } - } else if (isBaresipContact) { + else if (isBaresipContact) IconButton(onClick = onEdit) { Icon( imageVector = Icons.Filled.Edit, contentDescription = "Edit" ) } - } } ) } @@ -510,9 +492,9 @@ private fun AvatarSection( val newImageId = System.currentTimeMillis() val tempNewImageFile = File(BaresipService.filesPath, "${newImageId}.png") - if (saveBitmap(rotatedBitmap, tempNewImageFile)) { + if (saveBitmap(rotatedBitmap, tempNewImageFile)) onNewAvatarChosen(tempNewImageFile, newImageId) - } else { + else { Log.e(TAG, "Failed to save processed avatar image") if (tempNewImageFile.exists()) Utils.deleteFile(tempNewImageFile) } @@ -534,24 +516,18 @@ private fun AvatarSection( .clip(CircleShape) .background(if (currentAvatarUri == null) Color(color) else Color.Transparent) .let { modifier -> - if (isEditing) { + if (isEditing) modifier.combinedClickable( onClick = { avatarImagePicker.launch("image/*") }, onLongClick = { onAvatarColorChange(Utils.randomColor()) } ) - } else { + else modifier - } } ) { if (currentAvatarUri == null) { - Box( - modifier = Modifier.size(avatarSize.dp), - contentAlignment = Alignment.Center - ) { - Canvas(modifier = Modifier.fillMaxSize()) { - drawCircle(SolidColor(Color(color))) - } + Box(modifier = Modifier.size(avatarSize.dp), contentAlignment = Alignment.Center) { + Canvas(modifier = Modifier.fillMaxSize()) { drawCircle(SolidColor(Color(color))) } val text = if (name.isNotBlank()) name.take(1).uppercase() else "?" Text(text, fontSize = 72.sp, color = Color.White) } @@ -575,9 +551,7 @@ private fun ContactNameSection(name: String, isEditing: Boolean, new: Boolean, o value = name, placeholder = { Text(stringResource(R.string.contact_name)) }, onValueChange = onNameChange, - modifier = Modifier - .fillMaxWidth() - .focusRequester(focusRequester), + modifier = Modifier.fillMaxWidth().focusRequester(focusRequester), textStyle = androidx.compose.ui.text.TextStyle(fontSize = 18.sp), label = { Text(stringResource(R.string.contact_name)) }, keyboardOptions = KeyboardOptions( @@ -588,7 +562,8 @@ private fun ContactNameSection(name: String, isEditing: Boolean, new: Boolean, o LaunchedEffect(new) { if (new) focusRequester.requestFocus() } - } else { + } + else Row( Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, @@ -601,7 +576,6 @@ private fun ContactNameSection(name: String, isEditing: Boolean, new: Boolean, o textAlign = TextAlign.Center ) } - } } @Composable @@ -615,16 +589,10 @@ private fun UrisSection( ) { val selectedAor by viewModel.selectedAor.collectAsState() - if (isEditing) { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + if (isEditing) + Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) { uris.forEachIndexed { index, contactUri -> - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { + Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Column (modifier = Modifier.weight(1f)) { OutlinedTextField( value = contactUri.uri, @@ -656,7 +624,7 @@ private fun UrisSection( ) ) } - if (uris.size > 1) { + if (uris.size > 1) CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { IconButton( onClick = { @@ -674,7 +642,6 @@ private fun UrisSection( ) } } - } } } IconButton( @@ -692,11 +659,8 @@ private fun UrisSection( ) } } - } else { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { + else + Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) { uris.forEachIndexed { index, contactUri -> Row(verticalAlignment = Alignment.CenterVertically) { val uri = contactUri.uri @@ -735,11 +699,13 @@ private fun UrisSection( alertTitle.value = ctx.getString(R.string.notice) alertMessage.value = ctx.getString(R.string.airplane_mode) showAlert.value = true - } else if (ua.account.isMobile && !Utils.isDefaultSmsApp(ctx)) { + } + else if (ua.account.isMobile && !Utils.isDefaultSmsApp(ctx)) { alertTitle.value = ctx.getString(R.string.notice) alertMessage.value = ctx.getString(R.string.enable_default_messaging) showAlert.value = true - } else { + } + else { val intent = Intent(ctx, MainActivity::class.java) intent.putExtra("uap", ua.uap) intent.putExtra("peer", uri) @@ -760,17 +726,20 @@ private fun UrisSection( } // Call Button - if (ua != null && (if (uri.startsWith("tel:")) + if (ua != null && + if (uri.startsWith("tel:")) ua.account.isMobile || ua.account.telProvider != "" else - !ua.account.isMobile)) + !ua.account.isMobile + ) IconButton( onClick = { if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) { alertTitle.value = ctx.getString(R.string.notice) alertMessage.value = ctx.getString(R.string.airplane_mode) showAlert.value = true - } else { + } + else { val intent = Intent(ctx, MainActivity::class.java) intent.putExtra("uap", ua.uap) intent.putExtra("peer", uri) @@ -792,12 +761,11 @@ private fun UrisSection( } } } - } } @Composable private fun EmailSection(ctx: Context, email: String, isEditing: Boolean, onEmailChange: (String) -> Unit) { - if (isEditing) { + if (isEditing) OutlinedTextField( value = email, placeholder = { Text(stringResource(R.string.email)) }, @@ -807,7 +775,7 @@ private fun EmailSection(ctx: Context, email: String, isEditing: Boolean, onEmai label = { Text(stringResource(R.string.email)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email) ) - } else if (email.isNotEmpty()) { + else if (email.isNotEmpty()) Row( Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, @@ -846,7 +814,6 @@ private fun EmailSection(ctx: Context, email: String, isEditing: Boolean, onEmai ) } } - } } @Composable @@ -865,10 +832,7 @@ private fun FavoriteSection(ctx: Context, favorite: Boolean, onFavoriteChange: ( showAlert.value = true }, ) - Switch( - checked = favorite, - onCheckedChange = onFavoriteChange - ) + Switch(checked = favorite, onCheckedChange = onFavoriteChange) } } @@ -888,10 +852,7 @@ private fun AndroidSection(ctx: Context, android: Boolean, onAndroidChange: (Boo showAlert.value = true }, ) - Switch( - checked = android, - onCheckedChange = onAndroidChange - ) + Switch(checked = android, onCheckedChange = onAndroidChange) } } @@ -906,7 +867,6 @@ private fun checkOnClick( if (u == "") continue if (!u.startsWith("sip:") && !u.startsWith("tel:")) u = if (Utils.isTelNumber(u)) "tel:$u" else "sip:$u" - if (!Utils.checkUri(u)) { alertTitle.value = ctx.getString(R.string.notice) alertMessage.value = String.format(ctx.getString(R.string.invalid_sip_or_tel_uri), u) @@ -924,7 +884,10 @@ private fun checkOnClick( } var newName = currentState.name.trim() - if (newName == "") newName = if (newUris.isNotEmpty()) newUris[0].uri.substringAfter(":") else currentState.email + if (newName == "") newName = if (newUris.isNotEmpty()) + newUris[0].uri.substringAfter(":") + else + currentState.email if (!Utils.checkName(newName)) { alertTitle.value = ctx.getString(R.string.notice) alertMessage.value = String.format(ctx.getString(R.string.invalid_contact), newName) @@ -934,9 +897,8 @@ private fun checkOnClick( val alert: Boolean = if (currentState.new) Contact.nameExists(newName, BaresipService.contacts, true) - else { + else (uriOrNameArg != newName) && Contact.nameExists(newName, BaresipService.contacts, false) - } if (alert) { alertTitle.value = ctx.getString(R.string.notice) alertMessage.value = String.format(ctx.getString(R.string.contact_already_exists), newName) @@ -951,7 +913,8 @@ private fun checkOnClick( if (oldAvatar.exists()) Utils.deleteFile(oldAvatar) idToUse = currentState.newId } - } else if (currentState.avatarImageUri == null) { + } + else if (currentState.avatarImageUri == null) { val avatarFile = File(BaresipService.filesPath, "$idToUse.png") if (avatarFile.exists()) Utils.deleteFile(avatarFile) } @@ -979,12 +942,12 @@ private fun checkOnClick( Log.e(TAG, "Update of Android favorite failed: ${e.message}") } } - } else { + } + else if (currentState.new) Contact.addBaresipContact(contact) else Contact.updateBaresipContact(currentState.id, contact) - } return true } @@ -1010,7 +973,15 @@ private fun rotateBitmap(bitmap: Bitmap, orientation: Int): Bitmap { ExifInterface.ORIENTATION_ROTATE_270 -> matrix.setRotate(-90f) else -> return bitmap } - val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) + val rotatedBitmap = Bitmap.createBitmap( + bitmap, + 0, + 0, + bitmap.width, + bitmap.height, + matrix, + true + ) bitmap.recycle() return rotatedBitmap } @@ -1036,11 +1007,10 @@ private fun addOrUpdateAndroidContact(ctx: Context, contact: Contact.BaresipCont CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "' AND " + CommonDataKinds.StructuredName.DISPLAY_NAME + "='" + contact.name + "'" val c: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, selection, null, null) - if (c != null && c.moveToFirst()) { + if (c != null && c.moveToFirst()) updateAndroidContact(ctx, c.getLong(0), contact) - } else { + else addAndroidContact(ctx, contact) - } c?.close() } @@ -1053,17 +1023,19 @@ private fun addAndroidContact(ctx: Context, contact: Contact.BaresipContact): Bo .withValueBackReference(Data.RAW_CONTACT_ID, 0) .withValue(Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, contact.name).build()) - if (contact.email.isNotEmpty()) { + if (contact.email.isNotEmpty()) ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, 0) .withValue(Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) .withValue(CommonDataKinds.Email.ADDRESS, contact.email) .withValue(CommonDataKinds.Email.TYPE, CommonDataKinds.Email.TYPE_HOME).build()) - } for (contactUri in contact.uris) { val uri = contactUri.uri val label = contactUri.label - val mimeType = if (uri.startsWith("sip:")) CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE else CommonDataKinds.Phone.CONTENT_ITEM_TYPE + val mimeType = if (uri.startsWith("sip:")) + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + else + CommonDataKinds.Phone.CONTENT_ITEM_TYPE val builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, 0) .withValue(Data.MIMETYPE, mimeType) @@ -1078,12 +1050,11 @@ private fun addAndroidContact(ctx: Context, contact: Contact.BaresipContact): Bo } if (contact.avatarImage != null) { val photoData = bitmapToPNGByteArray(contact.avatarImage!!) - if (photoData != null) { + if (photoData != null) ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, 0) .withValue(Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE) .withValue(CommonDataKinds.Photo.PHOTO, photoData).build()) - } } try { ctx.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) @@ -1095,17 +1066,24 @@ private fun addAndroidContact(ctx: Context, contact: Contact.BaresipContact): Bo } private fun updateAndroidContact(ctx: Context, rawContactId: Long, contact: Contact.BaresipContact) { - if (contact.email.isNotEmpty()) { - if (updateAndroidEmail(ctx, rawContactId, contact.email) == 0) addAndroidEmail(ctx, rawContactId, contact.email) - } - for (contactUri in contact.uris) if (updateAndroidUri(ctx, rawContactId, contactUri) == 0) addAndroidUri(ctx, rawContactId, contactUri) - if (updateAndroidPhoto(ctx, rawContactId, contact.avatarImage) == 0) if (contact.avatarImage != null) addAndroidPhoto(ctx, rawContactId, contact.avatarImage!!) + if (contact.email.isNotEmpty()) + if (updateAndroidEmail(ctx, rawContactId, contact.email) == 0) + addAndroidEmail(ctx, rawContactId, contact.email) + for (contactUri in contact.uris) + if (updateAndroidUri(ctx, rawContactId, contactUri) == 0) + addAndroidUri(ctx, rawContactId, contactUri) + if (updateAndroidPhoto(ctx, rawContactId, contact.avatarImage) == 0) + if (contact.avatarImage != null) + addAndroidPhoto(ctx, rawContactId, contact.avatarImage!!) } private fun addAndroidUri(ctx: Context, rawContactId: Long, contactUri: Contact.ContactUri) { val uri = contactUri.uri val label = contactUri.label - val mimeType = if (uri.startsWith("sip:")) CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE else CommonDataKinds.Phone.CONTENT_ITEM_TYPE + val mimeType = if (uri.startsWith("sip:")) + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + else + CommonDataKinds.Phone.CONTENT_ITEM_TYPE val builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValue(Data.RAW_CONTACT_ID, rawContactId) .withValue(Data.MIMETYPE, mimeType) @@ -1128,7 +1106,10 @@ private fun addAndroidUri(ctx: Context, rawContactId: Long, contactUri: Contact. private fun updateAndroidUri(ctx: Context, rawContactId: Long, contactUri: Contact.ContactUri): Int { val uri = contactUri.uri val label = contactUri.label - val mimeType = if (uri.startsWith("sip:")) CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE else CommonDataKinds.Phone.CONTENT_ITEM_TYPE + val mimeType = if (uri.startsWith("sip:")) + CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + else + CommonDataKinds.Phone.CONTENT_ITEM_TYPE val contentValues = ContentValues() contentValues.put(ContactsContract.Data.DATA1, uri.substringAfter(":")) if (mimeType == CommonDataKinds.Phone.CONTENT_ITEM_TYPE) { @@ -1137,7 +1118,8 @@ private fun updateAndroidUri(ctx: Context, rawContactId: Long, contactUri: Conta if (type == CommonDataKinds.Phone.TYPE_CUSTOM) contentValues.put(CommonDataKinds.Phone.LABEL, label) } - val where = "${ContactsContract.Data.RAW_CONTACT_ID}=$rawContactId and ${ContactsContract.Data.MIMETYPE}='$mimeType'" + val where = "${ContactsContract.Data.RAW_CONTACT_ID}=$rawContactId and " + + "${ContactsContract.Data.MIMETYPE}='$mimeType'" return try { ctx.contentResolver.update(ContactsContract.Data.CONTENT_URI, contentValues, where, null) } catch (e: Exception) { @@ -1179,7 +1161,8 @@ private fun addAndroidEmail(ctx: Context, rawContactId: Long, email: String) { private fun updateAndroidEmail(ctx: Context, rawContactId: Long, email: String): Int { val contentValues = ContentValues() contentValues.put(CommonDataKinds.Email.ADDRESS, email) - val where = "${ContactsContract.Data.RAW_CONTACT_ID}=$rawContactId and ${ContactsContract.Data.MIMETYPE}='${CommonDataKinds.Email.CONTENT_ITEM_TYPE}'" + val where = "${ContactsContract.Data.RAW_CONTACT_ID}=$rawContactId and " + + "${ContactsContract.Data.MIMETYPE}='${CommonDataKinds.Email.CONTENT_ITEM_TYPE}'" return try { ctx.contentResolver.update(ContactsContract.Data.CONTENT_URI, contentValues, where, null) } catch (_: Exception) { @@ -1191,7 +1174,8 @@ private fun updateAndroidPhoto(ctx: Context, rawContactId: Long, photoBits: Bitm val photoBytes = if (photoBits == null) null else bitmapToPNGByteArray(photoBits) val contentValues = ContentValues() contentValues.put(CommonDataKinds.Photo.PHOTO, photoBytes) - val where = "${ContactsContract.Data.RAW_CONTACT_ID}=$rawContactId and ${ContactsContract.Data.MIMETYPE}='${CommonDataKinds.Photo.CONTENT_ITEM_TYPE}'" + val where = "${ContactsContract.Data.RAW_CONTACT_ID}=$rawContactId and " + + "${ContactsContract.Data.MIMETYPE}='${CommonDataKinds.Photo.CONTENT_ITEM_TYPE}'" return try { ctx.contentResolver.update(ContactsContract.Data.CONTENT_URI, contentValues, where, null) } catch (_: Exception) { diff --git a/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt index 064e1b29..2f539224 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt @@ -22,15 +22,15 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState @@ -128,7 +128,7 @@ private fun ContactsScreen(navController: NavController) { val vcfExportLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument("text/vcard") ) { uri: android.net.Uri? -> - if (uri != null) { + if (uri != null) try { ctx.contentResolver.openOutputStream(uri)?.use { outputStream -> val writer = outputStream.bufferedWriter() @@ -155,7 +155,8 @@ private fun ContactsScreen(navController: NavController) { writer.write("TEL;X-${u.label}:${u.uri.substring(4)}\n") else writer.write("TEL:${u.uri.substring(4)}\n") - } else if (u.uri.startsWith("sip:")) { + } + else if (u.uri.startsWith("sip:")) { if (u.label.isNotEmpty()) writer.write("X-SIP;X-${u.label}:${u.uri.substring(4)}\n") else @@ -171,25 +172,23 @@ private fun ContactsScreen(navController: NavController) { Log.e(TAG, "Failed to export VCF: ${e.message}") Toast.makeText(ctx, R.string.contact_export_failure, Toast.LENGTH_SHORT).show() } - } } val vcfImportLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocument() ) { uri: android.net.Uri? -> - if (uri != null) { + if (uri != null) try { ctx.contentResolver.openInputStream(uri)?.use { inputStream -> val reader = inputStream.bufferedReader() val lines = mutableListOf() reader.forEachLine { line -> if (line.startsWith(" ") || line.startsWith("\t")) { - if (lines.isNotEmpty()) { + if (lines.isNotEmpty()) lines[lines.size - 1] = lines.last() + line.trim() - } - } else { - lines.add(line) } + else + lines.add(line) } var name = "" @@ -199,7 +198,7 @@ private fun ContactsScreen(navController: NavController) { var contactNo = 0 val newBaresipContacts = BaresipService.baresipContacts.value.toMutableList() - for (line in lines) { + for (line in lines) when { line.startsWith("BEGIN:VCARD", ignoreCase = true) -> { name = "" @@ -241,7 +240,7 @@ private fun ContactsScreen(navController: NavController) { if (name.isNotEmpty() && (uris.isNotEmpty() || email.isNotEmpty())) { val existingContact = newBaresipContacts.find { it.name == name } val contactId = existingContact?.id ?: (System.currentTimeMillis() + contactNo++) - if (photoBase64.isNotEmpty()) { + if (photoBase64.isNotEmpty()) try { val decodedString = Base64.decode(photoBase64, Base64.DEFAULT) val decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.size) @@ -254,17 +253,14 @@ private fun ContactsScreen(navController: NavController) { } catch (e: Exception) { Log.e(TAG, "Failed to decode photo for $name: ${e.message}") } - } if (existingContact != null) { - for (u in uris) { - if (existingContact.uris.none { it.uri == u.uri }) { + for (u in uris) + if (existingContact.uris.none { it.uri == u.uri }) existingContact.uris.add(u) - } - } - if (existingContact.email.isEmpty() && email.isNotEmpty()) { + if (existingContact.email.isEmpty() && email.isNotEmpty()) existingContact.email = email - } - } else { + } + else newBaresipContacts.add( Contact.BaresipContact( name, @@ -275,30 +271,19 @@ private fun ContactsScreen(navController: NavController) { false ) ) - } } } } - } BaresipService.baresipContacts.value = newBaresipContacts.toList() Contact.saveBaresipContacts() Contact.restoreBaresipContacts() Contact.contactsUpdate() - Toast.makeText( - ctx, - R.string.contact_import_success, - Toast.LENGTH_SHORT - ).show() + Toast.makeText(ctx, R.string.contact_import_success, Toast.LENGTH_SHORT).show() } } catch (e: Exception) { Log.e(TAG, "Failed to import VCF: ${e.message}") - Toast.makeText( - ctx, - R.string.contact_import_failure, - Toast.LENGTH_SHORT - ).show() + Toast.makeText(ctx, R.string.contact_import_failure, Toast.LENGTH_SHORT).show() } - } } val contactNames = remember(BaresipService.contactsMode) { @@ -373,11 +358,9 @@ private fun ContactsScreen(navController: NavController) { contract = ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> if (permissions[Manifest.permission.READ_CONTACTS] == true && - permissions[Manifest.permission.WRITE_CONTACTS] == true) { - if (pendingMode.isNotEmpty()) { + permissions[Manifest.permission.WRITE_CONTACTS] == true) + if (pendingMode.isNotEmpty()) setContactsMode(pendingMode) - } - } pendingMode = "" } @@ -388,10 +371,7 @@ private fun ContactsScreen(navController: NavController) { lastButtonText = ok, onLastClicked = { requestPermissionsLauncher.launch( - arrayOf( - Manifest.permission.READ_CONTACTS, - Manifest.permission.WRITE_CONTACTS - ) + arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS) ) } ) @@ -400,20 +380,11 @@ private fun ContactsScreen(navController: NavController) { modifier = Modifier.fillMaxSize().imePadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { - Text( - text = stringResource(R.string.contacts), - fontWeight = FontWeight.Bold - ) + Text(text = stringResource(R.string.contacts), fontWeight = FontWeight.Bold) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, @@ -425,18 +396,13 @@ private fun ContactsScreen(navController: NavController) { IconButton( onClick = { navController.navigateUp() } ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back", - ) + Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") } }, + windowInsets = WindowInsets(0, 0, 0, 0), actions = { IconButton(onClick = { expanded = !expanded }) { - Icon( - imageVector = Icons.Filled.Menu, - contentDescription = "Menu" - ) + Icon(imageVector = Icons.Filled.Menu, contentDescription = "Menu") } CustomElements.DropdownMenu( expanded = expanded, @@ -498,7 +464,8 @@ private fun ContactsScreen(navController: NavController) { ) { Log.d(TAG, "Contacts permissions already granted") setContactsMode(mode) - } else { + } + else { if (shouldShowRequestPermissionRationale( activity, Manifest.permission.READ_CONTACTS ) || @@ -508,20 +475,20 @@ private fun ContactsScreen(navController: NavController) { ) { pendingMode = mode showNoticeDialog.value = true - } else { + } + else { pendingMode = mode requestPermissionsLauncher.launch(contactsPermissions) } } } showDialog.value = true - } else { - setContactsMode(mode) } + else + setContactsMode(mode) } ) - }, - windowInsets = WindowInsets(0, 0, 0, 0), + } ) } }, @@ -594,10 +561,7 @@ private fun BottomBar( }, label = { Text(stringResource(R.string.search)) }, textStyle = TextStyle(fontSize = 18.sp), - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Text, - imeAction = ImeAction.Done - ) + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done) ) SmallFloatingActionButton( onClick = { navController.navigate("contact//new") }, @@ -641,18 +605,17 @@ private fun ContactsContent( var lastSearchQuery by remember { mutableStateOf("") } LaunchedEffect(searchQuery) { - if (searchQuery.isBlank() && lastSearchQuery.isNotBlank()) { + if (searchQuery.isBlank() && lastSearchQuery.isNotBlank()) lazyListState.scrollToItem(0) - } lastSearchQuery = searchQuery } val filteredContacts = remember(BaresipService.contacts, searchQuery) { - if (searchQuery.isBlank()) { + if (searchQuery.isBlank()) BaresipService.contacts.map { contact -> Pair(contact, buildAnnotatedString { append(contact.name()) }) } - } else { + else { val normalizedQuery = Utils.unaccent(searchQuery) BaresipService.contacts .filter { contact -> @@ -667,9 +630,8 @@ private fun ContactsContent( LaunchedEffect(scrollToContact?.value, filteredContacts) { scrollToContact?.value?.let { name -> val index = filteredContacts.indexOfFirst { it.first.name() == name } - if (index != -1) { + if (index != -1) lazyListState.scrollToItem(index) - } navController.currentBackStackEntry?.savedStateHandle?.remove("scrollToContact") } } @@ -687,13 +649,11 @@ private fun ContactsContent( itemsIndexed( filteredContacts, key = { index, (contact, _) -> "${contact.id()}_$index" } ) { _, (contact, annotatedName) -> - Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { when (contact) { - is Contact.BaresipContact -> { val avatarImage = contact.avatarImage if (avatarImage != null) @@ -708,7 +668,6 @@ private fun ContactsContent( else TextAvatar(contact.name(), contact.color) } - is Contact.AndroidContact -> { val thumbNailUri = contact.thumbnailUri if (thumbNailUri != null) @@ -724,45 +683,32 @@ private fun ContactsContent( TextAvatar(contact.name(), contact.color) } } - when (contact) { - is Contact.BaresipContact -> { - Text(text = annotatedName, + Text( + text = annotatedName, fontSize = 20.sp, fontStyle = if (contact.favorite()) FontStyle.Italic else FontStyle.Normal, modifier = Modifier .weight(1f) .padding(start = 10.dp) .combinedClickable( - onClick = { - navController.navigate("contact/${contact.name()}/old") - }, + onClick = { navController.navigate("contact/${contact.name()}/old") }, onLongClick = { title.value = confirmationText - message.value = String.format( - contactDeleteQuestion, - contact.name() - ) + message.value = String.format(contactDeleteQuestion, contact.name()) firstButtonText.value = cancelText onFirstClicked.value = { } lastButtonText.value = deleteText onLastClicked.value = { val id = contact.id - val avatarFile = File( - BaresipService.filesPath, - "$id.png" - ) - if (avatarFile.exists()) { + val avatarFile = File(BaresipService.filesPath, "$id.png") + if (avatarFile.exists()) try { avatarFile.delete() } catch (e: IOException) { - Log.e( - TAG, - "Could not delete file $id.png: ${e.message}" - ) + Log.e(TAG, "Could not delete file $id.png: ${e.message}") } - } Contact.removeBaresipContact(contact) } showDialog.value = true @@ -770,7 +716,6 @@ private fun ContactsContent( ) ) } - is Contact.AndroidContact -> { Text(text = annotatedName, fontSize = 20.sp, @@ -779,15 +724,10 @@ private fun ContactsContent( .weight(1f) .padding(start = 10.dp, top = 4.dp, bottom = 4.dp) .combinedClickable( - onClick = { - navController.navigate("contact/${contact.name()}/old") - }, + onClick = { navController.navigate("contact/${contact.name()}/old") }, onLongClick = { title.value = confirmationText - message.value = String.format( - contactDeleteQuestion, - contact.name() - ) + message.value = String.format(contactDeleteQuestion, contact.name()) firstButtonText.value = cancelText onFirstClicked.value = { } lastButtonText.value = deleteText diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt index 58508c4e..9769efce 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt @@ -41,7 +41,6 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -50,7 +49,7 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -242,12 +241,10 @@ private fun MainScreen( BaresipService.isMainVisible = true viewModel.updateSpeakerPhoneStatus(BaresipService.speakerPhone) viewModel.updateCalls(Call.calls().toList()) - if (Call.inCall()) (ctx as? Activity)?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) else (ctx as? Activity)?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - (Call.call("incoming") ?: Call.calls().lastOrNull())?.let { spinToAor(viewModel, it.ua.account.aor, it) } ?: run { @@ -453,7 +450,8 @@ private fun MainScreen( showPasswordsDialog.value = true } ) - } else { + } + else { showPasswordsDialog.value = false showPasswordsDialog.value = true } @@ -471,27 +469,19 @@ private fun MainScreen( Charsets.UTF_8 ).lines().toMutableList() showPasswordsDialog.value = true - } else { + } + else // Baresip is started for the first time onRequestPermissions() - } } } Scaffold( - modifier = Modifier - .fillMaxSize() - .imePadding(), + modifier = Modifier.fillMaxSize().imePadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( viewModel = viewModel, navController = navController, @@ -541,11 +531,7 @@ private fun TopAppBar( TopAppBar( title = { - Text( - text = stringResource(R.string.baresip), - fontSize = 22.sp, - fontWeight = FontWeight.Bold - ) + Text(text = stringResource(R.string.baresip), fontSize = 22.sp, fontWeight = FontWeight.Bold) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, @@ -736,10 +722,7 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont val buttonSize = 48.dp Row( - modifier = Modifier - .fillMaxWidth() - .navigationBarsPadding() - .padding(bottom = 16.dp), + modifier = Modifier.fillMaxWidth().navigationBarsPadding().padding(bottom = 16.dp), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically ) { @@ -757,7 +740,8 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont intent.putExtra("uap", ua.uap) intent.putExtra("peer", acc.vmUri) handleIntent(ctx, viewModel, intent, "call") - } else { + } + else { dialogTitle.value = ctx.getString(R.string.voicemail_messages) dialogMessage.value = acc.vmMessages(ctx) firstText.value = ctx.getString(R.string.cancel) @@ -774,23 +758,22 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont } } }, - modifier = Modifier - .weight(1f) - .size(buttonSize) + modifier = Modifier.weight(1f).size(buttonSize) ) { Icon( imageVector = Icons.Filled.Voicemail, contentDescription = null, Modifier.size(buttonSize), - tint = if (hasNewVoicemail) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary + tint = if (hasNewVoicemail) + MaterialTheme.colorScheme.error + else + MaterialTheme.colorScheme.secondary ) } IconButton( onClick = { navController.navigate("contacts") }, - modifier = Modifier - .weight(1f) - .size(buttonSize) + modifier = Modifier.weight(1f).size(buttonSize) ) { Icon( imageVector = Icons.Filled.Person, @@ -803,13 +786,11 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont IconButton( enabled = aor.isNotEmpty(), onClick = { - if (isMobile) { - if (!Utils.isDefaultSmsApp(ctx)) { - alertTitle.value = ctx.getString(R.string.notice) - alertMessage.value = ctx.getString(R.string.enable_default_messaging) - showAlert.value = true - return@IconButton - } + if (isMobile && !Utils.isDefaultSmsApp(ctx)) { + alertTitle.value = ctx.getString(R.string.notice) + alertMessage.value = ctx.getString(R.string.enable_default_messaging) + showAlert.value = true + return@IconButton } navController.navigate("chats/$aor") }, @@ -828,12 +809,8 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont IconButton( enabled = aor.isNotEmpty(), - onClick = { - navController.navigate("calls/$aor") - }, - modifier = Modifier - .weight(1f) - .size(buttonSize) + onClick = { navController.navigate("calls/$aor") }, + modifier = Modifier.weight(1f).size(buttonSize) ) { Icon( imageVector = Icons.Filled.History, @@ -845,9 +822,7 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont IconButton( onClick = { viewModel.toggleDialpadVisibility() }, - modifier = Modifier - .weight(1f) - .size(buttonSize), + modifier = Modifier.weight(1f).size(buttonSize), enabled = dialpadButtonEnabled.value ) { Icon( @@ -985,7 +960,8 @@ private fun MainContent(navController: NavController, viewModel: ViewModel, cont showCall(ctx, viewModel, ua) } } - } else if (offset > swipeThreshold) { + } + else if (offset > swipeThreshold) { if (uas.value.isNotEmpty()) { val curPos = UserAgent.findAorIndex(viewModel.selectedAor.value) val newPos = when (curPos) { @@ -1046,26 +1022,18 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na val selected = viewModel.selectedAor.value if (uas.value.isEmpty()) { if (selected != "") viewModel.updateSelectedAor("") - } else { - if (selected == "" || UserAgent.ofAor(selected) == null) { - viewModel.updateSelectedAor(uas.value.first().account.aor) - } } + else if (selected == "" || UserAgent.ofAor(selected) == null) + viewModel.updateSelectedAor(uas.value.first().account.aor) val ua = UserAgent.ofAor(viewModel.selectedAor.value) - if (ua != null) { + if (ua != null) showCall(ctx, viewModel, ua, viewModel.focusedCall.value) - } } if (selected == "") { OutlinedButton( - onClick = { - navController.navigate("accounts") - }, - modifier = Modifier - .padding(horizontal = 4.dp) - .height(50.dp) - .fillMaxWidth(), + onClick = { navController.navigate("accounts") }, + modifier = Modifier.padding(horizontal = 4.dp).height(50.dp).fillMaxWidth(), colors = ButtonColors( containerColor = MaterialTheme.colorScheme.surfaceVariant, contentColor = MaterialTheme.colorScheme.onSurfaceVariant, @@ -1080,18 +1048,14 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na } else OutlinedButton( - onClick = { - expanded = !expanded - }, + onClick = { expanded = !expanded }, enabled = true, modifier = Modifier .padding(horizontal = 4.dp) .height(50.dp) .pointerInput(Unit) { detectTapGestures( - onPress = { - expanded = true - }, + onPress = { expanded = true }, onLongPress = { val ua = UserAgent.ofAor(selected) if (ua != null) { @@ -1100,11 +1064,9 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na if (Api.account_regint(acc.accp) > 0) { Api.account_set_regint(acc.accp, 0) Api.ua_unregister(ua.uap) - } else { - Api.account_set_regint( - acc.accp, - acc.configuredRegInt - ) + } + else { + Api.account_set_regint(acc.accp, acc.configuredRegInt) Api.ua_register(ua.uap) } acc.regint = Api.account_regint(acc.accp) @@ -1153,7 +1115,8 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na if (Api.account_regint(acc.accp) > 0) { Api.account_set_regint(acc.accp, 0) Api.ua_unregister(ua.uap) - } else { + } + else { Api.account_set_regint( acc.accp, acc.configuredRegInt @@ -1186,11 +1149,9 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na expanded = false spinToAor(viewModel, acc.aor) }, - text = { Text( - text = acc.text(), - fontSize = 17.sp, - fontWeight = FontWeight.Bold - ) }, + text = { + Text(text = acc.text(), fontSize = 17.sp, fontWeight = FontWeight.Bold) + }, leadingIcon = { Icon( imageVector = ImageVector.vectorResource(uasStatus.value[acc.aor]!!), @@ -1199,9 +1160,8 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na ) } ) - if (index < uas.value.size - 1) { + if (index < uas.value.size - 1) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) - } } } } @@ -1224,21 +1184,16 @@ private fun CallUriRow( val isDialpadVisible by viewModel.isDialpadVisible.collectAsState() Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = 4.dp, bottom = 8.dp), + modifier = Modifier.fillMaxWidth().padding(top = 4.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically ) { - Column( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { + Column(modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally) { OutlinedTextField( value = if (isDialer) dialerState.callUri.value else call!!.callUri.value, readOnly = if (isDialer) !dialerState.callUriEnabled.value else !call!!.callUriEnabled.value, singleLine = true, onValueChange = { - if (isDialer) { + if (isDialer) if (it != dialerState.callUri.value) { dialerState.callUri.value = it dialerState.redialUri = "" @@ -1258,7 +1213,6 @@ private fun CallUriRow( } dialerState.showSuggestions.value = it.length > 1 } - } }, trailingIcon = { if (isDialer && dialerState.callUriEnabled.value && dialerState.callUri.value.isNotEmpty()) @@ -1322,10 +1276,7 @@ private fun CallUriRow( .fillMaxWidth() .padding(start = 4.dp, end = 4.dp, top = 2.dp, bottom = 2.dp), label = { - Text( - text = call.callUriLabel2.value, - fontSize = 18.sp - ) + Text(text = call.callUriLabel2.value, fontSize = 18.sp) }, textStyle = TextStyle(fontSize = 18.sp) ) @@ -1343,9 +1294,7 @@ private fun CallUriRow( ) { if (isDialer && dialerState.showSuggestions.value && filteredSuggestions.isNotEmpty()) { Box( - modifier = Modifier - .fillMaxWidth() - .heightIn(max = 150.dp) + modifier = Modifier.fillMaxWidth().heightIn(max = 150.dp) ) { LazyColumn( modifier = Modifier @@ -1403,13 +1352,11 @@ private fun CallUriRow( alertMessage.value = ctx.getString(R.string.call_not_secure) showAlert.value = true } - R.color.colorTrafficYellow -> { alertTitle.value = ctx.getString(R.string.alert) alertMessage.value = ctx.getString(R.string.peer_not_verified) showAlert.value = true } - R.color.colorTrafficGreen -> { dialogTitle.value = ctx.getString(R.string.info) dialogMessage.value = ctx.getString(R.string.call_is_secure) @@ -1419,10 +1366,7 @@ private fun CallUriRow( lastText.value = ctx.getString(R.string.unverify) onLastClicked.value = { if (Api.cmd_exec("zrtp_unverify " + call.zid) != 0) - Log.e( - TAG, - "Command 'zrtp_unverify ${call.zid}' failed" - ) + Log.e(TAG, "Command 'zrtp_unverify ${call.zid}' failed") else call.securityIconTint.value = R.color.colorTrafficYellow } @@ -1446,10 +1390,7 @@ private fun CallUriRow( } @Composable -private fun CallTimer( - initialDurationSeconds: Long, - modifier: Modifier = Modifier -) { +private fun CallTimer(initialDurationSeconds: Long, modifier: Modifier = Modifier) { val startTime = remember(initialDurationSeconds) { SystemClock.elapsedRealtime() - (initialDurationSeconds * 1000L) } @@ -1489,11 +1430,12 @@ private fun CallRow( Row( modifier = Modifier .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = if (isDialer || call?.showCancelButton?.value == true || - call?.showAnswerRejectButtons?.value == true) - Arrangement.Center - else - Arrangement.SpaceBetween + horizontalArrangement = + if (isDialer || call?.showCancelButton?.value == true || + call?.showAnswerRejectButtons?.value == true) + Arrangement.Center + else + Arrangement.SpaceBetween ) { if (isDialer) { if (dialerState.showCallButton.value) @@ -1686,14 +1628,14 @@ private fun CallRow( transferUri = it if (it.length > 1) { val normalizedInput = Utils.unaccent(it) - filteredSuggestions = - suggestions.filter { suggestion -> + filteredSuggestions = suggestions + .filter { suggestion -> Utils.unaccent(suggestion) .contains(normalizedInput, ignoreCase = true) } - .map { suggestion -> - Utils.buildAnnotatedStringWithHighlight(suggestion, it) - } + .map { suggestion -> + Utils.buildAnnotatedStringWithHighlight(suggestion, it) + } } call.showSuggestions.value = transferUri.length > 1 } @@ -1714,12 +1656,7 @@ private fun CallRow( }, modifier = Modifier .fillMaxWidth() - .padding( - start = 4.dp, - end = 4.dp, - top = 12.dp, - bottom = 2.dp - ) + .padding(start = 4.dp, end = 4.dp, top = 12.dp, bottom = 2.dp) .focusRequester(focusRequester), label = { Text(stringResource(R.string.transfer_destination)) }, textStyle = TextStyle(fontSize = 18.sp), @@ -1740,16 +1677,11 @@ private fun CallRow( .animateContentSize() ) { if (call.showSuggestions.value && filteredSuggestions.isNotEmpty()) { - Box(modifier = Modifier - .fillMaxWidth() - .heightIn(max = 150.dp)) { + Box(modifier = Modifier.fillMaxWidth().heightIn(max = 150.dp)) { LazyColumn( modifier = Modifier .fillMaxWidth() - .verticalScrollbar( - state = lazyListState, - width = 6.dp, - ), + .verticalScrollbar(state = lazyListState, width = 6.dp), horizontalAlignment = Alignment.Start, state = lazyListState, ) { @@ -1761,10 +1693,8 @@ private fun CallRow( modifier = Modifier .fillMaxWidth() .clickable { - transferUri = - suggestion.toString() - call.showSuggestions.value = - false + transferUri = suggestion.toString() + call.showSuggestions.value = false } .padding(12.dp) ) { @@ -1793,9 +1723,7 @@ private fun CallRow( ) Switch( checked = blindChecked.value, - onCheckedChange = { - blindChecked.value = true - } + onCheckedChange = { blindChecked.value = true } ) } Spacer(modifier = Modifier.width(8.dp)) @@ -1807,9 +1735,7 @@ private fun CallRow( ) Switch( checked = !blindChecked.value, - onCheckedChange = { - blindChecked.value = false - } + onCheckedChange = { blindChecked.value = false } ) } } @@ -1902,14 +1828,9 @@ private fun CallRow( } call.dtmfText.value = newText }, - modifier = Modifier - .width(80.dp) - .focusRequester(focusRequester), + modifier = Modifier.width(80.dp).focusRequester(focusRequester), enabled = call.dtmfEnabled.value, - textStyle = TextStyle( - fontSize = 16.sp, - color = MaterialTheme.colorScheme.onSurface - ), + textStyle = TextStyle(fontSize = 16.sp, color = MaterialTheme.colorScheme.onSurface), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), singleLine = true, @@ -1923,17 +1844,9 @@ private fun CallRow( enabled = call.dtmfEnabled.value, interactionSource = interactionSource, label = { - Text( - stringResource(R.string.dtmf), - style = TextStyle(fontSize = 12.sp) - ) + Text(stringResource(R.string.dtmf), style = TextStyle(fontSize = 12.sp)) }, - contentPadding = PaddingValues( - start = 4.dp, - end = 4.dp, - top = 8.dp, - bottom = 8.dp - ), + contentPadding = PaddingValues(start = 4.dp, end = 4.dp, top = 8.dp, bottom = 8.dp), colors = OutlinedTextFieldDefaults.colors( focusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent, @@ -1979,7 +1892,8 @@ private fun CallRow( "${ctx.getString(R.string.lost)}: ${parts[3]}\n" + String.format(ctx.getString(R.string.jitter), parts[4]) showAlert.value = true - } else { + } + else { alertTitle.value = ctx.getString(R.string.call_info) alertMessage.value = ctx.getString(R.string.call_info_not_available) showAlert.value = true @@ -1995,12 +1909,9 @@ private fun CallRow( } if (call.showAnswerRejectButtons.value) { - IconButton( modifier = Modifier.size(48.dp), - onClick = { - answer(ctx, call) - }, + onClick = { answer(ctx, call) }, ) { Icon( imageVector = Icons.Filled.Call, @@ -2009,14 +1920,10 @@ private fun CallRow( contentDescription = null, ) } - Spacer(Modifier.width(48.dp)) - IconButton( modifier = Modifier.size(48.dp), - onClick = { - reject(call) - }, + onClick = { reject(call) }, ) { Icon( imageVector = Icons.Filled.CallEnd, @@ -2039,10 +1946,7 @@ private fun OnHoldNotice() { modifier = Modifier.padding(16.dp), shape = RoundedCornerShape(20) ) { - Text( - text = stringResource(R.string.call_is_on_hold), - fontSize = 18.sp - ) + Text(text = stringResource(R.string.call_is_on_hold), fontSize = 18.sp) } } } @@ -2252,8 +2156,7 @@ private fun transfer(ctx: Context, viewModel: ViewModel, ua: UserAgent, uriText: } private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCall: Call? = null) { - if (ua == null) - return + if (ua == null) return val call = showCall ?: ua.currentCall() val aor = ua.account.aor val callp = call?.callp ?: 0L @@ -2278,7 +2181,8 @@ private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCal BaresipService.isMicMuted = false viewModel.updateMicIcon(Icons.Filled.Mic) } - } else { + } + else { viewModel.dialerState.callUri.value = "" pullToRefreshEnabled.value = false call.callUriEnabled.value = false @@ -2323,9 +2227,8 @@ private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCal call.callUriLabel2.value = ctx.getString(R.string.diverted_by_dots) call.callUri2.value = Utils.friendlyUri(ctx, uri, ua.account) } - else { + else call.callUri2.value = "" - } call.showCallButton.value = false call.showCancelButton.value = false call.showHangupButton.value = false @@ -2388,13 +2291,10 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params Log.d(TAG, "Handling service event 'started' with URI '$uriString'") if (uriString != "") callAction(ctx, viewModel, uriString.toUri(), "dial") - else { - if (viewModel.selectedAor.value == "" && uas.value.isNotEmpty()) - viewModel.updateSelectedAor(uas.value.first().account.aor) - } - if (Preferences(ctx).displayTheme != AppCompatDelegate.getDefaultNightMode()) { + else if (viewModel.selectedAor.value == "" && uas.value.isNotEmpty()) + viewModel.updateSelectedAor(uas.value.first().account.aor) + if (Preferences(ctx).displayTheme != AppCompatDelegate.getDefaultNightMode()) AppCompatDelegate.setDefaultNightMode(Preferences(ctx).displayTheme) - } handleNextEvent() return } @@ -2430,8 +2330,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params val aor = ua.account.aor when (ev[0]) { - "call rejected" -> { - } + "call rejected" -> {} "call outgoing" -> { val callp = params[1] as Long if (!BaresipService.isMainVisible) @@ -2467,9 +2366,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params secondText.value = ctx.getString(R.string.no) onSecondClicked.value = { } lastText.value = ctx.getString(R.string.yes) - onLastClicked.value = { - redirect(ctx, viewModel, ua, redirectUri) - } + onLastClicked.value = { redirect(ctx, viewModel, ua, redirectUri) } showDialog.value = true } } @@ -2483,8 +2380,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params call.dtmfText.value = "" } } - "call update" -> { - } + "call update" -> {} "call verify" -> { val callp = params[1] as Long val call = Call.ofCallp(callp) @@ -2508,9 +2404,9 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params call.security = if (Api.cmd_exec("zrtp_verify ${ev[2]}") != 0) { Log.e(TAG, "Command 'zrtp_verify ${ev[2]}' failed") R.color.colorTrafficYellow - } else { - R.color.colorTrafficGreen } + else + R.color.colorTrafficGreen call.zid = ev[2] if (aor == viewModel.selectedAor.value) call.securityIconTint.value = call.security @@ -2524,9 +2420,8 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params handleNextEvent("Call $callp that is verified is not found") return } - if (aor == viewModel.selectedAor.value) { + if (aor == viewModel.selectedAor.value) call.securityIconTint.value = call.security - } } "call transfer", "transfer show" -> { if (!BaresipService.isMainVisible) @@ -2594,14 +2489,13 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params } "mwi notify" -> { val lines = ev[1].split("\n") - for (line in lines) { + for (line in lines) if (line.startsWith("Voice-Message:")) { val counts = (line.split(" ")[1]).split("/") acc.vmNew = counts[0].toInt() acc.vmOld = counts[1].toInt() break } - } } else -> Log.e(TAG, "Unknown event '${ev[0]}'") } @@ -2648,10 +2542,8 @@ fun handleIntent(ctx: Context, viewModel: ViewModel, intent: Intent, action: Str if (ev[0] == "call answer") answer(ctx, call) else - BaresipService.postServiceEvent(ServiceEvent( - "call incoming", - arrayListOf(call.ua.uap, callp), - System.nanoTime()) + BaresipService.postServiceEvent( + ServiceEvent("call incoming", arrayListOf(call.ua.uap, callp), System.nanoTime()) ) } "call missed" -> { @@ -2676,10 +2568,8 @@ fun handleIntent(ctx: Context, viewModel: ViewModel, intent: Intent, action: Str ev[1] else intent.getStringExtra("uri")!! - BaresipService.postServiceEvent(ServiceEvent( - ev[0] + "," + uri, - arrayListOf(call.ua.uap, callp), - System.nanoTime()) + BaresipService.postServiceEvent( + ServiceEvent(ev[0] + "," + uri, arrayListOf(call.ua.uap, callp), System.nanoTime()) ) } "message", "message show", "message reply" -> { @@ -2690,10 +2580,8 @@ fun handleIntent(ctx: Context, viewModel: ViewModel, intent: Intent, action: Str return } spinToAor(viewModel, ua.account.aor) - BaresipService.postServiceEvent(ServiceEvent( - ev[0], - arrayListOf(uap, intent.getStringExtra("peer")!!), - System.nanoTime()) + BaresipService.postServiceEvent( + ServiceEvent(ev[0], arrayListOf(uap, intent.getStringExtra("peer")!!), System.nanoTime()) ) } } @@ -2710,8 +2598,7 @@ fun handleDialog(ctx: Context, title: String, message: String, action: () -> Uni } fun callAction(ctx: Context, viewModel: ViewModel, uri: Uri?, action: String) { - if (Call.inCall() || uas.value.isEmpty()) - return + if (Call.inCall() || uas.value.isEmpty()) return Log.d(TAG, "Action $action to $uri") if (uri != null) { var uriStr: String @@ -2772,11 +2659,13 @@ private fun acceptTransfer(ctx: Context, viewModel: ViewModel, ua: UserAgent, ca if (ua.account.aor != viewModel.selectedAor.value) spinToAor(viewModel, ua.account.aor) showCall(ctx, viewModel, ua) - } else { + } + else { Log.w(TAG, "call_connect $newCallp failed") call.notifySipfrag(500, "Call Error") } - } else { + } + else { Log.w(TAG, "callAlloc for ua ${ua.uap} call ${call.callp} transfer failed") call.notifySipfrag(500, "Call Error") } @@ -2794,8 +2683,10 @@ private fun backup(ctx: Context, password: String) { if (!Utils.zip(files, zipFile)) { Log.w(TAG, "Failed to write zip file '$zipFile'") alertTitle.value = ctx.getString(R.string.error) - alertMessage.value = String.format(ctx.getString(R.string.backup_failed), - Utils.fileNameOfUri(ctx, downloadsOutputUri!!)) + alertMessage.value = String.format( + ctx.getString(R.string.backup_failed), + Utils.fileNameOfUri(ctx, downloadsOutputUri!!) + ) showAlert.value = true downloadsOutputUri = null return @@ -2804,23 +2695,29 @@ private fun backup(ctx: Context, password: String) { if (content == null) { Log.w(TAG, "Failed to read zip file '$zipFile'") alertTitle.value = ctx.getString(R.string.error) - alertMessage.value = String.format(ctx.getString(R.string.backup_failed), - Utils.fileNameOfUri(ctx, downloadsOutputUri!!)) + alertMessage.value = String.format( + ctx.getString(R.string.backup_failed), + Utils.fileNameOfUri(ctx, downloadsOutputUri!!) + ) showAlert.value = true downloadsOutputUri = null return } if (!Utils.encryptToUri(ctx, downloadsOutputUri!!, content, password)) { alertTitle.value = ctx.getString(R.string.error) - alertMessage.value = String.format(ctx.getString(R.string.backup_failed), - Utils.fileNameOfUri(ctx, downloadsOutputUri!!)) + alertMessage.value = String.format( + ctx.getString(R.string.backup_failed), + Utils.fileNameOfUri(ctx, downloadsOutputUri!!) + ) showAlert.value = true downloadsOutputUri = null return } alertTitle.value = ctx.getString(R.string.info) - alertMessage.value = String.format(ctx.getString(R.string.backed_up), - Utils.fileNameOfUri(ctx, downloadsOutputUri!!)) + alertMessage.value = String.format( + ctx.getString(R.string.backed_up), + Utils.fileNameOfUri(ctx, downloadsOutputUri!!) + ) showAlert.value = true Utils.deleteFile(File(zipFilePath)) downloadsInputUri = null @@ -2832,8 +2729,10 @@ private fun restore(ctx: Context, password: String, onRestartApp: () -> Unit) { val zipData = Utils.decryptFromUri(ctx, downloadsInputUri!!, password) if (zipData == null) { alertTitle.value = ctx.getString(R.string.error) - alertMessage.value = String.format(ctx.getString(R.string.restore_failed), - Utils.fileNameOfUri(ctx, downloadsInputUri!!)) + alertMessage.value = String.format( + ctx.getString(R.string.restore_failed), + Utils.fileNameOfUri(ctx, downloadsInputUri!!) + ) showAlert.value = true downloadsInputUri = null return @@ -2841,8 +2740,10 @@ private fun restore(ctx: Context, password: String, onRestartApp: () -> Unit) { if (!Utils.putFileContents(zipFilePath, zipData)) { Log.w(TAG, "Failed to write zip file '$zipFile'") alertTitle.value = ctx.getString(R.string.error) - alertMessage.value = String.format(ctx.getString(R.string.restore_failed), - Utils.fileNameOfUri(ctx, downloadsInputUri!!)) + alertMessage.value = String.format( + ctx.getString(R.string.restore_failed), + Utils.fileNameOfUri(ctx, downloadsInputUri!!) + ) showAlert.value = true downloadsInputUri = null return @@ -2871,9 +2772,7 @@ private fun restore(ctx: Context, password: String, onRestartApp: () -> Unit) { dialogTitle.value = ctx.getString(R.string.info) dialogMessage.value = ctx.getString(R.string.restored) firstText.value = ctx.getString(R.string.cancel) - onFirstClicked.value = { - showDialog.value = false - } + onFirstClicked.value = { showDialog.value = false } secondText.value = "" lastText.value = ctx.getString(R.string.restart) onLastClicked.value = { diff --git a/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt index 0fc0e69a..f2e0f282 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/SettingsScreen.kt @@ -25,14 +25,15 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -155,25 +156,14 @@ private fun SettingsScreen( } Scaffold( - modifier = Modifier - .fillMaxSize() - .imePadding(), + modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(), containerColor = MaterialTheme.colorScheme.background, topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .padding( - top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() - ) - ) { + Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { + Spacer(Modifier.statusBarsPadding()) TopAppBar( title = { - Text( - text = stringResource(R.string.configuration), - fontWeight = FontWeight.Bold - ) + Text(text = stringResource(R.string.configuration), fontWeight = FontWeight.Bold) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.primary, @@ -185,17 +175,14 @@ private fun SettingsScreen( IconButton(onClick = onBack) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = null, + contentDescription = "Back", ) } }, windowInsets = WindowInsets(0, 0, 0, 0), actions = { IconButton(onClick = checkOnClick) { - Icon( - imageVector = Icons.Filled.Check, - contentDescription = "Check" - ) + Icon(imageVector = Icons.Filled.Check, contentDescription = "Check") } }, ) @@ -209,13 +196,9 @@ private fun SettingsScreen( title = stringResource(R.string.restart_request), message = stringResource(R.string.config_restart), firstButtonText = stringResource(R.string.cancel), - onFirstClicked = { - navController.navigateUp() - }, + onFirstClicked = { navController.navigateUp() }, lastButtonText = stringResource(R.string.restart), - onLastClicked = { - onRestartApp() - }, + onLastClicked = { onRestartApp() }, ) } @@ -304,9 +287,7 @@ private fun SettingsContent( val startAutomaticallyHelp = stringResource(R.string.start_automatically_help) val appearOnTopPermissionMessage = stringResource(R.string.appear_on_top_permission) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -355,9 +336,7 @@ private fun SettingsContent( val addressFamilyTitle = stringResource(R.string.address_family) val addressFamilyHelp = stringResource(R.string.address_family_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -372,9 +351,7 @@ private fun SettingsContent( }, fontSize = 18.sp ) - val isDropDownExpanded = remember { - mutableStateOf(false) - } + val isDropDownExpanded = remember { mutableStateOf(false) } val familyNames = listOf("--", "IPv4", "IPv6") val familyValues = listOf("", "ipv4", "ipv6") val itemPosition = remember { mutableIntStateOf(familyValues.indexOf(addressFamily)) } @@ -382,9 +359,7 @@ private fun SettingsContent( Row( horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.clickable { - isDropDownExpanded.value = true - } + modifier = Modifier.clickable { isDropDownExpanded.value = true } ) { Text(text = familyNames[itemPosition.intValue]) Icon( @@ -396,9 +371,8 @@ private fun SettingsContent( } DropdownMenu( expanded = isDropDownExpanded.value, - onDismissRequest = { - isDropDownExpanded.value = false - }) { + onDismissRequest = { isDropDownExpanded.value = false } + ) { familyNames.forEachIndexed { index, family -> DropdownMenuItem( text = { Text(text = family) }, @@ -420,9 +394,7 @@ private fun SettingsContent( val listenAddressTitle = stringResource(R.string.listen_address) val listenAddressHelp = stringResource(R.string.listen_address_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start, ) { @@ -430,9 +402,7 @@ private fun SettingsContent( OutlinedTextField( value = listenAddress, placeholder = { Text(stringResource(R.string._0_0_0_0_5060)) }, - onValueChange = { - viewModel.listenAddress.value = it - }, + onValueChange = { viewModel.listenAddress.value = it }, modifier = Modifier .fillMaxWidth() .clickable { @@ -453,18 +423,14 @@ private fun SettingsContent( val transportProtocolsTitle = stringResource(R.string.transport_protocols) val transportProtocolsHelp = stringResource(R.string.transport_protocols_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { val transportProtocols by viewModel.transportProtocols.collectAsState() OutlinedTextField( value = transportProtocols, - onValueChange = { - viewModel.transportProtocols.value = it - }, + onValueChange = { viewModel.transportProtocols.value = it }, modifier = Modifier .fillMaxWidth() .clickable { @@ -484,18 +450,14 @@ private fun SettingsContent( val dnsServersTitle = stringResource(R.string.dns_servers) val dnsServersHelp = stringResource(R.string.dns_servers_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { val dnsServers by viewModel.dnsServers.collectAsState() OutlinedTextField( value = dnsServers, - onValueChange = { - viewModel.dnsServers.value = it - }, + onValueChange = { viewModel.dnsServers.value = it }, modifier = Modifier .fillMaxWidth() .clickable { @@ -555,9 +517,7 @@ private fun SettingsContent( val showAlertDialog = remember { mutableStateOf(false) } Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -635,9 +595,7 @@ private fun SettingsContent( val verifyServerTitle = stringResource(R.string.verify_server) val verifyServerHelp = stringResource(R.string.verify_server_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -697,9 +655,7 @@ private fun SettingsContent( } Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -719,7 +675,7 @@ private fun SettingsContent( checked = caFile, onCheckedChange = { viewModel.caFile.value = it - if (it) { + if (it) if (VERSION.SDK_INT < 29) { viewModel.caFile.value = false val permission = Manifest.permission.READ_EXTERNAL_STORAGE @@ -754,7 +710,6 @@ private fun SettingsContent( } else Utils.selectInputFile(caCertsRequest) - } else { Utils.deleteFile(File(BaresipService.filesPath + "/ca_certs.crt")) restart = true @@ -769,9 +724,7 @@ private fun SettingsContent( val userAgentTitle = stringResource(R.string.user_agent) val userAgentHelp = stringResource(R.string.user_agent_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -799,9 +752,7 @@ private fun SettingsContent( val uniqueContactUriTitle = stringResource(R.string.unique_contact_uri) val uniqueContactUriHelp = stringResource(R.string.unique_contact_uri_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -826,17 +777,13 @@ private fun SettingsContent( @Composable fun AudioSettings(navController: NavController) { Row( - Modifier - .fillMaxWidth() - .padding(top = 12.dp), + Modifier.fillMaxWidth().padding(top = 12.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { Text( text = stringResource(R.string.audio_settings), - modifier = Modifier - .weight(1f) - .clickable { navController.navigate("audio") }, + modifier = Modifier.weight(1f).clickable { navController.navigate("audio") }, fontSize = 18.sp, fontWeight = FontWeight. Bold ) @@ -848,9 +795,7 @@ private fun SettingsContent( val batteryOptimizationsTitle = stringResource(R.string.battery_optimizations) val batteryOptimizationsHelp = stringResource(R.string.battery_optimizations_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -889,9 +834,7 @@ private fun SettingsContent( val darkThemeTitle = stringResource(R.string.dark_theme) val darkThemeHelp = stringResource(R.string.dark_theme_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -918,9 +861,7 @@ private fun SettingsContent( val dynamicColorsTitle = stringResource(R.string.dynamic_colors) val dynamicColorsHelp = stringResource(R.string.dynamic_colors_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -947,9 +888,7 @@ private fun SettingsContent( val colorBlindTitle = stringResource(R.string.colorblind) val colorBlindHelp = stringResource(R.string.colorblind_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -976,9 +915,7 @@ private fun SettingsContent( val proximitySensingTitle = stringResource(R.string.proximity_sensing) val proximitySensingHelp = stringResource(R.string.proximity_sensing_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -1007,9 +944,7 @@ private fun SettingsContent( val defaultPhoneAppHelp = stringResource(R.string.default_phone_app_help) val dialerRoleNotAvailableMessage = stringResource(R.string.dialer_role_not_available) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -1041,14 +976,13 @@ private fun SettingsContent( val isHeld = roleManager.isRoleHeld(RoleManager.ROLE_DIALER) viewModel.defaultDialer.value = isHeld if (isHeld) { - if (Utils.checkPermissions(ctx, arrayOf(Manifest.permission.READ_PHONE_NUMBERS))) { + if (Utils.checkPermissions(ctx, arrayOf(Manifest.permission.READ_PHONE_NUMBERS))) BaresipService.instance?.addMobileUserAgent() - } else { + else requestPermissionLauncher.launch(Manifest.permission.READ_PHONE_NUMBERS) - } - } else { - BaresipService.instance?.addMobileUserAgent() } + else + BaresipService.instance?.addMobileUserAgent() } Switch( checked = defaultDialer, @@ -1063,7 +997,8 @@ private fun SettingsContent( else if (!roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) dialerRoleRequest.launch(roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER)) - } else { + } + else { viewModel.defaultMessaging.value = false try { dialerRoleRequest.launch(Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS")) @@ -1083,9 +1018,7 @@ private fun SettingsContent( val defaultMessagingAppHelp = stringResource(R.string.default_messaging_app_help) val messagingRoleNotAvailableMessage = stringResource(R.string.messaging_role_not_available) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -1122,13 +1055,13 @@ private fun SettingsContent( else if (!roleManager.isRoleHeld(RoleManager.ROLE_SMS)) messagingRoleRequest.launch(roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS)) - } else { + } + else try { messagingRoleRequest.launch(Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS")) } catch (e: ActivityNotFoundException) { Log.e(TAG, "ActivityNotFound exception: ${e.message}") } - } } ) } @@ -1139,9 +1072,7 @@ private fun SettingsContent( val debugTitle = stringResource(R.string.debug) val debugHelp = stringResource(R.string.debug_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -1168,9 +1099,7 @@ private fun SettingsContent( val sipTraceTitle = stringResource(R.string.sip_trace) val sipTraceHelp = stringResource(R.string.sip_trace_help) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -1199,9 +1128,7 @@ private fun SettingsContent( val resetConfigAlert = stringResource(R.string.reset_config_alert) val resetButtonText = stringResource(R.string.reset) Row( - Modifier - .fillMaxWidth() - .padding(end = 10.dp), + Modifier.fillMaxWidth().padding(end = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -1222,9 +1149,7 @@ private fun SettingsContent( dialogTitle.value = confirmationText dialogMessage.value = resetConfigAlert firstButtonText.value = cancelButtonText - onFirstClicked.value = { - reset = false - } + onFirstClicked.value = { reset = false } lastButtonText.value = resetButtonText onLastClicked.value = { Config.reset() @@ -1281,10 +1206,7 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean { val noticeTitle = ctx.getString(R.string.notice) if ((Config.variable("auto_start") == "yes") != viewModel.autoStart.value) { - Config.replaceVariable( - "auto_start", - if (viewModel.autoStart.value) "yes" else "no" - ) + Config.replaceVariable("auto_start", if (viewModel.autoStart.value) "yes" else "no") save = true } @@ -1344,7 +1266,8 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean { showAlert.value = true return false } - } else { + } + else { Config.replaceVariable("dyn_dns", "yes") Config.updateDnsServers(BaresipService.dnsServers) } @@ -1353,8 +1276,7 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean { } if ((Config.variable("sip_verify_server") == "yes") != viewModel.verifyServer.value) { - Config.replaceVariable("sip_verify_server", - if (viewModel.verifyServer.value) "yes" else "no") + Config.replaceVariable("sip_verify_server", if (viewModel.verifyServer.value) "yes" else "no") Api.config_verify_server_set(viewModel.verifyServer.value) save = true } @@ -1377,8 +1299,7 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean { } if ((Config.variable("sip_cuser_random") == "yes") != viewModel.uniqueContactUri.value) { - Config.replaceVariable("sip_cuser_random", - if (viewModel.uniqueContactUri.value) "yes" else "no") + Config.replaceVariable("sip_cuser_random", if (viewModel.uniqueContactUri.value) "yes" else "no") save = true restart = true } @@ -1392,25 +1313,20 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean { Preferences(ctx).displayTheme = newDisplayTheme BaresipService.darkTheme.value = darkTheme AppCompatDelegate.setDefaultNightMode(newDisplayTheme) - Config.replaceVariable("dark_theme", - if (darkTheme) "yes" else "no") + Config.replaceVariable("dark_theme", if (darkTheme) "yes" else "no") save = true } val dynamicColors = viewModel.dynamicColors.value if (BaresipService.dynamicColors.value != dynamicColors) { BaresipService.dynamicColors.value = dynamicColors - Config.replaceVariable("dynamic_colors", - if (dynamicColors) "yes" else "no") + Config.replaceVariable("dynamic_colors", if (dynamicColors) "yes" else "no") save = true } val colorblind = viewModel.colorblind.value if ((Config.variable("colorblind") == "yes") != colorblind) { - Config.replaceVariable( - "colorblind", - if (colorblind) "yes" else "no" - ) + Config.replaceVariable("colorblind", if (colorblind) "yes" else "no") BaresipService.colorblind = colorblind UserAgent.updateColorblindStatus() val baresipService = Intent(ctx, BaresipService::class.java) @@ -1421,10 +1337,7 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean { val proximitySensing = viewModel.proximitySensing.value if ((Config.variable("proximity_sensing") == "yes") != proximitySensing) { - Config.replaceVariable( - "proximity_sensing", - if (proximitySensing) "yes" else "no" - ) + Config.replaceVariable("proximity_sensing", if (proximitySensing) "yes" else "no") BaresipService.proximitySensing = proximitySensing save = true } @@ -1457,9 +1370,9 @@ private fun addMissingPorts(addressList: String): String { if (addressList == "") return "" var result = "" for (addr in addressList.split(",")) - result = if (Utils.checkIpPort(addr)) { + result = if (Utils.checkIpPort(addr)) "$result,$addr" - } else { + else { if (Utils.checkIpV4(addr)) "$result,$addr:53" else