issue 2890: Add an "ALT" sticker to the media preview container (#2942)
* issue 2890: Add an "ALT" sticker to the media preview container if there are descriptions * issue 2890: Use end and start for positioning * issue 2890: Adapt to new media view group * issue 2890: Use an indicator overlay for every (single) preview image * issue 2890: Reduce radius to match that of the preview layout * issue 2890: Remove (again) unused code * issue 2890: Set visibility in any case * issue 2890: Use a translatable text for ALT * issue 2890: Show ALT flag only when showing media * issue 2890: Call doOnLayout on the layout wrapper
This commit is contained in:
parent
2de2af0a8c
commit
6aed1c6806
7 changed files with 66 additions and 13 deletions
|
@ -411,14 +411,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
return ImageLoadingHelper.decodeBlurHash(this.avatar.getContext(), blurhash);
|
return ImageLoadingHelper.decodeBlurHash(this.avatar.getContext(), blurhash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadImage(MediaPreviewImageView imageView,
|
private void loadImage(View wrapper,
|
||||||
|
MediaPreviewImageView imageView,
|
||||||
@Nullable String previewUrl,
|
@Nullable String previewUrl,
|
||||||
@Nullable MetaData meta,
|
@Nullable MetaData meta,
|
||||||
@Nullable String blurhash) {
|
@Nullable String blurhash) {
|
||||||
|
|
||||||
Drawable placeholder = blurhash != null ? decodeBlurHash(blurhash) : mediaPreviewUnloaded;
|
Drawable placeholder = blurhash != null ? decodeBlurHash(blurhash) : mediaPreviewUnloaded;
|
||||||
|
|
||||||
ViewKt.doOnLayout(imageView, view -> {
|
ViewKt.doOnLayout(wrapper, view -> {
|
||||||
if (TextUtils.isEmpty(previewUrl)) {
|
if (TextUtils.isEmpty(previewUrl)) {
|
||||||
imageView.removeFocalPoint();
|
imageView.removeFocalPoint();
|
||||||
|
|
||||||
|
@ -433,7 +434,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
if (focus != null) { // If there is a focal point for this attachment:
|
if (focus != null) { // If there is a focal point for this attachment:
|
||||||
imageView.setFocalPoint(focus);
|
imageView.setFocalPoint(focus);
|
||||||
|
|
||||||
Glide.with(imageView)
|
Glide.with(imageView.getContext())
|
||||||
.load(previewUrl)
|
.load(previewUrl)
|
||||||
.placeholder(placeholder)
|
.placeholder(placeholder)
|
||||||
.centerInside()
|
.centerInside()
|
||||||
|
@ -460,19 +461,21 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
mediaPreview.setVisibility(View.VISIBLE);
|
mediaPreview.setVisibility(View.VISIBLE);
|
||||||
mediaPreview.setAspectRatios(AttachmentHelper.aspectRatios(attachments));
|
mediaPreview.setAspectRatios(AttachmentHelper.aspectRatios(attachments));
|
||||||
|
|
||||||
mediaPreview.forEachIndexed((i, imageView) -> {
|
mediaPreview.forEachIndexed((i, wrapper, imageView, descriptionIndicator) -> {
|
||||||
Attachment attachment = attachments.get(i);
|
Attachment attachment = attachments.get(i);
|
||||||
String previewUrl = attachment.getPreviewUrl();
|
String previewUrl = attachment.getPreviewUrl();
|
||||||
String description = attachment.getDescription();
|
String description = attachment.getDescription();
|
||||||
|
boolean hasDescription = !TextUtils.isEmpty(description);
|
||||||
|
|
||||||
if (TextUtils.isEmpty(description)) {
|
if (hasDescription) {
|
||||||
imageView.setContentDescription(imageView.getContext()
|
|
||||||
.getString(R.string.action_view_media));
|
|
||||||
} else {
|
|
||||||
imageView.setContentDescription(description);
|
imageView.setContentDescription(description);
|
||||||
|
} else {
|
||||||
|
imageView.setContentDescription(imageView.getContext().getString(R.string.action_view_media));
|
||||||
}
|
}
|
||||||
|
descriptionIndicator.setVisibility(hasDescription ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
loadImage(
|
loadImage(
|
||||||
|
wrapper,
|
||||||
imageView,
|
imageView,
|
||||||
showingContent ? previewUrl : null,
|
showingContent ? previewUrl : null,
|
||||||
attachment.getMeta(),
|
attachment.getMeta(),
|
||||||
|
@ -502,6 +505,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
}
|
}
|
||||||
v.setVisibility(View.GONE);
|
v.setVisibility(View.GONE);
|
||||||
sensitiveMediaWarning.setVisibility(View.VISIBLE);
|
sensitiveMediaWarning.setVisibility(View.VISIBLE);
|
||||||
|
descriptionIndicator.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
sensitiveMediaWarning.setOnClickListener(v -> {
|
sensitiveMediaWarning.setOnClickListener(v -> {
|
||||||
if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) {
|
if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||||
|
@ -509,6 +513,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
}
|
}
|
||||||
v.setVisibility(View.GONE);
|
v.setVisibility(View.GONE);
|
||||||
sensitiveMediaShow.setVisibility(View.VISIBLE);
|
sensitiveMediaShow.setVisibility(View.VISIBLE);
|
||||||
|
descriptionIndicator.setVisibility(hasDescription ? View.VISIBLE : View.GONE);
|
||||||
});
|
});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -2,9 +2,11 @@ package com.keylesspalace.tusky.view
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@ -26,7 +28,9 @@ class MediaPreviewLayout(context: Context, attrs: AttributeSet? = null) :
|
||||||
attachImageViews()
|
attachImageViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val imageViewCache = Array(4) { MediaPreviewImageView(context) }
|
private val imageViewCache = Array(4) {
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.item_image_preview_overlay, this, false)
|
||||||
|
}
|
||||||
|
|
||||||
private var measuredOrientation = LinearLayout.VERTICAL
|
private var measuredOrientation = LinearLayout.VERTICAL
|
||||||
|
|
||||||
|
@ -180,9 +184,15 @@ class MediaPreviewLayout(context: Context, attrs: AttributeSet? = null) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun forEachIndexed(action: (Int, MediaPreviewImageView) -> Unit) {
|
inline fun forEachIndexed(action: (Int, View, MediaPreviewImageView, TextView) -> Unit) {
|
||||||
for (index in 0 until childCount) {
|
for (index in 0 until childCount) {
|
||||||
action(index, getChildAt(index) as MediaPreviewImageView)
|
val wrapper = getChildAt(index)
|
||||||
|
action(
|
||||||
|
index,
|
||||||
|
wrapper,
|
||||||
|
wrapper.findViewById(R.id.preview_image_view) as MediaPreviewImageView,
|
||||||
|
wrapper.findViewById(R.id.preview_media_description_indicator) as TextView
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="7dp" />
|
||||||
<solid android:color="@color/color_background_transparent_60" />
|
<solid android:color="@color/color_background_transparent_60" />
|
||||||
</shape>
|
</shape>
|
36
app/src/main/res/layout/item_image_preview_overlay.xml
Normal file
36
app/src/main/res/layout/item_image_preview_overlay.xml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
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">
|
||||||
|
|
||||||
|
<com.keylesspalace.tusky.view.MediaPreviewImageView
|
||||||
|
android:id="@+id/preview_image_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
tools:ignore="RtlSymmetry"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/preview_media_description_indicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="?attr/status_text_small"
|
||||||
|
android:background="@drawable/media_warning_bg"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="@string/post_media_alt"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/preview_image_view"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/preview_image_view"
|
||||||
|
app:tint="@color/white" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -20,7 +20,7 @@
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:padding="@dimen/status_sensitive_media_button_padding"
|
android:padding="@dimen/status_sensitive_media_button_padding"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
|
app:layout_constraintStart_toStartOf="@+id/status_media_preview_container"
|
||||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
|
app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
|
||||||
app:srcCompat="@drawable/ic_eye_24dp"
|
app:srcCompat="@drawable/ic_eye_24dp"
|
||||||
app:tint="@color/white" />
|
app:tint="@color/white" />
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
<string name="post_boosted_format">%s teilte</string>
|
<string name="post_boosted_format">%s teilte</string>
|
||||||
<string name="post_sensitive_media_title">Heikle Inhalte</string>
|
<string name="post_sensitive_media_title">Heikle Inhalte</string>
|
||||||
<string name="post_media_hidden_title">Medien versteckt</string>
|
<string name="post_media_hidden_title">Medien versteckt</string>
|
||||||
|
<string name="post_media_alt">ALT</string>
|
||||||
<string name="post_sensitive_media_directions">Zum Anzeigen tippen</string>
|
<string name="post_sensitive_media_directions">Zum Anzeigen tippen</string>
|
||||||
<string name="post_content_warning_show_more">Zeige mehr</string>
|
<string name="post_content_warning_show_more">Zeige mehr</string>
|
||||||
<string name="post_content_warning_show_less">Zeige weniger</string>
|
<string name="post_content_warning_show_less">Zeige weniger</string>
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
<string name="post_boosted_format">%s boosted</string>
|
<string name="post_boosted_format">%s boosted</string>
|
||||||
<string name="post_sensitive_media_title">Sensitive content</string>
|
<string name="post_sensitive_media_title">Sensitive content</string>
|
||||||
<string name="post_media_hidden_title">Media hidden</string>
|
<string name="post_media_hidden_title">Media hidden</string>
|
||||||
|
<string name="post_media_alt">ALT</string>
|
||||||
<string name="post_sensitive_media_directions">Click to view</string>
|
<string name="post_sensitive_media_directions">Click to view</string>
|
||||||
<string name="post_content_warning_show_more">Show More</string>
|
<string name="post_content_warning_show_more">Show More</string>
|
||||||
<string name="post_content_warning_show_less">Show Less</string>
|
<string name="post_content_warning_show_less">Show Less</string>
|
||||||
|
|
Loading…
Reference in a new issue