From fad9f3708cb9ec517065bc6d17d3ce3d7292f62c Mon Sep 17 00:00:00 2001 From: tom79 Date: Sun, 21 Jul 2019 18:45:57 +0200 Subject: [PATCH] Remote image between img tags displayed --- .../android/client/Entities/Status.java | 147 ++++++++++++++++++ .../android/drawers/StatusListAdapter.java | 20 ++- .../interfaces/OnRetrieveImageInterface.java | 26 ++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/app/fedilab/android/interfaces/OnRetrieveImageInterface.java diff --git a/app/src/main/java/app/fedilab/android/client/Entities/Status.java b/app/src/main/java/app/fedilab/android/client/Entities/Status.java index 1db1f8a9e..5c316723f 100644 --- a/app/src/main/java/app/fedilab/android/client/Entities/Status.java +++ b/app/src/main/java/app/fedilab/android/client/Entities/Status.java @@ -37,7 +37,9 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.style.ClickableSpan; import android.text.style.ImageSpan; +import android.text.style.QuoteSpan; import android.text.style.URLSpan; +import android.util.Log; import android.util.Patterns; import android.view.View; @@ -70,6 +72,7 @@ import app.fedilab.android.asynctasks.UpdateAccountInfoAsyncTask; import app.fedilab.android.helper.CrossActions; import app.fedilab.android.helper.Helper; import app.fedilab.android.interfaces.OnRetrieveEmojiInterface; +import app.fedilab.android.interfaces.OnRetrieveImageInterface; import static app.fedilab.android.helper.Helper.THEME_BLACK; import static app.fedilab.android.helper.Helper.THEME_DARK; @@ -112,6 +115,7 @@ public class Status implements Parcelable{ private String language; private boolean isTranslated = false; private boolean isEmojiFound = false; + private boolean isImageFound = false; private boolean isEmojiTranslateFound = false; private boolean isClickable = false; private boolean isTranslationShown = false; @@ -144,6 +148,7 @@ public class Status implements Parcelable{ private boolean shortReply = false; private int warningFetched = -1; + private List imageURL; @Override public void writeToParcel(Parcel dest, int flags) { @@ -207,6 +212,7 @@ public class Status implements Parcelable{ dest.writeByte(this.customFeaturesDisplayed ? (byte) 1 : (byte) 0); dest.writeByte(this.shortReply ? (byte) 1 : (byte) 0); dest.writeInt(this.warningFetched); + dest.writeStringList(this.imageURL); } protected Status(Parcel in) { @@ -272,6 +278,7 @@ public class Status implements Parcelable{ this.customFeaturesDisplayed = in.readByte() != 0; this.shortReply = in.readByte() != 0; this.warningFetched = in.readInt(); + this.imageURL = in.createStringArrayList(); } public static final Creator CREATOR = new Creator() { @@ -559,6 +566,9 @@ public class Status implements Parcelable{ return isEmojiFound; } + public boolean isImageFound() { + return isImageFound; + } @@ -567,6 +577,10 @@ public class Status implements Parcelable{ isEmojiFound = emojiFound; } + public void setImageFound(boolean imageFound) { + isImageFound = imageFound; + } + public static void transform(Context context, Status status){ @@ -605,7 +619,60 @@ public class Status implements Parcelable{ content = content.replaceFirst(Pattern.quote(beforemodification), Matcher.quoteReplacement(urlText)); } } + Pattern imgPattern = Pattern.compile("]*src=\"([^\"]+)\"[^>]*>"); + Matcher matcher = imgPattern.matcher(content); + List imgs = new ArrayList<>(); + int i = 1; + while (matcher.find()) { + content = content.replaceAll(Pattern.quote(matcher.group(0)), "
[media_"+i+"]
"); + imgs.add("[media_"+i+"]|"+matcher.group(1)); + i++; + } + status.setImageURL(imgs); spannableStringContent = new SpannableString(content); + final int[] j = {0}; + if( status.getImageURL() != null && status.getImageURL().size() > 0){ + for(String val: status.getImageURL()){ + String[] valArray = val.split("\\|"); + if( valArray.length > 1 ){ + String contentOriginal = valArray[0]; + String url = valArray[1]; + Log.v(Helper.TAG,"contentOriginal: " + contentOriginal); + Log.v(Helper.TAG,"url: " + url); + Glide.with(context) + .asBitmap() + .load(url) + .into(new SimpleTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, Transition transition) { + final String targetedEmoji = contentOriginal; + Log.v(Helper.TAG,"contentSpan.toString(): " + spannableStringContent.toString()); + if (spannableStringContent != null && spannableStringContent.toString().contains(targetedEmoji)) { + //emojis can be used several times so we have to loop + Log.v(Helper.TAG,"ok: " + spannableStringContent.toString()); + for (int startPosition = -1; (startPosition = spannableStringContent.toString().indexOf(targetedEmoji, startPosition + 1)) != -1; startPosition++) { + final int endPosition = startPosition + targetedEmoji.length(); + Log.v(Helper.TAG,"startPosition: " + startPosition); + Log.v(Helper.TAG,"endPosition: " + endPosition); + if( endPosition <= spannableStringContent.toString().length() && endPosition >= startPosition) { + spannableStringContent.setSpan( + new ImageSpan(context, + Bitmap.createScaledBitmap(resource, (int) Helper.convertDpToPixel(300, context), + (int) Helper.convertDpToPixel(300, context), false)), startPosition, + endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } + j[0]++; + if( j[0] == (status.getImageURL().size())) { + status.setContentSpan(spannableStringContent); + } + } + }); + + } + } + } String spoilerText = ""; if( status.getReblog() != null && status.getReblog().getSpoiler_text() != null) spoilerText = status.getReblog().getSpoiler_text(); @@ -704,6 +771,9 @@ public class Status implements Parcelable{ accountsMentionUnknown.put(key, account); } } + + + SpannableString spannableStringT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) spannableStringT = new SpannableString(Html.fromHtml(spannableString.toString().replaceAll("^

","").replaceAll("

","

").replaceAll("

","").replaceAll("
","
").replaceAll("[\\s]{2}","  "), Html.FROM_HTML_MODE_LEGACY)); @@ -1057,6 +1127,75 @@ public class Status implements Parcelable{ } + public static void makeImage(final Context context, final OnRetrieveImageInterface listener, Status status){ + + if( ((Activity)context).isFinishing() ) + return; + if( status.getAccount() == null) + return; + if( status.getImageURL() == null || status.getImageURL().size() == 0) + return; + + SpannableString contentSpan = status.getContentSpan(); + + final int[] i = {0}; + for (final String img : status.getImageURL()) { + final String name = img.split("\\|")[0]; + final String imgURL = img.split("\\|")[1]; + Glide.with(context) + .asBitmap() + .load(imgURL) + .listener(new RequestListener() { + @Override + public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + return false; + } + + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + i[0]++; + if( i[0] == (status.getImageURL().size())) { + listener.onRetrieveImage(status,false); + } + return false; + } + }) + .into(new SimpleTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, Transition transition) { + + int w = resource.getWidth(); + int h = resource.getHeight(); + if( w > 300 ){ + h = (h * 300) / w; + w = 300; + } + final String targetedEmoji = name; + if (contentSpan != null && contentSpan.toString().contains(targetedEmoji)) { + //emojis can be used several times so we have to loop + for (int startPosition = -1; (startPosition = contentSpan.toString().indexOf(targetedEmoji, startPosition + 1)) != -1; startPosition++) { + final int endPosition = startPosition + targetedEmoji.length(); + if( endPosition <= contentSpan.toString().length() && endPosition >= startPosition) + contentSpan.setSpan( + new ImageSpan(context, + Bitmap.createScaledBitmap(resource, (int) Helper.convertDpToPixel(w, context), + (int) Helper.convertDpToPixel(h, context), false)), startPosition, + endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + i[0]++; + if( i[0] == (status.getImageURL().size())) { + status.setContentSpan(contentSpan); + status.setImageFound(true); + listener.onRetrieveImage(status, false); + } + } + }); + + } + } + + public static void makeEmojisTranslation(final Context context, final OnRetrieveEmojiInterface listener, Status status){ if( ((Activity)context).isFinishing() ) @@ -1383,4 +1522,12 @@ public class Status implements Parcelable{ public void setWarningFetched(int warningFetched) { this.warningFetched = warningFetched; } + + public List getImageURL() { + return imageURL; + } + + public void setImageURL(List imageURL) { + this.imageURL = imageURL; + } } diff --git a/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java b/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java index 275f130a5..b4e4585d0 100644 --- a/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java +++ b/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java @@ -133,6 +133,7 @@ import app.fedilab.android.helper.CustomTextView; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastalabAutoCompleteTextView; import app.fedilab.android.interfaces.OnPostStatusActionInterface; +import app.fedilab.android.interfaces.OnRetrieveImageInterface; import app.fedilab.android.interfaces.OnRetrieveRelationshipInterface; import app.fedilab.android.interfaces.OnRetrieveRelationshipQuickReplyInterface; import app.fedilab.android.interfaces.OnRetrieveSearcAccountshInterface; @@ -185,7 +186,7 @@ import static app.fedilab.android.helper.Helper.changeDrawableColor; * Created by Thomas on 24/04/2017. * Adapter for Status */ -public class StatusListAdapter extends RecyclerView.Adapter implements OnPostActionInterface, OnRetrieveFeedsInterface, OnRetrieveEmojiInterface, OnRetrieveRepliesInterface, OnRetrieveCardInterface, OnPollInterface, OnRefreshCachedStatusInterface, OnRetrieveSearcAccountshInterface, OnRetrieveSearchInterface, OnPostStatusActionInterface, OnRetrieveRelationshipQuickReplyInterface { +public class StatusListAdapter extends RecyclerView.Adapter implements OnPostActionInterface, OnRetrieveFeedsInterface, OnRetrieveImageInterface, OnRetrieveEmojiInterface, OnRetrieveRepliesInterface, OnRetrieveCardInterface, OnPollInterface, OnRefreshCachedStatusInterface, OnRetrieveSearcAccountshInterface, OnRetrieveSearchInterface, OnPostStatusActionInterface, OnRetrieveRelationshipQuickReplyInterface { private Context context; private List statuses; @@ -537,6 +538,7 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct } + private class ViewHolderEmpty extends RecyclerView.ViewHolder{ ViewHolderEmpty(View itemView) { super(itemView); @@ -1457,6 +1459,9 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct Status.transform(context, status); if (!status.isEmojiFound()) Status.makeEmojis(context, this, status); + if (!status.isImageFound()) + Status.makeImage(context, this, status); + holder.status_content.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { @@ -4110,6 +4115,19 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct } + @Override + public void onRetrieveImage(Status status, boolean fromTranslation) { + if( status != null) { + if( !fromTranslation) { + status.setImageFound(true); + }else { + status.setImageFound(true); + } + notifyStatusChanged(status); + } + } + + @Override public void onRetrieveEmoji(Status status, boolean fromTranslation) { if( status != null) { diff --git a/app/src/main/java/app/fedilab/android/interfaces/OnRetrieveImageInterface.java b/app/src/main/java/app/fedilab/android/interfaces/OnRetrieveImageInterface.java new file mode 100644 index 000000000..109ecee95 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/interfaces/OnRetrieveImageInterface.java @@ -0,0 +1,26 @@ +/* Copyright 2019 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ +package app.fedilab.android.interfaces; + +import app.fedilab.android.client.Entities.Status; + + +/** + * Created by Thomas on 19/07/2019. + * Interface when retrieving image from img tags + */ +public interface OnRetrieveImageInterface { + void onRetrieveImage(Status status, boolean fromTranslation); +}