瀏覽代碼

show detailed list of voters

+ refactoring adapter and viewholders for result screen

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 3 年之前
父節點
當前提交
73d48a395c

+ 19 - 0
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
+    }
+}

+ 29 - 0
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
+    }
+}

+ 4 - 7
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<PollDetails>?
-)
+    fun getViewType(): Int
+    // fun getView(inflater: LayoutInflater?, convertView: View?): View?
+}

+ 1 - 1
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)
 }

+ 5 - 102
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)
+}

+ 18 - 0
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
+    }
+}

+ 84 - 0
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
+    }
+}

+ 36 - 6
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<PollResultViewHolder>() {
-    internal var list: MutableList<PollResultItem> = ArrayList<PollResultItem>()
+    internal var list: MutableList<PollResultItem> = 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()
+    }
 }

+ 11 - 51
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() {

+ 90 - 0
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<ViewState> = MutableLiveData(InitialState)
     val viewState: LiveData<ViewState>
         get() = _viewState
 
+    private var _unfilteredItems: ArrayList<PollResultItem> = ArrayList()
+
+    private var _items: MutableLiveData<ArrayList<PollResultItem>> = MutableLiveData<ArrayList<PollResultItem>>()
+    val items: LiveData<ArrayList<PollResultItem>>
+        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<PollResultItem>
+
+            _items.value = ArrayList(filteredList)
+        } else {
+            _items.value = _unfilteredItems
+        }
+    }
+
     companion object {
         private val TAG = PollResultsViewModel::class.java.simpleName
     }

+ 1 - 1
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" />
     </LinearLayout>
 
     <com.google.android.material.button.MaterialButton

+ 1 - 11
app/src/main/res/layout/poll_result_item.xml → app/src/main/res/layout/poll_result_header_item.xml

@@ -34,17 +34,7 @@
         app:trackColor="@color/dialog_background"
         app:trackCornerRadius="5dp"
         app:trackThickness="5dp"
+        android:paddingBottom="@dimen/standard_half_padding"
         tools:progress="50" />
 
-    <LinearLayout
-        android:id="@+id/poll_option_detail"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="4dp"
-        android:layout_marginBottom="4dp"
-        android:orientation="horizontal"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="@+id/poll_option_text"
-        app:layout_constraintTop_toBottomOf="@+id/poll_option_bar" />
-
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 25 - 0
app/src/main/res/layout/poll_result_voter_item.xml

@@ -0,0 +1,25 @@
+<LinearLayout 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="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="4dp"
+    tools:background="@color/white">
+
+    <com.facebook.drawee.view.SimpleDraweeView
+        android:id="@+id/poll_voter_avatar"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="8dp"
+        android:layout_gravity="center"
+        app:roundAsCircle="true" />
+
+    <TextView
+        android:id="@+id/poll_voter_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        tools:text="Bill Murray" />
+
+</LinearLayout>