From 92fb55cb3a1626ed0f300f53965f27da810d9b7c Mon Sep 17 00:00:00 2001 From: charlag Date: Tue, 7 Nov 2017 22:36:19 +0300 Subject: [PATCH 1/2] Open status from notifications --- .../tusky/adapter/NotificationsAdapter.java | 58 ++++++++++++++----- .../tusky/fragment/NotificationsFragment.java | 12 ++++ 2 files changed, 54 insertions(+), 16 deletions(-) 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 619e6bd1..c4999e9d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -123,9 +123,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter { StatusNotificationViewHolder holder = (StatusNotificationViewHolder) viewHolder; holder.setMessage(type, concreteNotificaton.getAccount().getDisplayName(), concreteNotificaton.getStatusViewData()); - holder.setupButtons(notificationActionListener, concreteNotificaton.getAccount().id); + holder.setupButtons(notificationActionListener, + concreteNotificaton.getAccount().id, + concreteNotificaton.getId()); holder.setAvatars(concreteNotificaton.getStatusViewData().getAvatar(), - concreteNotificaton.getAccount().avatar); + concreteNotificaton.getId()); break; } case FOLLOW: { @@ -211,6 +213,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter { public interface NotificationActionListener { void onViewAccount(String id); + + void onViewStatusForNotificationId(String notificationId); } private static class FollowViewHolder extends RecyclerView.ViewHolder { @@ -258,13 +262,19 @@ public class NotificationsAdapter extends RecyclerView.Adapter { } } - private static class StatusNotificationViewHolder extends RecyclerView.ViewHolder { - private TextView message; - private ImageView icon; - private TextView statusContent; - private ViewGroup container; - private ImageView statusAvatar; - private ImageView notificationAvatar; + private static class StatusNotificationViewHolder extends RecyclerView.ViewHolder + implements View.OnClickListener { + private final TextView message; + private final ImageView icon; + private final TextView statusContent; + private final ViewGroup container; + private final ImageView statusAvatar; + private final ImageView notificationAvatar; + private final ViewGroup topBar; + + private String accountId; + private String notificationId; + private NotificationActionListener listener; StatusNotificationViewHolder(View itemView) { super(itemView); @@ -274,9 +284,14 @@ public class NotificationsAdapter extends RecyclerView.Adapter { container = itemView.findViewById(R.id.notification_container); statusAvatar = itemView.findViewById(R.id.notification_status_avatar); notificationAvatar = itemView.findViewById(R.id.notification_notification_avatar); + topBar = itemView.findViewById(R.id.notification_top_bar); + int darkerFilter = Color.rgb(123, 123, 123); statusAvatar.setColorFilter(darkerFilter, PorterDuff.Mode.MULTIPLY); notificationAvatar.setColorFilter(darkerFilter, PorterDuff.Mode.MULTIPLY); + + container.setOnClickListener(this); + topBar.setOnClickListener(this); } void setMessage(Notification.Type type, String displayName, @@ -308,13 +323,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter { statusContent.setText(status.getContent()); } - void setupButtons(final NotificationActionListener listener, final String accountId) { - container.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - listener.onViewAccount(accountId); - } - }); + void setupButtons(final NotificationActionListener listener, final String accountId, + final String notificationId) { + this.listener = listener; + this.accountId = accountId; + this.notificationId = notificationId; } void setAvatars(@Nullable String statusAvatarUrl, @Nullable String notificationAvatarUrl) { @@ -341,5 +354,18 @@ public class NotificationsAdapter extends RecyclerView.Adapter { .into(notificationAvatar); } } + + @Override + public void onClick(View v) { + if (listener == null) return; + switch (v.getId()) { + case R.id.notification_container: + listener.onViewStatusForNotificationId(notificationId); + break; + case R.id.notification_top_bar: + listener.onViewAccount(accountId); + break; + } + } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 583ee0ac..fe535fbe 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -394,6 +394,18 @@ public class NotificationsFragment extends SFragment implements super.viewAccount(id); } + @Override + public void onViewStatusForNotificationId(String notificationId) { + for (Either either : notifications) { + Notification notification = either.getAsRightOrNull(); + if (notification != null && notification.id.equals(notificationId)) { + super.viewThread(notification.status); + return; + } + } + Log.w(TAG, "Didn't find a notification for ID: " + notificationId); + } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { switch (key) { From 76c7a7e462e7203a70bb90e4ff7b5883da905bf3 Mon Sep 17 00:00:00 2001 From: charlag Date: Tue, 7 Nov 2017 23:31:44 +0300 Subject: [PATCH 2/2] Display CWs in notifications correctly --- .../tusky/adapter/NotificationsAdapter.java | 61 +++++++++++++++++-- .../res/layout/item_status_notification.xml | 43 ++++++++++++- 2 files changed, 97 insertions(+), 7 deletions(-) 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 c4999e9d..4a7dce80 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 dd7dd707..f8e12c02 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 @@ + + + + + + + +