mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-04-28 22:16:22 +00:00
Improved startup reactivity
This commit is contained in:
parent
f6545f5641
commit
26e30c6060
4 changed files with 422 additions and 406 deletions
|
|
@ -25,6 +25,7 @@ import android.os.Bundle
|
||||||
import android.provider.ContactsContract
|
import android.provider.ContactsContract
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.loader.app.LoaderManager
|
import androidx.loader.app.LoaderManager
|
||||||
import androidx.loader.content.CursorLoader
|
import androidx.loader.content.CursorLoader
|
||||||
import androidx.loader.content.Loader
|
import androidx.loader.content.Loader
|
||||||
|
|
@ -56,6 +57,8 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
private const val MIN_INTERVAL_TO_WAIT_BEFORE_REFRESH = 300000L // 5 minutes
|
private const val MIN_INTERVAL_TO_WAIT_BEFORE_REFRESH = 300000L // 5 minutes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val friends = HashMap<String, Friend>()
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
|
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
|
||||||
val mimeType = ContactsContract.Data.MIMETYPE
|
val mimeType = ContactsContract.Data.MIMETYPE
|
||||||
|
|
@ -92,322 +95,8 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
}
|
}
|
||||||
Log.i("$TAG Load finished, found ${cursor.count} entries in cursor")
|
Log.i("$TAG Load finished, found ${cursor.count} entries in cursor")
|
||||||
|
|
||||||
coreContext.postOnCoreThread { core ->
|
coreContext.postOnCoreThread {
|
||||||
val state = coreContext.core.globalState
|
parseFriends(cursor)
|
||||||
if (state == GlobalState.Shutdown || state == GlobalState.Off) {
|
|
||||||
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
|
||||||
return@postOnCoreThread
|
|
||||||
}
|
|
||||||
|
|
||||||
val friends = HashMap<String, Friend>()
|
|
||||||
try {
|
|
||||||
// Cursor can be null now that we are on a different dispatcher according to Crashlytics
|
|
||||||
val friendsPhoneNumbers = arrayListOf<String>()
|
|
||||||
val friendsAddresses = arrayListOf<Address>()
|
|
||||||
var previousId = ""
|
|
||||||
while (cursor != null && !cursor.isClosed && cursor.moveToNext()) {
|
|
||||||
try {
|
|
||||||
val id: String =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(ContactsContract.Data.CONTACT_ID)
|
|
||||||
)
|
|
||||||
val mime: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (previousId.isEmpty() || previousId != id) {
|
|
||||||
friendsPhoneNumbers.clear()
|
|
||||||
friendsAddresses.clear()
|
|
||||||
previousId = id
|
|
||||||
}
|
|
||||||
|
|
||||||
val friend = friends[id] ?: core.createFriend()
|
|
||||||
friend.refKey = id
|
|
||||||
if (friend.name.isNullOrEmpty()) {
|
|
||||||
val displayName: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.Data.DISPLAY_NAME_PRIMARY
|
|
||||||
)
|
|
||||||
)
|
|
||||||
friend.name = displayName
|
|
||||||
|
|
||||||
val uri = friend.getNativeContactPictureUri()
|
|
||||||
if (uri != null) {
|
|
||||||
friend.photo = uri.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
val starred =
|
|
||||||
cursor.getInt(
|
|
||||||
cursor.getColumnIndexOrThrow(ContactsContract.Contacts.STARRED)
|
|
||||||
) == 1
|
|
||||||
friend.starred = starred
|
|
||||||
|
|
||||||
val lookupKey =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.Contacts.LOOKUP_KEY
|
|
||||||
)
|
|
||||||
)
|
|
||||||
friend.nativeUri =
|
|
||||||
"${ContactsContract.Contacts.CONTENT_LOOKUP_URI}/$lookupKey"
|
|
||||||
|
|
||||||
friend.isSubscribesEnabled = false
|
|
||||||
// Disable peer to peer short term presence
|
|
||||||
friend.incSubscribePolicy = SubscribePolicy.SPDeny
|
|
||||||
}
|
|
||||||
|
|
||||||
when (mime) {
|
|
||||||
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
|
|
||||||
val data1: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Phone.NUMBER
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val data2: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Phone.TYPE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val data3: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Phone.LABEL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val data4: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val label =
|
|
||||||
PhoneNumberUtils.addressBookLabelTypeToVcardParamString(
|
|
||||||
data2?.toInt()
|
|
||||||
?: ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM,
|
|
||||||
data3
|
|
||||||
)
|
|
||||||
|
|
||||||
val number =
|
|
||||||
if (data1.isNullOrEmpty() ||
|
|
||||||
!Patterns.PHONE.matcher(data1).matches()
|
|
||||||
) {
|
|
||||||
data4 ?: data1
|
|
||||||
} else {
|
|
||||||
data1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number != null) {
|
|
||||||
if (
|
|
||||||
friendsPhoneNumbers.find {
|
|
||||||
PhoneNumberUtils.arePhoneNumberWeakEqual(
|
|
||||||
it,
|
|
||||||
number
|
|
||||||
)
|
|
||||||
} == null
|
|
||||||
) {
|
|
||||||
val phoneNumber = Factory.instance()
|
|
||||||
.createFriendPhoneNumber(number, label)
|
|
||||||
friend.addPhoneNumberWithLabel(phoneNumber)
|
|
||||||
friendsPhoneNumbers.add(number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> {
|
|
||||||
val sipAddress: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (sipAddress != null) {
|
|
||||||
val address = core.interpretUrl(sipAddress, false)
|
|
||||||
if (address != null &&
|
|
||||||
friendsAddresses.find {
|
|
||||||
it.weakEqual(address)
|
|
||||||
} == null
|
|
||||||
) {
|
|
||||||
friend.addAddress(address)
|
|
||||||
friendsAddresses.add(address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> {
|
|
||||||
val organization: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Organization.COMPANY
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (organization != null) {
|
|
||||||
friend.organization = organization
|
|
||||||
}
|
|
||||||
|
|
||||||
val job: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Organization.TITLE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (job != null) {
|
|
||||||
friend.jobTitle = job
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
|
|
||||||
val vCard = friend.vcard
|
|
||||||
if (vCard != null) {
|
|
||||||
val givenName: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (!givenName.isNullOrEmpty()) {
|
|
||||||
vCard.givenName = givenName
|
|
||||||
}
|
|
||||||
|
|
||||||
val familyName: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (!familyName.isNullOrEmpty()) {
|
|
||||||
vCard.familyName = familyName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
friends[id] = friend
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("$TAG Exception: $e")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) {
|
|
||||||
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
|
||||||
} else if (friends.isEmpty()) {
|
|
||||||
Log.w("$TAG No friend created!")
|
|
||||||
} else {
|
|
||||||
Log.i("$TAG ${friends.size} friends fetched")
|
|
||||||
|
|
||||||
val friendsList = core.getFriendListByName(NATIVE_ADDRESS_BOOK_FRIEND_LIST) ?: core.createFriendList()
|
|
||||||
if (friendsList.displayName.isNullOrEmpty()) {
|
|
||||||
Log.i(
|
|
||||||
"$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] didn't exist yet, let's create it"
|
|
||||||
)
|
|
||||||
friendsList.isDatabaseStorageEnabled = true // Store them to keep presence info available for push notifications & favorites
|
|
||||||
friendsList.type = FriendList.Type.Default
|
|
||||||
friendsList.displayName = NATIVE_ADDRESS_BOOK_FRIEND_LIST
|
|
||||||
core.addFriendList(friendsList)
|
|
||||||
|
|
||||||
for (friend in friends.values) {
|
|
||||||
friendsList.addLocalFriend(friend)
|
|
||||||
}
|
|
||||||
Log.i("$TAG Friends added")
|
|
||||||
} else {
|
|
||||||
Log.i(
|
|
||||||
"$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] found, synchronizing existing friends with new ones"
|
|
||||||
)
|
|
||||||
for (localFriend in friendsList.friends) {
|
|
||||||
val newlyFetchedFriend = friends[localFriend.refKey]
|
|
||||||
if (newlyFetchedFriend != null) {
|
|
||||||
Log.d(
|
|
||||||
"$TAG Friend [${localFriend.name}] with ref key [${localFriend.refKey}] found in newly fetched batch"
|
|
||||||
)
|
|
||||||
localFriend.edit()
|
|
||||||
localFriend.nativeUri = newlyFetchedFriend.nativeUri // Native URI isn't stored in linphone database, needs to be updated
|
|
||||||
|
|
||||||
// Update basic fields that may have changed
|
|
||||||
localFriend.name = newlyFetchedFriend.name
|
|
||||||
localFriend.organization = newlyFetchedFriend.organization
|
|
||||||
localFriend.jobTitle = newlyFetchedFriend.jobTitle
|
|
||||||
localFriend.photo = newlyFetchedFriend.photo
|
|
||||||
|
|
||||||
// Clear local friend phone numbers & add all newly fetched one ones
|
|
||||||
var atLeastAPhoneNumberWasRemoved = false
|
|
||||||
for (phoneNumber in localFriend.phoneNumbersWithLabel) {
|
|
||||||
val found = newlyFetchedFriend.phoneNumbers.find {
|
|
||||||
it == phoneNumber.phoneNumber
|
|
||||||
}
|
|
||||||
if (found == null) {
|
|
||||||
atLeastAPhoneNumberWasRemoved = true
|
|
||||||
}
|
|
||||||
localFriend.removePhoneNumberWithLabel(phoneNumber)
|
|
||||||
}
|
|
||||||
for (phoneNumber in newlyFetchedFriend.phoneNumbersWithLabel) {
|
|
||||||
localFriend.addPhoneNumberWithLabel(phoneNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If at least a phone number was removed, remove all SIP address from local friend before adding all from newly fetched one.
|
|
||||||
// If none was removed, simply add SIP addresses from fetched contact that aren't already in the local friend.
|
|
||||||
if (atLeastAPhoneNumberWasRemoved) {
|
|
||||||
Log.w(
|
|
||||||
"$TAG At least a phone number was removed from native contact [${localFriend.name}], clearing all SIP addresses from local friend before adding back the ones that still exists"
|
|
||||||
)
|
|
||||||
for (sipAddress in localFriend.addresses) {
|
|
||||||
localFriend.removeAddress(sipAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding only newly added SIP address(es) in native contact if any
|
|
||||||
for (sipAddress in newlyFetchedFriend.addresses) {
|
|
||||||
val found = localFriend.addresses.find {
|
|
||||||
it.weakEqual(sipAddress)
|
|
||||||
}
|
|
||||||
if (found == null) {
|
|
||||||
localFriend.addAddress(sipAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
localFriend.done()
|
|
||||||
} else {
|
|
||||||
Log.i(
|
|
||||||
"$TAG Friend [${localFriend.name}] with ref key [${localFriend.refKey}] not found in newly fetched batch, removing it"
|
|
||||||
)
|
|
||||||
friendsList.removeFriend(localFriend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for newly created friends since last sync
|
|
||||||
val localFriends = friendsList.friends
|
|
||||||
for (key in friends.keys) {
|
|
||||||
val found = localFriends.find {
|
|
||||||
it.refKey == key
|
|
||||||
}
|
|
||||||
if (found == null) {
|
|
||||||
val newFriend = friends[key]
|
|
||||||
if (newFriend != null) {
|
|
||||||
Log.i(
|
|
||||||
"$TAG Friend [${newFriend.name}] with ref key [${newFriend.refKey}] not found in currently stored list, adding it"
|
|
||||||
)
|
|
||||||
friendsList.addLocalFriend(newFriend)
|
|
||||||
} else {
|
|
||||||
Log.e(
|
|
||||||
"$TAG Expected to find newly fetched friend with ref key [$key] but was null!"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.i("$TAG Friends synchronized")
|
|
||||||
}
|
|
||||||
friends.clear()
|
|
||||||
|
|
||||||
friendsList.updateSubscriptions()
|
|
||||||
Log.i("$TAG Subscription(s) updated")
|
|
||||||
coreContext.contactsManager.onNativeContactsLoaded()
|
|
||||||
}
|
|
||||||
} catch (sde: StaleDataException) {
|
|
||||||
Log.e("$TAG State Data Exception: $sde")
|
|
||||||
} catch (ise: IllegalStateException) {
|
|
||||||
Log.e("$TAG Illegal State Exception: $ise")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("$TAG Exception: $e")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,4 +104,311 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
override fun onLoaderReset(loader: Loader<Cursor>) {
|
override fun onLoaderReset(loader: Loader<Cursor>) {
|
||||||
Log.i("$TAG Loader reset")
|
Log.i("$TAG Loader reset")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private fun parseFriends(cursor: Cursor) {
|
||||||
|
val core = coreContext.core
|
||||||
|
|
||||||
|
val state = core.globalState
|
||||||
|
if (state == GlobalState.Shutdown || state == GlobalState.Off) {
|
||||||
|
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val friendsPhoneNumbers = arrayListOf<String>()
|
||||||
|
val friendsAddresses = arrayListOf<Address>()
|
||||||
|
var previousId = ""
|
||||||
|
|
||||||
|
val contactIdColumn = cursor.getColumnIndexOrThrow(ContactsContract.Data.CONTACT_ID)
|
||||||
|
val mimetypeColumn = cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)
|
||||||
|
val displayNameColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.Data.DISPLAY_NAME_PRIMARY
|
||||||
|
)
|
||||||
|
val starredColumn = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.STARRED)
|
||||||
|
val lookupColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.Contacts.LOOKUP_KEY
|
||||||
|
)
|
||||||
|
val phoneNumberColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.NUMBER
|
||||||
|
)
|
||||||
|
val phoneTypeColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.TYPE
|
||||||
|
)
|
||||||
|
val phoneLabelColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.LABEL
|
||||||
|
)
|
||||||
|
val normalizedPhoneColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
|
||||||
|
)
|
||||||
|
val sipAddressColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS
|
||||||
|
)
|
||||||
|
val companyColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Organization.COMPANY
|
||||||
|
)
|
||||||
|
val jobTitleColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Organization.TITLE
|
||||||
|
)
|
||||||
|
val givenNameColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
|
||||||
|
)
|
||||||
|
val familyNameColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
|
||||||
|
)
|
||||||
|
while (!cursor.isClosed && cursor.moveToNext()) {
|
||||||
|
try {
|
||||||
|
val id: String = cursor.getString(contactIdColumn)
|
||||||
|
val mime: String? = cursor.getString(mimetypeColumn)
|
||||||
|
|
||||||
|
if (previousId.isEmpty() || previousId != id) {
|
||||||
|
friendsPhoneNumbers.clear()
|
||||||
|
friendsAddresses.clear()
|
||||||
|
previousId = id
|
||||||
|
}
|
||||||
|
|
||||||
|
val friend = friends[id] ?: core.createFriend()
|
||||||
|
friend.refKey = id
|
||||||
|
if (friend.name.isNullOrEmpty()) {
|
||||||
|
val displayName: String? = cursor.getString(displayNameColumn)
|
||||||
|
friend.name = displayName
|
||||||
|
|
||||||
|
val uri = friend.getNativeContactPictureUri()
|
||||||
|
if (uri != null) {
|
||||||
|
friend.photo = uri.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
val starred = cursor.getInt(starredColumn) == 1
|
||||||
|
friend.starred = starred
|
||||||
|
|
||||||
|
val lookupKey =
|
||||||
|
cursor.getString(lookupColumn)
|
||||||
|
friend.nativeUri =
|
||||||
|
"${ContactsContract.Contacts.CONTENT_LOOKUP_URI}/$lookupKey"
|
||||||
|
|
||||||
|
friend.isSubscribesEnabled = false
|
||||||
|
// Disable peer to peer short term presence
|
||||||
|
friend.incSubscribePolicy = SubscribePolicy.SPDeny
|
||||||
|
}
|
||||||
|
|
||||||
|
when (mime) {
|
||||||
|
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
|
||||||
|
val data1: String? = cursor.getString(phoneNumberColumn)
|
||||||
|
val data2: String? = cursor.getString(phoneTypeColumn)
|
||||||
|
val data3: String? = cursor.getString(phoneLabelColumn)
|
||||||
|
val data4: String? = cursor.getString(normalizedPhoneColumn)
|
||||||
|
|
||||||
|
val label =
|
||||||
|
PhoneNumberUtils.addressBookLabelTypeToVcardParamString(
|
||||||
|
data2?.toInt()
|
||||||
|
?: ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM,
|
||||||
|
data3
|
||||||
|
)
|
||||||
|
|
||||||
|
val number =
|
||||||
|
if (data1.isNullOrEmpty() ||
|
||||||
|
!Patterns.PHONE.matcher(data1).matches()
|
||||||
|
) {
|
||||||
|
data4 ?: data1
|
||||||
|
} else {
|
||||||
|
data1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number != null) {
|
||||||
|
if (
|
||||||
|
friendsPhoneNumbers.find {
|
||||||
|
PhoneNumberUtils.arePhoneNumberWeakEqual(
|
||||||
|
it,
|
||||||
|
number
|
||||||
|
)
|
||||||
|
} == null
|
||||||
|
) {
|
||||||
|
val phoneNumber = Factory.instance()
|
||||||
|
.createFriendPhoneNumber(number, label)
|
||||||
|
friend.addPhoneNumberWithLabel(phoneNumber)
|
||||||
|
friendsPhoneNumbers.add(number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> {
|
||||||
|
val sipAddress: String? = cursor.getString(sipAddressColumn)
|
||||||
|
if (sipAddress != null) {
|
||||||
|
val address = core.interpretUrl(sipAddress, false)
|
||||||
|
if (address != null &&
|
||||||
|
friendsAddresses.find {
|
||||||
|
it.weakEqual(address)
|
||||||
|
} == null
|
||||||
|
) {
|
||||||
|
friend.addAddress(address)
|
||||||
|
friendsAddresses.add(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> {
|
||||||
|
val organization: String? = cursor.getString(companyColumn)
|
||||||
|
if (organization != null) {
|
||||||
|
friend.organization = organization
|
||||||
|
}
|
||||||
|
|
||||||
|
val job: String? = cursor.getString(jobTitleColumn)
|
||||||
|
if (job != null) {
|
||||||
|
friend.jobTitle = job
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
|
||||||
|
val vCard = friend.vcard
|
||||||
|
if (vCard != null) {
|
||||||
|
val givenName: String? = cursor.getString(givenNameColumn)
|
||||||
|
if (!givenName.isNullOrEmpty()) {
|
||||||
|
vCard.givenName = givenName
|
||||||
|
}
|
||||||
|
|
||||||
|
val familyName: String? = cursor.getString(familyNameColumn)
|
||||||
|
if (!familyName.isNullOrEmpty()) {
|
||||||
|
vCard.familyName = familyName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friends[id] = friend
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("$TAG Exception: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i("$TAG Contacts parsed, posting another task to handle adding them (or not)")
|
||||||
|
// Re-post another task to allow other tasks on Core thread
|
||||||
|
coreContext.postOnCoreThread {
|
||||||
|
addFriendsIfNeeded()
|
||||||
|
}
|
||||||
|
} catch (sde: StaleDataException) {
|
||||||
|
Log.e("$TAG State Data Exception: $sde")
|
||||||
|
} catch (ise: IllegalStateException) {
|
||||||
|
Log.e("$TAG Illegal State Exception: $ise")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("$TAG Exception: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private fun addFriendsIfNeeded() {
|
||||||
|
val core = coreContext.core
|
||||||
|
|
||||||
|
if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) {
|
||||||
|
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
||||||
|
} else if (friends.isEmpty()) {
|
||||||
|
Log.w("$TAG No friend created!")
|
||||||
|
} else {
|
||||||
|
Log.i("$TAG ${friends.size} friends fetched")
|
||||||
|
|
||||||
|
val friendsList = core.getFriendListByName(NATIVE_ADDRESS_BOOK_FRIEND_LIST)
|
||||||
|
?: core.createFriendList()
|
||||||
|
if (friendsList.displayName.isNullOrEmpty()) {
|
||||||
|
Log.i(
|
||||||
|
"$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] didn't exist yet, let's create it"
|
||||||
|
)
|
||||||
|
friendsList.isDatabaseStorageEnabled =
|
||||||
|
true // Store them to keep presence info available for push notifications & favorites
|
||||||
|
friendsList.type = FriendList.Type.Default
|
||||||
|
friendsList.displayName = NATIVE_ADDRESS_BOOK_FRIEND_LIST
|
||||||
|
core.addFriendList(friendsList)
|
||||||
|
|
||||||
|
for (friend in friends.values) {
|
||||||
|
friendsList.addLocalFriend(friend)
|
||||||
|
}
|
||||||
|
Log.i("$TAG Friends added")
|
||||||
|
} else {
|
||||||
|
Log.i(
|
||||||
|
"$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] found, synchronizing existing friends with new ones"
|
||||||
|
)
|
||||||
|
for (localFriend in friendsList.friends) {
|
||||||
|
val newlyFetchedFriend = friends[localFriend.refKey]
|
||||||
|
if (newlyFetchedFriend != null) {
|
||||||
|
Log.d(
|
||||||
|
"$TAG Friend [${localFriend.name}] with ref key [${localFriend.refKey}] found in newly fetched batch"
|
||||||
|
)
|
||||||
|
localFriend.edit()
|
||||||
|
localFriend.nativeUri =
|
||||||
|
newlyFetchedFriend.nativeUri // Native URI isn't stored in linphone database, needs to be updated
|
||||||
|
|
||||||
|
// Update basic fields that may have changed
|
||||||
|
localFriend.name = newlyFetchedFriend.name
|
||||||
|
localFriend.organization = newlyFetchedFriend.organization
|
||||||
|
localFriend.jobTitle = newlyFetchedFriend.jobTitle
|
||||||
|
localFriend.photo = newlyFetchedFriend.photo
|
||||||
|
|
||||||
|
// Clear local friend phone numbers & add all newly fetched one ones
|
||||||
|
var atLeastAPhoneNumberWasRemoved = false
|
||||||
|
for (phoneNumber in localFriend.phoneNumbersWithLabel) {
|
||||||
|
val found = newlyFetchedFriend.phoneNumbers.find {
|
||||||
|
it == phoneNumber.phoneNumber
|
||||||
|
}
|
||||||
|
if (found == null) {
|
||||||
|
atLeastAPhoneNumberWasRemoved = true
|
||||||
|
}
|
||||||
|
localFriend.removePhoneNumberWithLabel(phoneNumber)
|
||||||
|
}
|
||||||
|
for (phoneNumber in newlyFetchedFriend.phoneNumbersWithLabel) {
|
||||||
|
localFriend.addPhoneNumberWithLabel(phoneNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If at least a phone number was removed, remove all SIP address from local friend before adding all from newly fetched one.
|
||||||
|
// If none was removed, simply add SIP addresses from fetched contact that aren't already in the local friend.
|
||||||
|
if (atLeastAPhoneNumberWasRemoved) {
|
||||||
|
Log.w(
|
||||||
|
"$TAG At least a phone number was removed from native contact [${localFriend.name}], clearing all SIP addresses from local friend before adding back the ones that still exists"
|
||||||
|
)
|
||||||
|
for (sipAddress in localFriend.addresses) {
|
||||||
|
localFriend.removeAddress(sipAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding only newly added SIP address(es) in native contact if any
|
||||||
|
for (sipAddress in newlyFetchedFriend.addresses) {
|
||||||
|
val found = localFriend.addresses.find {
|
||||||
|
it.weakEqual(sipAddress)
|
||||||
|
}
|
||||||
|
if (found == null) {
|
||||||
|
localFriend.addAddress(sipAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
localFriend.done()
|
||||||
|
} else {
|
||||||
|
Log.i(
|
||||||
|
"$TAG Friend [${localFriend.name}] with ref key [${localFriend.refKey}] not found in newly fetched batch, removing it"
|
||||||
|
)
|
||||||
|
friendsList.removeFriend(localFriend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for newly created friends since last sync
|
||||||
|
val localFriends = friendsList.friends
|
||||||
|
for (key in friends.keys) {
|
||||||
|
val found = localFriends.find {
|
||||||
|
it.refKey == key
|
||||||
|
}
|
||||||
|
if (found == null) {
|
||||||
|
val newFriend = friends[key]
|
||||||
|
if (newFriend != null) {
|
||||||
|
Log.i(
|
||||||
|
"$TAG Friend [${newFriend.name}] with ref key [${newFriend.refKey}] not found in currently stored list, adding it"
|
||||||
|
)
|
||||||
|
friendsList.addLocalFriend(newFriend)
|
||||||
|
} else {
|
||||||
|
Log.e(
|
||||||
|
"$TAG Expected to find newly fetched friend with ref key [$key] but was null!"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i("$TAG Friends synchronized")
|
||||||
|
}
|
||||||
|
friends.clear()
|
||||||
|
|
||||||
|
friendsList.updateSubscriptions()
|
||||||
|
Log.i("$TAG Subscription(s) updated")
|
||||||
|
coreContext.contactsManager.onNativeContactsLoaded()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ class ContactsManager @UiThread constructor() {
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
fun loadContacts(activity: MainActivity) {
|
fun loadContacts(activity: MainActivity) {
|
||||||
|
Log.i("$TAG Starting contacts loader")
|
||||||
val manager = LoaderManager.getInstance(activity)
|
val manager = LoaderManager.getInstance(activity)
|
||||||
manager.restartLoader(0, null, ContactLoader())
|
manager.restartLoader(0, null, ContactLoader())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import androidx.annotation.MainThread
|
import android.view.ViewTreeObserver
|
||||||
import androidx.annotation.UiThread
|
import androidx.annotation.UiThread
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.doOnAttach
|
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavDestination
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
|
|
@ -82,6 +82,20 @@ class MainActivity : GenericActivity() {
|
||||||
|
|
||||||
private var currentlyDisplayedAuthDialog: Dialog? = null
|
private var currentlyDisplayedAuthDialog: Dialog? = null
|
||||||
|
|
||||||
|
private var navigatedToDefaultFragment = false
|
||||||
|
|
||||||
|
private val destinationListener = object : NavController.OnDestinationChangedListener {
|
||||||
|
override fun onDestinationChanged(
|
||||||
|
controller: NavController,
|
||||||
|
destination: NavDestination,
|
||||||
|
arguments: Bundle?
|
||||||
|
) {
|
||||||
|
Log.i("$TAG Latest visited fragment was restored")
|
||||||
|
navigatedToDefaultFragment = true
|
||||||
|
controller.removeOnDestinationChangedListener(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
// Must be done before the setContentView
|
// Must be done before the setContentView
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
|
|
@ -165,14 +179,23 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.root.doOnAttach {
|
// Wait for latest visited fragment to be displayed before hiding the splashscreen
|
||||||
Log.i("$TAG Report UI has been fully drawn (TTFD)")
|
binding.root.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
|
||||||
try {
|
override fun onPreDraw(): Boolean {
|
||||||
reportFullyDrawn()
|
return if (navigatedToDefaultFragment) {
|
||||||
} catch (se: SecurityException) {
|
Log.i("$TAG Report UI has been fully drawn (TTFD)")
|
||||||
Log.e("$TAG Security exception when doing reportFullyDrawn(): $se")
|
try {
|
||||||
|
reportFullyDrawn()
|
||||||
|
} catch (se: SecurityException) {
|
||||||
|
Log.e("$TAG Security exception when doing reportFullyDrawn(): $se")
|
||||||
|
}
|
||||||
|
binding.root.viewTreeObserver.removeOnPreDrawListener(this)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
coreContext.bearerAuthenticationRequestedEvent.observe(this) {
|
coreContext.bearerAuthenticationRequestedEvent.observe(this) {
|
||||||
it.consume { pair ->
|
it.consume { pair ->
|
||||||
|
|
@ -224,8 +247,10 @@ class MainActivity : GenericActivity() {
|
||||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||||
super.onPostCreate(savedInstanceState)
|
super.onPostCreate(savedInstanceState)
|
||||||
|
|
||||||
|
goToLatestVisitedFragment()
|
||||||
|
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
handleIntent(intent, false)
|
handleIntent(intent)
|
||||||
} else {
|
} else {
|
||||||
// This should never happen!
|
// This should never happen!
|
||||||
Log.e("$TAG onPostCreate called without intent !")
|
Log.e("$TAG onPostCreate called without intent !")
|
||||||
|
|
@ -270,7 +295,7 @@ class MainActivity : GenericActivity() {
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
handleIntent(intent, true)
|
handleIntent(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RtlHardcoded")
|
@SuppressLint("RtlHardcoded")
|
||||||
|
|
@ -294,8 +319,71 @@ class MainActivity : GenericActivity() {
|
||||||
return findNavController(R.id.main_nav_host_fragment)
|
return findNavController(R.id.main_nav_host_fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
private fun goToLatestVisitedFragment() {
|
||||||
private fun handleIntent(intent: Intent, isNewIntent: Boolean) {
|
try {
|
||||||
|
// Prevent navigating to default fragment upon rotation (we only want to do it on first start)
|
||||||
|
if (intent.action == Intent.ACTION_MAIN && intent.type == null && intent.data == null) {
|
||||||
|
if (viewModel.mainIntentHandled) {
|
||||||
|
Log.d(
|
||||||
|
"$TAG Main intent without type nor data was already handled, do nothing"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
viewModel.mainIntentHandled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultFragmentId = getPreferences(Context.MODE_PRIVATE).getInt(
|
||||||
|
DEFAULT_FRAGMENT_KEY,
|
||||||
|
HISTORY_FRAGMENT_ID
|
||||||
|
)
|
||||||
|
Log.i(
|
||||||
|
"$TAG Trying to navigate to set default destination [$defaultFragmentId]"
|
||||||
|
)
|
||||||
|
val args = intent.extras
|
||||||
|
try {
|
||||||
|
val navOptionsBuilder = NavOptions.Builder()
|
||||||
|
navOptionsBuilder.setPopUpTo(R.id.historyListFragment, true)
|
||||||
|
navOptionsBuilder.setLaunchSingleTop(true)
|
||||||
|
val navOptions = navOptionsBuilder.build()
|
||||||
|
when (defaultFragmentId) {
|
||||||
|
CONTACTS_FRAGMENT_ID -> {
|
||||||
|
findNavController().addOnDestinationChangedListener(destinationListener)
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.contactsListFragment,
|
||||||
|
args,
|
||||||
|
navOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CHAT_FRAGMENT_ID -> {
|
||||||
|
findNavController().addOnDestinationChangedListener(destinationListener)
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.conversationsListFragment,
|
||||||
|
args,
|
||||||
|
navOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MEETINGS_FRAGMENT_ID -> {
|
||||||
|
findNavController().addOnDestinationChangedListener(destinationListener)
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.meetingsListFragment,
|
||||||
|
args,
|
||||||
|
navOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Log.i("$TAG Default fragment is the same as the latest visited one")
|
||||||
|
navigatedToDefaultFragment = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ise: IllegalStateException) {
|
||||||
|
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
||||||
|
}
|
||||||
|
} catch (ise: IllegalStateException) {
|
||||||
|
Log.i("$TAG Failed to handle intent: $ise")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIntent(intent: Intent) {
|
||||||
Log.i(
|
Log.i(
|
||||||
"$TAG Handling intent action [${intent.action}], type [${intent.type}] and data [${intent.data}]"
|
"$TAG Handling intent action [${intent.action}], type [${intent.type}] and data [${intent.data}]"
|
||||||
)
|
)
|
||||||
|
|
@ -326,12 +414,11 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
handleMainIntent(intent, isNewIntent)
|
handleMainIntent(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleLocusOrShortcut(id: String) {
|
private fun handleLocusOrShortcut(id: String) {
|
||||||
Log.i("$TAG Found locus ID [$id]")
|
Log.i("$TAG Found locus ID [$id]")
|
||||||
val pair = LinphoneUtils.getLocalAndPeerSipUrisFromChatRoomId(id)
|
val pair = LinphoneUtils.getLocalAndPeerSipUrisFromChatRoomId(id)
|
||||||
|
|
@ -345,8 +432,7 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
private fun handleMainIntent(intent: Intent) {
|
||||||
private fun handleMainIntent(intent: Intent, isNewIntent: Boolean) {
|
|
||||||
coreContext.postOnCoreThread { core ->
|
coreContext.postOnCoreThread { core ->
|
||||||
if (corePreferences.firstLaunch) {
|
if (corePreferences.firstLaunch) {
|
||||||
Log.i("$TAG First time Linphone 6.0 has been started, showing Welcome activity")
|
Log.i("$TAG First time Linphone 6.0 has been started, showing Welcome activity")
|
||||||
|
|
@ -361,18 +447,6 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
coreContext.postOnMainThread {
|
coreContext.postOnMainThread {
|
||||||
// Prevent navigating to default fragment upon rotation (we only want to do it on first start)
|
|
||||||
if (intent.action == Intent.ACTION_MAIN && intent.type == null && intent.data == null && !isNewIntent) {
|
|
||||||
if (viewModel.mainIntentHandled) {
|
|
||||||
Log.d(
|
|
||||||
"$TAG Main intent without type nor data was already handled, do nothing"
|
|
||||||
)
|
|
||||||
return@postOnMainThread
|
|
||||||
} else {
|
|
||||||
viewModel.mainIntentHandled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intent.hasExtra("Chat")) {
|
if (intent.hasExtra("Chat")) {
|
||||||
Log.i("$TAG Intent has [Chat] extra")
|
Log.i("$TAG Intent has [Chat] extra")
|
||||||
try {
|
try {
|
||||||
|
|
@ -404,64 +478,12 @@ class MainActivity : GenericActivity() {
|
||||||
} catch (ise: IllegalStateException) {
|
} catch (ise: IllegalStateException) {
|
||||||
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
||||||
}
|
}
|
||||||
} else if (!isNewIntent) {
|
|
||||||
try {
|
|
||||||
val defaultFragmentId = getPreferences(Context.MODE_PRIVATE).getInt(
|
|
||||||
DEFAULT_FRAGMENT_KEY,
|
|
||||||
HISTORY_FRAGMENT_ID
|
|
||||||
)
|
|
||||||
Log.i(
|
|
||||||
"$TAG Trying to navigate to set default destination [$defaultFragmentId]"
|
|
||||||
)
|
|
||||||
val args = intent.extras
|
|
||||||
try {
|
|
||||||
val navOptionsBuilder = NavOptions.Builder()
|
|
||||||
navOptionsBuilder.setPopUpTo(R.id.historyListFragment, true)
|
|
||||||
navOptionsBuilder.setLaunchSingleTop(true)
|
|
||||||
val navOptions = navOptionsBuilder.build()
|
|
||||||
when (defaultFragmentId) {
|
|
||||||
HISTORY_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.historyListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
CONTACTS_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.contactsListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
CHAT_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.conversationsListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MEETINGS_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.meetingsListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ise: IllegalStateException) {
|
|
||||||
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
|
||||||
}
|
|
||||||
} catch (ise: IllegalStateException) {
|
|
||||||
Log.i("$TAG Failed to handle intent: $ise")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleSendIntent(intent: Intent, multiple: Boolean) {
|
private fun handleSendIntent(intent: Intent, multiple: Boolean) {
|
||||||
val parcelablesUri = arrayListOf<Uri>()
|
val parcelablesUri = arrayListOf<Uri>()
|
||||||
|
|
||||||
|
|
@ -550,7 +572,6 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun parseShortcutIfAny(intent: Intent): Pair<String, String>? {
|
private fun parseShortcutIfAny(intent: Intent): Pair<String, String>? {
|
||||||
val shortcutId = intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
|
val shortcutId = intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
|
||||||
if (shortcutId != null) {
|
if (shortcutId != null) {
|
||||||
|
|
@ -562,7 +583,6 @@ class MainActivity : GenericActivity() {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleCallIntent(intent: Intent) {
|
private fun handleCallIntent(intent: Intent) {
|
||||||
val uri = intent.data?.toString()
|
val uri = intent.data?.toString()
|
||||||
if (uri.isNullOrEmpty()) {
|
if (uri.isNullOrEmpty()) {
|
||||||
|
|
@ -595,7 +615,6 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleConfigIntent(uri: String) {
|
private fun handleConfigIntent(uri: String) {
|
||||||
val remoteConfigUri = uri.substring("linphone-config:".length)
|
val remoteConfigUri = uri.substring("linphone-config:".length)
|
||||||
val url = when {
|
val url = when {
|
||||||
|
|
|
||||||
|
|
@ -699,6 +699,12 @@ class MessageModel @WorkerThread constructor(
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun startVoiceRecordPlayer() {
|
private fun startVoiceRecordPlayer() {
|
||||||
|
if (voiceRecordAudioFocusRequest == null) {
|
||||||
|
voiceRecordAudioFocusRequest = AudioUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
||||||
|
coreContext.context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (isPlayerClosed()) {
|
if (isPlayerClosed()) {
|
||||||
Log.w("$TAG Player closed, let's open it first")
|
Log.w("$TAG Player closed, let's open it first")
|
||||||
initVoiceRecordPlayer()
|
initVoiceRecordPlayer()
|
||||||
|
|
@ -712,12 +718,6 @@ class MessageModel @WorkerThread constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (voiceRecordAudioFocusRequest == null) {
|
|
||||||
voiceRecordAudioFocusRequest = AudioUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
|
||||||
coreContext.context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i("$TAG Playing voice record")
|
Log.i("$TAG Playing voice record")
|
||||||
isPlayingVoiceRecord.postValue(true)
|
isPlayingVoiceRecord.postValue(true)
|
||||||
voiceRecordPlayer.start()
|
voiceRecordPlayer.start()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue