Unlimited number of poll options (#1340)
* implement unlimited number of poll options * fixes * extract percent calculation into function so it can be used anywhere * add license headermain
parent
cacac782ca
commit
6a0d7014f5
@ -0,0 +1,123 @@ |
||||
/* Copyright 2019 Conny Duck |
||||
* |
||||
* This file is a part of Tusky. |
||||
* |
||||
* 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. |
||||
* |
||||
* Tusky 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 Tusky; if not, |
||||
* see <http://www.gnu.org/licenses>. */ |
||||
|
||||
package com.keylesspalace.tusky.adapter |
||||
|
||||
import android.view.LayoutInflater |
||||
import android.view.View |
||||
import android.view.ViewGroup |
||||
import android.widget.CheckBox |
||||
import android.widget.RadioButton |
||||
import android.widget.TextView |
||||
import androidx.emoji.text.EmojiCompat |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import com.keylesspalace.tusky.R |
||||
import com.keylesspalace.tusky.entity.Emoji |
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper |
||||
import com.keylesspalace.tusky.util.HtmlUtils |
||||
import com.keylesspalace.tusky.util.visible |
||||
import com.keylesspalace.tusky.viewdata.PollOptionViewData |
||||
import com.keylesspalace.tusky.viewdata.calculatePercent |
||||
|
||||
class PollAdapter: RecyclerView.Adapter<PollViewHolder>() { |
||||
|
||||
private var pollOptions: List<PollOptionViewData> = emptyList() |
||||
private var voteCount: Int = 0 |
||||
private var mode = RESULT |
||||
private var emojis: List<Emoji> = emptyList() |
||||
|
||||
fun setup(options: List<PollOptionViewData>, voteCount: Int, emojis: List<Emoji>, mode: Int) { |
||||
this.pollOptions = options |
||||
this.voteCount = voteCount |
||||
this.emojis = emojis |
||||
this.mode = mode |
||||
notifyDataSetChanged() |
||||
} |
||||
|
||||
fun getSelected() : List<Int> { |
||||
return pollOptions.filter { it.selected } |
||||
.map { pollOptions.indexOf(it) } |
||||
} |
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollViewHolder { |
||||
return PollViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_poll, parent, false)) |
||||
} |
||||
|
||||
override fun getItemCount(): Int { |
||||
return pollOptions.size |
||||
} |
||||
|
||||
override fun onBindViewHolder(holder: PollViewHolder, position: Int) { |
||||
|
||||
val option = pollOptions[position] |
||||
|
||||
holder.resultTextView.visible(mode == RESULT) |
||||
holder.radioButton.visible(mode == SINGLE) |
||||
holder.checkBox.visible(mode == MULTIPLE) |
||||
|
||||
when(mode) { |
||||
RESULT -> { |
||||
val percent = calculatePercent(option.votesCount, voteCount) |
||||
|
||||
val pollOptionText = holder.resultTextView.context.getString(R.string.poll_option_format, percent, option.title) |
||||
|
||||
val emojifiedPollOptionText = CustomEmojiHelper.emojifyText(HtmlUtils.fromHtml(pollOptionText), emojis, holder.resultTextView) |
||||
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText) |
||||
|
||||
val level = percent * 100 |
||||
|
||||
holder.resultTextView.background.level = level |
||||
|
||||
} |
||||
SINGLE -> { |
||||
val emojifiedPollOptionText = CustomEmojiHelper.emojifyString(option.title, emojis, holder.radioButton) |
||||
holder.radioButton.text = EmojiCompat.get().process(emojifiedPollOptionText) |
||||
holder.radioButton.isChecked = option.selected |
||||
holder.radioButton.setOnClickListener { |
||||
pollOptions.forEachIndexed { index, pollOption -> |
||||
pollOption.selected = index == holder.adapterPosition |
||||
notifyItemChanged(index) |
||||
} |
||||
} |
||||
} |
||||
MULTIPLE -> { |
||||
val emojifiedPollOptionText = CustomEmojiHelper.emojifyString(option.title, emojis, holder.checkBox) |
||||
holder.checkBox.text = EmojiCompat.get().process(emojifiedPollOptionText) |
||||
holder.checkBox.isChecked = option.selected |
||||
holder.checkBox.setOnCheckedChangeListener { _, isChecked -> |
||||
pollOptions[holder.adapterPosition].selected = isChecked |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
companion object { |
||||
const val RESULT = 0 |
||||
const val SINGLE = 1 |
||||
const val MULTIPLE = 2 |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
class PollViewHolder(view: View): RecyclerView.ViewHolder(view) { |
||||
|
||||
val resultTextView: TextView = view.findViewById(R.id.status_poll_option_result) |
||||
val radioButton: RadioButton = view.findViewById(R.id.status_poll_radio_button) |
||||
val checkBox: CheckBox = view.findViewById(R.id.status_poll_checkbox) |
||||
|
||||
} |
@ -0,0 +1,66 @@ |
||||
/* Copyright 2019 Conny Duck |
||||
* |
||||
* This file is a part of Tusky. |
||||
* |
||||
* 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. |
||||
* |
||||
* Tusky 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 Tusky; if not, |
||||
* see <http://www.gnu.org/licenses>. */ |
||||
|
||||
package com.keylesspalace.tusky.viewdata |
||||
|
||||
import com.keylesspalace.tusky.entity.Poll |
||||
import com.keylesspalace.tusky.entity.PollOption |
||||
import java.util.* |
||||
import kotlin.math.roundToInt |
||||
|
||||
data class PollViewData( |
||||
val id: String, |
||||
val expiresAt: Date?, |
||||
val expired: Boolean, |
||||
val multiple: Boolean, |
||||
val votesCount: Int, |
||||
val options: List<PollOptionViewData>, |
||||
var voted: Boolean |
||||
) |
||||
|
||||
data class PollOptionViewData( |
||||
val title: String, |
||||
var votesCount: Int, |
||||
var selected: Boolean |
||||
) |
||||
|
||||
fun calculatePercent(fraction: Int, total: Int): Int { |
||||
return if (fraction == 0) { |
||||
0 |
||||
} else { |
||||
(fraction / total.toDouble() * 100).roundToInt() |
||||
} |
||||
} |
||||
|
||||
fun Poll?.toViewData(): PollViewData? { |
||||
if (this == null) return null |
||||
return PollViewData( |
||||
id, |
||||
expiresAt, |
||||
expired, |
||||
multiple, |
||||
votesCount, |
||||
options.map { it.toViewData() }, |
||||
voted |
||||
) |
||||
} |
||||
|
||||
fun PollOption.toViewData(): PollOptionViewData { |
||||
return PollOptionViewData( |
||||
title, |
||||
votesCount, |
||||
false |
||||
) |
||||
} |
@ -0,0 +1,42 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<FrameLayout 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"> |
||||
|
||||
<androidx.emoji.widget.EmojiTextView |
||||
android:id="@+id/status_poll_option_result" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="6dp" |
||||
android:background="@drawable/poll_option_background" |
||||
android:ellipsize="end" |
||||
android:lines="1" |
||||
android:paddingStart="6dp" |
||||
android:paddingTop="2dp" |
||||
android:paddingEnd="6dp" |
||||
android:paddingBottom="2dp" |
||||
android:textColor="?android:attr/textColorPrimary" |
||||
android:textSize="?attr/status_text_medium" |
||||
tools:text="40%" /> |
||||
|
||||
<RadioButton |
||||
android:id="@+id/status_poll_radio_button" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textSize="?attr/status_text_medium" |
||||
app:buttonTint="?attr/compound_button_color" |
||||
tools:text="Option 1" /> |
||||
|
||||
<CheckBox |
||||
android:id="@+id/status_poll_checkbox" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:ellipsize="end" |
||||
android:lines="1" |
||||
android:textSize="?attr/status_text_medium" |
||||
app:buttonTint="?attr/compound_button_color" |
||||
tools:text="Option 1" /> |
||||
|
||||
</FrameLayout> |
Loading…
Reference in new issue