refactor: Extract Poll display code to `PollView` (#177)

This commit is contained in:
Nik Clayton 2023-10-15 22:26:34 +02:00 committed by GitHub
parent 24e195f5b9
commit c50f10a989
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 330 additions and 310 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.1.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.1)" variant="all" version="8.1.1">
<issues format="6" by="lint 8.1.2" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.2)" variant="all" version="8.1.2">
<issue
id="InvalidPackage"
@ -3787,7 +3787,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
line="236"
line="232"
column="25"/>
</issue>
@ -3798,7 +3798,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
line="324"
line="320"
column="34"/>
</issue>
@ -3809,7 +3809,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
line="337"
line="333"
column="13"/>
</issue>
@ -3820,7 +3820,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/fragment/ViewImageFragment.kt"
line="344"
line="340"
column="17"/>
</issue>
@ -3886,7 +3886,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/fragment/ViewVideoFragment.kt"
line="143"
line="145"
column="32"/>
</issue>
@ -3897,7 +3897,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/fragment/ViewVideoFragment.kt"
line="224"
line="226"
column="21"/>
</issue>
@ -4381,17 +4381,6 @@
column="6"/>
</issue>
<issue
id="SelectableText"
message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
errorLine1=" &lt;TextView"
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_conversation.xml"
line="234"
column="6"/>
</issue>
<issue
id="SelectableText"
message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
@ -4817,7 +4806,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status.xml"
line="292"
line="287"
column="6"/>
</issue>
@ -4828,7 +4817,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status.xml"
line="319"
line="315"
column="6"/>
</issue>
@ -4839,18 +4828,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status.xml"
line="347"
column="6"/>
</issue>
<issue
id="SelectableText"
message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
errorLine1=" &lt;TextView"
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status.xml"
line="375"
line="343"
column="6"/>
</issue>
@ -4916,7 +4894,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status_detailed.xml"
line="255"
line="234"
column="6"/>
</issue>
@ -4927,7 +4905,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status_detailed.xml"
line="268"
line="263"
column="6"/>
</issue>
@ -4938,18 +4916,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status_detailed.xml"
line="297"
column="6"/>
</issue>
<issue
id="SelectableText"
message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
errorLine1=" &lt;TextView"
errorLine2=" ~~~~~~~~">
<location
file="src/main/res/layout/item_status_detailed.xml"
line="311"
line="277"
column="6"/>
</issue>
@ -5235,7 +5202,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_status.xml"
line="309"
line="277"
column="9"/>
</issue>

View File

