Added Blocking screen to account settings

Added capability to add call and message peers to blocked list
Fixed background color of bottom navigation bar on some screens
This commit is contained in:
Juha Heinanen
2026-06-21 19:54:48 +03:00
parent eb85a5ef1c
commit 09fdec99cb
16 changed files with 141 additions and 131 deletions

View File

@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.rememberScrollState
@ -56,7 +55,7 @@ fun AboutScreen(onBack: () -> Unit) {
}
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {

View File

@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.WindowInsets
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.statusBarsPadding
@ -164,7 +163,7 @@ private fun AccountScreen(
}
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
@ -604,55 +603,17 @@ private fun AccountContent(
}
@Composable
fun BlockUnknown() {
val blockUnknownTitle = stringResource(R.string.block_unknown)
val blockUnknownHelp = stringResource(R.string.block_unknown_help)
val block by viewModel.blockUnknown.collectAsState()
fun Blocking(navController: NavController, aor: String) {
Row(
Modifier.fillMaxWidth().padding(end = 10.dp),
Modifier.fillMaxWidth().padding(top = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Text(text = blockUnknownTitle,
modifier = Modifier
.weight(1f)
.clickable {
alertTitle.value = blockUnknownTitle
alertMessage.value = blockUnknownHelp
showAlert.value = true
},
fontSize = 18.sp
)
Switch(
checked = block,
onCheckedChange = { viewModel.blockUnknown.value = it }
)
}
}
@Composable
fun BlockHidden() {
val blockHiddenTitle = stringResource(R.string.block_hidden)
val blockHiddenHelp = stringResource(R.string.block_hidden_help)
val block by viewModel.blockHidden.collectAsState()
Row(
Modifier.fillMaxWidth().padding(end = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Text(text = blockHiddenTitle,
modifier = Modifier
.weight(1f)
.clickable {
alertTitle.value = blockHiddenTitle
alertMessage.value = blockHiddenHelp
showAlert.value = true
},
fontSize = 18.sp
)
Switch(
checked = block,
onCheckedChange = { viewModel.blockHidden.value = it }
Text(
text = stringResource(R.string.blocking),
modifier = Modifier.weight(1f).clickable { navController.navigate("blocking/$aor") },
fontSize = 18.sp,
fontWeight = FontWeight.Bold
)
}
}
@ -660,7 +621,7 @@ private fun AccountContent(
@Composable
fun AudioCodecs(navController: NavController, aor: String) {
Row(
Modifier.fillMaxWidth(),
Modifier.fillMaxWidth().padding(top = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
@ -1343,8 +1304,7 @@ private fun AccountContent(
CheckOrigin()
}
}
BlockUnknown()
BlockHidden()
Blocking(navController, aor)
if (!ua.account.isMobile) {
AudioCodecs(navController, aor)
MediaEnc()

View File

@ -69,7 +69,7 @@ fun NavGraphBuilder.accountsScreenRoute(navController: NavController) {
@Composable
fun AccountsScreen(navController: NavController) {
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {

View File

@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.WindowInsets
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.statusBarsPadding
@ -101,7 +100,7 @@ private fun AudioScreen(
checkOnClick: () -> Unit,
) {
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {

View File

@ -553,6 +553,7 @@ class BaresipService: Service() {
}
Blocked.restore()
BlockRule.restore()
val recordings = File(filesDir, "recordings")
@ -988,6 +989,11 @@ class BaresipService: Service() {
)
else if (ua.account.blockHidden && peerUri.contains("anonymous"))
getString(R.string.hidden_call_blocked)
else if (isBlocked(peerUri))
String.format(
getString(R.string.call_blocked),
Utils.friendlyUri(this, peerUri, ua.account)
)
else if (!Utils.checkPermissions(this, arrayOf(RECORD_AUDIO)))
getString(R.string.no_calls)
else
@ -1452,6 +1458,28 @@ class BaresipService: Service() {
val aor = ua.account.aor
if ((ua.account.blockUnknown && Contact.contactName(peerUri) == peerUri) ||
(ua.account.blockHidden && peerUri.contains("anonymous")) ||
isBlocked(peerUri)) {
Log.d(TAG, "Auto-rejecting blocked message from $peerUri")
toast(
if (ua.account.blockHidden && peerUri.contains("anonymous"))
getString(R.string.hidden_message_blocked)
else
String.format(
getString(R.string.message_blocked),
Utils.friendlyUri(this, peerUri, ua.account)
)
)
Blocked(
ua.account.aor,
peerUri,
"message",
GregorianCalendar().timeInMillis
).add()
return
}
// Check for duplicates
val lastMsg = messages.lastOrNull { m -> m.aor == aor }
if (lastMsg != null && lastMsg.timeStamp == timeStamp && lastMsg.peerUri == peerUri &&
@ -2958,6 +2986,14 @@ class BaresipService: Service() {
val calls = ArrayList<Call>()
var callHistory = ArrayList<CallHistoryNew>()
var blocked = ArrayList<Blocked>()
var blockRules = mutableListOf<BlockRule>()
fun isBlocked(uri: String): Boolean {
for (rule in blockRules) {
if (rule.matches(uri)) return true
}
return false
}
var contactsMode by mutableStateOf("baresip")
var addressFamily = ""

View File

@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.WindowInsets
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.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
@ -108,7 +107,7 @@ private fun BlockedScreen(navController: NavController, request: String, aor: St
}
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
@ -220,7 +219,7 @@ private fun BlockedContent(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Account(account)
Blocked(ctx, navController, blocked)
Blocked(ctx, navController, account, blocked)
}
}
@ -237,7 +236,12 @@ private fun Account(account: Account) {
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun Blocked(ctx: Context, navController: NavController, blocked: MutableState<List<Blocked>>) {
private fun Blocked(
ctx: Context,
navController: NavController,
acc: Account,
blocked: MutableState<List<Blocked>>
) {
val showDialog = remember { mutableStateOf(false) }
val message = remember { mutableStateOf("") }
val lastButtonText = remember { mutableStateOf("") }
@ -286,7 +290,7 @@ private fun Blocked(ctx: Context, navController: NavController, blocked: Mutable
modifier = Modifier.padding(start = 8.dp, end = 4.dp),
fontSize = 18.sp)
Text(
text = peerUri.replace("sip:", ""),
text = Utils.friendlyUri(ctx, peerUri, acc),
fontSize = 18.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis

View File

@ -22,7 +22,6 @@ 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.statusBarsPadding
import androidx.compose.foundation.layout.width
@ -94,7 +93,7 @@ fun NavGraphBuilder.callDetailsScreenRoute(navController: NavController, viewMod
private fun CallDetailsScreen(navController: NavController, callRow: CallRow) {
val detailsState = remember { callRow.details.toMutableStateList() }
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
@ -294,8 +293,8 @@ private fun startTime(detail: Details, onDelete: (Details) -> Unit): String {
Text(
text = startTimeText,
modifier = Modifier.combinedClickable(
onClick = {},
onLongClick = { showDialog.value = true }
onClick = { showDialog.value = true },
onLongClick = { }
)
)

View File

@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.WindowInsets
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.statusBarsPadding
@ -121,7 +120,7 @@ private fun CallsScreen(navController: NavController, viewModel: ViewModel, aor:
}
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
@ -417,8 +416,7 @@ private fun Calls(
peerNameWithLabel, callText
)
secondButtonText.value = ""
thirdButtonText.value = ctx.getString(R.string.copy_uri)
thirdAction.value = { Utils.copyToClipboard(ctx, peerUri) }
thirdButtonText.value = ""
lastButtonText.value = ctx.getString(R.string.delete)
lastAction.value = { removeFromHistory(callHistory, callRow) }
}
@ -433,8 +431,12 @@ private fun Calls(
val uri = Utils.sipToTel(peerUri)
navController.navigate("contact/$uri/new")
}
thirdButtonText.value = ctx.getString(R.string.copy_uri)
thirdAction.value = { Utils.copyToClipboard(ctx, peerUri)
thirdButtonText.value = ctx.getString(R.string.block)
thirdAction.value = {
if (!BlockRule.exists(peerUri)) {
BaresipService.blockRules.add(BlockRule(peerUri))
BlockRule.save()
}
}
lastButtonText.value = ctx.getString(R.string.delete)
lastAction.value = { removeFromHistory(callHistory, callRow) }

View File

@ -37,8 +37,6 @@ import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material.icons.outlined.Call
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.Email
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@ -191,7 +189,6 @@ private fun ChatScreen(
if (areMessagesLoaded)
ChatContent(
ctx,
navController,
contentPadding,
account, peerUri,
chatMessages,
@ -286,7 +283,6 @@ private fun TopAppBar(
@Composable
private fun ChatContent(
ctx: Context,
navController: NavController,
contentPadding: PaddingValues,
account: Account,
peerUri: String,
@ -299,7 +295,7 @@ private fun ChatContent(
) {
Account(ctx, account)
Spacer(modifier = Modifier.weight(1f))
Messages(ctx, navController, account, peerUri, messages, onMessageDeleted)
Messages(ctx, account, peerUri, messages, onMessageDeleted)
}
}
@ -317,7 +313,6 @@ private fun Account(ctx: Context, account: Account) {
@Composable
private fun Messages(
ctx: Context,
navController: NavController,
account: Account,
peerUri: String,
messages: List<Message>,
@ -389,21 +384,8 @@ private fun Messages(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(end = 12.dp)
) {
Button(
CustomElements.Button(
onClick = {
if (Contact.findContact(peerUri) == null) {
dialogMessage.value = String.format(
ctx.getString(R.string.long_message_question),
peerUri
)
secondButtonText.value = ctx.getString(R.string.add_contact)
secondAction.value = { navController.navigate("contact/$peerUri/new") }
lastButtonText.value = ctx.getString(R.string.delete)
lastAction.value = {
message.delete()
onMessageDeleted()
}
} else {
dialogMessage.value = ctx.getString(R.string.short_message_question)
secondButtonText.value = ""
lastButtonText.value = ctx.getString(R.string.delete)
@ -411,20 +393,19 @@ private fun Messages(
message.delete()
onMessageDeleted()
}
}
showDialog.value = true
},
onLongClick = {
},
shape = if (message.direction == MESSAGE_DOWN)
RoundedCornerShape(50.dp, 20.dp, 20.dp, 10.dp)
else
RoundedCornerShape(20.dp, 10.dp, 50.dp, 20.dp),
colors = ButtonDefaults.buttonColors(
containerColor =
color =
if (message.direction == MESSAGE_DOWN)
MaterialTheme.colorScheme.secondaryContainer
else
MaterialTheme.colorScheme.primaryContainer
),
MaterialTheme.colorScheme.primaryContainer,
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()

View File

@ -253,6 +253,8 @@ private fun Chats(
val dialogMessage = remember { mutableStateOf("") }
val secondButtonText = remember { mutableStateOf("") }
val secondAction = remember { mutableStateOf({}) }
val thirdButtonText = remember { mutableStateOf("") }
val thirdAction = remember { mutableStateOf({}) }
val lastButtonText = remember { mutableStateOf("") }
val lastAction = remember { mutableStateOf({}) }
@ -264,6 +266,8 @@ private fun Chats(
firstButtonText = stringResource(R.string.cancel),
secondButtonText = secondButtonText.value,
onSecondClicked = secondAction.value,
thirdButtonText = thirdButtonText.value,
onThirdClicked = thirdAction.value,
lastButtonText = lastButtonText.value,
onLastClicked = lastAction.value,
)
@ -344,10 +348,17 @@ private fun Chats(
ctx.getString(R.string.long_chat_question),
peerName
)
secondButtonText.value = ctx.getString(R.string.delete)
secondAction.value = { deleteMessages(uaMessages, account, message.peerUri) }
lastButtonText.value = ctx.getString(R.string.add_contact)
lastAction.value = { navController.navigate("contact/${message.peerUri}/new") }
secondButtonText.value = ctx.getString(R.string.add_contact)
secondAction.value = { navController.navigate("contact/${message.peerUri}/new") }
thirdButtonText.value = ctx.getString(R.string.block)
thirdAction.value = {
if (!BlockRule.exists(message.peerUri)) {
BaresipService.blockRules.add(BlockRule(message.peerUri))
BlockRule.save()
}
}
lastButtonText.value = ctx.getString(R.string.delete)
lastAction.value = { deleteMessages(uaMessages, account, message.peerUri) }
}
showDialog.value = true
},

View File

@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.WindowInsets
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.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
@ -118,7 +117,7 @@ private fun CodecsScreen(
}
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {

View File

@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.WindowInsets
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.statusBarsPadding
@ -275,7 +274,7 @@ private fun ContactScreen(
BackHandler(enabled = true) { onBack() }
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {

View File

@ -290,6 +290,7 @@ class MainActivity : ComponentActivity() {
callsScreenRoute(navController, viewModel)
callDetailsScreenRoute(navController, viewModel)
blockedScreenRoute(navController)
blockingScreenRoute(navController)
chatsScreenRoute(navController)
chatScreenRoute(navController, viewModel)
}

View File

@ -30,7 +30,6 @@ import androidx.compose.foundation.layout.WindowInsets
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.statusBarsPadding
@ -156,7 +155,7 @@ private fun SettingsScreen(
}
Scaffold(
modifier = Modifier.fillMaxSize().imePadding().navigationBarsPadding(),
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {

View File

@ -279,6 +279,17 @@
<string name="hidden_call_blocked">Estetty puhelu tuntemattomalta soittajalta</string>
<string name="message_blocked">Estetty viesti vieraalta lähettäjältä \`%1$s\`</string>
<string name="hidden_message_blocked">Estetty viesti tuntemattomalta lähettäjältä</string>
<!-- Blocking Activity -->
<string name="blocking">Estot</string>
<string name="blocking_list">Estolista</string>
<string name="new_blocking_rule">Uusi estosääntö</string>
<string name="blocking_rule_help">Estosääntö voi olla joko merkkijono tai
säännöllinen lauseke. Jos merkkijono löytyy soittajan tai viestin
lähettäjän URI:sta tai jos säännöllinen lauseke sopii siihen, niin
puhelu tai viesti estetään.
</string>
<string name="blocking_delete_alert">Haluatko poistaa estosäännön \'%1$s\'\?</string>
<!-- Blocked Activity -->
<string name="blocked">Estetyt</string>
<string name="blocked_calls">Estetyt puhelut</string>
@ -294,11 +305,10 @@
<string name="peer">Kumppani</string>
<string name="time">Aika</string>
<string name="calls_duration">Kesto</string>
<string name="calls_add_delete_question">Haluatko luoda kohteelle \'%1$s\' uuden yhteystiedon,
kopioida sen URI:n tai poistaa %2$s puheluhistoriasta\?
<string name="calls_add_delete_question">Haluatko lisätä URI:n \'%1$s\' yhteystietoihin tai
estolistaan tai poistaa %2$s puheluhistoriasta\?
</string>
<string name="calls_delete_question">Haluatko kopioida yhteystiedon \'%1$s\' URI:n tai poistaa
%2$s puheluhistoriasta\?</string>
<string name="calls_delete_question">Haluatko poistaa %2$s puheluhistoriasta\?</string>
<string name="disable_history">Poista käytöstä</string>
<string name="enable_history">Ota käyttöön</string>
<string name="delete_history_alert">Haluatko tyhjentää tilin \'%1$s\' puheluhistorian\?</string>
@ -326,8 +336,9 @@
<string name="today">Tänään</string>
<string name="you">Sinä</string>
<string name="new_chat_peer">Uusi viestin kohde</string>
<string name="long_chat_question">Haluatko poistaa viestiketjun tai
luoda uuden yhteystiedon \'%1$s\'\?</string>
<string name="long_chat_question">Haluatko lisätä URI:n \'%1$s\' yhteystietoihin tai estolistaan
tai poistaa tämän viestiketjun?
</string>
<string name="short_chat_question">Haluatko poistaa viestiketjun \'%1$s\'\?</string>
<string name="delete_chats_alert">Haluatko tyhjentää tilin \'%1$s\' viestihistorian\?</string>
<!-- Codecs Activity -->
@ -403,10 +414,10 @@
nimeltään \'ca_certs.crt\' ladataan Download-kansiosta.
</string>
<string name="no_read_permission">Ei ulkoisen muistin lukuoikeutta</string>
<string name="unique_contact_uri">Uniikki Contact URI</string>
<string name="unique_contact_uri_help">Jos merkitty, Contact URI:n taataan olevan uniikki.
Merkittävä, jos on useammalla tilillä on same SIP URI:n käyttäjäosa, mutta myös
suojaa tilejä hyökkäyksiltä.
<string name="unique_contact_uri">Uniikki kontakti-URI</string>
<string name="unique_contact_uri_help">Jos merkitty, kontakti-URI:n taataan olevan uniikki.
Merkittävä, jos on useammalla tilillä on sama SIP URI:n käyttäjäosa, mutta se myös
parantaa tilien suojaa hyökkäyksiltä.
</string>
<string name="audio_settings">Audio-asetukset</string>
<string name="speaker_phone">Kaiutin</string>
@ -531,6 +542,7 @@
<string name="deny">Estä</string>
<string name="add">Lisää</string>
<string name="delete">Poista</string>
<string name="block">Estä</string>
<string name="edit">Muokkaa</string>
<string name="copy_uri">Kopioi URI</string>
<string name="copied_to_clipboard">Kopioitu leikepöydälle</string>

View File

@ -271,6 +271,15 @@
<string name="hidden_call_blocked">Auto-rejected call from hidden caller</string>
<string name="message_blocked">Auto-rejected blocked message from \`%1$s\`</string>
<string name="hidden_message_blocked">Auto-rejected message from hidden sender</string>
<!-- Blocking Activity -->
<string name="blocking">Blocking</string>
<string name="blocking_list">Blocking List</string>
<string name="new_blocking_rule">New Blocking Rule</string>
<string name="blocking_rule_help">Blocking rule can be either a string or a regular expression.
If the string is included in the peer URI or regular expression matches,
the call or message will be blocked.
</string>
<string name="blocking_delete_alert">Do you want to delete blocking rule \'%1$s\'\?</string>
<!-- Blocked Activity -->
<string name="blocked">Blocked</string>
<string name="blocked_calls">Blocked Calls</string>
@ -287,10 +296,9 @@
<string name="direction">Direction</string>
<string name="time">Time</string>
<string name="calls_duration">Duration</string>
<string name="calls_add_delete_question">Do you want to add \'%1$s\' to contacts, copy its URI,
or delete this %2$s from call history\?</string>
<string name="calls_delete_question">Do you want to copy URI of \'%1$s\' or delete this %2$s
from call history\?</string>
<string name="calls_add_delete_question">Do you want to add \'%1$s\' to contacts, block it,
or delete %2$s from call history\?</string>
<string name="calls_delete_question">Do you want to delete %2$s from call history\?</string>
<string name="disable_history">Disable</string>
<string name="enable_history">Enable</string>
<string name="delete_history_alert">Do you want to delete call history of account \'%1$s\'\?</string>
@ -308,7 +316,7 @@
<string name="chat_with">Chat with %1$s</string>
<string name="new_message">New message</string>
<string name="long_message_question">Do you want to delete message or add peer \'%1$s\' to contacts\?</string>
<string name="short_message_question">Do you want to delete message\?</string>
<string name="short_message_question">Do you want to delete this message\?</string>
<string name="add_contact">Add Contact</string>
<string name="sending_failed">Sending of message failed</string>
<string name="message_failed">Failed</string>
@ -317,8 +325,8 @@
<string name="today">Today</string>
<string name="you">You</string>
<string name="new_chat_peer">New Chat Peer</string>
<string name="long_chat_question">Do you want to delete chat with peer \'%1$s\' or
add peer to contacts\?</string>
<string name="long_chat_question">Do you want to add \'%1$s\' to contacts, block it, or
delete this chat?</string>
<string name="short_chat_question">Do you want to delete chat with \'%1$s\'\?</string>
<string name="delete_chats_alert">Do you want to delete chat history of account \'%1$s\'\?</string>
<!-- Codecs Activity -->
@ -510,6 +518,7 @@
<string name="deny">Deny</string>
<string name="add">Add</string>
<string name="delete">Delete</string>
<string name="block">Block</string>
<string name="edit">Edit</string>
<string name="copy_uri">Copy URI</string>
<string name="copied_to_clipboard">Copied to clipboard</string>