mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Improvements
This commit is contained in:
parent
c77fae435f
commit
7b71f32d18
15 changed files with 267 additions and 59 deletions
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
|
|
@ -7,6 +7,7 @@
|
|||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="Embedded JDK" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
|
|
|
|||
|
|
@ -258,6 +258,7 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
|||
|
||||
fl.updateSubscriptions()
|
||||
Log.i("[Contacts Loader] Subscription(s) updated")
|
||||
coreContext.contactsManager.onContactsLoaded()
|
||||
}
|
||||
} catch (sde: StaleDataException) {
|
||||
Log.e("[Contacts Loader] State Data Exception: $sde")
|
||||
|
|
|
|||
67
app/src/main/java/org/linphone/contacts/ContactsManager.kt
Normal file
67
app/src/main/java/org/linphone/contacts/ContactsManager.kt
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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 androidx.loader.app.LoaderManager
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.ui.MainActivity
|
||||
|
||||
class ContactsManager {
|
||||
private val listeners = arrayListOf<ContactsListener>()
|
||||
|
||||
fun loadContacts(activity: MainActivity) {
|
||||
val manager = LoaderManager.getInstance(activity)
|
||||
manager.restartLoader(0, null, ContactLoader())
|
||||
}
|
||||
|
||||
fun addListener(listener: ContactsListener) {
|
||||
if (coreContext.isReady()) {
|
||||
coreContext.postOnCoreThread {
|
||||
listeners.add(listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeListener(listener: ContactsListener) {
|
||||
if (coreContext.isReady()) {
|
||||
coreContext.postOnCoreThread {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onContactsLoaded() {
|
||||
coreContext.postOnCoreThread {
|
||||
for (listener in listeners) {
|
||||
listener.onContactsLoaded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onCoreStarted() {
|
||||
}
|
||||
|
||||
fun onCoreStopped() {
|
||||
}
|
||||
}
|
||||
|
||||
interface ContactsListener {
|
||||
fun onContactsLoaded()
|
||||
}
|
||||
|
|
@ -27,12 +27,14 @@ import android.os.Looper
|
|||
import java.util.*
|
||||
import org.linphone.BuildConfig
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
import org.linphone.contacts.ContactsManager
|
||||
import org.linphone.core.tools.Log
|
||||
|
||||
class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
||||
lateinit var core: Core
|
||||
|
||||
val contactsManager = ContactsManager()
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
private lateinit var coreThread: Handler
|
||||
|
||||
|
|
@ -69,11 +71,14 @@ class CoreContext(val context: Context) : HandlerThread("Core Thread") {
|
|||
computeUserAgent()
|
||||
core.start()
|
||||
|
||||
contactsManager.onCoreStarted()
|
||||
|
||||
Looper.loop()
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
core.stop()
|
||||
contactsManager.onCoreStopped()
|
||||
|
||||
quitSafely()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,11 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.core.view.WindowCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.loader.app.LoaderManager
|
||||
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() {
|
||||
|
|
@ -54,8 +52,7 @@ class MainActivity : AppCompatActivity() {
|
|||
)
|
||||
|
||||
if (checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
|
||||
val manager = LoaderManager.getInstance(this)
|
||||
manager.restartLoader(0, null, ContactLoader())
|
||||
coreContext.contactsManager.loadContacts(this)
|
||||
}
|
||||
|
||||
while (!coreContext.isReady()) {
|
||||
|
|
@ -99,8 +96,7 @@ class MainActivity : AppCompatActivity() {
|
|||
grantResults: IntArray
|
||||
) {
|
||||
if (requestCode == CONTACTS_PERMISSION_REQUEST && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
val manager = LoaderManager.getInstance(this)
|
||||
manager.restartLoader(0, null, ContactLoader())
|
||||
coreContext.contactsManager.loadContacts(this)
|
||||
}
|
||||
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,14 @@ class ChatRoomData(val chatRoom: ChatRoom) {
|
|||
chatRoom.hasCapability(ChatRoom.Capabilities.OneToOne.toInt())
|
||||
}
|
||||
|
||||
private val coreListener = object : CoreListenerStub() {
|
||||
override fun onChatRoomRead(core: Core, chatRoom: ChatRoom) {
|
||||
if (chatRoom == this@ChatRoomData.chatRoom) {
|
||||
unreadChatCount.postValue(chatRoom.unreadMessagesCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val chatRoomListener = object : ChatRoomListenerStub() {
|
||||
override fun onIsComposingReceived(
|
||||
chatRoom: ChatRoom,
|
||||
|
|
@ -96,7 +104,42 @@ class ChatRoomData(val chatRoom: ChatRoom) {
|
|||
|
||||
init {
|
||||
chatRoom.addListener(chatRoomListener)
|
||||
coreContext.core.addListener(coreListener)
|
||||
|
||||
lastMessageImdnIcon.postValue(R.drawable.imdn_sent)
|
||||
showLastMessageImdnIcon.postValue(false)
|
||||
|
||||
contactLookup()
|
||||
subject.postValue(
|
||||
chatRoom.subject ?: LinphoneUtils.getDisplayName(chatRoom.peerAddress)
|
||||
)
|
||||
computeLastMessage()
|
||||
|
||||
unreadChatCount.postValue(chatRoom.unreadMessagesCount)
|
||||
isComposing.postValue(chatRoom.isRemoteComposing)
|
||||
isSecure.postValue(chatRoom.securityLevel == ChatRoom.SecurityLevel.Encrypted)
|
||||
isSecureVerified.postValue(chatRoom.securityLevel == ChatRoom.SecurityLevel.Safe)
|
||||
isEphemeral.postValue(chatRoom.isEphemeralEnabled)
|
||||
isMuted.postValue(areNotificationsMuted())
|
||||
}
|
||||
|
||||
fun onCleared() {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
chatRoom.removeListener(chatRoomListener)
|
||||
core.removeListener(coreListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun onClicked() {
|
||||
chatRoomDataListener?.onClicked()
|
||||
}
|
||||
|
||||
fun onLongClicked(): Boolean {
|
||||
chatRoomDataListener?.onLongClicked()
|
||||
return true
|
||||
}
|
||||
|
||||
fun contactLookup() {
|
||||
if (chatRoom.hasCapability(ChatRoom.Capabilities.Basic.toInt())) {
|
||||
val remoteAddress = chatRoom.peerAddress
|
||||
val friend = chatRoom.core.findFriend(remoteAddress)
|
||||
|
|
@ -121,35 +164,7 @@ class ChatRoomData(val chatRoom: ChatRoom) {
|
|||
}
|
||||
}
|
||||
}
|
||||
subject.postValue(
|
||||
chatRoom.subject ?: LinphoneUtils.getDisplayName(chatRoom.peerAddress)
|
||||
)
|
||||
|
||||
lastMessageImdnIcon.postValue(R.drawable.imdn_sent)
|
||||
showLastMessageImdnIcon.postValue(false)
|
||||
computeLastMessage()
|
||||
|
||||
unreadChatCount.postValue(chatRoom.unreadMessagesCount)
|
||||
isComposing.postValue(chatRoom.isRemoteComposing)
|
||||
isSecure.postValue(chatRoom.securityLevel == ChatRoom.SecurityLevel.Encrypted)
|
||||
isSecureVerified.postValue(chatRoom.securityLevel == ChatRoom.SecurityLevel.Safe)
|
||||
isEphemeral.postValue(chatRoom.isEphemeralEnabled)
|
||||
isMuted.postValue(areNotificationsMuted())
|
||||
}
|
||||
|
||||
fun onCleared() {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
chatRoom.removeListener(chatRoomListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun onClicked() {
|
||||
chatRoomDataListener?.onClicked()
|
||||
}
|
||||
|
||||
fun onLongClicked(): Boolean {
|
||||
chatRoomDataListener?.onLongClicked()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun computeLastMessageImdnIcon(message: ChatMessage) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.ui.conversations
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.linphone.databinding.ConversationFragmentBinding
|
||||
|
||||
class ConversationFragment : Fragment() {
|
||||
private lateinit var binding: ConversationFragmentBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = ConversationFragmentBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
}
|
||||
}
|
||||
|
|
@ -91,6 +91,9 @@ class ConversationsFragment : Fragment() {
|
|||
|
||||
adapter.chatRoomClickedEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { data ->
|
||||
findNavController().navigate(
|
||||
R.id.action_conversationsFragment_to_conversationFragment
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -123,16 +126,12 @@ class ConversationsFragment : Fragment() {
|
|||
}
|
||||
|
||||
binding.setOnNewConversationClicked {
|
||||
goToNewConversation()
|
||||
findNavController().navigate(
|
||||
R.id.action_conversationsFragment_to_newConversationFragment
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun goToNewConversation() {
|
||||
findNavController().navigate(
|
||||
R.id.action_conversationsFragment_to_newConversationFragment
|
||||
)
|
||||
}
|
||||
|
||||
private fun scrollToTop() {
|
||||
binding.conversationsList.scrollToPosition(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import java.util.ArrayList
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.contacts.ContactsListener
|
||||
import org.linphone.core.ChatMessage
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.Core
|
||||
|
|
@ -36,6 +37,14 @@ class ConversationsListViewModel : ViewModel() {
|
|||
|
||||
val notifyItemChangedEvent = MutableLiveData<Event<Int>>()
|
||||
|
||||
private val contactsListener = object : ContactsListener {
|
||||
override fun onContactsLoaded() {
|
||||
for (chatRoomData in chatRoomsList.value.orEmpty()) {
|
||||
chatRoomData.contactLookup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val coreListener = object : CoreListenerStub() {
|
||||
override fun onChatRoomStateChanged(
|
||||
core: Core,
|
||||
|
|
@ -85,10 +94,12 @@ class ConversationsListViewModel : ViewModel() {
|
|||
coreContext.postOnCoreThread { core ->
|
||||
core.addListener(coreListener)
|
||||
}
|
||||
coreContext.contactsManager.addListener(contactsListener)
|
||||
updateChatRoomsList()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
coreContext.contactsManager.removeListener(contactsListener)
|
||||
coreContext.postOnCoreThread { core ->
|
||||
core.removeListener(coreListener)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import androidx.core.view.doOnPreDraw
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.navGraphViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.contacts.ContactsSelectionAdapter
|
||||
import org.linphone.databinding.NewConversationFragmentBinding
|
||||
|
|
@ -75,7 +76,10 @@ class NewConversationFragment : Fragment() {
|
|||
viewModel.filter.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
viewModel.applyFilter(it.orEmpty().trim())
|
||||
val filter = it.orEmpty().trim()
|
||||
coreContext.postOnCoreThread {
|
||||
viewModel.applyFilter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
binding.setCancelClickListener {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.contacts.ContactData
|
||||
import org.linphone.contacts.ContactsListener
|
||||
import org.linphone.core.MagicSearch
|
||||
import org.linphone.core.MagicSearchListenerStub
|
||||
import org.linphone.core.SearchResult
|
||||
|
|
@ -46,13 +47,25 @@ class NewConversationViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
private val contactsListener = object : ContactsListener {
|
||||
override fun onContactsLoaded() {
|
||||
applyFilter(filter.value.orEmpty().trim())
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
magicSearch.addListener(magicSearchListener)
|
||||
applyFilter("")
|
||||
coreContext.postOnCoreThread {
|
||||
magicSearch.addListener(magicSearchListener)
|
||||
coreContext.contactsManager.addListener(contactsListener)
|
||||
applyFilter("")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
magicSearch.removeListener(magicSearchListener)
|
||||
coreContext.postOnCoreThread {
|
||||
coreContext.contactsManager.removeListener(contactsListener)
|
||||
magicSearch.removeListener(magicSearchListener)
|
||||
}
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
|
|
@ -63,20 +76,16 @@ class NewConversationViewModel : ViewModel() {
|
|||
(previousFilter.length == filterValue.length && previousFilter != filterValue)
|
||||
)
|
||||
) {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
magicSearch.resetSearchCache()
|
||||
}
|
||||
magicSearch.resetSearchCache()
|
||||
}
|
||||
previousFilter = filterValue
|
||||
|
||||
coreContext.postOnCoreThread { core ->
|
||||
magicSearch.getContactsListAsync(
|
||||
filterValue,
|
||||
"",
|
||||
MagicSearch.Source.Friends.toInt(),
|
||||
MagicSearch.Aggregation.Friend
|
||||
)
|
||||
}
|
||||
magicSearch.getContactsListAsync(
|
||||
filterValue,
|
||||
"",
|
||||
MagicSearch.Source.Friends.toInt(),
|
||||
MagicSearch.Aggregation.Friend
|
||||
)
|
||||
}
|
||||
|
||||
private fun processMagicSearchResults(results: Array<SearchResult>) {
|
||||
|
|
|
|||
5
app/src/main/res/anim/slide_in_right.xml
Normal file
5
app/src/main/res/anim/slide_in_right.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<translate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0%"
|
||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||
5
app/src/main/res/anim/slide_out_right.xml
Normal file
5
app/src/main/res/anim/slide_out_right.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<translate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="100%"
|
||||
android:duration="@android:integer/config_mediumAnimTime"/>
|
||||
30
app/src/main/res/layout/conversation_fragment.xml
Normal file
30
app/src/main/res/layout/conversation_fragment.xml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="backClickListener"
|
||||
type="View.OnClickListener" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:src="@drawable/shape_white_background"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
||||
|
|
@ -14,14 +14,28 @@
|
|||
android:id="@+id/action_conversationsFragment_to_newConversationFragment"
|
||||
app:destination="@id/newConversationFragment"
|
||||
app:enterAnim="@anim/slide_in"
|
||||
app:launchSingleTop="true"
|
||||
app:popExitAnim="@anim/slide_out" />
|
||||
<action
|
||||
android:id="@+id/action_conversationsFragment_to_conversationFragment"
|
||||
app:destination="@id/conversationFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/newConversationFragment"
|
||||
android:name="org.linphone.ui.conversations.NewConversationFragment"
|
||||
android:label="NewConversationFragment"
|
||||
tools:layout="@layout/new_conversation_fragment" />
|
||||
tools:layout="@layout/new_conversation_fragment" >
|
||||
<action
|
||||
android:id="@+id/action_newConversationFragment_to_conversationFragment"
|
||||
app:destination="@id/conversationFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/conversationFragment"
|
||||
android:name="org.linphone.ui.conversations.ConversationFragment"
|
||||
android:label="ConversationFragment"
|
||||
tools:layout="@layout/conversation_fragment" />
|
||||
|
||||
</navigation>
|
||||
Loading…
Add table
Reference in a new issue