@ -1,7 +1,5 @@
package app.pachli.adapter;
import static app.pachli.viewdata.PollViewDataKt.buildDescription;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
@ -26,8 +24,6 @@ import androidx.appcompat.widget.PopupMenu;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import androidx.core.text.HtmlCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
@ -69,9 +65,8 @@ import app.pachli.util.TimestampUtils;
import app.pachli.util.TouchDelegateHelper;
import app.pachli.view.MediaPreviewImageView;
import app.pachli.view.MediaPreviewLayout;
import app.pachli.viewdata.PollOptionViewData;
import app.pachli.view.PollView;
import app.pachli.viewdata.PollViewData;
import app.pachli.viewdata.PollViewDataKt;
import app.pachli.viewdata.StatusViewData;
import at.connyduck.sparkbutton.SparkButton;
import at.connyduck.sparkbutton.helpers.Utils;
@ -108,18 +103,14 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
public final TextView content;
public final TextView contentWarningDescription;
private final RecyclerView pollOptions;
private final TextView pollDescription;
private final Button pollButton;
@NonNull
private final PollView pollView;
private final LinearLayout cardView;
private final LinearLayout cardInfo;
private final ShapeableImageView cardImage;
private final TextView cardTitle;
private final TextView cardDescription;
private final TextView cardUrl;
@NonNull
private final PollAdapter pollAdapter;
protected final LinearLayout filteredPlaceholder;
protected final TextView filteredPlaceholderLabel;
protected final Button filteredPlaceholderShowButton;
@ -166,9 +157,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
contentWarningButton = itemView.findViewById(R.id.status_content_warning_button);
avatarInset = itemView.findViewById(R.id.status_avatar_inset);
pollOptions = itemView.findViewById(R.id.status_poll_options);
pollDescription = itemView.findViewById(R.id.status_poll_description);
pollButton = itemView.findViewById(R.id.status_poll_button);
pollView = itemView.findViewById(R.id.status_poll);
cardView = itemView.findViewById(R.id.status_card_view);
cardInfo = itemView.findViewById(R.id.card_info);
@ -182,13 +171,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
filteredPlaceholderShowButton = itemView.findViewById(R.id.status_filter_show_anyway);
statusContainer = itemView.findViewById(R.id.status_container);
pollAdapter = new PollAdapter();
pollOptions.setAdapter(pollAdapter);
pollOptions.setLayoutManager(new LinearLayoutManager(pollOptions.getContext()));
DefaultItemAnimator itemAnimator = (DefaultItemAnimator) pollOptions.getItemAnimator();
if (itemAnimator != null) itemAnimator.setSupportsChangeAnimations(false);
this.avatarRadius48dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_48dp);
this.avatarRadius36dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_36dp);
this.avatarRadius24dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_24dp);
@ -289,12 +271,29 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
updateMediaLabel(i, sensitive, true);
}
if (poll != null) {
setupPoll(PollViewData.Companion.from(poll), emojis, statusDisplayOptions, listener);
PollView.OnClickListener pollListener = (List<Integer> choices) -> {
int position = getBindingAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
if (choices == null) {
listener.onViewThread(position);
} else {
listener.onVoteInPoll(position, choices);
}
}
};
pollView.bind(
PollViewData.Companion.from(poll),
emojis,
statusDisplayOptions,
numberFormat,
absoluteTimeFormatter,
pollListener
);
} else {
hidePoll();
pollView.hide();
}
} else {
hidePoll();
pollView.hide();
LinkHelper.setClickableMentions(this.content, mentions, listener);
}
if (TextUtils.isEmpty(this.content.getText())) {
@ -304,12 +303,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
}
private void hidePoll() {
pollButton.setVisibility(View.GONE);
pollDescription.setVisibility(View.GONE);
pollOptions.setVisibility(View.GONE);
}
private void setAvatar(String url,
@Nullable String rebloggedUrl,
boolean isBot,
@ -875,6 +868,17 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
Context context = itemView.getContext();
Status actionable = status.getActionable();
Poll poll = actionable.getPoll();
CharSequence pollDescription = "";
if (poll != null) {
pollDescription = pollView.getPollDescription(
PollViewData.Companion.from(poll),
statusDisplayOptions,
numberFormat,
absoluteTimeFormatter
);
}
String description = context.getString(R.string.description_status,
actionable.getAccount().getDisplayName(),
getContentWarningDescription(context, status),
@ -890,7 +894,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
getVisibilityDescription(context, actionable.getVisibility()),
getFavsText(context, actionable.getFavouritesCount()),
getReblogsText(context, actionable.getReblogsCount()),
getPollDescription(status, context, statusDisplayOptions)
pollDescription
);
itemView.setContentDescription(description);
}
@ -959,34 +963,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
return context.getString(resource);
}
@NonNull
private CharSequence getPollDescription(@NonNull StatusViewData status,
@NonNull Context context,
@NonNull StatusDisplayOptions statusDisplayOptions) {
Poll poll = status.getActionable().getPoll();
if (poll == null) {
return "";
}
PollViewData pollViewData = PollViewData.Companion.from(poll);
Object[] args = new CharSequence[5];
List<PollOptionViewData> options = pollViewData.getOptions();
int totalVotes = pollViewData.getVotesCount();
Integer totalVoters = pollViewData.getVotersCount();
for (int i = 0; i < args.length; i++) {
if (i < options.size()) {
int percent = PollViewDataKt.calculatePercent(options.get(i).getVotesCount(), totalVoters, totalVotes);
args[i] = buildDescription(options.get(i).getTitle(), percent, options.get(i).getVoted(), context);
} else {
args[i] = "";
}
}
args[4] = getPollInfoText(System.currentTimeMillis(), pollViewData, statusDisplayOptions,
context);
return context.getString(R.string.description_poll, args);
}
@NonNull
protected CharSequence getFavsText(@NonNull Context context, int count) {
if (count > 0) {
@ -1007,100 +983,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
}
private void setupPoll(@NonNull PollViewData poll, @NonNull List<Emoji> emojis,
@NonNull StatusDisplayOptions statusDisplayOptions,
@NonNull StatusActionListener listener) {
long timestamp = System.currentTimeMillis();
boolean expired = poll.getExpired() || (poll.getExpiresAt() != null && timestamp > poll.getExpiresAt().getTime());
Context context = pollDescription.getContext();
pollOptions.setVisibility(View.VISIBLE);
if (expired || poll.getVoted()) {
// no voting possible
View.OnClickListener viewThreadListener = v -> {
int position = getBindingAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onViewThread(position);
}
};
pollAdapter.setup(
poll.getOptions(),
poll.getVotesCount(),
poll.getVotersCount(),
emojis,
PollAdapter.RESULT,
viewThreadListener,
statusDisplayOptions.animateEmojis()
);
pollButton.setVisibility(View.GONE);
} else {
// voting possible
View.OnClickListener optionClickListener = v -> {
pollButton.setEnabled(!pollAdapter.getSelected().isEmpty());
};
pollAdapter.setup(
poll.getOptions(),
poll.getVotesCount(),
poll.getVotersCount(),
emojis,
poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE,
null,
statusDisplayOptions.animateEmojis(),
true,
optionClickListener
);
pollButton.setVisibility(View.VISIBLE);
pollButton.setEnabled(false);
pollButton.setOnClickListener(v -> {
int position = getBindingAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
List<Integer> pollResult = pollAdapter.getSelected();
if (!pollResult.isEmpty()) {
listener.onVoteInPoll(position, pollResult);
}
}
});
}
pollDescription.setVisibility(View.VISIBLE);
pollDescription.setText(getPollInfoText(timestamp, poll, statusDisplayOptions, context));
}
@NonNull
private CharSequence getPollInfoText(long timestamp, @NonNull PollViewData poll,
@NonNull StatusDisplayOptions statusDisplayOptions,
@NonNull Context context) {
String votesText;
if (poll.getVotersCount() == null) {
String voters = numberFormat.format(poll.getVotesCount());
votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), voters);
} else {
String voters = numberFormat.format(poll.getVotersCount());
votesText = context.getResources().getQuantityString(R.plurals.poll_info_people, poll.getVotersCount(), voters);
}
CharSequence pollDurationInfo;
if (poll.getExpired()) {
pollDurationInfo = context.getString(R.string.poll_info_closed);
} else if (poll.getExpiresAt() == null) {
return votesText;
} else {
if (statusDisplayOptions.useAbsoluteTime()) {
pollDurationInfo = context.getString(R.string.poll_info_time_absolute, absoluteTimeFormatter.format(poll.getExpiresAt(), false));
} else {
pollDurationInfo = TimestampUtils.formatPollDuration(pollDescription.getContext(), poll.getExpiresAt().getTime(), timestamp);
}
}
return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo);
}
protected void setupCard(
@NonNull final StatusViewData status,
boolean expanded,
@ -1245,9 +1127,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
content.setVisibility(visibility);
cardView.setVisibility(visibility);
mediaContainer.setVisibility(visibility);
pollOptions.setVisibility(visibility);
pollButton.setVisibility(visibility);
pollDescription.setVisibility(visibility);
pollView.setVisibility(visibility);
replyButton.setVisibility(visibility);
reblogButton.setVisibility(visibility);
favouriteButton.setVisibility(visibility);

View File

@ -0,0 +1,218 @@
/*
* Copyright 2023 Pachli Association
*
* This file is a part of Pachli.
*
* 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.
*
* Pachli 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 Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/
package app.pachli.view
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import app.pachli.R
import app.pachli.adapter.PollAdapter
import app.pachli.databinding.StatusPollBinding
import app.pachli.entity.Emoji
import app.pachli.util.AbsoluteTimeFormatter
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.formatPollDuration
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.viewdata.PollViewData
import app.pachli.viewdata.buildDescription
import app.pachli.viewdata.calculatePercent
import java.text.NumberFormat
/**
* Compound view that displays [PollViewData].
*
* If the poll is still active / the user hasn't voted then poll options are shown as radio
* buttons / checkboxes (depending on whether or not it is multiple choice) and the user can
* vote.
*
* Otherwise the results of the poll are shown.
*
* Classes hosting this should provide a [PollView.OnClickListener] to be notified when the
* user clicks on the poll (either to vote, or to navigate).
*/
class PollView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0,
) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
fun interface OnClickListener {
/**
* @param choices If null the user has clicked on the poll without voting and this
* should be treated as a navigation click. If non-null the user has voted,
* and [choices] contains the option(s) they voted for.
*/
fun onClick(choices: List<Int>?): Unit
}
val binding: StatusPollBinding
init {
val inflater = context.getSystemService(LayoutInflater::class.java)
binding = StatusPollBinding.inflate(inflater, this)
orientation = VERTICAL
}
fun bind(
pollViewData: PollViewData,
emojis: List<Emoji>,
statusDisplayOptions: StatusDisplayOptions,
numberFormat: NumberFormat,
absoluteTimeFormatter: AbsoluteTimeFormatter,
listener: OnClickListener,
) {
val adapter = PollAdapter()
binding.statusPollOptions.adapter = adapter
binding.statusPollOptions.layoutManager = LinearLayoutManager(context)
(binding.statusPollOptions.itemAnimator as? DefaultItemAnimator)?.supportsChangeAnimations = false
val now = System.currentTimeMillis()
binding.statusPollOptions.show()
binding.statusPollDescription.text = getPollInfoText(
pollViewData,
now,
statusDisplayOptions,
numberFormat,
absoluteTimeFormatter,
)
binding.statusPollDescription.show()
val expired = pollViewData.expired || ((pollViewData.expiresAt != null) && (now > pollViewData.expiresAt.time))
// Poll expired or already voted, can't vote now
if (expired || pollViewData.voted) {
adapter.setup(
pollViewData.options,
pollViewData.votesCount,
pollViewData.votersCount,
emojis,
PollAdapter.RESULT,
{ listener.onClick(null) },
statusDisplayOptions.animateEmojis,
)
binding.statusPollButton.hide()
return
}
// Active poll, can vote
adapter.setup(
pollViewData.options,
pollViewData.votesCount,
pollViewData.votersCount,
emojis,
if (pollViewData.multiple) PollAdapter.MULTIPLE else PollAdapter.SINGLE,
null,
statusDisplayOptions.animateEmojis,
true,
) {
binding.statusPollButton.isEnabled = adapter.getSelected().isNotEmpty()
}
binding.statusPollButton.show()
binding.statusPollButton.isEnabled = false
binding.statusPollButton.setOnClickListener {
val selected = adapter.getSelected()
if (selected.isNotEmpty()) listener.onClick(selected)
}
}
fun hide() {
binding.statusPollOptions.hide()
binding.statusPollButton.hide()
binding.statusPollDescription.hide()
}
private fun getPollInfoText(
pollViewData: PollViewData,
timestamp: Long,
statusDisplayOptions: StatusDisplayOptions,
numberFormat: NumberFormat,
absoluteTimeFormatter: AbsoluteTimeFormatter,
): CharSequence {
val votesText: String = if (pollViewData.votersCount == null) {
val voters = numberFormat.format(pollViewData.votesCount.toLong())
context.resources.getQuantityString(
R.plurals.poll_info_votes,
pollViewData.votesCount,
voters,
)
} else {
val voters = numberFormat.format(pollViewData.votersCount)
context.resources.getQuantityString(
R.plurals.poll_info_people,
pollViewData.votersCount,
voters,
)
}
val pollDurationInfo: CharSequence = if (pollViewData.expired) {
context.getString(R.string.poll_info_closed)
} else if (pollViewData.expiresAt == null) {
return votesText
} else {
if (statusDisplayOptions.useAbsoluteTime) {
context.getString(
R.string.poll_info_time_absolute,
absoluteTimeFormatter.format(pollViewData.expiresAt, false),
)
} else {
formatPollDuration(context, pollViewData.expiresAt.time, timestamp)
}
}
return context.getString(
R.string.poll_info_format,
votesText,
pollDurationInfo,
)
}
@SuppressLint("StringFormatMatches") // Lint doesn't understand the spread (*) operator
fun getPollDescription(
pollViewData: PollViewData,
statusDisplayOptions: StatusDisplayOptions,
numberFormat: NumberFormat,
absoluteTimeFormatter: AbsoluteTimeFormatter,
): CharSequence {
val args: Array<CharSequence?> = arrayOfNulls(5)
val options = pollViewData.options
val totalVotes = pollViewData.votesCount
val totalVoters = pollViewData.votersCount
for (i in args.indices) {
if (i < options.size) {
val percent = calculatePercent(options[i].votesCount, totalVoters, totalVotes)
args[i] = buildDescription(options[i].title, percent, options[i].voted, context)
} else {
args[i] = ""
}
}
args[4] = getPollInfoText(
pollViewData,
System.currentTimeMillis(),
statusDisplayOptions,
numberFormat,
absoluteTimeFormatter,
)
return context.getString(R.string.description_poll, *args)
}
}

