Added contacts export to vcf file
Fixed crash when entering empty contacts
This commit is contained in:
@@ -29,14 +29,13 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.statusBars
|
import androidx.compose.foundation.layout.statusBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
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.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Add
|
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.filled.Menu
|
||||||
import androidx.compose.material.icons.outlined.Clear
|
import androidx.compose.material.icons.outlined.Clear
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -112,6 +111,43 @@ private fun ContactsScreen(navController: NavController) {
|
|||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
val both = stringResource(R.string.both)
|
val both = stringResource(R.string.both)
|
||||||
val import = stringResource(R.string.import_contacts)
|
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(
|
val vcfImportLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.OpenDocument()
|
contract = ActivityResultContracts.OpenDocument()
|
||||||
@@ -357,13 +393,17 @@ private fun ContactsScreen(navController: NavController) {
|
|||||||
CustomElements.DropdownMenu(
|
CustomElements.DropdownMenu(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onDismissRequest = { expanded = false },
|
onDismissRequest = { expanded = false },
|
||||||
items = contactNames + import,
|
items = contactNames + import + export,
|
||||||
onItemClick = { name ->
|
onItemClick = { name ->
|
||||||
expanded = false
|
expanded = false
|
||||||
if (name == import) {
|
if (name == import) {
|
||||||
vcfImportLauncher.launch(arrayOf("text/vcard", "text/x-vcard"))
|
vcfImportLauncher.launch(arrayOf("text/vcard", "text/x-vcard"))
|
||||||
return@DropdownMenu
|
return@DropdownMenu
|
||||||
}
|
}
|
||||||
|
if (name == export) {
|
||||||
|
vcfExportLauncher.launch("baresip_contacts.vcf")
|
||||||
|
return@DropdownMenu
|
||||||
|
}
|
||||||
val mode = contactValues[contactNames.indexOf(name)]
|
val mode = contactValues[contactNames.indexOf(name)]
|
||||||
val contactsPermissions = arrayOf(
|
val contactsPermissions = arrayOf(
|
||||||
Manifest.permission.READ_CONTACTS,
|
Manifest.permission.READ_CONTACTS,
|
||||||
@@ -571,7 +611,9 @@ private fun ContactsContent(
|
|||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
) {
|
) {
|
||||||
items(filteredContacts, key = { it.first.id() }) { (contact, annotatedName) ->
|
itemsIndexed(
|
||||||
|
filteredContacts, key = { index, (contact, _) -> "${contact.id()}_$index" }
|
||||||
|
) { _, (contact, annotatedName) ->
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|||||||
@@ -508,6 +508,8 @@
|
|||||||
<string name="import_contacts">Tuo</string>
|
<string name="import_contacts">Tuo</string>
|
||||||
<string name="contact_import_success">Yhteystietojen tuonti onnistui</string>
|
<string name="contact_import_success">Yhteystietojen tuonti onnistui</string>
|
||||||
<string name="contact_import_failure">Yhteystietojen tuonti epä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 -->
|
<!-- Generic -->
|
||||||
<string name="alert">Varoitus</string>
|
<string name="alert">Varoitus</string>
|
||||||
|
|||||||
@@ -477,8 +477,11 @@
|
|||||||
<string name="contact_delete_question">Do you want to delete contact \'%1$s\'\?</string>
|
<string name="contact_delete_question">Do you want to delete contact \'%1$s\'\?</string>
|
||||||
<string name="search">Search</string>
|
<string name="search">Search</string>
|
||||||
<string name="import_contacts">Import</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_success">Contacts imported successfully</string>
|
||||||
<string name="contact_import_failure">Failed to import contacts</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 -->
|
<!-- Generic -->
|
||||||
<string name="alert">Alert</string>
|
<string name="alert">Alert</string>
|
||||||
<string name="info">Info</string>
|
<string name="info">Info</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user