Added contacts export to vcf file

Fixed crash when entering empty contacts
This commit is contained in:
Juha Heinanen
2026-05-20 13:39:45 +03:00
parent 98cab8e2bc
commit 06730b4cfa
3 changed files with 51 additions and 4 deletions

View File

@ -29,14 +29,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material3.ExperimentalMaterial3Api
@ -112,6 +111,43 @@ private fun ContactsScreen(navController: NavController) {
var expanded by remember { mutableStateOf(false) }
val both = stringResource(R.string.both)
val import = stringResource(R.string.import_contacts)
val export = stringResource(R.string.export_contacts)
val vcfExportLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.CreateDocument("text/vcard")
) { uri: android.net.Uri? ->
if (uri != null) {
try {
ctx.contentResolver.openOutputStream(uri)?.use { outputStream ->
val writer = outputStream.bufferedWriter()
for (contact in BaresipService.baresipContacts.value) {
writer.write("BEGIN:VCARD\n")
writer.write("VERSION:3.0\n")
writer.write("FN:${contact.name}\n")
val nameParts = contact.name.split(" ", limit = 2)
if (nameParts.size == 2)
writer.write("N:${nameParts[1]};${nameParts[0]};;;\n")
else
writer.write("N:;${contact.name};;;\n")
if (contact.email.isNotEmpty())
writer.write("EMAIL:${contact.email}\n")
for (u in contact.uris) {
if (u.startsWith("tel:"))
writer.write("TEL:${u.substring(4)}\n")
else if (u.startsWith("sip:"))
writer.write("X-SIP:${u.substring(4)}\n")
}
writer.write("END:VCARD\n")
}
writer.flush()
}
Toast.makeText(ctx, R.string.contact_export_success, Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
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()
@ -357,13 +393,17 @@ private fun ContactsScreen(navController: NavController) {
CustomElements.DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
items = contactNames + import,
items = contactNames + import + export,
onItemClick = { name ->
expanded = false
if (name == import) {
vcfImportLauncher.launch(arrayOf("text/vcard", "text/x-vcard"))
return@DropdownMenu
}
if (name == export) {
vcfExportLauncher.launch("baresip_contacts.vcf")
return@DropdownMenu
}
val mode = contactValues[contactNames.indexOf(name)]
val contactsPermissions = arrayOf(
Manifest.permission.READ_CONTACTS,
@ -571,7 +611,9 @@ private fun ContactsContent(
state = lazyListState,
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
items(filteredContacts, key = { it.first.id() }) { (contact, annotatedName) ->
itemsIndexed(
filteredContacts, key = { index, (contact, _) -> "${contact.id()}_$index" }
) { _, (contact, annotatedName) ->
Row(
verticalAlignment = Alignment.CenterVertically,

View File

@ -508,6 +508,8 @@
<string name="import_contacts">Tuo</string>
<string name="contact_import_success">Yhteystietojen tuonti onnistui</string>
<string name="contact_import_failure">Yhteystietojen tuonti epäonnistui</string>
<string name="contact_export_success">Yhteistietojen vienti onnistui</string>
<string name="contact_export_failure">Yhteistietojen vienti epäonnistui</string>
<!-- Generic -->
<string name="alert">Varoitus</string>

View File

@ -477,8 +477,11 @@
<string name="contact_delete_question">Do you want to delete contact \'%1$s\'\?</string>
<string name="search">Search</string>
<string name="import_contacts">Import</string>
<string name="export_contacts">Export</string>
<string name="contact_import_success">Contacts imported successfully</string>
<string name="contact_import_failure">Failed to import contacts</string>
<string name="contact_export_success">Contacts exported successfully</string>
<string name="contact_export_failure">Failed to export contacts</string>
<!-- Generic -->
<string name="alert">Alert</string>
<string name="info">Info</string>