mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Fixes & improvements
This commit is contained in:
parent
09ca9b5351
commit
a2d038eb46
8 changed files with 515 additions and 30 deletions
|
|
@ -56,8 +56,6 @@ dependencies {
|
|||
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.1-rc01'
|
||||
implementation 'androidx.core:core-ktx:+'
|
||||
implementation 'androidx.core:core-ktx:+'
|
||||
|
||||
def nav_version = "2.6.0"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- To be able to display contacts list & match calling/called numbers -->
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
|
||||
<application
|
||||
android:name=".LinphoneApplication"
|
||||
android:allowBackup="true"
|
||||
|
|
|
|||
275
app/src/main/java/org/linphone/contacts/ContactLoader.kt
Normal file
275
app/src/main/java/org/linphone/contacts/ContactLoader.kt
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.contacts
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.database.Cursor
|
||||
import android.database.StaleDataException
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.ContactsContract
|
||||
import android.util.Patterns
|
||||
import androidx.loader.app.LoaderManager
|
||||
import androidx.loader.content.CursorLoader
|
||||
import androidx.loader.content.Loader
|
||||
import java.lang.Exception
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.core.*
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.PhoneNumberUtils
|
||||
|
||||
class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||
companion object {
|
||||
val projection = arrayOf(
|
||||
ContactsContract.Data.CONTACT_ID,
|
||||
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.Contacts.STARRED,
|
||||
ContactsContract.Contacts.LOOKUP_KEY,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE,
|
||||
ContactsContract.CommonDataKinds.Phone.LABEL,
|
||||
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
|
||||
)
|
||||
|
||||
const val linphoneMime = "vnd.android.cursor.item/vnd.org.linphone.provider.sip_address"
|
||||
}
|
||||
|
||||
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
|
||||
val mimeType = ContactsContract.Data.MIMETYPE
|
||||
val mimeSelection = "$mimeType = ? OR $mimeType = ? OR $mimeType = ?"
|
||||
|
||||
val selection = ContactsContract.Data.IN_DEFAULT_DIRECTORY + " == 1 AND ($mimeSelection)"
|
||||
val selectionArgs = arrayOf(
|
||||
linphoneMime,
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
|
||||
ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
|
||||
)
|
||||
|
||||
return CursorLoader(
|
||||
coreContext.context,
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
projection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
ContactsContract.Data.CONTACT_ID + " ASC"
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
|
||||
if (cursor == null) {
|
||||
Log.e("[Contacts Loader] Cursor is null!")
|
||||
return
|
||||
}
|
||||
Log.i("[Contacts Loader] Load finished, found ${cursor.count} entries in cursor")
|
||||
|
||||
val core = coreContext.core
|
||||
if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) {
|
||||
Log.w("[Contacts Loader] Core is being stopped or already destroyed, abort")
|
||||
return
|
||||
}
|
||||
|
||||
coreContext.postOnCoreThread { core ->
|
||||
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
|
||||
|
||||
friend.photo = Uri.withAppendedPath(
|
||||
ContentUris.withAppendedId(
|
||||
ContactsContract.Contacts.CONTENT_URI,
|
||||
id.toLong()
|
||||
),
|
||||
ContactsContract.Contacts.Photo.CONTENT_DIRECTORY
|
||||
).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"
|
||||
|
||||
// Disable short term presence
|
||||
friend.isSubscribesEnabled = false
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
linphoneMime, 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, true)
|
||||
if (address != null &&
|
||||
friendsAddresses.find {
|
||||
it.weakEqual(address)
|
||||
} == null
|
||||
) {
|
||||
friend.addAddress(address)
|
||||
friendsAddresses.add(address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
friends[id] = friend
|
||||
} catch (e: Exception) {
|
||||
Log.e("[Contacts Loader] Exception: $e")
|
||||
}
|
||||
}
|
||||
|
||||
if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) {
|
||||
Log.w("[Contacts Loader] Core is being stopped or already destroyed, abort")
|
||||
} else if (friends.isEmpty()) {
|
||||
Log.w("[Contacts Loader] No friend created!")
|
||||
} else {
|
||||
Log.i("[Contacts Loader] ${friends.size} friends created")
|
||||
|
||||
val fl = core.defaultFriendList ?: core.createFriendList()
|
||||
for (friend in fl.friends) {
|
||||
fl.removeFriend(friend)
|
||||
}
|
||||
|
||||
if (fl != core.defaultFriendList) core.addFriendList(fl)
|
||||
|
||||
val friendsList = friends.values
|
||||
for (friend in friendsList) {
|
||||
fl.addLocalFriend(friend)
|
||||
}
|
||||
friends.clear()
|
||||
Log.i("[Contacts Loader] Friends added")
|
||||
|
||||
fl.updateSubscriptions()
|
||||
Log.i("[Contacts Loader] Subscription(s) updated")
|
||||
}
|
||||
} catch (sde: StaleDataException) {
|
||||
Log.e("[Contacts Loader] State Data Exception: $sde")
|
||||
} catch (ise: IllegalStateException) {
|
||||
Log.e("[Contacts Loader] Illegal State Exception: $ise")
|
||||
} catch (e: Exception) {
|
||||
Log.e("[Contacts Loader] Exception: $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoaderReset(loader: Loader<Cursor>) {
|
||||
Log.i("[Contacts Loader] Loader reset")
|
||||
}
|
||||
}
|
||||
|
|
@ -19,21 +19,29 @@
|
|||
*/
|
||||
package org.linphone.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.loader.app.LoaderManager
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.contacts.ContactLoader
|
||||
import org.linphone.databinding.ActivityMainBinding
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val CONTACTS_PERMISSION_REQUEST = 0
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var viewModel: MainViewModel
|
||||
|
||||
|
|
@ -46,6 +54,11 @@ class MainActivity : AppCompatActivity() {
|
|||
WindowCompat.setDecorFitsSystemWindows(window, true)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
|
||||
val manager = LoaderManager.getInstance(this)
|
||||
manager.restartLoader(0, null, ContactLoader())
|
||||
}
|
||||
|
||||
while (!coreContext.isReady()) {
|
||||
Thread.sleep(20)
|
||||
}
|
||||
|
|
@ -75,6 +88,26 @@ class MainActivity : AppCompatActivity() {
|
|||
.addOnDestinationChangedListener(onNavDestinationChangedListener)
|
||||
|
||||
getNavBar()?.setupWithNavController(binding.mainNavHostFragment.findNavController())
|
||||
|
||||
if (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(
|
||||
arrayOf(Manifest.permission.READ_CONTACTS),
|
||||
CONTACTS_PERMISSION_REQUEST
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
if (requestCode == CONTACTS_PERMISSION_REQUEST && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
val manager = LoaderManager.getInstance(this)
|
||||
manager.restartLoader(0, null, ContactLoader())
|
||||
}
|
||||
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
|
||||
private fun getNavBar(): NavigationBarView? {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,27 @@ class ChatRoomData(val chatRoom: ChatRoom) {
|
|||
}
|
||||
|
||||
init {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
chatRoom.addListener(chatRoomListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCleared() {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
chatRoom.removeListener(chatRoomListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun onClicked() {
|
||||
chatRoomDataListener?.onClicked()
|
||||
}
|
||||
|
||||
fun onLongClicked(): Boolean {
|
||||
chatRoomDataListener?.onLongClicked()
|
||||
return true
|
||||
}
|
||||
|
||||
fun update() {
|
||||
if (chatRoom.hasCapability(ChatRoom.Capabilities.Basic.toInt())) {
|
||||
val remoteAddress = chatRoom.peerAddress
|
||||
val friend = chatRoom.core.findFriend(remoteAddress)
|
||||
|
|
@ -122,25 +143,6 @@ class ChatRoomData(val chatRoom: ChatRoom) {
|
|||
isSecureVerified.postValue(chatRoom.securityLevel == ChatRoom.SecurityLevel.Safe)
|
||||
isEphemeral.postValue(chatRoom.isEphemeralEnabled)
|
||||
isMuted.postValue(areNotificationsMuted())
|
||||
|
||||
coreContext.postOnCoreThread { core ->
|
||||
chatRoom.addListener(chatRoomListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCleared() {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
chatRoom.removeListener(chatRoomListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun onClicked() {
|
||||
chatRoomDataListener?.onClicked()
|
||||
}
|
||||
|
||||
fun onLongClicked(): Boolean {
|
||||
chatRoomDataListener?.onLongClicked()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun computeLastMessageImdnIcon(message: ChatMessage) {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ class ConversationsListAdapter(
|
|||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(chatRoomData: ChatRoomData) {
|
||||
with(binding) {
|
||||
chatRoomData.update()
|
||||
data = chatRoomData
|
||||
|
||||
lifecycleOwner = viewLifecycleOwner
|
||||
|
|
@ -93,7 +94,7 @@ class ConversationsListAdapter(
|
|||
|
||||
private class ConversationDiffCallback : DiffUtil.ItemCallback<ChatRoomData>() {
|
||||
override fun areItemsTheSame(oldItem: ChatRoomData, newItem: ChatRoomData): Boolean {
|
||||
return oldItem.id.compareTo(newItem.id) == 0
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ChatRoomData, newItem: ChatRoomData): Boolean {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ import org.linphone.core.ChatMessage
|
|||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.Core
|
||||
import org.linphone.core.CoreListenerStub
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
class ConversationsListViewModel : ViewModel() {
|
||||
val chatRoomsList = MutableLiveData<ArrayList<ChatRoomData>>()
|
||||
|
|
@ -40,25 +42,42 @@ class ConversationsListViewModel : ViewModel() {
|
|||
chatRoom: ChatRoom,
|
||||
state: ChatRoom.State?
|
||||
) {
|
||||
if (state == ChatRoom.State.Created || state == ChatRoom.State.Instantiated || state == ChatRoom.State.Deleted) {
|
||||
updateChatRoomsList()
|
||||
Log.i(
|
||||
"[Conversations List] Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]"
|
||||
)
|
||||
when (state) {
|
||||
ChatRoom.State.Created -> {
|
||||
addChatRoomToList(chatRoom)
|
||||
}
|
||||
ChatRoom.State.Deleted -> {
|
||||
removeChatRoomFromList(chatRoom)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {
|
||||
updateChatRoomsList()
|
||||
override fun onMessageSent(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
||||
onChatRoomMessageEvent(chatRoom)
|
||||
}
|
||||
|
||||
override fun onMessagesReceived(
|
||||
core: Core,
|
||||
room: ChatRoom,
|
||||
chatRoom: ChatRoom,
|
||||
messages: Array<out ChatMessage>
|
||||
) {
|
||||
reorderChatRoomsList()
|
||||
onChatRoomMessageEvent(chatRoom)
|
||||
}
|
||||
|
||||
override fun onMessageSent(core: Core, chatRoom: ChatRoom, message: ChatMessage) {
|
||||
reorderChatRoomsList()
|
||||
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {
|
||||
notifyChatRoomUpdate(chatRoom)
|
||||
}
|
||||
|
||||
override fun onChatRoomEphemeralMessageDeleted(core: Core, chatRoom: ChatRoom) {
|
||||
notifyChatRoomUpdate(chatRoom)
|
||||
}
|
||||
|
||||
override fun onChatRoomSubjectChanged(core: Core, chatRoom: ChatRoom) {
|
||||
notifyChatRoomUpdate(chatRoom)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +95,62 @@ class ConversationsListViewModel : ViewModel() {
|
|||
super.onCleared()
|
||||
}
|
||||
|
||||
private fun addChatRoomToList(chatRoom: ChatRoom) {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
val list = arrayListOf<ChatRoomData>()
|
||||
|
||||
val data = ChatRoomData(chatRoom)
|
||||
list.add(data)
|
||||
list.addAll(chatRoomsList.value.orEmpty())
|
||||
|
||||
chatRoomsList.postValue(list)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeChatRoomFromList(chatRoom: ChatRoom) {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
val list = arrayListOf<ChatRoomData>()
|
||||
|
||||
for (data in chatRoomsList.value.orEmpty()) {
|
||||
if (LinphoneUtils.getChatRoomId(chatRoom) != LinphoneUtils.getChatRoomId(
|
||||
data.chatRoom
|
||||
)
|
||||
) {
|
||||
list.add(data)
|
||||
}
|
||||
}
|
||||
|
||||
chatRoomsList.postValue(list)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findChatRoomIndex(chatRoom: ChatRoom): Int {
|
||||
val id = LinphoneUtils.getChatRoomId(chatRoom)
|
||||
for ((index, data) in chatRoomsList.value.orEmpty().withIndex()) {
|
||||
if (id == data.id) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
private fun notifyChatRoomUpdate(chatRoom: ChatRoom) {
|
||||
when (val index = findChatRoomIndex(chatRoom)) {
|
||||
-1 -> updateChatRoomsList()
|
||||
else -> notifyItemChangedEvent.postValue(Event(index))
|
||||
}
|
||||
}
|
||||
|
||||
private fun onChatRoomMessageEvent(chatRoom: ChatRoom) {
|
||||
when (findChatRoomIndex(chatRoom)) {
|
||||
-1 -> updateChatRoomsList()
|
||||
0 -> notifyItemChangedEvent.postValue(Event(0))
|
||||
else -> reorderChatRoomsList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateChatRoomsList() {
|
||||
Log.i("[Conversations List] Updating chat rooms list")
|
||||
coreContext.postOnCoreThread { core ->
|
||||
chatRoomsList.value.orEmpty().forEach(ChatRoomData::onCleared)
|
||||
|
||||
|
|
@ -90,6 +164,7 @@ class ConversationsListViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
private fun reorderChatRoomsList() {
|
||||
Log.i("[Conversations List] Re-ordering chat rooms list")
|
||||
coreContext.postOnCoreThread { core ->
|
||||
val list = arrayListOf<ChatRoomData>()
|
||||
list.addAll(chatRoomsList.value.orEmpty())
|
||||
|
|
|
|||
98
app/src/main/java/org/linphone/utils/PhoneNumberUtils.kt
Normal file
98
app/src/main/java/org/linphone/utils/PhoneNumberUtils.kt
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 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.utils
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.provider.ContactsContract
|
||||
|
||||
class PhoneNumberUtils {
|
||||
companion object {
|
||||
fun addressBookLabelTypeToVcardParamString(type: Int, default: String?): String {
|
||||
return when (type) {
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT -> "assistant"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK -> "callback"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CAR -> "car"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN -> "work,main"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME -> "home,fax"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK -> "work,fax"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_HOME -> "home"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_ISDN -> "isdn"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MAIN -> "main"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MMS -> "text"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE -> "cell"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_OTHER -> "other"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX -> "fax"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_PAGER -> "pager"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_RADIO -> "radio"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_TELEX -> "telex"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD -> "textphone"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_WORK -> "work"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE -> "work,cell"
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER -> "work,pager"
|
||||
ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM -> default ?: "custom"
|
||||
else -> default ?: type.toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun vcardParamStringToAddressBookLabel(resources: Resources, label: String): String {
|
||||
if (label.isEmpty()) return label
|
||||
val type = labelToType(label)
|
||||
return ContactsContract.CommonDataKinds.Phone.getTypeLabel(resources, type, label).toString()
|
||||
}
|
||||
|
||||
private fun labelToType(label: String): Int {
|
||||
return when (label) {
|
||||
"assistant" -> ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT
|
||||
"callback" -> ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK
|
||||
"car" -> ContactsContract.CommonDataKinds.Phone.TYPE_CAR
|
||||
"work,main" -> ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN
|
||||
"home,fax" -> ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME
|
||||
"work,fax" -> ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK
|
||||
"home" -> ContactsContract.CommonDataKinds.Phone.TYPE_HOME
|
||||
"isdn" -> ContactsContract.CommonDataKinds.Phone.TYPE_ISDN
|
||||
"main" -> ContactsContract.CommonDataKinds.Phone.TYPE_MAIN
|
||||
"text" -> ContactsContract.CommonDataKinds.Phone.TYPE_MMS
|
||||
"cell" -> ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE
|
||||
"other" -> ContactsContract.CommonDataKinds.Phone.TYPE_OTHER
|
||||
"fax" -> ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX
|
||||
"pager" -> ContactsContract.CommonDataKinds.Phone.TYPE_PAGER
|
||||
"radio" -> ContactsContract.CommonDataKinds.Phone.TYPE_RADIO
|
||||
"telex" -> ContactsContract.CommonDataKinds.Phone.TYPE_TELEX
|
||||
"textphone" -> ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD
|
||||
"work" -> ContactsContract.CommonDataKinds.Phone.TYPE_WORK
|
||||
"work,cell" -> ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE
|
||||
"work,pager" -> ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER
|
||||
"custom" -> ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM
|
||||
else -> ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM
|
||||
}
|
||||
}
|
||||
|
||||
fun arePhoneNumberWeakEqual(number1: String, number2: String): Boolean {
|
||||
return trimPhoneNumber(number1) == trimPhoneNumber(number2)
|
||||
}
|
||||
|
||||
private fun trimPhoneNumber(phoneNumber: String): String {
|
||||
return phoneNumber.replace(" ", "")
|
||||
.replace("-", "")
|
||||
.replace("(", "")
|
||||
.replace(")", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue