diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt new file mode 100644 index 000000000..a0dd6bfd3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderItem.kt @@ -0,0 +1,19 @@ +package com.nextcloud.talk.polls.adapters + +import com.nextcloud.talk.R + +data class PollResultHeaderItem( + val name: String, + val percent: Int, + val selfVoted: Boolean +) : PollResultItem { + + override fun getViewType(): Int { + return VIEW_TYPE + } + + companion object { + // layout is used as view type for uniqueness + public val VIEW_TYPE: Int = R.layout.poll_result_header_item + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt new file mode 100644 index 000000000..0c73ac55e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultHeaderViewHolder.kt @@ -0,0 +1,29 @@ +package com.nextcloud.talk.polls.adapters + +import android.annotation.SuppressLint +import android.graphics.Typeface +import com.nextcloud.talk.databinding.PollResultHeaderItemBinding +import com.nextcloud.talk.models.database.UserEntity + +class PollResultHeaderViewHolder( + private val user: UserEntity, + override val binding: PollResultHeaderItemBinding +) : PollResultViewHolder(binding) { + + @SuppressLint("SetTextI18n") + override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { + val item = pollResultItem as PollResultHeaderItem + + binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } + + binding.pollOptionText.text = item.name + binding.pollOptionPercentText.text = "${item.percent}%" + + if (item.selfVoted) { + binding.pollOptionText.setTypeface(null, Typeface.BOLD) + binding.pollOptionPercentText.setTypeface(null, Typeface.BOLD) + } + + binding.pollOptionBar.progress = item.percent + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt index 19051ea61..8349c2886 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItem.kt @@ -1,10 +1,7 @@ package com.nextcloud.talk.polls.adapters -import com.nextcloud.talk.polls.model.PollDetails +interface PollResultItem { -class PollResultItem( - val name: String, - val percent: Int, - val selfVoted: Boolean, - val voters: List? -) + fun getViewType(): Int + // fun getView(inflater: LayoutInflater?, convertView: View?): View? +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt index 0f73a37a2..9da748950 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultItemClickListener.kt @@ -1,5 +1,5 @@ package com.nextcloud.talk.polls.adapters interface PollResultItemClickListener { - fun onClick(pollResultItem: PollResultItem) + fun onClick(pollResultHeaderItem: PollResultHeaderItem) } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt index dab8c90bd..5eda74cce 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultViewHolder.kt @@ -1,107 +1,10 @@ package com.nextcloud.talk.polls.adapters -import android.annotation.SuppressLint -import android.graphics.Typeface -import android.text.TextUtils -import android.view.View -import android.widget.LinearLayout import androidx.recyclerview.widget.RecyclerView -import com.facebook.drawee.backends.pipeline.Fresco -import com.facebook.drawee.generic.RoundingParams -import com.facebook.drawee.interfaces.DraweeController -import com.facebook.drawee.view.SimpleDraweeView -import com.nextcloud.talk.R -import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.databinding.PollResultItemBinding -import com.nextcloud.talk.models.database.UserEntity -import com.nextcloud.talk.polls.model.PollDetails -import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.DisplayUtils +import androidx.viewbinding.ViewBinding -class PollResultViewHolder( - private val user: UserEntity, - private val binding: PollResultItemBinding +abstract class PollResultViewHolder( + open val binding: ViewBinding ) : RecyclerView.ViewHolder(binding.root) { - - @SuppressLint("SetTextI18n") - fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { - binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } - - binding.root.setOnClickListener { clickListener.onClick(pollResultItem) } - - binding.pollOptionText.text = pollResultItem.name - binding.pollOptionPercentText.text = "${pollResultItem.percent}%" - - if (pollResultItem.selfVoted) { - binding.pollOptionText.setTypeface(null, Typeface.BOLD) - binding.pollOptionPercentText.setTypeface(null, Typeface.BOLD) - } - - binding.pollOptionBar.progress = pollResultItem.percent - - if (!pollResultItem.voters.isNullOrEmpty()) { - binding.pollOptionDetail.visibility = View.VISIBLE - - val lp = LinearLayout.LayoutParams( - 60, - 50 - ) - - pollResultItem.voters.forEach { - val avatar = SimpleDraweeView(binding.root.context) - avatar.layoutParams = lp - - val roundingParams = RoundingParams.fromCornersRadius(5f) - roundingParams.roundAsCircle = true - - avatar.hierarchy.roundingParams = roundingParams - avatar.controller = getAvatarDraweeController(it) - - binding.pollOptionDetail.addView(avatar) - } - } else { - binding.pollOptionDetail.visibility = View.GONE - } - } - - private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? { - if (pollDetail.actorType == "guests") { - var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest) - if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { - displayName = pollDetail.actorDisplayName!! - } - val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() - // .setOldController(binding.avatar.controller) - .setAutoPlayAnimations(true) - .setImageRequest( - DisplayUtils.getImageRequestForUrl( - ApiUtils.getUrlForGuestAvatar( - user.baseUrl, - displayName, - false - ), - null - ) - ) - .build() - return draweeController - } else if (pollDetail.actorType == "users") { - val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() - // .setOldController(binding.avatar.controller) - .setAutoPlayAnimations(true) - .setImageRequest( - DisplayUtils.getImageRequestForUrl( - ApiUtils.getUrlForAvatar( - user.baseUrl, - pollDetail.actorId, - false - ), - null - ) - ) - .build() - return draweeController - } - return null - } -} + abstract fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt new file mode 100644 index 000000000..e05943f09 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterItem.kt @@ -0,0 +1,18 @@ +package com.nextcloud.talk.polls.adapters + +import com.nextcloud.talk.R +import com.nextcloud.talk.polls.model.PollDetails + +data class PollResultVoterItem( + val details: PollDetails +) : PollResultItem { + + override fun getViewType(): Int { + return VIEW_TYPE + } + + companion object { + // layout is used as view type for uniqueness + public val VIEW_TYPE: Int = R.layout.poll_result_voter_item + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt new file mode 100644 index 000000000..1e105e216 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVoterViewHolder.kt @@ -0,0 +1,84 @@ +package com.nextcloud.talk.polls.adapters + +import android.annotation.SuppressLint +import android.text.TextUtils +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.interfaces.DraweeController +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.databinding.PollResultVoterItemBinding +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.polls.model.PollDetails +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DisplayUtils + +class PollResultVoterViewHolder( + private val user: UserEntity, + override val binding: PollResultVoterItemBinding +) : PollResultViewHolder(binding) { + + @SuppressLint("SetTextI18n") + override fun bind(pollResultItem: PollResultItem, clickListener: PollResultItemClickListener) { + val item = pollResultItem as PollResultVoterItem + + // binding.root.setOnClickListener { clickListener.onClick(pollResultVoterItem) } + + // binding.pollVoterAvatar = pollResultHeaderItem.name + binding.pollVoterName.text = item.details.actorDisplayName + + // val lp = LinearLayout.LayoutParams( + // 60, + // 50 + // ) + // + // val avatar = SimpleDraweeView(binding.root.context) + // avatar.layoutParams = lp + + // val roundingParams = RoundingParams.fromCornersRadius(5f) + // roundingParams.roundAsCircle = true + // + // binding.pollVoterAvatar.hierarchy.roundingParams = roundingParams + binding.pollVoterAvatar.controller = getAvatarDraweeController(item.details) + } + + private fun getAvatarDraweeController(pollDetail: PollDetails): DraweeController? { + if (pollDetail.actorType == "guests") { + var displayName = NextcloudTalkApplication.sharedApplication?.resources?.getString(R.string.nc_guest) + if (!TextUtils.isEmpty(pollDetail.actorDisplayName)) { + displayName = pollDetail.actorDisplayName!! + } + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + // .setOldController(binding.avatar.controller) + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForGuestAvatar( + user.baseUrl, + displayName, + false + ), + null + ) + ) + .build() + return draweeController + } else if (pollDetail.actorType == "users") { + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + // .setOldController(binding.avatar.controller) + .setAutoPlayAnimations(true) + .setImageRequest( + DisplayUtils.getImageRequestForUrl( + ApiUtils.getUrlForAvatar( + user.baseUrl, + pollDetail.actorId, + false + ), + null + ) + ) + .build() + return draweeController + } + return null + } +} diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index 63827661b..153a198e5 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -3,26 +3,56 @@ package com.nextcloud.talk.polls.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.nextcloud.talk.databinding.PollResultItemBinding +import com.nextcloud.talk.databinding.PollResultHeaderItemBinding +import com.nextcloud.talk.databinding.PollResultVoterItemBinding import com.nextcloud.talk.models.database.UserEntity class PollResultsAdapter( private val user: UserEntity, private val clickListener: PollResultItemClickListener, ) : RecyclerView.Adapter() { - internal var list: MutableList = ArrayList() + internal var list: MutableList = ArrayList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollResultViewHolder { - val itemBinding = PollResultItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return PollResultViewHolder(user, itemBinding) + when (viewType) { + PollResultHeaderItem.VIEW_TYPE -> { + val itemBinding = PollResultHeaderItemBinding.inflate( + LayoutInflater.from(parent.context), parent, + false + ) + return PollResultHeaderViewHolder(user, itemBinding) + } + PollResultVoterItem.VIEW_TYPE -> { + val itemBinding = PollResultVoterItemBinding.inflate( + LayoutInflater.from(parent.context), parent, + false + ) + return PollResultVoterViewHolder(user, itemBinding) + } + } + + val itemBinding = PollResultHeaderItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return PollResultHeaderViewHolder(user, itemBinding) } override fun onBindViewHolder(holder: PollResultViewHolder, position: Int) { - val pollResultItem = list[position] - holder.bind(pollResultItem, clickListener) + when (holder.itemViewType) { + PollResultHeaderItem.VIEW_TYPE -> { + val pollResultItem = list[position] + holder.bind(pollResultItem as PollResultHeaderItem, clickListener) + } + PollResultVoterItem.VIEW_TYPE -> { + val pollResultItem = list[position] + holder.bind(pollResultItem as PollResultVoterItem, clickListener) + } + } } override fun getItemCount(): Int { return list.size } + + override fun getItemViewType(position: Int): Int { + return list[position].getViewType() + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 85dbcb5e3..f67fa958e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -22,7 +22,6 @@ package com.nextcloud.talk.polls.ui import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -33,10 +32,9 @@ import autodagger.AutoInjector import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogPollResultsBinding import com.nextcloud.talk.models.database.UserEntity -import com.nextcloud.talk.polls.adapters.PollResultItem +import com.nextcloud.talk.polls.adapters.PollResultHeaderItem import com.nextcloud.talk.polls.adapters.PollResultItemClickListener import com.nextcloud.talk.polls.adapters.PollResultsAdapter -import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel import javax.inject.Inject @@ -81,11 +79,18 @@ class PollResultsFragment( parentViewModel.viewState.observe(viewLifecycleOwner) { state -> if (state is PollMainViewModel.PollResultState) { initAdapter() - initPollResults(state.poll) + viewModel.setPoll(state.poll) initEditButton(state.showEditButton) initCloseButton(state.showCloseButton) } } + + viewModel.items.observe(viewLifecycleOwner) { + val adapter = PollResultsAdapter(user, this).apply { + list = it + } + binding.pollResultsList.adapter = adapter + } } private fun initAdapter() { @@ -94,51 +99,6 @@ class PollResultsFragment( _binding?.pollResultsList?.layoutManager = LinearLayoutManager(context) } - private fun initPollResults(poll: Poll) { - if (poll.details != null) { - val votersAmount = poll.details.size - val oneVoteInPercent = 100 / votersAmount // TODO: poll.numVoters when fixed on api - - poll.options?.forEachIndexed { index, option -> - val votersForThisOption = poll.details.filter { it.optionId == index } - val optionsPercent = oneVoteInPercent * votersForThisOption.size - - val pollResultItem = PollResultItem( - option, - optionsPercent, - isOptionSelfVoted(poll, index), - votersForThisOption - ) - adapter?.list?.add(pollResultItem) - } - } else if (poll.votes != null) { - val votersAmount = poll.numVoters - val oneVoteInPercent = 100 / votersAmount - - poll.options?.forEachIndexed { index, option -> - var votersAmountForThisOption = poll.votes.filter { it.key.toInt() == index }[index.toString()] - if (votersAmountForThisOption == null) { - votersAmountForThisOption = 0 - } - val optionsPercent = oneVoteInPercent * votersAmountForThisOption - - val pollResultItem = PollResultItem( - option, - optionsPercent, - isOptionSelfVoted(poll, index), - null - ) - adapter?.list?.add(pollResultItem) - } - } else { - Log.e(TAG, "failed to get data to show poll results") - } - } - - private fun isOptionSelfVoted(poll: Poll, index: Int): Boolean { - return poll.votedSelf?.contains(index) == true - } - private fun initEditButton(showEditButton: Boolean) { if (showEditButton) { _binding?.editVoteButton?.visibility = View.VISIBLE @@ -161,8 +121,8 @@ class PollResultsFragment( } } - override fun onClick(pollResultItem: PollResultItem) { - Log.d(TAG, "click..") + override fun onClick(pollResultHeaderItem: PollResultHeaderItem) { + viewModel.filterItems() } override fun onDestroyView() { diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index 63bf30339..869fdbbf3 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -24,6 +24,10 @@ package com.nextcloud.talk.polls.viewmodels import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.nextcloud.talk.polls.adapters.PollResultHeaderItem +import com.nextcloud.talk.polls.adapters.PollResultItem +import com.nextcloud.talk.polls.adapters.PollResultVoterItem +import com.nextcloud.talk.polls.model.Poll import com.nextcloud.talk.polls.repositories.PollRepository import io.reactivex.disposables.Disposable import javax.inject.Inject @@ -33,10 +37,20 @@ class PollResultsViewModel @Inject constructor(private val repository: PollRepos sealed interface ViewState object InitialState : ViewState + private var _poll: Poll? = null + val poll: Poll? + get() = _poll + private val _viewState: MutableLiveData = MutableLiveData(InitialState) val viewState: LiveData get() = _viewState + private var _unfilteredItems: ArrayList = ArrayList() + + private var _items: MutableLiveData> = MutableLiveData>() + val items: LiveData> + get() = _items + private var disposable: Disposable? = null override fun onCleared() { @@ -44,6 +58,82 @@ class PollResultsViewModel @Inject constructor(private val repository: PollRepos disposable?.dispose() } + fun setPoll(poll: Poll) { + _poll = poll + initPollResults(_poll!!) + } + + private fun initPollResults(poll: Poll) { + _items.value = ArrayList() + + val votersAmount = getVotersAmount(poll) + val oneVoteInPercent = 100 / votersAmount + + poll.options?.forEachIndexed { index, option -> + val votersAmountForThisOption = getVotersAmountForOption(poll, index) + val optionsPercent = oneVoteInPercent * votersAmountForThisOption + + val pollResultHeaderItem = PollResultHeaderItem( + option, + optionsPercent, + isOptionSelfVoted(poll, index) + ) + addToItems(pollResultHeaderItem) + + val voters = poll.details?.filter { it.optionId == index } + if (!voters.isNullOrEmpty()) { + voters.forEach { + addToItems(PollResultVoterItem(it)) + } + } + } + + _unfilteredItems = _items.value?.let { ArrayList(it) }!! + } + + private fun addToItems(pollResultItem: PollResultItem) { + val tempList = _items.value + tempList!!.add(pollResultItem) + _items.value = tempList + } + + private fun getVotersAmount(poll: Poll): Int { + if (poll.details != null) { + return poll.details.size + } else if (poll.votes != null) { + return poll.numVoters + } + return 0 + } + + private fun getVotersAmountForOption(poll: Poll, index: Int): Int { + var votersAmountForThisOption: Int? = 0 + if (poll.details != null) { + votersAmountForThisOption = poll.details.filter { it.optionId == index }.size + } else if (poll.votes != null) { + votersAmountForThisOption = poll.votes.filter { it.key.toInt() == index }[index.toString()] + if (votersAmountForThisOption == null) { + votersAmountForThisOption = 0 + } + } + return votersAmountForThisOption!! + } + + private fun isOptionSelfVoted(poll: Poll, index: Int): Boolean { + return poll.votedSelf?.contains(index) == true + } + + fun filterItems() { + if (_items.value?.containsAll(_unfilteredItems) == true) { + val filteredList = _items.value?.filter { it.getViewType() == PollResultHeaderItem.VIEW_TYPE } as + MutableList + + _items.value = ArrayList(filteredList) + } else { + _items.value = _unfilteredItems + } + } + companion object { private val TAG = PollResultsViewModel::class.java.simpleName } diff --git a/app/src/main/res/layout/dialog_poll_results.xml b/app/src/main/res/layout/dialog_poll_results.xml index 8b7df33de..5fc62f79e 100644 --- a/app/src/main/res/layout/dialog_poll_results.xml +++ b/app/src/main/res/layout/dialog_poll_results.xml @@ -34,7 +34,7 @@ android:id="@+id/poll_results_list" android:layout_width="match_parent" android:layout_height="wrap_content" - tools:listitem="@layout/poll_result_item" /> + tools:listitem="@layout/poll_result_header_item" /> - - diff --git a/app/src/main/res/layout/poll_result_voter_item.xml b/app/src/main/res/layout/poll_result_voter_item.xml new file mode 100644 index 000000000..8039f06bf --- /dev/null +++ b/app/src/main/res/layout/poll_result_voter_item.xml @@ -0,0 +1,25 @@ + + + + + + +