@Nullable @Override public Object image(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull AsyncDrawable.Loader loader, @NonNull ImageSizeResolver imageSizeResolver, @Nullable ImageSize imageSize, boolean replacementTextIsLink) { return new AsyncDrawableSpan( theme, new AsyncDrawable( destination, loader, imageSizeResolver, imageSize ), AsyncDrawableSpan.ALIGN_BOTTOM, replacementTextIsLink ); }
@Override public int getIntrinsicWidth() { final int out; if (hasResult()) { out = result.getIntrinsicWidth(); } else { out = 0; } return out; }
static void unschedule(@NonNull TextView view) { for (AsyncDrawable drawable : extract(view)) { drawable.setCallback2(null); } }
public AsyncDrawableSpan( @NonNull SpannableTheme theme, @NonNull AsyncDrawable drawable, @Alignment int alignment, boolean replacementTextIsLink) { this.theme = theme; this.drawable = drawable; this.alignment = alignment; this.replacementTextIsLink = replacementTextIsLink; // additionally set intrinsic bounds if empty final Rect rect = drawable.getBounds(); if (rect.isEmpty()) { drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); } }
@Test public void previous_result_detached() { // when result is present it will be detached (setCallback(null)) final AsyncDrawable drawable = new AsyncDrawable("", mock(AsyncDrawable.Loader.class), imageSizeResolver, null); drawable.setCallback2(mock(Drawable.Callback.class)); drawable.initWithKnownDimensions(100, 1); final Drawable result1 = new AbstractDrawable(); final Drawable result2 = new AbstractDrawable(); drawable.setResult(result1); assertNotNull(result1.getCallback()); drawable.setResult(result2); assertNull(result1.getCallback()); assertNotNull(result2.getCallback()); }
@Test public void no_dimensions_await() { // when drawable have no known dimensions yet, it will await for them final AsyncDrawable drawable = new AsyncDrawable("", mock(AsyncDrawable.Loader.class), imageSizeResolver, new ImageSize(new ImageSize.Dimension(100.F, "%"), null)); final Drawable result = new AbstractDrawable(); result.setBounds(0, 0, 0, 0); assertFalse(drawable.hasResult()); drawable.setResult(result); assertTrue(drawable.hasResult()); assertTrue(result.getBounds().isEmpty()); drawable.initWithKnownDimensions(100, 1); assertEquals( new Rect(0, 0, 100, 0), result.getBounds() ); }
@NonNull Paint paint) { drawable.initWithKnownDimensions(canvas.getWidth(), paint.getTextSize()); if (drawable.hasResult()) { final int b = bottom - drawable.getBounds().bottom; final int translationY; if (ALIGN_CENTER == alignment) { translationY = b - ((bottom - top - drawable.getBounds().height()) / 2); } else if (ALIGN_BASELINE == alignment) { translationY = b - paint.getFontMetricsInt().descent; drawable.draw(canvas); } finally { canvas.restoreToCount(save);
@Override public int getSize( @NonNull Paint paint, CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @Nullable Paint.FontMetricsInt fm) { // if we have no async drawable result - we will just render text final int size; if (drawable.hasResult()) { final Rect rect = drawable.getBounds(); if (fm != null) { fm.ascent = -rect.bottom; fm.descent = 0; fm.top = fm.ascent; fm.bottom = 0; } size = rect.right; } else { // we will apply style here in case if theme modifies textSize or style (affects metrics) if (replacementTextIsLink) { theme.applyLinkStyle(paint); } // NB, no specific text handling (no new lines, etc) size = (int) (paint.measureText(text, start, end) + .5F); } return size; }
static void schedule(@NonNull final TextView textView) { final List<AsyncDrawable> list = extract(textView); if (list.size() > 0) { if (textView.getTag(R.id.markwon_drawables_scheduler) == null) { final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { unschedule(textView); v.removeOnAttachStateChangeListener(this); v.setTag(R.id.markwon_drawables_scheduler, null); } }; textView.addOnAttachStateChangeListener(listener); textView.setTag(R.id.markwon_drawables_scheduler, listener); } for (AsyncDrawable drawable : list) { drawable.setCallback2(new DrawableCallbackImpl(textView, drawable.getBounds())); } } }
@Override public void setResult(@NonNull Drawable result) { super.setResult(result); isGif = result instanceof GifDrawable; if (isGif && onGifResultListener != null) { onGifResultListener.onGifResult(this); } }
@Override public void draw(@NonNull Canvas canvas) { super.draw(canvas); if (isGif) { final GifDrawable drawable = (GifDrawable) getResult(); if (!drawable.isPlaying()) { gifPlaceholder.setBounds(drawable.getBounds()); gifPlaceholder.draw(canvas); } } } }
public boolean isAttached() { return getCallback() != null; }
@Override public int getIntrinsicHeight() { final int out; if (hasResult()) { out = result.getIntrinsicHeight(); } else { out = 0; } return out; }
@Override public void draw(@NonNull Canvas canvas) { if (hasResult()) { result.draw(canvas); } }
@Override public int getOpacity() { final int opacity; if (hasResult()) { opacity = result.getOpacity(); } else { opacity = PixelFormat.TRANSPARENT; } return opacity; }