~singpolyma/cheogram-android

e6753d60f885678b3420eb509f242e9d829119d1 — Stephen Paul Weber 1 year, 2 months ago f38d0a2
Support animated images in SVG

If they are the *only* thing in the SVG.
M src/main/java/eu/siacs/conversations/entities/Conversation.java => src/main/java/eu/siacs/conversations/entities/Conversation.java +46 -48
@@ 6,6 6,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.Bitmap;


@@ 61,7 62,6 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.viewpager.widget.ViewPager;

import com.caverock.androidsvg.SVG;
import com.caverock.androidsvg.SVGParseException;

import com.cheogram.android.ConversationPage;
import com.cheogram.android.Util;


@@ 1811,24 1811,8 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                            if (mimeType == null || uriS == null) continue;
                            Uri uri = Uri.parse(uriS);
                            if (mimeType.startsWith("image/") && "https".equals(uri.getScheme())) {
                                final Drawable d = cache.get(uri.toString());
                                if (d == null) {
                                    synchronized (CommandSession.this) {
                                        waitingForRefresh = true;
                                    }
                                    int size = (int)(xmppConnectionService.getResources().getDisplayMetrics().density * 288);
                                    Message dummy = new Message(Conversation.this, uri.toString(), Message.ENCRYPTION_NONE);
                                    dummy.setFileParams(new Message.FileParams(uri.toString()));
                                    httpManager.createNewDownloadConnection(dummy, true, (file) -> {
                                        if (file == null) {
                                            dummy.getTransferable().start();
                                        } else {
                                            try {
                                                xmppConnectionService.getFileBackend().getThumbnail(file, xmppConnectionService.getResources(), size, false, uri.toString());
                                            } catch (final Exception e) { }
                                        }
                                    });
                                } else {
                                final Drawable d = getDrawableForUrl(uri.toString());
                                if (d != null) {
                                    binding.mediaImage.setImageDrawable(d);
                                    binding.mediaImage.setVisibility(View.VISIBLE);
                                }


@@ 2178,29 2162,21 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl

                            final SVG icon = getItem(position).getIcon();
                            if (icon != null) {
                                 synchronized (CommandSession.this) {
                                     waitingForRefresh = true;
                                 }
                                 final Element iconEl = getItem(position).getIconEl();
                                 if (height < 1) {
                                     v.measure(0, 0);
                                     height = v.getMeasuredHeight();
                                 }
                                 if (height < 1) return v;
                                 icon.setDocumentPreserveAspectRatio(com.caverock.androidsvg.PreserveAspectRatio.LETTERBOX);
                                 try {
                                     icon.setDocumentWidth("100%");
                                     icon.setDocumentHeight("100%");
                                 } catch (final SVGParseException e) { }
                                 if (mediaSelector) {
                                     Bitmap bitmap = Bitmap.createBitmap(height * 4, height * 4, Bitmap.Config.ARGB_8888);
                                     Canvas bmcanvas = new Canvas(bitmap);
                                     icon.renderToCanvas(bmcanvas);
                                     v.setCompoundDrawablesRelativeWithIntrinsicBounds(null, new BitmapDrawable(bitmap), null, null);
                                     final Drawable d = getDrawableForSVG(icon, iconEl, height * 4);
                                     if (d != null) {
                                         final int boundsHeight = 35 + (int)((height * 4) / xmppConnectionService.getResources().getDisplayMetrics().density);
                                         d.setBounds(0, 0, d.getIntrinsicWidth(), boundsHeight);
                                     }
                                     v.setCompoundDrawables(null, d, null, null);
                                 } else {
                                     Bitmap bitmap = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888);
                                     Canvas bmcanvas = new Canvas(bitmap);
                                     icon.renderToCanvas(bmcanvas);
                                     v.setCompoundDrawablesRelativeWithIntrinsicBounds(new BitmapDrawable(bitmap), null, null, null);
                                     v.setCompoundDrawablesRelativeWithIntrinsicBounds(getDrawableForSVG(icon, iconEl, height), null, null, null);
                                 }
                            }



@@ 2287,20 2263,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl

                        final SVG defaultIcon = defaultOption.getIcon();
                        if (defaultIcon != null) {
                             synchronized (CommandSession.this) {
                                 waitingForRefresh = true;
                             }
                             defaultIcon.setDocumentPreserveAspectRatio(com.caverock.androidsvg.PreserveAspectRatio.LETTERBOX);
                             try {
                                 defaultIcon.setDocumentWidth("100%");
                                 defaultIcon.setDocumentHeight("100%");
                             } catch (final SVGParseException e) { }
                             DisplayMetrics display = mPager.getContext().getResources().getDisplayMetrics();
                             Bitmap bitmap = Bitmap.createBitmap((int)(display.heightPixels*display.density/4), (int)(display.heightPixels*display.density/4), Bitmap.Config.ARGB_8888);
                             bitmap.setDensity(display.densityDpi);
                             Canvas bmcanvas = new Canvas(bitmap);
                             defaultIcon.renderToCanvas(bmcanvas);
                             binding.defaultButton.setCompoundDrawablesRelativeWithIntrinsicBounds(null, new BitmapDrawable(bitmap), null, null);
                             int height = (int)(display.heightPixels*display.density/4);
                             binding.defaultButton.setCompoundDrawablesRelativeWithIntrinsicBounds(null, getDrawableForSVG(defaultIcon, defaultOption.getIconEl(), height), null, null);
                        }

                        binding.defaultButton.setText(defaultOption.toString());


@@ 3262,6 3227,39 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                }
            }

            private Drawable getDrawableForSVG(SVG svg, Element svgElement, int size) {
               if (svgElement != null && svgElement.getChildren().size() == 1 && svgElement.getChildren().get(0).getName().equals("image"))  {
                   return getDrawableForUrl(svgElement.getChildren().get(0).getAttribute("href"));
               } else {
                   return xmppConnectionService.getFileBackend().drawSVG(svg, size);
               }
            }

            private Drawable getDrawableForUrl(final String url) {
                final LruCache<String, Drawable> cache = xmppConnectionService.getDrawableCache();
                final HttpConnectionManager httpManager = xmppConnectionService.getHttpConnectionManager();
                final Drawable d = cache.get(url);
                if (Build.VERSION.SDK_INT >= 28 && d instanceof AnimatedImageDrawable) ((AnimatedImageDrawable) d).start();
                if (d == null) {
                    synchronized (CommandSession.this) {
                        waitingForRefresh = true;
                    }
                    int size = (int)(xmppConnectionService.getResources().getDisplayMetrics().density * 288);
                    Message dummy = new Message(Conversation.this, url, Message.ENCRYPTION_NONE);
                    dummy.setFileParams(new Message.FileParams(url));
                    httpManager.createNewDownloadConnection(dummy, true, (file) -> {
                        if (file == null) {
                            dummy.getTransferable().start();
                        } else {
                            try {
                                xmppConnectionService.getFileBackend().getThumbnail(file, xmppConnectionService.getResources(), size, false, url);
                            } catch (final Exception e) { }
                        }
                    });
                }
                return d;
            }

            public View inflateUi(Context context, Consumer<ConversationPage> remover) {
                CommandPageBinding binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.command_page, null, false);
                setBinding(binding);

