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 header
main
Konrad Pozniak 6 years ago committed by GitHub
parent cacac782ca
commit 6a0d7014f5
  1. 123
      app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt
  2. 225
      app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
  3. 3
      app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java
  4. 3
      app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt
  5. 11
      app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt
  6. 9
      app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
  7. 14
      app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
  8. 66
      app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt
  9. 10
      app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java
  10. 169
      app/src/main/res/layout/item_conversation.xml
  11. 42
      app/src/main/res/layout/item_poll.xml
  12. 169
      app/src/main/res/layout/item_status.xml
  13. 170
      app/src/main/res/layout/item_status_detailed.xml

@ -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)
}

@ -7,11 +7,8 @@ import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.ToggleButton; import android.widget.ToggleButton;
@ -19,7 +16,8 @@ import android.widget.ToggleButton;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.emoji.text.EmojiCompat; import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
@ -28,8 +26,6 @@ import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.entity.Attachment.Focus; import com.keylesspalace.tusky.entity.Attachment.Focus;
import com.keylesspalace.tusky.entity.Attachment.MetaData; import com.keylesspalace.tusky.entity.Attachment.MetaData;
import com.keylesspalace.tusky.entity.Emoji; import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.Poll;
import com.keylesspalace.tusky.entity.PollOption;
import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper; import com.keylesspalace.tusky.util.CustomEmojiHelper;
@ -39,13 +35,14 @@ import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper; import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.ThemeUtils; import com.keylesspalace.tusky.util.ThemeUtils;
import com.keylesspalace.tusky.view.MediaPreviewImageView; import com.keylesspalace.tusky.view.MediaPreviewImageView;
import com.keylesspalace.tusky.viewdata.PollOptionViewData;
import com.keylesspalace.tusky.viewdata.PollViewData;
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
import com.keylesspalace.tusky.viewdata.StatusViewData; import com.keylesspalace.tusky.viewdata.StatusViewData;
import com.mikepenz.iconics.utils.Utils; import com.mikepenz.iconics.utils.Utils;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -74,20 +71,19 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private View sensitiveMediaShow; private View sensitiveMediaShow;
protected TextView[] mediaLabels; protected TextView[] mediaLabels;
private ToggleButton contentWarningButton; private ToggleButton contentWarningButton;
protected ImageView avatarInset; private ImageView avatarInset;
public ImageView avatar; public ImageView avatar;
public TextView timestampInfo; public TextView timestampInfo;
public TextView content; public TextView content;
public TextView contentWarningDescription; public TextView contentWarningDescription;
private TextView[] pollResults; private RecyclerView pollOptions;
private TextView pollDescription; private TextView pollDescription;
private RadioGroup pollRadioGroup;
private RadioButton[] pollRadioOptions;
private CheckBox[] pollCheckboxOptions;
private Button pollButton; private Button pollButton;
private PollAdapter pollAdapter;
private boolean useAbsoluteTime; private boolean useAbsoluteTime;
private SimpleDateFormat shortSdf; private SimpleDateFormat shortSdf;
private SimpleDateFormat longSdf; private SimpleDateFormat longSdf;
@ -135,31 +131,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
contentWarningButton = itemView.findViewById(R.id.status_content_warning_button); contentWarningButton = itemView.findViewById(R.id.status_content_warning_button);
avatarInset = itemView.findViewById(R.id.status_avatar_inset); avatarInset = itemView.findViewById(R.id.status_avatar_inset);
pollResults = new TextView[]{ pollOptions = itemView.findViewById(R.id.status_poll_options);
itemView.findViewById(R.id.status_poll_option_result_0),
itemView.findViewById(R.id.status_poll_option_result_1),
itemView.findViewById(R.id.status_poll_option_result_2),
itemView.findViewById(R.id.status_poll_option_result_3)
};
pollDescription = itemView.findViewById(R.id.status_poll_description); pollDescription = itemView.findViewById(R.id.status_poll_description);
pollRadioGroup = itemView.findViewById(R.id.status_poll_radio_group);
pollRadioOptions = new RadioButton[] {
pollRadioGroup.findViewById(R.id.status_poll_radio_button_0),
pollRadioGroup.findViewById(R.id.status_poll_radio_button_1),
pollRadioGroup.findViewById(R.id.status_poll_radio_button_2),
pollRadioGroup.findViewById(R.id.status_poll_radio_button_3)
};
pollCheckboxOptions = new CheckBox[] {
itemView.findViewById(R.id.status_poll_checkbox_0),
itemView.findViewById(R.id.status_poll_checkbox_1),
itemView.findViewById(R.id.status_poll_checkbox_2),
itemView.findViewById(R.id.status_poll_checkbox_3)
};
pollButton = itemView.findViewById(R.id.status_poll_button); pollButton = itemView.findViewById(R.id.status_poll_button);
pollAdapter = new PollAdapter();
pollOptions.setAdapter(pollAdapter);
pollOptions.setLayoutManager(new LinearLayoutManager(pollOptions.getContext()));
((DefaultItemAnimator) pollOptions.getItemAnimator()).setSupportsChangeAnimations(false);
this.useAbsoluteTime = useAbsoluteTime; this.useAbsoluteTime = useAbsoluteTime;
shortSdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); shortSdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
longSdf = new SimpleDateFormat("MM/dd HH:mm:ss", Locale.getDefault()); longSdf = new SimpleDateFormat("MM/dd HH:mm:ss", Locale.getDefault());
@ -795,15 +775,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private CharSequence getPollDescription(Context context, private CharSequence getPollDescription(Context context,
@NonNull StatusViewData.Concrete status) { @NonNull StatusViewData.Concrete status) {
Poll poll = status.getPoll(); PollViewData poll = status.getPoll();
if (poll == null) { if (poll == null) {
return ""; return "";
} else { } else {
CharSequence[] args = new CharSequence[5]; CharSequence[] args = new CharSequence[5];
List<PollOption> options = poll.getOptions(); List<PollOptionViewData> options = poll.getOptions();
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if (i < options.size()) { if (i < options.size()) {
int percent = options.get(i).getPercent(poll.getVotesCount()); int percent = PollViewDataKt.calculatePercent(options.get(i).getVotesCount(), poll.getVotesCount());
args[i] = HtmlUtils.fromHtml(context.getString( args[i] = HtmlUtils.fromHtml(context.getString(
R.string.poll_option_format, R.string.poll_option_format,
percent, percent,
@ -835,19 +815,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
} }
} }
protected void setupPoll(Poll poll, List<Emoji> emojis, StatusActionListener listener) { protected void setupPoll(PollViewData poll, List<Emoji> emojis, StatusActionListener listener) {
if (poll == null) { if (poll == null) {
for (TextView pollResult : pollResults) {
pollResult.setVisibility(View.GONE);
}
pollDescription.setVisibility(View.GONE);
pollRadioGroup.setVisibility(View.GONE);
for (CheckBox checkBox : pollCheckboxOptions) { pollOptions.setVisibility(View.GONE);
checkBox.setVisibility(View.GONE);
} pollDescription.setVisibility(View.GONE);
pollButton.setVisibility(View.GONE); pollButton.setVisibility(View.GONE);
} else { } else {
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
@ -855,25 +831,49 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
Context context = pollDescription.getContext(); Context context = pollDescription.getContext();
pollOptions.setVisibility(View.VISIBLE);
if (expired || poll.getVoted()) { if (expired || poll.getVoted()) {
// no voting possible // no voting possible
setupPollResult(poll, emojis); pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), emojis, PollAdapter.RESULT);
pollButton.setVisibility(View.GONE);
} else { } else {
// voting possible // voting possible
setupPollVoting(poll, emojis, listener); pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), emojis, poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE);
pollButton.setVisibility(View.VISIBLE);
pollButton.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
List<Integer> pollResult = pollAdapter.getSelected();
if(!pollResult.isEmpty()) {
listener.onVoteInPoll(position, pollResult);
}
}
});
} }
pollDescription.setVisibility(View.VISIBLE); pollDescription.setVisibility(View.VISIBLE);
pollDescription.setText(getPollInfoText(timestamp, poll, context)); pollDescription.setText(getPollInfoText(timestamp, poll, context));
} }
} }
private CharSequence getPollInfoText(long timestamp, Poll poll, Context context) { private CharSequence getPollInfoText(long timestamp, PollViewData poll, Context context) {
String votes = numberFormat.format(poll.getVotesCount()); String votes = numberFormat.format(poll.getVotesCount());
String votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), votes); String votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), votes);
CharSequence pollDurationInfo; CharSequence pollDurationInfo;
if (poll.getExpired()) { if (poll.getExpired()) {
pollDurationInfo = context.getString(R.string.poll_info_closed); pollDurationInfo = context.getString(R.string.poll_info_closed);
} else if (poll.getExpiresAt() == null) {
return votesText;
} else { } else {
if (useAbsoluteTime) { if (useAbsoluteTime) {
pollDurationInfo = context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.getExpiresAt())); pollDurationInfo = context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.getExpiresAt()));
@ -886,129 +886,4 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo); return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo);
} }
private void setupPollResult(Poll poll, List<Emoji> emojis) {
List<PollOption> options = poll.getOptions();
for(int i = 0; i < Status.MAX_POLL_OPTIONS; i++) {
if(i < options.size()) {
int percent = options.get(i).getPercent(poll.getVotesCount());
String pollOptionText = pollResults[i].getContext().getString(R.string.poll_option_format, percent, options.get(i).getTitle());
pollResults[i].setText(CustomEmojiHelper.emojifyText(HtmlUtils.fromHtml(pollOptionText), emojis, pollResults[i]));
pollResults[i].setVisibility(View.VISIBLE);
int level = percent * 100;
pollResults[i].getBackground().setLevel(level);
} else {
pollResults[i].setVisibility(View.GONE);
}
}
pollRadioGroup.setVisibility(View.GONE);
for(CheckBox checkBox: pollCheckboxOptions) {
checkBox.setVisibility(View.GONE);
}
pollButton.setVisibility(View.GONE);
}
private void setupPollVoting(Poll poll, List<Emoji> emojis, StatusActionListener listener) {
List<PollOption> options = poll.getOptions();
pollButton.setVisibility(View.VISIBLE);
for(TextView pollResult: pollResults) {
pollResult.setVisibility(View.GONE);
}
if(poll.getMultiple()) {
pollRadioGroup.setVisibility(View.GONE);
for(int i = 0; i < Status.MAX_POLL_OPTIONS; i++) {
if(i < options.size()) {
CharSequence emojifiedPollOptionText = CustomEmojiHelper.emojifyString(options.get(i).getTitle(), emojis, pollCheckboxOptions[i]);
emojifiedPollOptionText = EmojiCompat.get().process(emojifiedPollOptionText);
pollCheckboxOptions[i].setText(emojifiedPollOptionText);
pollCheckboxOptions[i].setVisibility(View.VISIBLE);
pollCheckboxOptions[i].setChecked(false);
} else {
pollCheckboxOptions[i].setVisibility(View.GONE);
}
}
pollButton.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
List<Integer> pollResult = new ArrayList<>(options.size());
for (int i = 0; i < options.size(); i++) {
if (pollCheckboxOptions[i].isChecked()) {
pollResult.add(i);
}
}
if (pollResult.size() == 0) {
return;
}
listener.onVoteInPoll(position, pollResult);
}
});
} else {
for(CheckBox pollCheckbox: pollCheckboxOptions) {
pollCheckbox.setVisibility(View.GONE);
}
pollRadioGroup.setVisibility(View.VISIBLE);
pollRadioGroup.clearCheck();
for(int i = 0; i < Status.MAX_POLL_OPTIONS; i++) {
if(i < options.size()) {
CharSequence emojifiedPollOptionText = CustomEmojiHelper.emojifyString(options.get(i).getTitle(), emojis, pollRadioOptions[i]);
emojifiedPollOptionText = EmojiCompat.get().process(emojifiedPollOptionText);
pollRadioOptions[i].setText(emojifiedPollOptionText);
pollRadioOptions[i].setVisibility(View.VISIBLE);
} else {
pollRadioOptions[i].setVisibility(View.GONE);
}
}
pollButton.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
int selectedRadioButtonIndex;
switch (pollRadioGroup.getCheckedRadioButtonId()) {
case R.id.status_poll_radio_button_0:
selectedRadioButtonIndex = 0;
break;
case R.id.status_poll_radio_button_1:
selectedRadioButtonIndex = 1;
break;
case R.id.status_poll_radio_button_2:
selectedRadioButtonIndex = 2;
break;
case R.id.status_poll_radio_button_3:
selectedRadioButtonIndex = 3;
break;
default:
return;
}
listener.onVoteInPoll(position, Collections.singletonList(selectedRadioButtonIndex));
}
});
}
}
} }

