mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-04-18 06:08:29 +00:00
Access shared media & documents from contact page if a 1-1 chat room is found
This commit is contained in:
parent
3c9d08e14d
commit
3ad6c6c8ed
7 changed files with 232 additions and 14 deletions
|
|
@ -17,6 +17,7 @@ Group changes to describe their impact on the project, as follows:
|
|||
- Added keyboard shortcuts on IncomingCallFragment: Ctrl + Shift + A to answer the call, Ctrl + Shift + D to decline it
|
||||
- Added seeking feature to recordings & media player within app
|
||||
- Added PDF preview in conversation (message bubble & documents list)
|
||||
- Added media/documents access from contact page if a 1-1 conversation with any of the contact SIP addresses is found
|
||||
- Added hover effect when using a mouse (useful for tablets or devices with desktop mode)
|
||||
- Support right click on some items to open bottom sheet/menu
|
||||
- Added toggle speaker action in active call notification
|
||||
|
|
|
|||
|
|
@ -115,6 +115,24 @@ class ContactFragment : SlidingPaneChildFragment() {
|
|||
showDeleteConfirmationDialog()
|
||||
}
|
||||
|
||||
binding.setGoToSharedMediaClickListener {
|
||||
if (findNavController().currentDestination?.id == R.id.contactFragment) {
|
||||
val conversationId = viewModel.existingConversationId.value.orEmpty()
|
||||
Log.i("$TAG Going to shared media fragment for conversation [$conversationId]")
|
||||
val action = ContactFragmentDirections.actionContactFragmentToConversationMediaListFragment(conversationId)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
|
||||
binding.setGoToSharedDocumentsClickListener {
|
||||
if (findNavController().currentDestination?.id == R.id.contactFragment) {
|
||||
val conversationId = viewModel.existingConversationId.value.orEmpty()
|
||||
Log.i("$TAG Going to shared documents fragment for conversation [$conversationId]")
|
||||
val action = ContactFragmentDirections.actionContactFragmentToConversationDocumentsListFragment(conversationId)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) { slideable ->
|
||||
viewModel.showBackButton.value = slideable
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ import org.linphone.core.FriendList
|
|||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.ContactsListFilterPopupMenuBinding
|
||||
import org.linphone.databinding.ContactsListFragmentBinding
|
||||
import org.linphone.ui.fileviewer.FileViewerActivity
|
||||
import org.linphone.ui.fileviewer.MediaViewerActivity
|
||||
import org.linphone.ui.main.MainActivity
|
||||
import org.linphone.ui.main.contacts.adapter.ContactsListAdapter
|
||||
import org.linphone.ui.main.contacts.model.ContactAvatarModel
|
||||
|
|
@ -234,6 +236,32 @@ class ContactsListFragment : AbstractMainFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
sharedViewModel.displayFileEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { bundle ->
|
||||
if (findNavController().currentDestination?.id == R.id.contactsListFragment) {
|
||||
val path = bundle.getString("path", "")
|
||||
val isMedia = bundle.getBoolean("isMedia", false)
|
||||
if (path.isEmpty()) {
|
||||
Log.e("$TAG Can't navigate to file viewer for empty path!")
|
||||
return@consume
|
||||
}
|
||||
|
||||
Log.i(
|
||||
"$TAG Navigating to [${if (isMedia) "media" else "file"}] viewer fragment with path [$path]"
|
||||
)
|
||||
if (isMedia) {
|
||||
val intent = Intent(requireActivity(), MediaViewerActivity::class.java)
|
||||
intent.putExtras(bundle)
|
||||
startActivity(intent)
|
||||
} else {
|
||||
val intent = Intent(requireActivity(), FileViewerActivity::class.java)
|
||||
intent.putExtras(bundle)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AbstractMainFragment related
|
||||
|
||||
listViewModel.title.value = getString(R.string.bottom_navigation_contacts_label)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ class ContactViewModel
|
|||
|
||||
val videoCallDisabled = MutableLiveData<Boolean>()
|
||||
|
||||
val existingConversationId = MutableLiveData<String>()
|
||||
|
||||
val operationInProgress = MutableLiveData<Boolean>()
|
||||
|
||||
val showLongPressMenuForNumberOrAddressEvent: MutableLiveData<Event<ContactNumberOrAddressModel>> by lazy {
|
||||
|
|
@ -207,7 +209,12 @@ class ContactViewModel
|
|||
Log.i("$TAG Conversation [$id] successfully created")
|
||||
chatRoom.removeListener(this)
|
||||
operationInProgress.postValue(false)
|
||||
goToConversationEvent.postValue(Event(LinphoneUtils.getConversationId(chatRoom)))
|
||||
|
||||
val conversationId = LinphoneUtils.getConversationId(chatRoom)
|
||||
if (existingConversationId.value.orEmpty().isEmpty()) {
|
||||
existingConversationId.postValue(conversationId)
|
||||
}
|
||||
goToConversationEvent.postValue(Event(conversationId))
|
||||
} else if (state == ChatRoom.State.CreationFailed) {
|
||||
Log.e("$TAG Conversation [$id] creation has failed!")
|
||||
chatRoom.removeListener(this)
|
||||
|
|
@ -330,6 +337,7 @@ class ContactViewModel
|
|||
sipAddressesAndPhoneNumbers.postValue(addressesAndNumbers)
|
||||
|
||||
fetchDevicesAndTrust()
|
||||
lookUpExistingChatRoom()
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
|
@ -545,7 +553,12 @@ class ContactViewModel
|
|||
existingChatRoom
|
||||
)}], going to it"
|
||||
)
|
||||
goToConversationEvent.postValue(Event(LinphoneUtils.getConversationId(existingChatRoom)))
|
||||
|
||||
val conversationId = LinphoneUtils.getConversationId(existingChatRoom)
|
||||
if (existingConversationId.value.orEmpty().isEmpty()) {
|
||||
existingConversationId.postValue(conversationId)
|
||||
}
|
||||
goToConversationEvent.postValue(Event(conversationId))
|
||||
} else {
|
||||
Log.i(
|
||||
"$TAG No existing conversation between [$localSipUri] and [$remoteSipUri] was found, let's create it"
|
||||
|
|
@ -558,7 +571,12 @@ class ContactViewModel
|
|||
val id = LinphoneUtils.getConversationId(chatRoom)
|
||||
Log.i("$TAG 1-1 conversation [$id] has been created")
|
||||
operationInProgress.postValue(false)
|
||||
goToConversationEvent.postValue(Event(LinphoneUtils.getConversationId(chatRoom)))
|
||||
|
||||
val conversationId = LinphoneUtils.getConversationId(chatRoom)
|
||||
if (existingConversationId.value.orEmpty().isEmpty()) {
|
||||
existingConversationId.postValue(conversationId)
|
||||
}
|
||||
goToConversationEvent.postValue(Event(conversationId))
|
||||
} else {
|
||||
Log.i("$TAG Conversation isn't in Created state yet, wait for it")
|
||||
chatRoom.addListener(chatRoomListener)
|
||||
|
|
@ -567,7 +585,12 @@ class ContactViewModel
|
|||
val id = LinphoneUtils.getConversationId(chatRoom)
|
||||
Log.i("$TAG Conversation successfully created [$id]")
|
||||
operationInProgress.postValue(false)
|
||||
goToConversationEvent.postValue(Event(LinphoneUtils.getConversationId(chatRoom)))
|
||||
|
||||
val conversationId = LinphoneUtils.getConversationId(chatRoom)
|
||||
if (existingConversationId.value.orEmpty().isEmpty()) {
|
||||
existingConversationId.postValue(conversationId)
|
||||
}
|
||||
goToConversationEvent.postValue(Event(conversationId))
|
||||
}
|
||||
} else {
|
||||
Log.e(
|
||||
|
|
@ -627,4 +650,44 @@ class ContactViewModel
|
|||
|
||||
devices.postValue(devicesList)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun lookUpExistingChatRoom() {
|
||||
val account = LinphoneUtils.getDefaultAccount()
|
||||
if (account != null) {
|
||||
val params = coreContext.core.createConferenceParams(null)
|
||||
params.isChatEnabled = true
|
||||
params.isGroupEnabled = false
|
||||
params.account = account
|
||||
|
||||
val localAddress = account.params.identityAddress
|
||||
val addresses = friend.addresses
|
||||
for (address in addresses) {
|
||||
val sameDomain = address.domain == corePreferences.defaultDomain && address.domain == account.params.domain
|
||||
if (account.params.instantMessagingEncryptionMandatory && sameDomain) {
|
||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
||||
} else if (!account.params.instantMessagingEncryptionMandatory) {
|
||||
if (LinphoneUtils.isEndToEndEncryptedChatAvailable(coreContext.core)) {
|
||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
||||
} else {
|
||||
params.securityLevel = Conference.SecurityLevel.None
|
||||
}
|
||||
}
|
||||
|
||||
val participants = arrayOf(address)
|
||||
val existingChatRoom = coreContext.core.searchChatRoom(params, localAddress, null, participants)
|
||||
if (existingChatRoom != null) {
|
||||
val conversationId = LinphoneUtils.getConversationId(existingChatRoom)
|
||||
Log.i("$TAG Found existing conversation with ID [$conversationId]")
|
||||
existingConversationId.postValue(conversationId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Log.w("$TAG No existing conversation was found for this contact with any of it's SIP addresses")
|
||||
existingConversationId.postValue("")
|
||||
} else {
|
||||
Log.e("$TAG No default account found!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -416,10 +416,10 @@
|
|||
style="@style/section_header_style"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:padding="5dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:padding="10dp"
|
||||
android:text="@string/conversation_details_media_documents_title"
|
||||
app:layout_constraintWidth_max="@dimen/section_max_width"
|
||||
app:layout_constraintBottom_toTopOf="@id/action_media"
|
||||
|
|
@ -476,10 +476,10 @@
|
|||
style="@style/section_header_style"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:padding="5dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:padding="10dp"
|
||||
android:text="@string/contact_details_actions_title"
|
||||
app:layout_constraintWidth_max="@dimen/section_max_width"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@
|
|||
<variable
|
||||
name="deleteClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="goToSharedMediaClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="goToSharedDocumentsClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.ui.main.contacts.viewmodel.ContactViewModel" />
|
||||
|
|
@ -55,7 +61,6 @@
|
|||
app:layout_constraintStart_toEndOf="@id/back"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
|
||||
<ImageView
|
||||
style="@style/icon_top_bar_button_style"
|
||||
android:onClick="@{() -> viewModel.editContact()}"
|
||||
|
|
@ -86,6 +91,12 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/screen_bottom_margin">
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="@{viewModel.existingConversationId.empty ? View.GONE : View.VISIBLE, default=gone}"
|
||||
app:constraint_referenced_ids="action_media, action_documents, media_documents_actions, media_documents_separator" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/avatar_content"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -520,20 +531,81 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/media_documents_actions"
|
||||
style="@style/section_header_style"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:padding="10dp"
|
||||
android:text="@string/conversation_details_media_documents_title"
|
||||
app:layout_constraintWidth_max="@dimen/section_max_width"
|
||||
app:layout_constraintBottom_toTopOf="@id/action_media"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/devices_trust" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/action_media"
|
||||
style="@style/context_menu_action_label_style"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/action_background_top"
|
||||
android:drawableStart="@drawable/image"
|
||||
android:onClick="@{goToSharedMediaClickListener}"
|
||||
android:text="@string/conversation_media_list_title"
|
||||
app:layout_constraintWidth_max="@dimen/section_max_width"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/media_documents_actions" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/action_documents"
|
||||
style="@style/context_menu_action_label_style"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="@dimen/screen_bottom_margin"
|
||||
android:background="@drawable/action_background_bottom"
|
||||
android:drawableStart="@drawable/file_pdf"
|
||||
android:onClick="@{goToSharedDocumentsClickListener}"
|
||||
android:text="@string/conversation_document_list_title"
|
||||
app:layout_constraintWidth_max="@dimen/section_max_width"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/action_media" />
|
||||
|
||||
<View
|
||||
android:id="@+id/media_documents_separator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?attr/color_separator"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintEnd_toEndOf="@id/action_media"
|
||||
app:layout_constraintStart_toStartOf="@id/action_media"
|
||||
app:layout_constraintTop_toBottomOf="@id/action_media" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/section_header_style"
|
||||
android:id="@+id/actions"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/contact_details_actions_title"
|
||||
app:layout_constraintWidth_max="@dimen/section_max_width"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/devices_trust"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/action_documents"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/context_menu_action_label_style"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,22 @@
|
|||
app:destination="@id/emptyFragment"
|
||||
app:popUpTo="@id/contactFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_contactFragment_to_conversationDocumentsListFragment"
|
||||
app:destination="@id/conversationDocumentsListFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_contactFragment_to_conversationMediaListFragment"
|
||||
app:destination="@id/conversationMediaListFragment"
|
||||
app:launchSingleTop="true"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
</fragment>
|
||||
|
||||
<action
|
||||
|
|
@ -50,4 +66,24 @@
|
|||
app:argType="string" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/conversationMediaListFragment"
|
||||
android:name="org.linphone.ui.main.chat.fragment.ConversationMediaListFragment"
|
||||
android:label="ConversationMediaListFragment"
|
||||
tools:layout="@layout/chat_media_fragment">
|
||||
<argument
|
||||
android:name="conversationId"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/conversationDocumentsListFragment"
|
||||
android:name="org.linphone.ui.main.chat.fragment.ConversationDocumentsListFragment"
|
||||
android:label="ConversationDocumentsListFragment"
|
||||
tools:layout="@layout/chat_documents_fragment">
|
||||
<argument
|
||||
android:name="conversationId"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
|
||||
</navigation>
|
||||
Loading…
Add table
Reference in a new issue