Fixed issue with contact editor since we switched to Friend as original value from addressbook will be lost in Friend causing edition/removal in native addressbook to silently fail
This commit is contained in:
parent
11f36dcb63
commit
ee40995f34
5 changed files with 103 additions and 61 deletions
|
@ -43,6 +43,7 @@ Group changes to describe their impact on the project, as follows:
|
|||
- Show service notification sooner to prevent crash if Core creation takes too long
|
||||
- Incoming call screen not being showed up to user (& screen staying off) when using app in Samsung secure folder
|
||||
- One to one chat room creation process waiting indefinitely if chat room already exists
|
||||
- Contact edition (SIP addresses & phone numbers) not working due to original value being lost in Friend parsing
|
||||
- "Blinking" in some views when presence is being received
|
||||
- Trying to keep the preferred driver (OpenSLES / AAudio) when switching device
|
||||
- Issues when storing presence in native contacts + potentially duplicated SIP addresses in contact details
|
||||
|
|
|
@ -506,13 +506,15 @@ internal fun MasterContactsFragment.clearDisplayedContact() {
|
|||
}
|
||||
|
||||
internal fun ContactEditorFragment.navigateToContact(id: String) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("id", id)
|
||||
findNavController().navigate(
|
||||
R.id.action_contactEditorFragment_to_detailContactFragment,
|
||||
bundle,
|
||||
popupTo(R.id.contactEditorFragment, true)
|
||||
)
|
||||
if (findNavController().currentDestination?.id == R.id.contactEditorFragment) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("id", id)
|
||||
findNavController().navigate(
|
||||
R.id.action_contactEditorFragment_to_detailContactFragment,
|
||||
bundle,
|
||||
popupTo(R.id.contactEditorFragment, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun DetailContactFragment.navigateToChatRoom(args: Bundle?) {
|
||||
|
|
|
@ -17,43 +17,32 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.linphone.activities.main.contact.viewmodels
|
||||
package org.linphone.activities.main.contact.data
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.media.ExifInterface
|
||||
import android.provider.ContactsContract
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.activities.main.contact.data.NumberOrAddressEditorData
|
||||
import org.linphone.R
|
||||
import org.linphone.contact.*
|
||||
import org.linphone.core.ChatRoomSecurityLevel
|
||||
import org.linphone.core.Friend
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.ImageUtils
|
||||
import org.linphone.utils.PermissionHelper
|
||||
|
||||
class ContactEditorViewModelFactory(private val friend: Friend?) :
|
||||
ViewModelProvider.NewInstanceFactory() {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ContactEditorViewModel(friend) as T
|
||||
}
|
||||
}
|
||||
|
||||
class ContactEditorViewModel(val c: Friend?) : ViewModel(), ContactDataInterface {
|
||||
class ContactEditorData(val friend: Friend?) : ContactDataInterface {
|
||||
override val contact: MutableLiveData<Friend> = MutableLiveData<Friend>()
|
||||
override val displayName: MutableLiveData<String> = MutableLiveData<String>()
|
||||
override val securityLevel: MutableLiveData<ChatRoomSecurityLevel> = MutableLiveData<ChatRoomSecurityLevel>()
|
||||
override val coroutineScope: CoroutineScope = viewModelScope
|
||||
override val coroutineScope: CoroutineScope = coreContext.coroutineScope
|
||||
|
||||
val firstName = MutableLiveData<String>()
|
||||
|
||||
|
@ -74,20 +63,20 @@ class ContactEditorViewModel(val c: Friend?) : ViewModel(), ContactDataInterface
|
|||
var syncAccountType: String? = null
|
||||
|
||||
init {
|
||||
if (c != null) {
|
||||
contact.value = c!!
|
||||
displayName.value = c.name ?: ""
|
||||
if (friend != null) {
|
||||
contact.value = friend!!
|
||||
displayName.value = friend.name ?: ""
|
||||
} else {
|
||||
displayName.value = ""
|
||||
}
|
||||
|
||||
organization.value = c?.organization ?: ""
|
||||
organization.value = friend?.organization ?: ""
|
||||
|
||||
firstName.value = ""
|
||||
lastName.value = ""
|
||||
val vCard = c?.vcard
|
||||
val refKey = friend?.refKey
|
||||
val vCard = friend?.vcard
|
||||
if (vCard?.familyName.isNullOrEmpty() && vCard?.givenName.isNullOrEmpty()) {
|
||||
val refKey = c?.refKey
|
||||
if (refKey != null) {
|
||||
Log.w("[Contact Editor] vCard first & last name not filled-in yet, doing it now")
|
||||
fetchFirstAndLastNames(refKey)
|
||||
|
@ -99,11 +88,11 @@ class ContactEditorViewModel(val c: Friend?) : ViewModel(), ContactDataInterface
|
|||
lastName.value = vCard?.familyName
|
||||
}
|
||||
|
||||
updateNumbersAndAddresses()
|
||||
updateNumbersAndAddresses(refKey)
|
||||
}
|
||||
|
||||
fun save(): Friend {
|
||||
var contact = c
|
||||
var contact = friend
|
||||
var created = false
|
||||
|
||||
if (contact == null) {
|
||||
|
@ -144,7 +133,7 @@ class ContactEditorViewModel(val c: Friend?) : ViewModel(), ContactDataInterface
|
|||
}
|
||||
for (address in addresses.value.orEmpty()) {
|
||||
val sipAddress = address.newValue.value.orEmpty()
|
||||
if (sipAddress.isEmpty()) continue
|
||||
if (sipAddress.isEmpty() || address.toRemove.value == true) continue
|
||||
|
||||
val parsed = coreContext.core.interpretUrl(sipAddress, false)
|
||||
if (parsed != null) contact.addAddress(parsed)
|
||||
|
@ -155,7 +144,7 @@ class ContactEditorViewModel(val c: Friend?) : ViewModel(), ContactDataInterface
|
|||
}
|
||||
for (phone in numbers.value.orEmpty()) {
|
||||
val phoneNumber = phone.newValue.value.orEmpty()
|
||||
if (phoneNumber.isEmpty()) continue
|
||||
if (phoneNumber.isEmpty() || phone.toRemove.value == true) continue
|
||||
|
||||
contact.addPhoneNumber(phoneNumber)
|
||||
}
|
||||
|
@ -228,20 +217,71 @@ class ContactEditorViewModel(val c: Friend?) : ViewModel(), ContactDataInterface
|
|||
numbers.value = list
|
||||
}
|
||||
|
||||
private fun updateNumbersAndAddresses() {
|
||||
private fun updateNumbersAndAddresses(contactId: String?) {
|
||||
val phoneNumbers = arrayListOf<NumberOrAddressEditorData>()
|
||||
for (number in c?.phoneNumbers.orEmpty()) {
|
||||
phoneNumbers.add(NumberOrAddressEditorData(number, false))
|
||||
val sipAddresses = arrayListOf<NumberOrAddressEditorData>()
|
||||
var fetched = false
|
||||
|
||||
if (contactId != null) {
|
||||
try {
|
||||
// Try to get real values from contact to ensure edition/removal in native address book will go well
|
||||
val cursor = coreContext.context.contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
arrayOf(
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER
|
||||
),
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?",
|
||||
arrayOf(contactId),
|
||||
null
|
||||
)
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
val linphoneMime = AppUtils.getString(R.string.linphone_address_mime_type)
|
||||
val mime: String? =
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE))
|
||||
if (mime == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) {
|
||||
val data1: String? =
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER))
|
||||
if (data1 != null) {
|
||||
phoneNumbers.add(NumberOrAddressEditorData(data1, false))
|
||||
}
|
||||
} else if (
|
||||
mime == ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE ||
|
||||
mime == linphoneMime
|
||||
) {
|
||||
val data1: String? =
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS))
|
||||
if (data1 != null) {
|
||||
sipAddresses.add(NumberOrAddressEditorData(data1, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor?.close()
|
||||
fetched = true
|
||||
} catch (e: Exception) {
|
||||
Log.e("[Contact Editor] Failed to sip addresses & phone number: $e")
|
||||
fetched = false
|
||||
}
|
||||
}
|
||||
|
||||
if (!fetched) {
|
||||
Log.w("[Contact Editor] Fall-backing to friend info (might be inaccurate and thus edition/removal might fail)")
|
||||
for (number in friend?.phoneNumbers.orEmpty()) {
|
||||
phoneNumbers.add(NumberOrAddressEditorData(number, false))
|
||||
}
|
||||
|
||||
for (address in friend?.addresses.orEmpty()) {
|
||||
sipAddresses.add(NumberOrAddressEditorData(address.asStringUriOnly(), true))
|
||||
}
|
||||
}
|
||||
|
||||
if (phoneNumbers.isEmpty()) {
|
||||
phoneNumbers.add(NumberOrAddressEditorData("", false))
|
||||
}
|
||||
numbers.value = phoneNumbers
|
||||
|
||||
val sipAddresses = arrayListOf<NumberOrAddressEditorData>()
|
||||
for (address in c?.addresses.orEmpty()) {
|
||||
sipAddresses.add(NumberOrAddressEditorData(address.asStringUriOnly(), true))
|
||||
}
|
||||
if (sipAddresses.isEmpty()) {
|
||||
sipAddresses.add(NumberOrAddressEditorData("", true))
|
||||
}
|
||||
|
@ -268,14 +308,14 @@ class ContactEditorViewModel(val c: Friend?) : ViewModel(), ContactDataInterface
|
|||
val givenName: String? =
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME))
|
||||
if (!givenName.isNullOrEmpty()) {
|
||||
c?.vcard?.givenName = givenName
|
||||
friend?.vcard?.givenName = givenName
|
||||
firstName.value = givenName!!
|
||||
}
|
||||
|
||||
val familyName: String? =
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME))
|
||||
if (!familyName.isNullOrEmpty()) {
|
||||
c?.vcard?.familyName = familyName
|
||||
friend?.vcard?.familyName = familyName
|
||||
lastName.value = familyName!!
|
||||
}
|
||||
}
|
|
@ -27,7 +27,6 @@ import android.os.Parcelable
|
|||
import android.provider.MediaStore
|
||||
import android.view.View
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -35,6 +34,7 @@ import org.linphone.LinphoneApplication.Companion.corePreferences
|
|||
import org.linphone.R
|
||||
import org.linphone.activities.GenericFragment
|
||||
import org.linphone.activities.main.MainActivity
|
||||
import org.linphone.activities.main.contact.data.ContactEditorData
|
||||
import org.linphone.activities.main.contact.data.NumberOrAddressEditorData
|
||||
import org.linphone.activities.main.contact.viewmodels.*
|
||||
import org.linphone.activities.navigateToContact
|
||||
|
@ -44,7 +44,7 @@ import org.linphone.utils.FileUtils
|
|||
import org.linphone.utils.PermissionHelper
|
||||
|
||||
class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), SyncAccountPickerFragment.SyncAccountPickedListener {
|
||||
private lateinit var viewModel: ContactEditorViewModel
|
||||
private lateinit var data: ContactEditorData
|
||||
private var temporaryPicturePath: File? = null
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.contact_editor_fragment
|
||||
|
@ -54,11 +54,10 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
|
|||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
ContactEditorViewModelFactory(sharedViewModel.selectedContact.value)
|
||||
)[ContactEditorViewModel::class.java]
|
||||
binding.viewModel = viewModel
|
||||
val contact = sharedViewModel.selectedContact.value
|
||||
// TODO: FIXME: contact can be const! Find a way to get it not-const!
|
||||
data = ContactEditorData(contact)
|
||||
binding.viewModel = data
|
||||
|
||||
useMaterialSharedAxisXForwardAnimation = sharedViewModel.isSlidingPaneSlideable.value == false
|
||||
|
||||
|
@ -67,10 +66,10 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
|
|||
}
|
||||
|
||||
binding.setSaveChangesClickListener {
|
||||
viewModel.syncAccountName = null
|
||||
viewModel.syncAccountType = null
|
||||
data.syncAccountName = null
|
||||
data.syncAccountType = null
|
||||
|
||||
if (viewModel.c == null && corePreferences.showNewContactAccountDialog) {
|
||||
if (data.friend == null && corePreferences.showNewContactAccountDialog) {
|
||||
Log.i("[Contact Editor] New contact, ask user where to store it")
|
||||
SyncAccountPickerFragment(this).show(childFragmentManager, "SyncAccountPicker")
|
||||
} else {
|
||||
|
@ -85,9 +84,9 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
|
|||
newSipUri.newValue.value = sipUri
|
||||
|
||||
val list = arrayListOf<NumberOrAddressEditorData>()
|
||||
list.addAll(viewModel.addresses.value.orEmpty())
|
||||
list.addAll(data.addresses.value.orEmpty())
|
||||
list.add(newSipUri)
|
||||
viewModel.addresses.value = list
|
||||
data.addresses.value = list
|
||||
}
|
||||
|
||||
if (!PermissionHelper.required(requireContext()).hasWriteContactsPermission()) {
|
||||
|
@ -98,8 +97,8 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
|
|||
|
||||
override fun onSyncAccountClicked(name: String?, type: String?) {
|
||||
Log.i("[Contact Editor] Using account $name / $type")
|
||||
viewModel.syncAccountName = name
|
||||
viewModel.syncAccountType = type
|
||||
data.syncAccountName = name
|
||||
data.syncAccountType = type
|
||||
saveContact()
|
||||
}
|
||||
|
||||
|
@ -122,19 +121,19 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
|
|||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
lifecycleScope.launch {
|
||||
val contactImageFilePath = FileUtils.getFilePathFromPickerIntent(data, temporaryPicturePath)
|
||||
val contactImageFilePath = FileUtils.getFilePathFromPickerIntent(intent, temporaryPicturePath)
|
||||
if (contactImageFilePath != null) {
|
||||
viewModel.setPictureFromPath(contactImageFilePath)
|
||||
data.setPictureFromPath(contactImageFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveContact() {
|
||||
val savedContact = viewModel.save()
|
||||
val savedContact = data.save()
|
||||
val id = savedContact.refKey
|
||||
if (id != null) {
|
||||
Log.i("[Contact Editor] Displaying contact $savedContact")
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
type="android.view.View.OnClickListener"/>
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.activities.main.contact.viewmodels.ContactEditorViewModel" />
|
||||
type="org.linphone.activities.main.contact.data.ContactEditorData" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
|
|
Loading…
Reference in a new issue