@ -33,6 +33,7 @@ import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.ImageLoadingHelper; import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.SmartLengthInputFilter; import com.keylesspalace.tusky.util.SmartLengthInputFilter;
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
import java.util.List; import java.util.List;
@ -109,7 +110,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
setAvatars(conversation.getAccounts()); setAvatars(conversation.getAccounts());
setupPoll(status.getPoll(), status.getEmojis(), listener); setupPoll(PollViewDataKt.toViewData(status.getPoll()), status.getEmojis(), listener);
} }

@ -27,6 +27,7 @@ import com.keylesspalace.tusky.interfaces.LinkListener
import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.util.StatusViewHelper.Companion.COLLAPSE_INPUT_FILTER import com.keylesspalace.tusky.util.StatusViewHelper.Companion.COLLAPSE_INPUT_FILTER
import com.keylesspalace.tusky.util.StatusViewHelper.Companion.NO_INPUT_FILTER import com.keylesspalace.tusky.util.StatusViewHelper.Companion.NO_INPUT_FILTER
import com.keylesspalace.tusky.viewdata.toViewData
import kotlinx.android.synthetic.main.item_report_status.view.* import kotlinx.android.synthetic.main.item_report_status.view.*
import java.util.* import java.util.*
@ -72,7 +73,7 @@ class StatusViewHolder(itemView: View,
viewState.isMediaShow(status.id, status.sensitive), viewState.isMediaShow(status.id, status.sensitive),
mediaViewHeight) mediaViewHeight)
statusViewHelper.setupPollReadonly(status.poll, status.emojis, useAbsoluteTime) statusViewHelper.setupPollReadonly(status.poll.toViewData(), status.emojis, useAbsoluteTime)
setCreatedAt(status.createdAt) setCreatedAt(status.createdAt)
} }