View File

@ -201,46 +201,15 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/status_poll_options"
<app.pachli.view.PollView
android:id="@+id/status_poll"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:nestedScrollingEnabled="false"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container" />
<Button
android:id="@+id/status_poll_button"
style="@style/AppButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center"
android:minWidth="150dp"
android:minHeight="0dp"
android:paddingLeft="16dp"
android:paddingTop="4dp"
android:paddingRight="16dp"
android:paddingBottom="4dp"
android:text="@string/poll_vote"
android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_options" />
<TextView
android:id="@+id/status_poll_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
tools:text="7 votes • 7 hours remaining" />
<ImageButton
android:id="@+id/status_reply"
style="@style/AppImageButton"
@ -254,7 +223,7 @@
app:layout_constraintEnd_toStartOf="@id/status_favourite"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
app:layout_constraintTop_toBottomOf="@id/status_poll"
app:srcCompat="@drawable/ic_reply_24dp" />
<at.connyduck.sparkbutton.SparkButton

View File

@ -259,48 +259,16 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/status_poll_options"
<app.pachli.view.PollView
android:id="@+id/status_poll"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginEnd="14dp"
android:layout_marginBottom="4dp"
android:nestedScrollingEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container" />
<Button
android:id="@+id/status_poll_button"
style="@style/AppButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center"
android:minWidth="150dp"
android:minHeight="0dp"
android:paddingLeft="16dp"
android:paddingTop="4dp"
android:paddingRight="16dp"
android:paddingBottom="4dp"
android:text="@string/poll_vote"
android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_options" />
<TextView
android:id="@+id/status_poll_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginEnd="14dp"
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_button"
tools:text="7 votes • 7 hours remaining" />
<ImageButton
android:id="@+id/status_reply"
style="@style/AppImageButton"
@ -313,7 +281,7 @@
app:layout_constraintEnd_toStartOf="@id/status_inset"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
app:layout_constraintTop_toBottomOf="@id/status_poll"
app:srcCompat="@drawable/ic_reply_24dp" />
<TextView

View File

@ -220,51 +220,17 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/status_poll_options"
<app.pachli.view.PollView
android:id="@+id/status_poll"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="14dp"
android:layout_marginBottom="4dp"
android:nestedScrollingEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_media_preview_container" />
<Button
android:id="@+id/status_poll_button"
style="@style/AppButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="4dp"
android:gravity="center"
android:minWidth="150dp"
android:minHeight="0dp"
android:paddingLeft="16dp"
android:paddingTop="4dp"
android:paddingRight="16dp"
android:paddingBottom="4dp"
android:text="@string/poll_vote"
android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_options" />
<TextView
android:id="@+id/status_poll_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="14dp"
android:textSize="?attr/status_text_medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
tools:text="7 votes • 7 hours remaining" />
<TextView
android:id="@+id/status_meta_info"
android:layout_width="0dp"
@ -279,7 +245,7 @@
android:textSize="?attr/status_text_medium"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
app:layout_constraintTop_toBottomOf="@id/status_poll"
tools:text="21 Dec 2018 18:45" />
<com.google.android.material.divider.MaterialDivider

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2023 Pachli Association
~
~ This file is a part of Pachli.
~
~ 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.
~
~ Pachli 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 Pachli; if not,
~ see <http://www.gnu.org/licenses>.
-->
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/status_poll_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
<Button
android:id="@+id/status_poll_button"
style="@style/AppButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center"
android:minWidth="150dp"
android:minHeight="0dp"
android:paddingLeft="16dp"
android:paddingTop="4dp"
android:paddingRight="16dp"
android:paddingBottom="4dp"
android:text="@string/poll_vote"
android:textSize="?attr/status_text_medium" />
<TextView
android:id="@+id/status_poll_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:textSize="?attr/status_text_medium"
android:textIsSelectable="true"
tools:text="7 votes • 7 hours remaining" />
</merge>