mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Started new call fragment
This commit is contained in:
parent
407e474896
commit
9ad121f7d7
17 changed files with 493 additions and 42 deletions
|
|
@ -23,13 +23,58 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.navGraphViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.core.Address
|
||||
import org.linphone.databinding.CallStartFragmentBinding
|
||||
import org.linphone.ui.main.calls.viewmodel.StartCallViewModel
|
||||
import org.linphone.ui.main.calls.viewmodel.SuggestionsListViewModel
|
||||
import org.linphone.ui.main.contacts.adapter.ContactsListAdapter
|
||||
import org.linphone.ui.main.contacts.model.ContactAvatarModel
|
||||
import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener
|
||||
import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel
|
||||
import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel
|
||||
import org.linphone.ui.main.contacts.viewmodel.ContactsListViewModel
|
||||
import org.linphone.ui.main.fragment.GenericFragment
|
||||
import org.linphone.utils.DialogUtils
|
||||
|
||||
class StartCallFragment : GenericFragment() {
|
||||
private lateinit var binding: CallStartFragmentBinding
|
||||
|
||||
private val viewModel: StartCallViewModel by navGraphViewModels(
|
||||
R.id.startCallFragment
|
||||
)
|
||||
|
||||
private val contactsListViewModel: ContactsListViewModel by navGraphViewModels(
|
||||
R.id.startCallFragment
|
||||
)
|
||||
|
||||
private val suggestionsListViewModel: SuggestionsListViewModel by navGraphViewModels(
|
||||
R.id.startCallFragment
|
||||
)
|
||||
|
||||
private lateinit var contactsAdapter: ContactsListAdapter
|
||||
private lateinit var suggestionsAdapter: ContactsListAdapter
|
||||
|
||||
private val listener = object : ContactNumberOrAddressClickListener {
|
||||
override fun onClicked(address: Address?) {
|
||||
// UI thread
|
||||
if (address != null) {
|
||||
coreContext.postOnCoreThread {
|
||||
coreContext.startCall(address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongPress(model: ContactNumberOrAddressModel) {
|
||||
// UI thread
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
|
@ -46,10 +91,111 @@ class StartCallFragment : GenericFragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
postponeEnterTransition()
|
||||
|
||||
binding.setCancelClickListener {
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
binding.viewModel = viewModel
|
||||
|
||||
binding.setBackClickListener {
|
||||
goBack()
|
||||
}
|
||||
|
||||
contactsAdapter = ContactsListAdapter(viewLifecycleOwner, false)
|
||||
binding.contactsList.setHasFixedSize(true)
|
||||
binding.contactsList.adapter = contactsAdapter
|
||||
|
||||
contactsAdapter.contactClickedEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { model ->
|
||||
startCall(model)
|
||||
}
|
||||
}
|
||||
|
||||
binding.contactsList.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
suggestionsAdapter = ContactsListAdapter(viewLifecycleOwner, false)
|
||||
binding.suggestionsList.setHasFixedSize(true)
|
||||
binding.suggestionsList.adapter = suggestionsAdapter
|
||||
|
||||
suggestionsAdapter.contactClickedEvent.observe(viewLifecycleOwner) {
|
||||
it.consume { model ->
|
||||
startCall(model)
|
||||
}
|
||||
}
|
||||
|
||||
binding.suggestionsList.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
contactsListViewModel.contactsList.observe(
|
||||
viewLifecycleOwner
|
||||
) {
|
||||
contactsAdapter.submitList(it)
|
||||
|
||||
(view.parent as? ViewGroup)?.doOnPreDraw {
|
||||
startPostponedEnterTransition()
|
||||
}
|
||||
}
|
||||
|
||||
suggestionsListViewModel.suggestionsList.observe(viewLifecycleOwner) {
|
||||
suggestionsAdapter.submitList(it)
|
||||
}
|
||||
|
||||
viewModel.searchFilter.observe(viewLifecycleOwner) { filter ->
|
||||
contactsListViewModel.applyFilter(filter)
|
||||
suggestionsListViewModel.applyFilter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startCall(model: ContactAvatarModel) {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
val friend = model.friend
|
||||
val addressesCount = friend.addresses.size
|
||||
val numbersCount = friend.phoneNumbers.size
|
||||
if (addressesCount == 1 && numbersCount == 0) {
|
||||
val address = friend.addresses.first()
|
||||
coreContext.startCall(address)
|
||||
} else if (addressesCount == 1 && numbersCount == 0) {
|
||||
val number = friend.phoneNumbers.first()
|
||||
val address = core.interpretUrl(number, true)
|
||||
if (address != null) {
|
||||
coreContext.startCall(address)
|
||||
}
|
||||
} else {
|
||||
val list = arrayListOf<ContactNumberOrAddressModel>()
|
||||
for (address in friend.addresses) {
|
||||
val addressModel = ContactNumberOrAddressModel(
|
||||
address,
|
||||
address.asStringUriOnly(),
|
||||
listener,
|
||||
true
|
||||
)
|
||||
list.add(addressModel)
|
||||
}
|
||||
|
||||
for (number in friend.phoneNumbersWithLabel) {
|
||||
val address = core.interpretUrl(number.phoneNumber, true)
|
||||
val addressModel = ContactNumberOrAddressModel(
|
||||
address,
|
||||
number.phoneNumber,
|
||||
listener,
|
||||
false,
|
||||
number.label.orEmpty()
|
||||
)
|
||||
list.add(addressModel)
|
||||
}
|
||||
|
||||
coreContext.postOnMainThread {
|
||||
val model = NumberOrAddressPickerDialogModel(list)
|
||||
val dialog =
|
||||
DialogUtils.getNumberOrAddressPickerDialog(requireActivity(), model)
|
||||
|
||||
model.dismissEvent.observe(viewLifecycleOwner) { event ->
|
||||
event.consume {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.main.calls.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class StartCallViewModel : ViewModel() {
|
||||
val searchFilter = MutableLiveData<String>()
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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.main.calls.viewmodel
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import java.util.ArrayList
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.contacts.ContactsListener
|
||||
import org.linphone.core.Friend
|
||||
import org.linphone.core.MagicSearch
|
||||
import org.linphone.core.MagicSearchListenerStub
|
||||
import org.linphone.core.SearchResult
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.main.contacts.model.ContactAvatarModel
|
||||
|
||||
class SuggestionsListViewModel : ViewModel() {
|
||||
companion object {
|
||||
const val TAG = "[Suggestions List ViewModel]"
|
||||
}
|
||||
|
||||
val suggestionsList = MutableLiveData<ArrayList<ContactAvatarModel>>()
|
||||
|
||||
private var currentFilter = ""
|
||||
private var previousFilter = "NotSet"
|
||||
private var limitSearchToLinphoneAccounts = true
|
||||
|
||||
private lateinit var magicSearch: MagicSearch
|
||||
|
||||
private val magicSearchListener = object : MagicSearchListenerStub() {
|
||||
override fun onSearchResultsReceived(magicSearch: MagicSearch) {
|
||||
// Core thread
|
||||
Log.i("$TAG Magic search contacts available")
|
||||
processMagicSearchResults(magicSearch.lastSearch)
|
||||
}
|
||||
}
|
||||
|
||||
private val contactsListener = object : ContactsListener {
|
||||
override fun onContactsLoaded() {
|
||||
// Core thread
|
||||
Log.i("$TAG Contacts have been (re)loaded, updating list")
|
||||
applyFilter(
|
||||
currentFilter,
|
||||
if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "",
|
||||
MagicSearch.Source.CallLogs.toInt() or MagicSearch.Source.ChatRooms.toInt(),
|
||||
MagicSearch.Aggregation.Friend
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
coreContext.contactsManager.addListener(contactsListener)
|
||||
magicSearch = core.createMagicSearch()
|
||||
magicSearch.limitedSearch = false
|
||||
magicSearch.addListener(magicSearchListener)
|
||||
}
|
||||
|
||||
applyFilter(currentFilter)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
coreContext.postOnCoreThread {
|
||||
magicSearch.removeListener(magicSearchListener)
|
||||
coreContext.contactsManager.removeListener(contactsListener)
|
||||
}
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
fun processMagicSearchResults(results: Array<SearchResult>) {
|
||||
// Core thread
|
||||
Log.i("$TAG Processing ${results.size} results")
|
||||
suggestionsList.value.orEmpty().forEach(ContactAvatarModel::destroy)
|
||||
|
||||
val list = arrayListOf<ContactAvatarModel>()
|
||||
|
||||
for (result in results) {
|
||||
val friend = result.friend
|
||||
|
||||
val model = if (friend != null) {
|
||||
ContactAvatarModel(friend)
|
||||
} else {
|
||||
Log.w("$TAG SearchResult [$result] has no Friend!")
|
||||
val fakeFriend =
|
||||
createFriendFromSearchResult(result)
|
||||
ContactAvatarModel(fakeFriend)
|
||||
}
|
||||
model.noAlphabet.postValue(true)
|
||||
|
||||
list.add(model)
|
||||
}
|
||||
|
||||
suggestionsList.postValue(list)
|
||||
|
||||
Log.i("$TAG Processed ${results.size} results")
|
||||
}
|
||||
|
||||
fun applyFilter(filter: String) {
|
||||
// UI thread
|
||||
coreContext.postOnCoreThread {
|
||||
applyFilter(
|
||||
filter,
|
||||
if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "",
|
||||
MagicSearch.Source.CallLogs.toInt() or MagicSearch.Source.ChatRooms.toInt(),
|
||||
MagicSearch.Aggregation.Friend
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyFilter(
|
||||
filter: String,
|
||||
domain: String,
|
||||
sources: Int,
|
||||
aggregation: MagicSearch.Aggregation
|
||||
) {
|
||||
// Core thread
|
||||
if (previousFilter.isNotEmpty() && (
|
||||
previousFilter.length > filter.length ||
|
||||
(previousFilter.length == filter.length && previousFilter != filter)
|
||||
)
|
||||
) {
|
||||
magicSearch.resetSearchCache()
|
||||
}
|
||||
currentFilter = filter
|
||||
previousFilter = filter
|
||||
|
||||
Log.i(
|
||||
"$TAG Asking Magic search for contacts matching filter [$filter], domain [$domain] and in sources [$sources]"
|
||||
)
|
||||
magicSearch.getContactsListAsync(
|
||||
filter,
|
||||
domain,
|
||||
sources,
|
||||
aggregation
|
||||
)
|
||||
}
|
||||
|
||||
private fun createFriendFromSearchResult(searchResult: SearchResult): Friend {
|
||||
// Core thread
|
||||
val searchResultFriend = searchResult.friend
|
||||
if (searchResultFriend != null) return searchResultFriend
|
||||
|
||||
val friend = coreContext.core.createFriend()
|
||||
|
||||
val address = searchResult.address
|
||||
if (address != null) {
|
||||
friend.address = address
|
||||
}
|
||||
|
||||
val number = searchResult.phoneNumber
|
||||
if (number != null) {
|
||||
friend.addPhoneNumber(number)
|
||||
|
||||
if (address != null && address.username == number) {
|
||||
friend.removeAddress(address)
|
||||
}
|
||||
}
|
||||
|
||||
return friend
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@ private class ContactDiffCallback : DiffUtil.ItemCallback<ContactAvatarModel>()
|
|||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ContactAvatarModel, newItem: ContactAvatarModel): Boolean {
|
||||
return oldItem.showFirstLetter.value == newItem.showFirstLetter.value &&
|
||||
return oldItem.firstContactStartingByThatLetter.value == newItem.firstContactStartingByThatLetter.value &&
|
||||
oldItem.presenceStatus.value == newItem.presenceStatus.value
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,9 @@ class ContactFragment : GenericFragment() {
|
|||
|
||||
viewModel.showNumberOrAddressPickerDialogEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
val model = NumberOrAddressPickerDialogModel(viewModel)
|
||||
val model = NumberOrAddressPickerDialogModel(
|
||||
viewModel.sipAddressesAndPhoneNumbers.value.orEmpty()
|
||||
)
|
||||
val dialog = DialogUtils.getNumberOrAddressPickerDialog(requireActivity(), model)
|
||||
|
||||
model.dismissEvent.observe(viewLifecycleOwner) { event ->
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ class ContactAvatarModel(val friend: Friend) {
|
|||
|
||||
val firstLetter: String = LinphoneUtils.getFirstLetter(friend.name.orEmpty())
|
||||
|
||||
val showFirstLetter = MutableLiveData<Boolean>()
|
||||
val firstContactStartingByThatLetter = MutableLiveData<Boolean>()
|
||||
|
||||
val noAlphabet = MutableLiveData<Boolean>()
|
||||
|
||||
private val friendListener = object : FriendListenerStub() {
|
||||
override fun onPresenceReceived(fr: Friend) {
|
||||
|
|
|
|||
|
|
@ -20,16 +20,15 @@
|
|||
package org.linphone.ui.main.contacts.model
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.linphone.ui.main.contacts.viewmodel.ContactViewModel
|
||||
import org.linphone.utils.Event
|
||||
|
||||
class NumberOrAddressPickerDialogModel(viewModel: ContactViewModel) {
|
||||
val sipAddressesAndPhoneNumbers = MutableLiveData<ArrayList<ContactNumberOrAddressModel>>()
|
||||
class NumberOrAddressPickerDialogModel(list: List<ContactNumberOrAddressModel>) {
|
||||
val sipAddressesAndPhoneNumbers = MutableLiveData<List<ContactNumberOrAddressModel>>()
|
||||
|
||||
val dismissEvent = MutableLiveData<Event<Boolean>>()
|
||||
|
||||
init {
|
||||
sipAddressesAndPhoneNumbers.value = viewModel.sipAddressesAndPhoneNumbers.value
|
||||
sipAddressesAndPhoneNumbers.value = list
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class ContactsListViewModel : ViewModel() {
|
|||
applyFilter(
|
||||
currentFilter,
|
||||
if (limitSearchToLinphoneAccounts) corePreferences.defaultDomain else "",
|
||||
MagicSearch.Source.Friends.toInt(),
|
||||
MagicSearch.Source.Friends.toInt() or MagicSearch.Source.LdapServers.toInt(),
|
||||
MagicSearch.Aggregation.Friend
|
||||
)
|
||||
}
|
||||
|
|
@ -81,6 +81,7 @@ class ContactsListViewModel : ViewModel() {
|
|||
magicSearch.limitedSearch = false
|
||||
magicSearch.addListener(magicSearchListener)
|
||||
}
|
||||
|
||||
applyFilter(currentFilter)
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +126,7 @@ class ContactsListViewModel : ViewModel() {
|
|||
if (currentLetter != previousLetter) {
|
||||
previousLetter = currentLetter
|
||||
}
|
||||
model.showFirstLetter.postValue(displayLetter)
|
||||
model.firstContactStartingByThatLetter.postValue(displayLetter)
|
||||
|
||||
list.add(model)
|
||||
if (friend?.starred == true) {
|
||||
|
|
|
|||
|
|
@ -291,7 +291,11 @@ class CurrentCallViewModel() : ViewModel() {
|
|||
displayedName.postValue(friend.name)
|
||||
contact.postValue(ContactAvatarModel(friend))
|
||||
} else {
|
||||
displayedName.postValue(LinphoneUtils.getDisplayName(address))
|
||||
val fakeFriend = coreContext.core.createFriend()
|
||||
fakeFriend.name = LinphoneUtils.getDisplayName(address)
|
||||
fakeFriend.addAddress(address)
|
||||
contact.postValue(ContactAvatarModel(fakeFriend))
|
||||
displayedName.postValue(fakeFriend.name)
|
||||
}
|
||||
|
||||
updateEncryption()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<corners android:radius="5dp" />
|
||||
<corners android:radius="28dp" />
|
||||
<solid android:color="@color/gray_6"/>
|
||||
</shape>
|
||||
|
|
@ -99,8 +99,8 @@
|
|||
android:id="@+id/calls_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_bar"
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@
|
|||
android:visibility="@{viewModel.showFavourites && !viewModel.isListFiltered && viewModel.favourites.size() > 0 ? View.VISIBLE : View.GONE}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
|
|
@ -135,8 +135,6 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constraintStart_toEndOf="@id/bottom_nav_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/all_contacts_label"
|
||||
|
|
|
|||
|
|
@ -26,13 +26,14 @@
|
|||
android:onLongClickListener="@{onLongClickListener}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:background="@drawable/cell_background">
|
||||
|
||||
<io.getstream.avatarview.AvatarView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="@dimen/avatar_list_cell_size"
|
||||
android:layout_height="@dimen/avatar_list_cell_size"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:adjustViewBounds="true"
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@
|
|||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="cancelClickListener"
|
||||
name="backClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.ui.main.calls.viewmodel.StartCallViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
@ -17,7 +20,7 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/back"
|
||||
android:onClick="@{cancelClickListener}"
|
||||
android:onClick="@{backClickListener}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginStart="10dp"
|
||||
|
|
@ -44,25 +47,114 @@
|
|||
app:layout_constraintStart_toEndOf="@id/back"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/gray_7"
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/search_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"
|
||||
android:drawableStart="@drawable/search"
|
||||
android:drawableEnd="@drawable/dialer"
|
||||
android:drawablePadding="10dp"
|
||||
android:drawableTint="@color/gray_9"
|
||||
android:background="@drawable/shape_search_square_background"
|
||||
android:hint="Search contact or history call"
|
||||
android:text="@={viewModel.searchFilter}"
|
||||
android:textSize="14sp"
|
||||
android:inputType="textPersonName|textNoSuggestions"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
android:id="@+id/group_call_icon"
|
||||
android:visibility="@{viewModel.searchFilter.length() > 0 ? View.GONE : View.VISIBLE}"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/meetings"
|
||||
android:background="@drawable/shape_orange_round"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/search_bar" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_800"
|
||||
android:id="@+id/group_call_label"
|
||||
android:visibility="@{viewModel.searchFilter.length() > 0 ? View.GONE : View.VISIBLE}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="Create a group call"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/black"
|
||||
android:drawableEnd="@drawable/arrow"
|
||||
app:layout_constraintStart_toEndOf="@id/group_call_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/group_call_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/group_call_icon"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_800"
|
||||
android:id="@+id/all_contacts_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="All contacts"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/group_call_icon"
|
||||
app:layout_constraintBottom_toTopOf="@id/contacts_list"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/contacts_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/all_contacts_label"
|
||||
app:layout_constraintBottom_toTopOf="@id/suggestions_label"/>
|
||||
|
||||
</ScrollView>
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_800"
|
||||
android:id="@+id/suggestions_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="Suggestions"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/contacts_list"
|
||||
app:layout_constraintBottom_toTopOf="@id/suggestions_list"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/suggestions_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintHeight_max="220dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/suggestions_label"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@
|
|||
android:id="@+id/calls_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/top_bar"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
android:onLongClick="@{onLongClickListener}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/cell_background">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
|
|
@ -30,11 +32,11 @@
|
|||
android:id="@+id/header"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:text="@{model.firstLetter, default=`A`}"
|
||||
android:visibility="@{model.showFirstLetter ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:visibility="@{model.noAlphabet ? View.GONE : model.firstContactStartingByThatLetter ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:textColor="@color/gray_10"
|
||||
android:textSize="20sp"
|
||||
android:layout_marginStart="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@
|
|||
android:visibility="@{viewModel.showFavourites && !viewModel.isListFiltered && viewModel.favourites.size() > 0 ? View.VISIBLE : View.GONE}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -127,8 +127,6 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/all_contacts_label"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue