Adds option to hide media previews if reduced data usage is desired Closes #56.

This commit is contained in:
Vavassor 2017-06-26 05:15:47 -04:00
commit 3f54ead962
12 changed files with 368 additions and 186 deletions

View file

@ -37,7 +37,6 @@ import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRemover {
@ -56,6 +55,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
private StatusActionListener statusListener;
private NotificationActionListener notificationActionListener;
private FooterState footerState = FooterState.END;
private boolean mediaPreviewEnabled;
public NotificationsAdapter(StatusActionListener statusListener,
NotificationActionListener notificationActionListener) {
@ -63,15 +63,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
notifications = new ArrayList<>();
this.statusListener = statusListener;
this.notificationActionListener = notificationActionListener;
}
public void setFooterState(FooterState newFooterState) {
FooterState oldValue = footerState;
footerState = newFooterState;
if (footerState != oldValue) {
notifyItemChanged(notifications.size());
}
mediaPreviewEnabled = true;
}
@Override
@ -126,7 +118,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
case MENTION: {
StatusViewHolder holder = (StatusViewHolder) viewHolder;
Status status = notification.status;
holder.setupWithStatus(status, statusListener);
holder.setupWithStatus(status, statusListener, mediaPreviewEnabled);
break;
}
case FAVOURITE:
@ -139,8 +131,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
}
case FOLLOW: {
FollowViewHolder holder = (FollowViewHolder) viewHolder;
holder.setMessage(notification.account.getDisplayName(), notification.account.username,
notification.account.avatar);
holder.setMessage(notification.account.getDisplayName(),
notification.account.username, notification.account.avatar);
holder.setupButtons(notificationActionListener, notification.account.id);
break;
}
@ -175,6 +167,25 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
}
}
@Override
public void removeItem(int position) {
notifications.remove(position);
notifyItemChanged(position);
}
@Override
public void removeAllByAccountId(String id) {
for (int i = 0; i < notifications.size();) {
Notification notification = notifications.get(i);
if (id.equals(notification.account.id)) {
notifications.remove(i);
notifyItemRemoved(i);
} else {
i += 1;
}
}
}
public @Nullable Notification getItem(int position) {
if (position >= 0 && position < notifications.size()) {
return notifications.get(position);
@ -209,25 +220,23 @@ public class NotificationsAdapter extends RecyclerView.Adapter implements Adapte
notifyItemRangeInserted(end, new_notifications.size());
}
@Override
public void removeItem(int position) {
notifications.remove(position);
notifyItemChanged(position);
public void clear() {
notifications.clear();
notifyDataSetChanged();
}
@Override
public void removeAllByAccountId(String id) {
for (int i = 0; i < notifications.size();) {
Notification notification = notifications.get(i);
if (id.equals(notification.account.id)) {
notifications.remove(i);
notifyItemRemoved(i);
} else {
i += 1;
}
public void setFooterState(FooterState newFooterState) {
FooterState oldValue = footerState;
footerState = newFooterState;
if (footerState != oldValue) {
notifyItemChanged(notifications.size());
}
}
public void setMediaPreviewEnabled(boolean enabled) {
mediaPreviewEnabled = enabled;
}
public interface NotificationActionListener {
void onViewAccount(String id);
}

View file

@ -16,8 +16,11 @@
package com.keylesspalace.tusky.adapter;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.text.Spanned;
import android.view.View;
@ -60,6 +63,7 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
private ImageView mediaPreview2;
private ImageView mediaPreview3;
private View sensitiveMediaWarning;
private TextView mediaLabel;
private View contentWarningBar;
private TextView contentWarningDescription;
private ToggleButton contentWarningButton;
@ -85,6 +89,7 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
mediaPreview2 = (ImageView) itemView.findViewById(R.id.status_media_preview_2);
mediaPreview3 = (ImageView) itemView.findViewById(R.id.status_media_preview_3);
sensitiveMediaWarning = itemView.findViewById(R.id.status_sensitive_media_warning);
mediaLabel = (TextView) itemView.findViewById(R.id.status_media_label);
contentWarningBar = itemView.findViewById(R.id.status_content_warning_bar);
contentWarningDescription =
(TextView) itemView.findViewById(R.id.status_content_warning_description);
@ -187,8 +192,8 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
favouriteButton.setChecked(favourited);
}
private void setMediaPreviews(final Status.MediaAttachment[] attachments,
boolean sensitive, final StatusActionListener listener) {
private void setMediaPreviews(final Status.MediaAttachment[] attachments, boolean sensitive,
final StatusActionListener listener) {
final ImageView[] previews = {
mediaPreview0,
mediaPreview1,
@ -212,7 +217,7 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
previews[i].setVisibility(View.VISIBLE);
if(previewUrl == null || previewUrl.isEmpty()) {
if (previewUrl == null || previewUrl.isEmpty()) {
Picasso.with(context)
.load(mediaPreviewUnloadedId)
.into(previews[i]);
@ -254,6 +259,62 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
}
}
private static String getLabelTypeText(Context context, Status.MediaAttachment.Type type) {
switch (type) {
default:
case IMAGE: return context.getString(R.string.status_media_images);
case GIFV:
case VIDEO: return context.getString(R.string.status_media_video);
}
}
private static @DrawableRes int getLabelIcon(Status.MediaAttachment.Type type) {
switch (type) {
default:
case IMAGE: return R.drawable.ic_photo_24dp;
case GIFV:
case VIDEO: return R.drawable.ic_videocam_24dp;
}
}
private void setMediaLabel(Status.MediaAttachment[] attachments, boolean sensitive,
final StatusActionListener listener) {
if (attachments.length == 0) {
mediaLabel.setVisibility(View.GONE);
return;
}
mediaLabel.setVisibility(View.VISIBLE);
// Set the label's text.
Context context = itemView.getContext();
String labelText = getLabelTypeText(context, attachments[0].type);
if (sensitive) {
String sensitiveText = context.getString(R.string.status_sensitive_media_title);
labelText += String.format(" (%s)", sensitiveText);
}
mediaLabel.setText(labelText);
// Set the icon next to the label.
int drawableId = getLabelIcon(attachments[0].type);
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
ThemeUtils.setDrawableTint(context, drawable, android.R.attr.textColorTertiary);
mediaLabel.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
// Set the listener for the media view action.
int n = Math.min(attachments.length, Status.MAX_MEDIA_ATTACHMENTS);
final String[] urls = new String[n];
for (int i = 0; i < n; i++) {
urls[i] = attachments[i].url;
}
final Status.MediaAttachment.Type type = attachments[0].type;
mediaLabel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onViewMedia(urls, 0, type);
}
});
}
private void hideSensitiveMediaWarning() {
sensitiveMediaWarning.setVisibility(View.GONE);
}
@ -302,20 +363,20 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
}
});
reblogButton.setEventListener(new SparkEventListener() {
@Override
public void onEvent(ImageView button, boolean buttonState) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onReblog(!reblogged, position);
}
}
@Override
public void onEvent(ImageView button, boolean buttonState) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onReblog(!reblogged, position);
}
}
@Override
public void onEventAnimationEnd(ImageView button, boolean buttonState) {}
@Override
public void onEventAnimationEnd(ImageView button, boolean buttonState) {}
@Override
public void onEventAnimationStart(ImageView button, boolean buttonState) {}
});
@Override
public void onEventAnimationStart(ImageView button, boolean buttonState) {}
});
favouriteButton.setEventListener(new SparkEventListener() {
@Override
public void onEvent(ImageView button, boolean buttonState) {
@ -357,7 +418,8 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
container.setOnClickListener(viewThreadListener);
}
void setupWithStatus(Status status, StatusActionListener listener) {
void setupWithStatus(Status status, StatusActionListener listener,
boolean mediaPreviewEnabled) {
Status realStatus = status.getActionableStatus();
setDisplayName(realStatus.account.getDisplayName());
@ -375,12 +437,25 @@ class StatusViewHolder extends RecyclerView.ViewHolder {
}
Status.MediaAttachment[] attachments = realStatus.attachments;
boolean sensitive = realStatus.sensitive;
setMediaPreviews(attachments, sensitive, listener);
/* A status without attachments is sometimes still marked sensitive, so it's necessary to
* check both whether there are any attachments and if it's marked sensitive. */
if (!sensitive || attachments.length == 0) {
if (mediaPreviewEnabled) {
setMediaPreviews(attachments, sensitive, listener);
/* A status without attachments is sometimes still marked sensitive, so it's necessary
* to check both whether there are any attachments and if it's marked sensitive. */
if (!sensitive || attachments.length == 0) {
hideSensitiveMediaWarning();
}
// Hide the unused label.
mediaLabel.setVisibility(View.GONE);
} else {
setMediaLabel(attachments, sensitive, listener);
// Hide all unused views.
mediaPreview0.setVisibility(View.GONE);
mediaPreview1.setVisibility(View.GONE);
mediaPreview2.setVisibility(View.GONE);
mediaPreview3.setVisibility(View.GONE);
hideSensitiveMediaWarning();
}
setupButtons(listener, realStatus.account.id);
setRebloggingEnabled(status.rebloggingAllowed());
if (realStatus.spoilerText.isEmpty()) {

View file

@ -32,11 +32,13 @@ public class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRe
private List<Status> statuses;
private StatusActionListener statusActionListener;
private int statusIndex;
private boolean mediaPreviewEnabled;
public ThreadAdapter(StatusActionListener listener) {
this.statusActionListener = listener;
this.statuses = new ArrayList<>();
this.statusIndex = 0;
mediaPreviewEnabled = true;
}
@Override
@ -50,7 +52,7 @@ public class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRe
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
StatusViewHolder holder = (StatusViewHolder) viewHolder;
Status status = statuses.get(position);
holder.setupWithStatus(status, statusActionListener);
holder.setupWithStatus(status, statusActionListener, mediaPreviewEnabled);
}
@Override
@ -58,10 +60,6 @@ public class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRe
return statuses.size();
}
public Status getItem(int position) {
return statuses.get(position);
}
@Override
public void removeItem(int position) {
statuses.remove(position);
@ -81,6 +79,10 @@ public class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRe
}
}
public Status getItem(int position) {
return statuses.get(position);
}
public int setStatus(Status status) {
if (statuses.size() > 0
&& statusIndex < statuses.size()
@ -124,4 +126,14 @@ public class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRe
statuses.addAll(descendants);
notifyItemRangeInserted(end, descendants.size());
}
public void clear() {
statuses.clear();
notifyDataSetChanged();
statusIndex = 0;
}
public void setMediaPreviewEnabled(boolean enabled) {
mediaPreviewEnabled = enabled;
}
}

View file

@ -42,11 +42,13 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
private List<Status> statuses;
private StatusActionListener statusListener;
private FooterState footerState = FooterState.END;
private boolean mediaPreviewEnabled;
public TimelineAdapter(StatusActionListener statusListener) {
super();
statuses = new ArrayList<>();
this.statusListener = statusListener;
mediaPreviewEnabled = true;
}
@Override
@ -82,20 +84,12 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
}
}
public void setFooterState(FooterState newFooterState) {
FooterState oldValue = footerState;
footerState = newFooterState;
if (footerState != oldValue) {
notifyItemChanged(statuses.size());
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (position < statuses.size()) {
StatusViewHolder holder = (StatusViewHolder) viewHolder;
Status status = statuses.get(position);
holder.setupWithStatus(status, statusListener);
holder.setupWithStatus(status, statusListener, mediaPreviewEnabled);
}
}
@ -113,6 +107,25 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
}
}
@Override
public void removeItem(int position) {
statuses.remove(position);
notifyItemRemoved(position);
}
@Override
public void removeAllByAccountId(String accountId) {
for (int i = 0; i < statuses.size();) {
Status status = statuses.get(i);
if (accountId.equals(status.account.id)) {
statuses.remove(i);
notifyItemRemoved(i);
} else {
i += 1;
}
}
}
public void update(List<Status> newStatuses) {
if (newStatuses == null || newStatuses.isEmpty()) {
return;
@ -140,25 +153,6 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
notifyItemRangeInserted(end, newStatuses.size());
}
@Override
public void removeItem(int position) {
statuses.remove(position);
notifyItemRemoved(position);
}
@Override
public void removeAllByAccountId(String accountId) {
for (int i = 0; i < statuses.size();) {
Status status = statuses.get(i);
if (accountId.equals(status.account.id)) {
statuses.remove(i);
notifyItemRemoved(i);
} else {
i += 1;
}
}
}
public void clear() {
statuses.clear();
notifyDataSetChanged();
@ -171,4 +165,16 @@ public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItem
}
return null;
}
public void setFooterState(FooterState newFooterState) {
FooterState oldValue = footerState;
footerState = newFooterState;
if (footerState != oldValue) {
notifyItemChanged(statuses.size());
}
}
public void setMediaPreviewEnabled(boolean enabled) {
mediaPreviewEnabled = enabled;
}
}