diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 081c7f31770c891193b3ea46cc546b4837a4aed0..62e2001c9a059e38ab1516562e644a507737c756 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -7,9 +7,13 @@ import android.graphics.Color; import android.os.Build; import android.text.Html; import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ImageSpan; +import android.text.style.ClickableSpan; import android.util.Base64; import android.util.Log; import android.util.Pair; +import android.view.View; import com.cheogram.android.BobTransfer; import com.cheogram.android.GetThumbnailForCid; @@ -854,6 +858,20 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable (opening, tag, output, xmlReader) -> {} )); + // Make images clickable and long-clickable with BetterLinkMovementMethod + ImageSpan[] imageSpans = spannable.getSpans(0, spannable.length(), ImageSpan.class); + for (ImageSpan span : imageSpans) { + final int start = spannable.getSpanStart(span); + final int end = spannable.getSpanEnd(span); + + ClickableSpan click_span = new ClickableSpan() { + @Override + public void onClick(View widget) { } + }; + + spannable.setSpan(click_span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + // https://stackoverflow.com/a/10187511/8611 int i = spannable.length(); while(--i >= 0 && Character.isWhitespace(spannable.charAt(i))) { } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 5d57403a4edb8469225143e3200dab52ac2c9001..50721d9dfeff1defbae6dba280f6b20445ab97c2 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -75,6 +75,8 @@ import com.google.common.collect.ImmutableList; import org.jetbrains.annotations.NotNull; +import io.ipfs.cid.Cid; + import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; @@ -159,7 +161,8 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class ConversationFragment extends XmppFragment implements EditMessage.KeyboardListener, MessageAdapter.OnContactPictureLongClicked, - MessageAdapter.OnContactPictureClicked { + MessageAdapter.OnContactPictureClicked, + MessageAdapter.OnInlineImageLongClicked { public static final int REQUEST_SEND_MESSAGE = 0x0201; public static final int REQUEST_DECRYPT_PGP = 0x0202; @@ -1282,6 +1285,7 @@ public class ConversationFragment extends XmppFragment messageListAdapter = new MessageAdapter((XmppActivity) getActivity(), this.messageList); messageListAdapter.setOnContactPictureClicked(this); messageListAdapter.setOnContactPictureLongClicked(this); + messageListAdapter.setOnInlineImageLongClicked(this); binding.messagesView.setAdapter(messageListAdapter); registerForContextMenu(binding.messagesView); @@ -1340,6 +1344,7 @@ public class ConversationFragment extends XmppFragment Log.d(Config.LOGTAG, "ConversationFragment.onDestroyView()"); messageListAdapter.setOnContactPictureClicked(null); messageListAdapter.setOnContactPictureLongClicked(null); + messageListAdapter.setOnInlineImageLongClicked(null); if (conversation != null) conversation.setupViewPager(null, null); } @@ -2326,6 +2331,14 @@ public class ConversationFragment extends XmppFragment builder.create().show(); } + public boolean onInlineImageLongClicked(Cid cid) { + DownloadableFile f = activity.xmppConnectionService.getFileForCid(cid); + if (f == null) return false; + + saveAsSticker(f, null); + return true; + } + private void saveAsSticker(final Message m) { String existingName = m.getFileParams() != null && m.getFileParams().getName() != null ? m.getFileParams().getName() : ""; existingName = existingName.lastIndexOf(".") == -1 ? existingName : existingName.substring(0, existingName.lastIndexOf(".")); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index a6e53d77a3b45713efd628a1d1c8511185e98c00..cf126078c807a454d4bf943dda0dab8f56912d66 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -14,6 +14,8 @@ import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; +import android.text.style.ClickableSpan; import android.text.format.DateUtils; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; @@ -111,6 +113,7 @@ public class MessageAdapter extends ArrayAdapter { private OnContactPictureClicked mOnContactPictureClickedListener; private OnContactPictureClicked mOnMessageBoxClickedListener; private OnContactPictureLongClicked mOnContactPictureLongClickedListener; + private OnInlineImageLongClicked mOnInlineImageLongClickedListener; private boolean mUseGreenBackground = false; private final boolean mForceNames; @@ -162,6 +165,10 @@ public class MessageAdapter extends ArrayAdapter { this.mOnContactPictureLongClickedListener = listener; } + public void setOnInlineImageLongClicked(OnInlineImageLongClicked listener) { + this.mOnInlineImageLongClickedListener = listener; + } + @Override public int getViewTypeCount() { return 5; @@ -577,7 +584,26 @@ public class MessageAdapter extends ArrayAdapter { MyLinkify.addLinks(body, message.getConversation().getAccount()); viewHolder.messageBody.setAutoLinkMask(0); viewHolder.messageBody.setText(body); - BetterLinkMovementMethod method = BetterLinkMovementMethod.newInstance(); + BetterLinkMovementMethod method = new BetterLinkMovementMethod() { + @Override + protected void dispatchUrlLongClick(TextView tv, ClickableSpan span) { + if (span instanceof URLSpan || mOnInlineImageLongClickedListener == null) { + super.dispatchUrlLongClick(tv, span); + return; + } + + Spannable body = (Spannable) tv.getText(); + ImageSpan[] imageSpans = body.getSpans(body.getSpanStart(span), body.getSpanEnd(span), ImageSpan.class); + if (imageSpans.length > 0) { + Uri uri = Uri.parse(imageSpans[0].getSource()); + Cid cid = BobTransfer.cid(uri); + if (cid == null) return; + if (mOnInlineImageLongClickedListener.onInlineImageLongClicked(cid)) { + tv.dispatchTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0)); + } + } + } + }; method.setOnLinkLongClickListener((tv, url) -> { tv.dispatchTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0)); ShareUtil.copyLinkToClipboard(activity, url); @@ -1096,6 +1122,10 @@ public class MessageAdapter extends ArrayAdapter { void onContactPictureLongClicked(View v, Message message); } + public interface OnInlineImageLongClicked { + boolean onInlineImageLongClicked(Cid cid); + } + private static class ViewHolder { public Button load_more_messages;