mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Added contact export as vCard from contacts list
This commit is contained in:
parent
855b03fb34
commit
5af3a490d4
6 changed files with 99 additions and 14 deletions
|
|
@ -93,6 +93,7 @@ class ContactFragment : GenericFragment() {
|
|||
}
|
||||
|
||||
binding.setShareClickListener {
|
||||
Log.i("$TAG Sharing friend, exporting it as vCard file first")
|
||||
viewModel.exportContactAsVCard()
|
||||
}
|
||||
|
||||
|
|
@ -173,9 +174,13 @@ class ContactFragment : GenericFragment() {
|
|||
}
|
||||
|
||||
viewModel.vCardTerminatedEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { file ->
|
||||
Log.i("$TAG Friend was exported as vCard file [${file.absolutePath}]")
|
||||
shareContact(file)
|
||||
it.consume { pair ->
|
||||
val contactName = pair.first
|
||||
val file = pair.second
|
||||
Log.i(
|
||||
"$TAG Friend [$contactName] was exported as vCard file [${file.absolutePath}], sharing it"
|
||||
)
|
||||
shareContact(contactName, file)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +208,7 @@ class ContactFragment : GenericFragment() {
|
|||
)
|
||||
}
|
||||
|
||||
private fun shareContact(file: File) {
|
||||
private fun shareContact(name: String, file: File) {
|
||||
val publicUri = FileProvider.getUriForFile(
|
||||
requireContext(),
|
||||
requireContext().getString(R.string.file_provider),
|
||||
|
|
@ -214,7 +219,7 @@ class ContactFragment : GenericFragment() {
|
|||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_STREAM, publicUri)
|
||||
putExtra(Intent.EXTRA_SUBJECT, viewModel.contact.value?.friend?.name)
|
||||
putExtra(Intent.EXTRA_SUBJECT, name)
|
||||
type = ContactsContract.Contacts.CONTENT_VCARD_TYPE
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,17 +19,21 @@
|
|||
*/
|
||||
package org.linphone.ui.main.contacts.fragment
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.ContactsContract
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.navGraphViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import java.io.File
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.core.tools.Log
|
||||
|
|
@ -115,6 +119,17 @@ class ContactsListFragment : GenericFragment() {
|
|||
Log.i("$TAG Favourites contacts list is ready with [${it.size}] items")
|
||||
}
|
||||
|
||||
listViewModel.vCardTerminatedEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { pair ->
|
||||
val contactName = pair.first
|
||||
val file = pair.second
|
||||
Log.i(
|
||||
"$TAG Friend [$contactName] was exported as vCard file [${file.absolutePath}], sharing it"
|
||||
)
|
||||
shareContact(contactName, file)
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.searchFilter.observe(viewLifecycleOwner) {
|
||||
it.consume { filter ->
|
||||
listViewModel.applyFilter(filter)
|
||||
|
|
@ -130,7 +145,7 @@ class ContactsListFragment : GenericFragment() {
|
|||
adapter.contactLongClickedEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { model ->
|
||||
val modalBottomSheet = ContactsListMenuDialogFragment(
|
||||
model.friend.starred,
|
||||
model.starred,
|
||||
{ // onDismiss
|
||||
adapter.resetSelection()
|
||||
},
|
||||
|
|
@ -147,8 +162,10 @@ class ContactsListFragment : GenericFragment() {
|
|||
}
|
||||
},
|
||||
{ // onShare
|
||||
Log.i("$TAG Sharing friend [${model.name.value}]")
|
||||
// TODO
|
||||
Log.i(
|
||||
"$TAG Sharing friend [${model.name.value}], exporting it as vCard file first"
|
||||
)
|
||||
listViewModel.exportContactAsVCard(model.friend)
|
||||
},
|
||||
{ // onDelete
|
||||
coreContext.postOnCoreThread {
|
||||
|
|
@ -168,4 +185,23 @@ class ContactsListFragment : GenericFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareContact(name: String, file: File) {
|
||||
val publicUri = FileProvider.getUriForFile(
|
||||
requireContext(),
|
||||
requireContext().getString(R.string.file_provider),
|
||||
file
|
||||
)
|
||||
Log.i("$TAG Public URI for vCard file is [$publicUri], starting intent chooser")
|
||||
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_STREAM, publicUri)
|
||||
putExtra(Intent.EXTRA_SUBJECT, name)
|
||||
type = ContactsContract.Contacts.CONTENT_VCARD_TYPE
|
||||
}
|
||||
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
startActivity(shareIntent)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ class ContactAvatarModel @WorkerThread constructor(val friend: Friend) {
|
|||
|
||||
val id = friend.refKey
|
||||
|
||||
val starred = friend.starred
|
||||
|
||||
val avatar = MutableLiveData<Uri>()
|
||||
|
||||
val initials = LinphoneUtils.getInitials(friend.name.orEmpty())
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ class ContactViewModel @UiThread constructor() : ViewModel() {
|
|||
MutableLiveData<Event<String>>()
|
||||
}
|
||||
|
||||
val vCardTerminatedEvent: MutableLiveData<Event<File>> by lazy {
|
||||
MutableLiveData<Event<File>>()
|
||||
val vCardTerminatedEvent: MutableLiveData<Event<Pair<String, File>>> by lazy {
|
||||
MutableLiveData<Event<Pair<String, File>>>()
|
||||
}
|
||||
|
||||
val displayTrustProcessDialogEvent: MutableLiveData<Event<Boolean>> by lazy {
|
||||
|
|
@ -287,11 +287,14 @@ class ContactViewModel @UiThread constructor() : ViewModel() {
|
|||
val fileName = friend.name.orEmpty().replace(" ", "_").toLowerCase(
|
||||
Locale.getDefault()
|
||||
)
|
||||
val file = FileUtils.getFileStorageCacheDir("$fileName.vcf")
|
||||
val file = FileUtils.getFileStorageCacheDir(
|
||||
"$fileName.vcf",
|
||||
overrideExisting = true
|
||||
)
|
||||
viewModelScope.launch {
|
||||
if (FileUtils.dumpStringToFile(vCard, file)) {
|
||||
Log.i("$TAG vCard string saved as file in cache folder")
|
||||
vCardTerminatedEvent.postValue(Event(file))
|
||||
vCardTerminatedEvent.postValue(Event(Pair(friend.name.orEmpty(), file)))
|
||||
} else {
|
||||
Log.e("$TAG Failed to save vCard string as file in cache folder")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ import androidx.annotation.UiThread
|
|||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import java.io.File
|
||||
import java.util.ArrayList
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.launch
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.contacts.ContactsListener
|
||||
|
|
@ -34,6 +38,8 @@ import org.linphone.core.SearchResult
|
|||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.main.contacts.model.ContactAvatarModel
|
||||
import org.linphone.ui.main.model.isInSecureMode
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.FileUtils
|
||||
|
||||
class ContactsListViewModel @UiThread constructor() : ViewModel() {
|
||||
companion object {
|
||||
|
|
@ -48,6 +54,10 @@ class ContactsListViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
val isListFiltered = MutableLiveData<Boolean>()
|
||||
|
||||
val vCardTerminatedEvent: MutableLiveData<Event<Pair<String, File>>> by lazy {
|
||||
MutableLiveData<Event<Pair<String, File>>>()
|
||||
}
|
||||
|
||||
private var currentFilter = ""
|
||||
private var previousFilter = "NotSet"
|
||||
private var limitSearchToLinphoneAccounts = true
|
||||
|
|
@ -160,6 +170,33 @@ class ContactsListViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun exportContactAsVCard(friend: Friend) {
|
||||
coreContext.postOnCoreThread {
|
||||
val vCard = friend.vcard?.asVcard4String()
|
||||
if (!vCard.isNullOrEmpty()) {
|
||||
Log.i("$TAG Friend has been successfully dumped as vCard string")
|
||||
val fileName = friend.name.orEmpty().replace(" ", "_").toLowerCase(
|
||||
Locale.getDefault()
|
||||
)
|
||||
val file = FileUtils.getFileStorageCacheDir(
|
||||
"$fileName.vcf",
|
||||
overrideExisting = true
|
||||
)
|
||||
viewModelScope.launch {
|
||||
if (FileUtils.dumpStringToFile(vCard, file)) {
|
||||
Log.i("$TAG vCard string saved as file in cache folder")
|
||||
vCardTerminatedEvent.postValue(Event(Pair(friend.name.orEmpty(), file)))
|
||||
} else {
|
||||
Log.e("$TAG Failed to save vCard string as file in cache folder")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e("$TAG Failed to dump contact as vCard string")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun applyFilter(
|
||||
filter: String,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ import org.linphone.core.tools.Log
|
|||
|
||||
class LinphoneUtils {
|
||||
companion object {
|
||||
private const val TAG = "[App Utils]"
|
||||
|
||||
@AnyThread
|
||||
fun getFirstLetter(displayName: String): String {
|
||||
return getInitials(displayName, 1)
|
||||
|
|
@ -62,7 +64,7 @@ class LinphoneUtils {
|
|||
) {
|
||||
val glyph = emoji.process(split[i])
|
||||
if (characters > 0) { // Limit initial to 1 emoji only
|
||||
Log.d("[App Utils] We limit initials to one emoji only")
|
||||
Log.d("$TAG We limit initials to one emoji only")
|
||||
initials = ""
|
||||
}
|
||||
initials += glyph
|
||||
|
|
@ -71,7 +73,7 @@ class LinphoneUtils {
|
|||
initials += split[i][0]
|
||||
}
|
||||
} catch (ise: IllegalStateException) {
|
||||
Log.e("[App Utils] Can't call hasEmojiGlyph: $ise")
|
||||
Log.e("$TAG Can't call hasEmojiGlyph: $ise")
|
||||
initials += split[i][0]
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue