diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index c4999e9db..4a7dce80d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -24,15 +24,20 @@ import android.support.v4.content.ContextCompat; import android.support.v7.widget.RecyclerView; import android.text.SpannableStringBuilder; import android.text.Spanned; +import android.text.TextUtils; import android.text.style.StyleSpan; +import android.text.style.URLSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.TextView; +import android.widget.ToggleButton; import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Notification; +import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.view.RoundedTransformation; import com.keylesspalace.tusky.viewdata.NotificationViewData; @@ -263,7 +268,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter { } private static class StatusNotificationViewHolder extends RecyclerView.ViewHolder - implements View.OnClickListener { + implements View.OnClickListener, ToggleButton.OnCheckedChangeListener { private final TextView message; private final ImageView icon; private final TextView statusContent; @@ -271,10 +276,14 @@ public class NotificationsAdapter extends RecyclerView.Adapter { private final ImageView statusAvatar; private final ImageView notificationAvatar; private final ViewGroup topBar; + private final View contentWarningBar; + private final TextView contentWarningDescriptionTextView; + private final ToggleButton contentWarningButton; private String accountId; private String notificationId; private NotificationActionListener listener; + private StatusViewData.Concrete statusViewData; StatusNotificationViewHolder(View itemView) { super(itemView); @@ -285,6 +294,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter { statusAvatar = itemView.findViewById(R.id.notification_status_avatar); notificationAvatar = itemView.findViewById(R.id.notification_notification_avatar); topBar = itemView.findViewById(R.id.notification_top_bar); + contentWarningBar = itemView.findViewById(R.id.notification_content_warning_bar); + contentWarningDescriptionTextView = itemView.findViewById(R.id.notification_content_warning_description); + contentWarningButton = itemView.findViewById(R.id.notification_content_warning_button); int darkerFilter = Color.rgb(123, 123, 123); statusAvatar.setColorFilter(darkerFilter, PorterDuff.Mode.MULTIPLY); @@ -292,10 +304,13 @@ public class NotificationsAdapter extends RecyclerView.Adapter { container.setOnClickListener(this); topBar.setOnClickListener(this); + contentWarningButton.setOnCheckedChangeListener(this); } void setMessage(Notification.Type type, String displayName, StatusViewData.Concrete status) { + this.statusViewData = status; + Context context = message.getContext(); String format; switch (type) { @@ -320,7 +335,10 @@ public class NotificationsAdapter extends RecyclerView.Adapter { str.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); message.setText(str); - statusContent.setText(status.getContent()); + + boolean hasSpoiler = !TextUtils.isEmpty(statusViewData.getSpoilerText()); + contentWarningBar.setVisibility(hasSpoiler ? View.VISIBLE : View.GONE); + setupContentAndSpoiler(false); } void setupButtons(final NotificationActionListener listener, final String accountId, @@ -357,15 +375,48 @@ public class NotificationsAdapter extends RecyclerView.Adapter { @Override public void onClick(View v) { - if (listener == null) return; switch (v.getId()) { case R.id.notification_container: - listener.onViewStatusForNotificationId(notificationId); + if (listener != null) listener.onViewStatusForNotificationId(notificationId); break; case R.id.notification_top_bar: - listener.onViewAccount(accountId); + if (listener != null) listener.onViewAccount(accountId); break; } } + + private void setupContentAndSpoiler(boolean shouldShowContentIfSpoiler) { + boolean hasSpoiler = !TextUtils.isEmpty(statusViewData.getSpoilerText()); + CharSequence content; + if (!shouldShowContentIfSpoiler && hasSpoiler) { + if (statusViewData.getMentions() != null && + statusViewData.getMentions().length > 0) { + // If there is a content warning and mentions we're alternating between + // showing mentions and showing full content. As mentions are plain text we + // have to construct URLSpans ourselves. + SpannableStringBuilder contentBuilder = new SpannableStringBuilder(); + for (Status.Mention mention : statusViewData.getMentions()) { + int start = contentBuilder.length() > 0 ? contentBuilder.length() - 1 : 0; + contentBuilder.append('@'); + contentBuilder.append(mention.username); + contentBuilder.append(' '); + contentBuilder.setSpan(new URLSpan(mention.url), start, + mention.username.length() + 1, 0); + } + content = contentBuilder; + } else { + content = null; + } + } else { + content = statusViewData.getContent(); + } + statusContent.setText(content); + contentWarningDescriptionTextView.setText(statusViewData.getSpoilerText()); + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + setupContentAndSpoiler(isChecked); + } } } diff --git a/app/src/main/res/layout/item_status_notification.xml b/app/src/main/res/layout/item_status_notification.xml index dd7dd707d..f8e12c02e 100644 --- a/app/src/main/res/layout/item_status_notification.xml +++ b/app/src/main/res/layout/item_status_notification.xml @@ -40,11 +40,50 @@ + + + + + + + +