M src/main/java/eu/siacs/conversations/persistance/FileBackend.java => src/main/java/eu/siacs/conversations/persistance/FileBackend.java +11 -2
@@ 2168,11 2168,20 @@ public class FileBackend {
    public Drawable getSVG(File file, int size) {
        try {
            SVG svg = SVG.getFromInputStream(new FileInputStream(file));
            return drawSVG(svg, size);
        } catch (final IOException | SVGParseException | IllegalArgumentException e) {
            Log.w(Config.LOGTAG, "Could not parse SVG: " + e);
            return null;
        }
    }

    public Drawable drawSVG(SVG svg, int size) {
        try {
            svg.setDocumentPreserveAspectRatio(com.caverock.androidsvg.PreserveAspectRatio.LETTERBOX);

            float w = svg.getDocumentWidth();
            float h = svg.getDocumentHeight();
            Rect r = rectForSize((int) w, (int) h, size);
            Rect r = rectForSize(w < 1 ? size : (int) w, h < 1 ? size : (int) h, size);
            svg.setDocumentWidth("100%");
            svg.setDocumentHeight("100%");



@@ 2181,7 2190,7 @@ public class FileBackend {
            svg.renderToCanvas(canvas);

            return new SVGDrawable(output);
        } catch (final IOException | SVGParseException | IllegalArgumentException e) {
        } catch (final SVGParseException e) {
            Log.w(Config.LOGTAG, "Could not parse SVG: " + e);
            return null;
        }

M src/main/java/eu/siacs/conversations/services/XmppConnectionService.java => src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +0 -30
@@ 353,7 353,6 @@ public class XmppConnectionService extends Service {
    private final Set<OnMucRosterUpdate> mOnMucRosterUpdate = Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>());
    private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated = Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>());
    private final Set<OnJingleRtpConnectionUpdate> onJingleRtpConnectionUpdate = Collections.newSetFromMap(new WeakHashMap<OnJingleRtpConnectionUpdate, Boolean>());
    private final Set<String> alreadyAttemptedSVGDownload = new HashSet<>();

    private final Object LISTENER_LOCK = new Object();



@@ 1432,35 1431,6 @@ public class XmppConnectionService extends Service {
        toggleForegroundService();
        setupPhoneStateListener();
        rescanStickers();
        com.caverock.androidsvg.SVG.registerExternalFileResolver(new com.caverock.androidsvg.SVGExternalFileResolver() {
            @Override
            public Bitmap resolveImage(String filename) {
                final LruCache<String, Drawable> cache = getDrawableCache();
                final Drawable d = cache.get(filename);
                if (d != null) return FileBackend.drawDrawable(d);
                if (getLongPreference("auto_accept_file_size", R.integer.auto_accept_filesize) < 3000000) {
                    return null;
                }
                synchronized(alreadyAttemptedSVGDownload) {
                    if (alreadyAttemptedSVGDownload.contains(filename)) return null;
                    alreadyAttemptedSVGDownload.add(filename);
                }
                final HttpConnectionManager httpManager = getHttpConnectionManager();
                Message dummy = new Message(new Conversation(null, getAccounts().get(0), null, 0), filename, Message.ENCRYPTION_NONE);
                dummy.setFileParams(new Message.FileParams(filename));
                httpManager.createNewDownloadConnection(dummy, true, (file) -> {
                    if (file == null) {
                        dummy.getTransferable().start();
                    } else {
                        try {
                            int size = (int)(getResources().getDisplayMetrics().density * 288);
                            getFileBackend().getThumbnail(file, getResources(), size, false, filename);
                        } catch (final Exception e) { }
                    }
                });
                return null;
            }
        });
    }



M src/main/java/eu/siacs/conversations/xmpp/forms/Option.java => src/main/java/eu/siacs/conversations/xmpp/forms/Option.java +8 -3
@@ 10,6 10,7 @@ public class Option {
    protected final String value;
    protected final String label;
    protected final SVG icon;
    protected final Element iconEl;

    public static List<Option> forField(Element field) {
        List<Option> options = new ArrayList<>();


@@ 25,18 26,20 @@ public class Option {
        this(
            option.findChildContent("value", "jabber:x:data"),
            option.getAttribute("label"),
            parseSVG(option.findChild("svg", "http://www.w3.org/2000/svg"))
            parseSVG(option.findChild("svg", "http://www.w3.org/2000/svg")),
            option.findChild("svg", "http://www.w3.org/2000/svg")
        );
    }

    public Option(final String value, final String label) {
        this(value, label, null);
        this(value, label, null, null);
    }

    public Option(final String value, final String label, final SVG icon) {
    public Option(final String value, final String label, final SVG icon, final Element iconEl) {
        this.value = value;
        this.label = label == null ? value : label;
        this.icon = icon;
        this.iconEl = iconEl;
    }

    public boolean equals(Object o) {


@@ 53,6 56,8 @@ public class Option {

    public SVG getIcon() { return icon; }

    public Element getIconEl() { return iconEl; }

    private static SVG parseSVG(final Element svg) {
        if (svg == null) return null;
        try {