mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-21 12:39:58 +01:00
Lots of progress on selecting contacts
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
df1e65e238
commit
6427e0bafd
@ -137,7 +137,7 @@ class AdvancedUserItem(
|
||||
) : FlexibleViewHolder(view, adapter) {
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.name_text)
|
||||
@BindView(R.id.participantNameTextView)
|
||||
var contactDisplayName: EmojiTextView? = null
|
||||
@JvmField
|
||||
@BindView(R.id.secondary_text)
|
||||
|
@ -268,7 +268,7 @@ class UserItem(
|
||||
var checkedImageView: ImageView? = null
|
||||
|
||||
init {
|
||||
contactDisplayName = view.findViewById(R.id.name_text)
|
||||
contactDisplayName = view.findViewById(R.id.participantNameTextView)
|
||||
avatarImageView = view.findViewById(R.id.avatarImageView)
|
||||
contactMentionId = view.findViewById(R.id.secondary_text)
|
||||
voiceOrSimpleCallImageView = view.findViewById(R.id.voiceOrSimpleCallImageView)
|
||||
|
@ -17,6 +17,9 @@ import com.otaliastudios.elements.Presenter
|
||||
import com.otaliastudios.elements.extensions.FooterSource
|
||||
import com.otaliastudios.elements.extensions.HeaderSource
|
||||
import kotlinx.android.synthetic.main.rv_item_contact.view.*
|
||||
import kotlinx.android.synthetic.main.rv_item_contact.view.avatarImageView
|
||||
import kotlinx.android.synthetic.main.rv_item_contact.view.participantNameTextView
|
||||
import kotlinx.android.synthetic.main.rv_item_contact_selected.view.*
|
||||
import kotlinx.android.synthetic.main.rv_item_participant_rv_footer.view.*
|
||||
import kotlinx.android.synthetic.main.rv_item_title_header.view.*
|
||||
import org.koin.core.KoinComponent
|
||||
@ -26,13 +29,16 @@ open class ContactPresenter<T : Any>(context: Context, onElementClick: ((Page, H
|
||||
private val globalService: GlobalService by inject()
|
||||
|
||||
override val elementTypes: Collection<Int>
|
||||
get() = listOf(ParticipantElementType.PARTICIPANT.ordinal, ParticipantElementType.PARTICIPANT_HEADER.ordinal, ParticipantElementType.PARTICIPANT_FOOTER.ordinal)
|
||||
get() = listOf(ParticipantElementType.PARTICIPANT.ordinal, ParticipantElementType.PARTICIPANT_SELECTED.ordinal, ParticipantElementType.PARTICIPANT_HEADER.ordinal, ParticipantElementType.PARTICIPANT_FOOTER.ordinal)
|
||||
|
||||
override fun onCreate(parent: ViewGroup, elementType: Int): Holder {
|
||||
return when (elementType) {
|
||||
ParticipantElementType.PARTICIPANT.ordinal -> {
|
||||
Holder(getLayoutInflater().inflate(R.layout.rv_item_contact, parent, false))
|
||||
}
|
||||
ParticipantElementType.PARTICIPANT_SELECTED.ordinal -> {
|
||||
Holder(getLayoutInflater().inflate(R.layout.rv_item_contact_selected, parent, false))
|
||||
}
|
||||
ParticipantElementType.PARTICIPANT_HEADER.ordinal -> {
|
||||
Holder(getLayoutInflater().inflate(R.layout.rv_item_title_header, parent, false))
|
||||
}
|
||||
@ -45,19 +51,26 @@ open class ContactPresenter<T : Any>(context: Context, onElementClick: ((Page, H
|
||||
override fun onBind(page: Page, holder: Holder, element: Element<T>, payloads: List<Any>) {
|
||||
super.onBind(page, holder, element, payloads)
|
||||
|
||||
if (element.type == ParticipantElementType.PARTICIPANT.ordinal) {
|
||||
if (element.type == ParticipantElementType.PARTICIPANT.ordinal || element.type == ParticipantElementType.PARTICIPANT_SELECTED.ordinal) {
|
||||
val participant = element.data as Participant?
|
||||
val user = globalService.currentUserLiveData.value
|
||||
|
||||
holder.itemView.checkedImageView.isVisible = participant?.selected == true
|
||||
holder.itemView.checkedImageView?.isVisible = participant?.selected == true
|
||||
|
||||
if (!payloads.contains(ElementPayload.SELECTION_TOGGLE)) {
|
||||
participant?.displayName?.let {
|
||||
holder.itemView.name_text.text = it
|
||||
if (element.type == ParticipantElementType.PARTICIPANT_SELECTED.ordinal) {
|
||||
holder.itemView.participantNameTextView.text = it.substringBefore(" ", it)
|
||||
} else {
|
||||
holder.itemView.participantNameTextView.text = it
|
||||
|
||||
}
|
||||
} ?: run {
|
||||
holder.itemView.name_text.text = context.getString(R.string.nc_guest)
|
||||
holder.itemView.participantNameTextView.text = context.getString(R.string.nc_guest)
|
||||
}
|
||||
|
||||
holder.itemView.clearImageView?.load(Images().getImageWithBackground(context, R.drawable.ic_baseline_clear_24, R.color.white))
|
||||
|
||||
when (participant?.source) {
|
||||
"users" -> {
|
||||
when (participant.type) {
|
||||
|
@ -2,6 +2,7 @@ package com.nextcloud.talk.newarch.features.contactsflow
|
||||
|
||||
enum class ParticipantElementType {
|
||||
PARTICIPANT,
|
||||
PARTICIPANT_SELECTED,
|
||||
PARTICIPANT_HEADER,
|
||||
PARTICIPANT_FOOTER
|
||||
}
|
@ -5,7 +5,11 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.observe
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
@ -18,7 +22,8 @@ import com.otaliastudios.elements.Element
|
||||
import com.otaliastudios.elements.Page
|
||||
import com.otaliastudios.elements.Presenter
|
||||
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
|
||||
import kotlinx.android.synthetic.main.conversations_list_view.view.*
|
||||
import kotlinx.android.synthetic.main.contacts_list_view.view.*
|
||||
import kotlinx.android.synthetic.main.conversations_list_view.view.recyclerView
|
||||
import kotlinx.android.synthetic.main.message_state.view.*
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
@ -27,7 +32,8 @@ class ContactsView<T : Any>(private val bundle: Bundle? = null) : BaseView() {
|
||||
|
||||
private lateinit var viewModel: ContactsViewModel
|
||||
val factory: ContactsViewModelFactory by inject()
|
||||
lateinit var adapter: Adapter
|
||||
lateinit var participantsAdapter: Adapter
|
||||
lateinit var selectedParticipantsAdapter: Adapter
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.contacts_list_view
|
||||
}
|
||||
@ -44,8 +50,8 @@ class ContactsView<T : Any>(private val bundle: Bundle? = null) : BaseView() {
|
||||
val view = super.onCreateView(inflater, container)
|
||||
|
||||
// todo - change empty state magic
|
||||
adapter = Adapter.builder(this)
|
||||
.addSource(ContactsViewSource(viewModel.contactsLiveData, ParticipantElementType.PARTICIPANT.ordinal))
|
||||
participantsAdapter = Adapter.builder(this)
|
||||
.addSource(ContactsViewSource(data = viewModel.contactsLiveData, elementType = ParticipantElementType.PARTICIPANT.ordinal))
|
||||
.addSource(ContactsHeaderSource(activity as Context, ParticipantElementType.PARTICIPANT_HEADER.ordinal))
|
||||
.addSource(ContactsFooterSource(activity as Context, ParticipantElementType.PARTICIPANT_FOOTER.ordinal))
|
||||
.addPresenter(ContactPresenter(activity as Context, ::onElementClick))
|
||||
@ -58,8 +64,24 @@ class ContactsView<T : Any>(private val bundle: Bundle? = null) : BaseView() {
|
||||
.setAutoScrollMode(Adapter.AUTOSCROLL_POSITION_0, true)
|
||||
.into(view.recyclerView)
|
||||
|
||||
selectedParticipantsAdapter = Adapter.builder(this)
|
||||
.addSource(ContactsViewSource(data = viewModel.selectedParticipantsLiveData, elementType = ParticipantElementType.PARTICIPANT_SELECTED.ordinal, loadingIndicatorsEnabled = false, errorIndicatorEnabled = false, emptyIndicatorEnabled = false))
|
||||
.addPresenter(ContactPresenter(activity as Context, ::onElementClick))
|
||||
.setAutoScrollMode(Adapter.AUTOSCROLL_POSITION_ANY, true)
|
||||
.into(view.selectedParticipantsRecyclerView)
|
||||
|
||||
view.apply {
|
||||
recyclerView.initRecyclerView(LinearLayoutManager(activity), adapter, true)
|
||||
recyclerView.initRecyclerView(LinearLayoutManager(activity), participantsAdapter, true)
|
||||
selectedParticipantsRecyclerView.initRecyclerView(LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false), selectedParticipantsAdapter, true)
|
||||
}
|
||||
|
||||
viewModel.apply {
|
||||
selectedParticipantsLiveData.observe(this@ContactsView) { participants ->
|
||||
view.selectedParticipantsRecyclerView.isVisible = participants.isNotEmpty()
|
||||
view.divider.isVisible = participants.isNotEmpty()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
viewModel.loadContacts()
|
||||
@ -71,8 +93,16 @@ class ContactsView<T : Any>(private val bundle: Bundle? = null) : BaseView() {
|
||||
if (element.data is Participant?) {
|
||||
val participant = element.data as Participant?
|
||||
val isElementSelected = participant?.selected == true
|
||||
participant?.selected = !isElementSelected
|
||||
adapter.notifyItemChanged(holder.adapterPosition, ElementPayload.SELECTION_TOGGLE)
|
||||
participant?.let {
|
||||
if (isElementSelected) {
|
||||
viewModel.unselectParticipant(it)
|
||||
} else {
|
||||
viewModel.selectParticipant(it)
|
||||
}
|
||||
it.selected = !isElementSelected
|
||||
participantsAdapter.notifyItemChanged(holder.adapterPosition, ElementPayload.SELECTION_TOGGLE)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.nextcloud.talk.newarch.features.contactsflow
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
@ -17,7 +18,10 @@ class ContactsViewModel constructor(
|
||||
private val getContactsUseCase: GetContactsUseCase,
|
||||
val globalService: GlobalService
|
||||
) : BaseViewModel<ConversationsListView>(application) {
|
||||
val contactsLiveData = MutableLiveData<List<Participant>>()
|
||||
private val selectedParticipants = mutableListOf<Participant>()
|
||||
val selectedParticipantsLiveData: MutableLiveData<List<Participant>> = MutableLiveData()
|
||||
val contactsLiveData: MutableLiveData<List<Participant>> = MutableLiveData()
|
||||
|
||||
private var searchQuery: String? = null
|
||||
var conversationToken: String? = null
|
||||
|
||||
@ -26,6 +30,16 @@ class ContactsViewModel constructor(
|
||||
loadContacts()
|
||||
}
|
||||
|
||||
fun selectParticipant(participant: Participant) {
|
||||
selectedParticipants.add(participant)
|
||||
selectedParticipantsLiveData.postValue(selectedParticipants)
|
||||
}
|
||||
|
||||
fun unselectParticipant(participant: Participant) {
|
||||
selectedParticipants.remove(participant)
|
||||
selectedParticipantsLiveData.postValue(selectedParticipants)
|
||||
}
|
||||
|
||||
fun loadContacts() {
|
||||
getContactsUseCase.invoke(viewModelScope, parametersOf(globalService.currentUserLiveData.value, searchQuery, conversationToken), object :
|
||||
UseCaseResponse<List<Participant>> {
|
||||
|
@ -38,6 +38,10 @@ class ContactsViewSource<T : Participant>(private val data: LiveData<List<T>>, p
|
||||
}
|
||||
}
|
||||
|
||||
override fun getElementType(data: T): Int {
|
||||
return elementType
|
||||
}
|
||||
|
||||
override fun dependsOn(source: Source<*>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ open class ConversationPresenter(context: Context, onElementClick: ((Page, Holde
|
||||
)
|
||||
} else {
|
||||
authorDisplayName = if (!TextUtils.isEmpty(conversation.lastMessage?.actorDisplayName)) {
|
||||
conversation.lastMessage?.actorDisplayName!!.substringBefore(" ")
|
||||
conversation.lastMessage?.actorDisplayName!!.substringBefore(" ", conversation.lastMessage?.actorDisplayName!!)
|
||||
} else if ("guests" == conversation.lastMessage!!.actorType)
|
||||
context.getString(R.string.nc_guest)
|
||||
else
|
||||
|
@ -68,7 +68,7 @@ class Images {
|
||||
}
|
||||
}
|
||||
|
||||
fun getImageWithBackground(context: Context, drawableId: Int): Bitmap {
|
||||
fun getImageWithBackground(context: Context, drawableId: Int, foregroundColorTint: Int? = null): Bitmap {
|
||||
val layers = arrayOfNulls<Drawable>(2)
|
||||
layers[0] = context.getDrawable(R.color.bg_message_list_incoming_bubble)
|
||||
var scale = 0.25f
|
||||
@ -76,8 +76,12 @@ class Images {
|
||||
scale = 0.5f
|
||||
}
|
||||
layers[1] = ScaleDrawable(context.getDrawable(drawableId), Gravity.CENTER, scale, scale)
|
||||
if (foregroundColorTint != null) {
|
||||
layers[1]?.setTint(context.resources.getColor(foregroundColorTint))
|
||||
}
|
||||
layers[0]?.level = 0
|
||||
layers[1]?.level = 1
|
||||
|
||||
return LayerDrawable(layers).toBitmap()
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,29 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/selectedParticipantsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="88dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/rv_item_contact_selected" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:layout_below="@id/selectedParticipantsRecyclerView"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/divider"
|
||||
tools:listitem="@layout/rv_item_contact" />
|
||||
|
||||
</RelativeLayout>
|
@ -26,20 +26,20 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/rv_item_view_height"
|
||||
android:layout_margin="@dimen/margin_between_elements"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/avatarImageView"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="@dimen/double_margin_between_elements"
|
||||
android:layout_marginEnd="@dimen/margin_between_elements"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
app:shapeAppearanceOverlay="@style/circleImageView"
|
||||
tools:srcCompat="@tools:sample/avatars"/>
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/name_text"
|
||||
android:id="@+id/participantNameTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
|
66
app/src/main/res/layout/rv_item_contact_selected.xml
Normal file
66
app/src/main/res/layout/rv_item_contact_selected.xml
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Andy Scherzinger
|
||||
~ Copyright (C) 2017 Mario Danic
|
||||
~ Copyright (C) 2017 Andy Scherzinger
|
||||
~
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="8dp"
|
||||
android:id="@+id/avatarFrameLayout">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/avatarImageView"
|
||||
android:layout_width="@dimen/small_item_height"
|
||||
android:layout_height="@dimen/small_item_height"
|
||||
app:shapeAppearanceOverlay="@style/circleImageView"
|
||||
tools:srcCompat="@tools:sample/avatars"/>
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/clearImageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
app:shapeAppearanceOverlay="@style/circleImageView"
|
||||
android:layout_gravity="bottom|end"/>
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/participantNameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_below="@id/avatarFrameLayout"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
tools:text="Contact item text" />
|
||||
|
||||
</RelativeLayout>
|
@ -74,7 +74,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/name_text"
|
||||
android:id="@+id/participantNameTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
|
@ -69,7 +69,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/name_text"
|
||||
android:id="@+id/participantNameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
|
@ -53,7 +53,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/name_text"
|
||||
android:id="@+id/participantNameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
|
Loading…
Reference in New Issue
Block a user