Improved how presence is stored in native contact + fixed issue where write contacts permission was missing + ask for contacts permission in contacts settings

This commit is contained in:
Sylvain Berfini 2020-06-08 15:44:34 +02:00
parent d79c8f09e6
commit c15ecd54ac
5 changed files with 88 additions and 15 deletions

View file

@ -19,6 +19,7 @@
*/ */
package org.linphone.activities.main.settings.fragments package org.linphone.activities.main.settings.fragments
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -27,11 +28,14 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.activities.main.settings.viewmodels.ContactsSettingsViewModel import org.linphone.activities.main.settings.viewmodels.ContactsSettingsViewModel
import org.linphone.compatibility.Compatibility import org.linphone.compatibility.Compatibility
import org.linphone.core.tools.Log
import org.linphone.databinding.SettingsContactsFragmentBinding import org.linphone.databinding.SettingsContactsFragmentBinding
import org.linphone.utils.PermissionHelper
class ContactsSettingsFragment : Fragment() { class ContactsSettingsFragment : Fragment() {
private lateinit var binding: SettingsContactsFragmentBinding private lateinit var binding: SettingsContactsFragmentBinding
@ -69,5 +73,52 @@ class ContactsSettingsFragment : Fragment() {
} }
} }
}) })
viewModel.askWriteContactsPermissionForPresenceStorageEvent.observe(viewLifecycleOwner, Observer {
it.consume {
Log.i("[Contacts Settings] Asking for WRITE_CONTACTS permission to be able to store presence")
requestPermissions(arrayOf(android.Manifest.permission.WRITE_CONTACTS), 1)
}
})
if (!PermissionHelper.required(requireContext()).hasReadContactsPermission()) {
Log.i("[Contacts Settings] Asking for READ_CONTACTS permission")
requestPermissions(arrayOf(android.Manifest.permission.READ_CONTACTS), 0)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
when (requestCode) {
0 -> {
val granted = grantResults[0] == PackageManager.PERMISSION_GRANTED
if (granted) {
Log.i("[Contacts Settings] READ_CONTACTS permission granted")
viewModel.readContactsPermissionGranted.value = true
coreContext.contactsManager.fetchContactsAsync()
} else {
Log.w("[Contacts Settings] READ_CONTACTS permission denied")
}
}
1 -> {
val granted = grantResults[0] == PackageManager.PERMISSION_GRANTED
if (granted) {
Log.i("[Contacts Settings] WRITE_CONTACTS permission granted")
corePreferences.storePresenceInNativeContact = true
if (coreContext.core.isFriendListSubscriptionEnabled) {
Log.i("[Contacts Settings] Updating subscription")
for (list in coreContext.core.friendsLists) {
list.updateSubscriptions()
}
}
} else {
Log.w("[Contacts Settings] WRITE_CONTACTS permission denied")
}
}
}
} }
} }

View file

@ -22,8 +22,15 @@ package org.linphone.activities.main.settings.viewmodels
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import org.linphone.activities.main.settings.SettingListenerStub import org.linphone.activities.main.settings.SettingListenerStub
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.utils.PermissionHelper
class ContactsSettingsViewModel : GenericSettingsViewModel() { class ContactsSettingsViewModel : GenericSettingsViewModel() {
val askWriteContactsPermissionForPresenceStorageEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val readContactsPermissionGranted = MutableLiveData<Boolean>()
val friendListSubscribeListener = object : SettingListenerStub() { val friendListSubscribeListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
core.enableFriendListSubscription(newValue) core.enableFriendListSubscription(newValue)
@ -33,7 +40,15 @@ class ContactsSettingsViewModel : GenericSettingsViewModel() {
val nativePresenceListener = object : SettingListenerStub() { val nativePresenceListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
prefs.storePresenceInNativeContact = newValue if (newValue) {
if (PermissionHelper.get().hasWriteContactsPermission()) {
prefs.storePresenceInNativeContact = newValue
} else {
askWriteContactsPermissionForPresenceStorageEvent.value = Event(true)
}
} else {
prefs.storePresenceInNativeContact = newValue
}
} }
} }
val nativePresence = MutableLiveData<Boolean>() val nativePresence = MutableLiveData<Boolean>()
@ -55,6 +70,8 @@ class ContactsSettingsViewModel : GenericSettingsViewModel() {
val launcherShortcutsEvent = MutableLiveData<Event<Boolean>>() val launcherShortcutsEvent = MutableLiveData<Event<Boolean>>()
init { init {
readContactsPermissionGranted.value = PermissionHelper.get().hasReadContactsPermission()
friendListSubscribe.value = core.isFriendListSubscriptionEnabled friendListSubscribe.value = core.isFriendListSubscriptionEnabled
nativePresence.value = prefs.storePresenceInNativeContact nativePresence.value = prefs.storePresenceInNativeContact
showOrganization.value = prefs.displayOrganization showOrganization.value = prefs.displayOrganization

View file

@ -287,7 +287,7 @@ class ContactsManager(private val context: Context) {
listener.onContactUpdated(contact) listener.onContactUpdated(contact)
} }
if (corePreferences.storePresenceInNativeContact) { if (corePreferences.storePresenceInNativeContact && PermissionHelper.get().hasWriteContactsPermission()) {
if (contact is NativeContact) { if (contact is NativeContact) {
for (phoneNumber in contact.phoneNumbers) { for (phoneNumber in contact.phoneNumbers) {
val sipAddress = contact.getContactForPhoneNumberOrAddress(phoneNumber) val sipAddress = contact.getContactForPhoneNumberOrAddress(phoneNumber)

View file

@ -235,10 +235,11 @@ class NativeContactEditor(
sipAddress.currentValue.isEmpty() -> { sipAddress.currentValue.isEmpty() -> {
// New address to add // New address to add
addCount++ addCount++
val address = sipAddress.newValue.value.orEmpty()
if (useLinphoneSyncAccount) { if (useLinphoneSyncAccount) {
addAddress(sipAddress.newValue.value.orEmpty()) addAddress(address, address)
} else { } else {
addSipAddress(sipAddress.newValue.value.orEmpty()) addSipAddress(address)
} }
} }
sipAddress.toRemove.value == true -> { sipAddress.toRemove.value == true -> {
@ -268,7 +269,7 @@ class NativeContactEditor(
Log.i("[Native Contact Editor] Trying to add presence information to contact as ${if (useLinphoneSyncAccount) "phone number" else "SIP address"}") Log.i("[Native Contact Editor] Trying to add presence information to contact as ${if (useLinphoneSyncAccount) "phone number" else "SIP address"}")
if (useLinphoneSyncAccount) { if (useLinphoneSyncAccount) {
setPresencePhoneNumber(phoneNumber) setPresence(sipAddress, phoneNumber)
} else { } else {
setPresenceSipAddress(sipAddress) setPresenceSipAddress(sipAddress)
} }
@ -359,7 +360,7 @@ class NativeContactEditor(
addChanges(delete) addChanges(delete)
} }
private fun addAddress(address: String) { private fun addAddress(address: String, detail: String) {
val insert = ContentProviderOperation.newInsert(contactUri) val insert = ContentProviderOperation.newInsert(contactUri)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, linphoneRawId) .withValue(ContactsContract.Data.RAW_CONTACT_ID, linphoneRawId)
.withValue( .withValue(
@ -368,7 +369,7 @@ class NativeContactEditor(
) )
.withValue("data1", address) // value .withValue("data1", address) // value
.withValue("data2", AppUtils.getString(R.string.app_name)) // summary .withValue("data2", AppUtils.getString(R.string.app_name)) // summary
.withValue("data3", address) // detail .withValue("data3", detail) // detail
.build() .build()
addChanges(insert) addChanges(insert)
} }
@ -422,13 +423,13 @@ class NativeContactEditor(
addChanges(delete) addChanges(delete)
} }
private fun setPresencePhoneNumber(phoneNumber: String) { private fun setPresence(sipAddress: String, phoneNumber: String) {
val contentResolver = coreContext.context.contentResolver val contentResolver = coreContext.context.contentResolver
val cursor = contentResolver.query( val cursor = contentResolver.query(
ContactsContract.Data.CONTENT_URI, ContactsContract.Data.CONTENT_URI,
arrayOf("data1"), arrayOf("data1"),
"${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND data1 = ?", "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND data1 = ? AND data3 = ?",
arrayOf(linphoneRawId, AppUtils.getString(R.string.linphone_address_mime_type), phoneNumber), arrayOf(linphoneRawId, AppUtils.getString(R.string.linphone_address_mime_type), sipAddress, phoneNumber),
null null
) )
val count = cursor?.count ?: 0 val count = cursor?.count ?: 0
@ -436,7 +437,7 @@ class NativeContactEditor(
if (count == 0) { if (count == 0) {
Log.i("[Native Contact Editor] No existing presence information found for this phone number, let's add it") Log.i("[Native Contact Editor] No existing presence information found for this phone number, let's add it")
addAddress(phoneNumber) addAddress(sipAddress, phoneNumber)
} else { } else {
Log.i("[Native Contact Editor] There is already an entry for this, skipping") Log.i("[Native Contact Editor] There is already an entry for this, skipping")
} }

View file

@ -69,28 +69,32 @@
linphone:title="@{@string/contacts_settings_friendlist_subscribe_title}" linphone:title="@{@string/contacts_settings_friendlist_subscribe_title}"
linphone:subtitle="@{@string/contacts_settings_friendlist_subscribe_summary}" linphone:subtitle="@{@string/contacts_settings_friendlist_subscribe_summary}"
linphone:listener="@{viewModel.friendListSubscribeListener}" linphone:listener="@{viewModel.friendListSubscribeListener}"
linphone:checked="@={viewModel.friendListSubscribe}"/> linphone:checked="@={viewModel.friendListSubscribe}"
linphone:enabled="@{viewModel.readContactsPermissionGranted}"/>
<include <include
layout="@layout/settings_widget_switch" layout="@layout/settings_widget_switch"
linphone:title="@{@string/contacts_settings_native_presence_title}" linphone:title="@{@string/contacts_settings_native_presence_title}"
linphone:subtitle="@{@string/contacts_settings_native_presence_summary}" linphone:subtitle="@{@string/contacts_settings_native_presence_summary}"
linphone:listener="@{viewModel.nativePresenceListener}" linphone:listener="@{viewModel.nativePresenceListener}"
linphone:checked="@={viewModel.nativePresence}"/> linphone:checked="@={viewModel.nativePresence}"
linphone:enabled="@{viewModel.readContactsPermissionGranted &amp;&amp; viewModel.friendListSubscribe}"/>
<include <include
layout="@layout/settings_widget_switch" layout="@layout/settings_widget_switch"
linphone:title="@{@string/contacts_settings_show_organization_title}" linphone:title="@{@string/contacts_settings_show_organization_title}"
linphone:subtitle="@{@string/contacts_settings_show_organization_summary}" linphone:subtitle="@{@string/contacts_settings_show_organization_summary}"
linphone:listener="@{viewModel.showOrganizationListener}" linphone:listener="@{viewModel.showOrganizationListener}"
linphone:checked="@={viewModel.showOrganization}"/> linphone:checked="@={viewModel.showOrganization}"
linphone:enabled="@{viewModel.readContactsPermissionGranted}"/>
<include <include
layout="@layout/settings_widget_switch" layout="@layout/settings_widget_switch"
linphone:title="@{@string/contacts_settings_launcher_shortcuts_title}" linphone:title="@{@string/contacts_settings_launcher_shortcuts_title}"
linphone:subtitle="@{@string/contacts_settings_launcher_shortcuts_summary}" linphone:subtitle="@{@string/contacts_settings_launcher_shortcuts_summary}"
linphone:listener="@{viewModel.launcherShortcutsListener}" linphone:listener="@{viewModel.launcherShortcutsListener}"
linphone:checked="@={viewModel.launcherShortcuts}"/> linphone:checked="@={viewModel.launcherShortcuts}"
linphone:enabled="@{viewModel.readContactsPermissionGranted}"/>
</LinearLayout> </LinearLayout>