@ -2,7 +2,6 @@ package com.keylesspalace.tusky.entity
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import java.util.* import java.util.*
import kotlin.math.roundToInt
data class Poll( data class Poll(
val id: String, val id: String,
@ -31,12 +30,4 @@ data class Poll(
data class PollOption( data class PollOption(
val title: String, val title: String,
@SerializedName("votes_count") val votesCount: Int @SerializedName("votes_count") val votesCount: Int
) { )
fun getPercent(totalVotes: Int): Int {
return if (votesCount == 0) {
0
} else {
(votesCount / totalVotes.toDouble() * 100).roundToInt()
}
}
}

@ -27,6 +27,9 @@ import android.graphics.BitmapFactory;
import android.graphics.Color; import android.graphics.Color;
import android.os.Build; import android.os.Build;
import android.provider.Settings; import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
@ -36,9 +39,6 @@ import androidx.core.app.TaskStackBuilder;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.text.BidiFormatter; import androidx.core.text.BidiFormatter;
import android.text.TextUtils;
import android.util.Log;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.FutureTarget; import com.bumptech.glide.request.FutureTarget;
@ -55,6 +55,7 @@ import com.keylesspalace.tusky.entity.PollOption;
import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver; import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver;
import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver; import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver;
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@ -626,7 +627,7 @@ public class NotificationHelper {
builder.append('\n'); builder.append('\n');
Poll poll = notification.getStatus().getPoll(); Poll poll = notification.getStatus().getPoll();
for(PollOption option: poll.getOptions()) { for(PollOption option: poll.getOptions()) {
int percent = option.getPercent(poll.getVotesCount()); int percent = PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotesCount());
CharSequence optionText = HtmlUtils.fromHtml(context.getString(R.string.poll_option_format, percent, option.getTitle())); CharSequence optionText = HtmlUtils.fromHtml(context.getString(R.string.poll_option_format, percent, option.getTitle()));
builder.append(optionText); builder.append(optionText);
builder.append('\n'); builder.append('\n');

@ -27,12 +27,14 @@ import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.view.MediaPreviewImageView import com.keylesspalace.tusky.view.MediaPreviewImageView
import com.keylesspalace.tusky.viewdata.PollViewData
import com.keylesspalace.tusky.viewdata.calculatePercent
import java.text.NumberFormat import java.text.NumberFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.math.min
class StatusViewHelper(private val itemView: View) { class StatusViewHelper(private val itemView: View) {
interface MediaPreviewListener { interface MediaPreviewListener {
@ -85,7 +87,7 @@ class StatusViewHelper(private val itemView: View) {
val mediaPreviewUnloadedId = ThemeUtils.getDrawableId(context, R.attr.media_preview_unloaded_drawable, android.R.color.black) val mediaPreviewUnloadedId = ThemeUtils.getDrawableId(context, R.attr.media_preview_unloaded_drawable, android.R.color.black)
val n = Math.min(attachments.size, Status.MAX_MEDIA_ATTACHMENTS) val n = min(attachments.size, Status.MAX_MEDIA_ATTACHMENTS)
for (i in 0 until n) { for (i in 0 until n) {
val previewUrl = attachments[i].previewUrl val previewUrl = attachments[i].previewUrl
@ -229,7 +231,7 @@ class StatusViewHelper(private val itemView: View) {
} }
} }
fun setupPollReadonly(poll: Poll?, emojis: List<Emoji>, useAbsoluteTime: Boolean) { fun setupPollReadonly(poll: PollViewData?, emojis: List<Emoji>, useAbsoluteTime: Boolean) {
val pollResults = listOf<TextView>( val pollResults = listOf<TextView>(
itemView.findViewById(R.id.status_poll_option_result_0), itemView.findViewById(R.id.status_poll_option_result_0),
itemView.findViewById(R.id.status_poll_option_result_1), itemView.findViewById(R.id.status_poll_option_result_1),
@ -254,7 +256,7 @@ class StatusViewHelper(private val itemView: View) {
} }
} }
private fun getPollInfoText(timestamp: Long, poll: Poll, pollDescription: TextView, useAbsoluteTime: Boolean): CharSequence { private fun getPollInfoText(timestamp: Long, poll: PollViewData, pollDescription: TextView, useAbsoluteTime: Boolean): CharSequence {
val context = pollDescription.context val context = pollDescription.context
val votes = NumberFormat.getNumberInstance().format(poll.votesCount.toLong()) val votes = NumberFormat.getNumberInstance().format(poll.votesCount.toLong())
val votesText = context.resources.getQuantityString(R.plurals.poll_info_votes, poll.votesCount, votes) val votesText = context.resources.getQuantityString(R.plurals.poll_info_votes, poll.votesCount, votes)
@ -274,12 +276,12 @@ class StatusViewHelper(private val itemView: View) {
} }
private fun setupPollResult(poll: Poll, emojis: List<Emoji>, pollResults: List<TextView>) { private fun setupPollResult(poll: PollViewData, emojis: List<Emoji>, pollResults: List<TextView>) {
val options = poll.options val options = poll.options
for (i in 0 until Status.MAX_POLL_OPTIONS) { for (i in 0 until Status.MAX_POLL_OPTIONS) {
if (i < options.size) { if (i < options.size) {
val percent = options[i].getPercent(poll.votesCount) val percent = calculatePercent(options[i].votesCount, poll.votesCount)
val pollOptionText = pollResults[i].context.getString(R.string.poll_option_format, percent, options[i].title) val pollOptionText = pollResults[i].context.getString(R.string.poll_option_format, percent, options[i].title)
pollResults[i].text = CustomEmojiHelper.emojifyText(HtmlUtils.fromHtml(pollOptionText), emojis, pollResults[i]) pollResults[i].text = CustomEmojiHelper.emojifyText(HtmlUtils.fromHtml(pollOptionText), emojis, pollResults[i])

@ -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
)
}

@ -89,7 +89,7 @@ public abstract class StatusViewData {
private final boolean isCollapsible; /** Whether the status meets the requirement to be collapse */ private final boolean isCollapsible; /** Whether the status meets the requirement to be collapse */
final boolean isCollapsed; /** Whether the status is shown partially or fully */ final boolean isCollapsed; /** Whether the status is shown partially or fully */
@Nullable @Nullable
private final Poll poll; private final PollViewData poll;
private final boolean isBot; private final boolean isBot;
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, public Concrete(String id, Spanned content, boolean reblogged, boolean favourited,
@ -99,7 +99,7 @@ public abstract class StatusViewData {
Date createdAt, int reblogsCount, int favouritesCount, @Nullable String inReplyToId, Date createdAt, int reblogsCount, int favouritesCount, @Nullable String inReplyToId,
@Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled, @Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled,
Status.Application application, List<Emoji> statusEmojis, List<Emoji> accountEmojis, @Nullable Card card, Status.Application application, List<Emoji> statusEmojis, List<Emoji> accountEmojis, @Nullable Card card,
boolean isCollapsible, boolean isCollapsed, @Nullable Poll poll, boolean isBot) { boolean isCollapsible, boolean isCollapsed, @Nullable PollViewData poll, boolean isBot) {
this.id = id; this.id = id;
if (Build.VERSION.SDK_INT == 23) { if (Build.VERSION.SDK_INT == 23) {
@ -273,7 +273,7 @@ public abstract class StatusViewData {
} }
@Nullable @Nullable
public Poll getPoll() { public PollViewData getPoll() {
return poll; return poll;
} }
@ -418,7 +418,7 @@ public abstract class StatusViewData {
private Card card; private Card card;
private boolean isCollapsible; /** Whether the status meets the requirement to be collapsed */ private boolean isCollapsible; /** Whether the status meets the requirement to be collapsed */
private boolean isCollapsed; /** Whether the status is shown partially or fully */ private boolean isCollapsed; /** Whether the status is shown partially or fully */
private Poll poll; private PollViewData poll;
private boolean isBot; private boolean isBot;
public Builder() { public Builder() {
@ -617,7 +617,7 @@ public abstract class StatusViewData {
} }
public Builder setPoll(Poll poll) { public Builder setPoll(Poll poll) {
this.poll = poll; this.poll = PollViewDataKt.toViewData(poll);
return this; return this;
} }

@ -382,171 +382,16 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<androidx.emoji.widget.EmojiTextView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/status_poll_option_result_0" android:id="@+id/status_poll_options"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container"
tools:text="40%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_1"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_0"
tools:text="10%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_2"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_1"
tools:text="20%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_3"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_2"
tools:text="30%" />
<RadioGroup
android:id="@+id/status_poll_radio_group"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="6dp" android:layout_marginTop="4dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginBottom="4dp"
app:layout_constraintStart_toStartOf="@id/status_display_name" android:nestedScrollingEnabled="false"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_3">
<RadioButton
android:id="@+id/status_poll_radio_button_0"
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" />
<RadioButton
android:id="@+id/status_poll_radio_button_1"
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 2" />
<RadioButton
android:id="@+id/status_poll_radio_button_2"
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 3" />
<RadioButton
android:id="@+id/status_poll_radio_button_3"
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 4" />
</RadioGroup>
<CheckBox
android:id="@+id/status_poll_checkbox_0"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" app:layout_constraintTop_toBottomOf="@id/status_media_preview_container" />
tools:text="Option 1" />
<!-- using AppCompatButton because we don't want the inflater to turn it into a MaterialButton --> <!-- using AppCompatButton because we don't want the inflater to turn it into a MaterialButton -->
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
@ -565,7 +410,7 @@
android:text="@string/poll_vote" android:text="@string/poll_vote"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_3" /> app:layout_constraintTop_toBottomOf="@id/status_poll_options" />
<TextView <TextView
android:id="@+id/status_poll_description" android:id="@+id/status_poll_description"

@ -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>

@ -366,171 +366,16 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<androidx.emoji.widget.EmojiTextView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/status_poll_option_result_0" android:id="@+id/status_poll_options"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container"
tools:text="40%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_1"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_0"
tools:text="10%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_2"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_1"
tools:text="20%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_3"
android:layout_width="0dp"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_2"
tools:text="30%" />
<RadioGroup
android:id="@+id/status_poll_radio_group"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="6dp" android:layout_marginTop="4dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginBottom="4dp"
app:layout_constraintStart_toStartOf="@id/status_display_name" android:nestedScrollingEnabled="false"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_3">
<RadioButton
android:id="@+id/status_poll_radio_button_0"
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" />
<RadioButton
android:id="@+id/status_poll_radio_button_1"
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 2" />
<RadioButton
android:id="@+id/status_poll_radio_button_2"
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 3" />
<RadioButton
android:id="@+id/status_poll_radio_button_3"
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 4" />
</RadioGroup>
<CheckBox
android:id="@+id/status_poll_checkbox_0"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" app:layout_constraintTop_toBottomOf="@id/status_media_preview_container" />
tools:text="Option 1" />
<!-- using AppCompatButton because we don't want the inflater to turn it into a MaterialButton --> <!-- using AppCompatButton because we don't want the inflater to turn it into a MaterialButton -->
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
@ -549,7 +394,7 @@
android:text="@string/poll_vote" android:text="@string/poll_vote"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="@id/status_display_name" app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_3" /> app:layout_constraintTop_toBottomOf="@id/status_poll_options" />
<TextView <TextView
android:id="@+id/status_poll_description" android:id="@+id/status_poll_description"

@ -381,172 +381,16 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
<androidx.emoji.widget.EmojiTextView android:id="@+id/status_poll_options"
android:id="@+id/status_poll_option_result_0"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:background="@drawable/poll_option_background"
android:ellipsize="end"
android:lines="1"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="?attr/status_text_medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container"
tools:text="40%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:background="@drawable/poll_option_background"
android:ellipsize="end"
android:lines="1"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="?attr/status_text_medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_0"
tools:text="10%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:background="@drawable/poll_option_background"
android:ellipsize="end"
android:lines="1"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="?attr/status_text_medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_1"
tools:text="20%" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/status_poll_option_result_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:background="@drawable/poll_option_background"
android:ellipsize="end"
android:lines="1"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="?attr/status_text_medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_2"
tools:text="30%" />
<RadioGroup
android:id="@+id/status_poll_radio_group"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_option_result_3">
<RadioButton
android:id="@+id/status_poll_radio_button_0"
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" />
<RadioButton
android:id="@+id/status_poll_radio_button_1"
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 2" />
<RadioButton
android:id="@+id/status_poll_radio_button_2"
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 3" />
<RadioButton
android:id="@+id/status_poll_radio_button_3"
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 4" />
</RadioGroup>
<CheckBox
android:id="@+id/status_poll_checkbox_0"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_radio_group"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_0"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_2"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:layout_marginTop="4dp"
android:lines="1" android:layout_marginBottom="4dp"
app:buttonTint="?attr/compound_button_color" android:nestedScrollingEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_1"
tools:text="Option 1" />
<CheckBox
android:id="@+id/status_poll_checkbox_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
app:buttonTint="?attr/compound_button_color"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_2" app:layout_constraintTop_toBottomOf="@id/status_media_preview_container" />
tools:text="Option 1" />
<!-- using AppCompatButton because we don't want the inflater to turn it into a MaterialButton --> <!-- using AppCompatButton because we don't want the inflater to turn it into a MaterialButton -->
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
@ -565,7 +409,7 @@
android:text="@string/poll_vote" android:text="@string/poll_vote"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_checkbox_3" /> app:layout_constraintTop_toBottomOf="@id/status_poll_options" />
<TextView <TextView
android:id="@+id/status_poll_description" android:id="@+id/status_poll_description"

Loading…
Cancel
Save