diff --git a/markwon-span-ext/.gitignore b/markwon-span-ext/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/markwon-span-ext/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/markwon-span-ext/build.gradle b/markwon-span-ext/build.gradle new file mode 100644 index 00000000..60a27933 --- /dev/null +++ b/markwon-span-ext/build.gradle @@ -0,0 +1,50 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 31 + + defaultConfig { + minSdkVersion 21 + targetSdkVersion 31 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + api project(':markwon-core') + api deps['glide'] + + + deps.with { + // add a compileOnly dependency, so if this artifact is present + // we will try to obtain a SpanFactory for a Strikethrough node and use + // it to be consistent with markdown (please note that we do not use markwon plugin + // for that in case if different implementation is used) + compileOnly it['commonmark-strikethrough'] + + testImplementation it['ix-java'] + } + + deps.test.with { + testImplementation it['junit'] + testImplementation it['robolectric'] + } +} \ No newline at end of file diff --git a/markwon-span-ext/src/androidTest/java/io/noties/markwon/span/ext/ExampleInstrumentedTest.java b/markwon-span-ext/src/androidTest/java/io/noties/markwon/span/ext/ExampleInstrumentedTest.java new file mode 100644 index 00000000..89e367cf --- /dev/null +++ b/markwon-span-ext/src/androidTest/java/io/noties/markwon/span/ext/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package io.noties.markwon.span.ext; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("io.noties.markwon.span.ext.test", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/markwon-span-ext/src/main/AndroidManifest.xml b/markwon-span-ext/src/main/AndroidManifest.xml new file mode 100644 index 00000000..fd254356 --- /dev/null +++ b/markwon-span-ext/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/BlockQuoteSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/BlockQuoteSpan.java new file mode 100755 index 00000000..40940762 --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/BlockQuoteSpan.java @@ -0,0 +1,82 @@ +package io.noties.markwon.span.ext; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.text.Layout; +import android.text.Spanned; +import android.text.style.LeadingMarginSpan; +import android.text.style.LineHeightSpan; +import io.noties.markwon.utils.ColorUtils; + +public class BlockQuoteSpan implements LeadingMarginSpan, LineHeightSpan { + protected int BLOCK_QUOTE_DEF_COLOR_ALPHA = 100; + protected static final int BLOCK_QUOTE_WIDTH = 10; + public static int BLOCK_QUOTE_MARGIN = 100; + protected int BLOCK_COLOR = Color.LTGRAY; + private final int VERTICAL_SPACING = 20; + private final Rect rect = ObjectsPool.rect(); + private final Paint paint = ObjectsPool.paint(); + + public BlockQuoteSpan() { + + } + + @Override + public int getLeadingMargin(boolean first) { + return BLOCK_QUOTE_MARGIN; + } + + @Override + public void drawLeadingMargin( + Canvas c, + Paint p, + int x, + int dir, + int top, + int baseline, + int bottom, + CharSequence text, + int start, + int end, + boolean first, + Layout layout) { + + final int width = BLOCK_QUOTE_WIDTH; + + paint.set(p); + + applyBlockQuoteStyle(paint); + + rect.set(x, top, x + dir * width, bottom); + + c.drawRect(rect, paint); + } + + public void applyBlockQuoteStyle(Paint paint) { + final int color = ColorUtils.applyAlpha(BLOCK_COLOR, BLOCK_QUOTE_DEF_COLOR_ALPHA); + paint.setStyle(Paint.Style.FILL); + paint.setColor(color); + } + + @Override + public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) { + final Spanned txt = (Spanned)text; + final int spanEnd = txt.getSpanEnd(this); + final int spanStart = txt.getSpanStart(this); + + // add top spacing to first line + if (start == spanStart) { + fm.ascent -= VERTICAL_SPACING; + fm.top -= VERTICAL_SPACING; + } + + // add bottom spacing to last line + if (Math.abs(spanEnd - end) <= 1) { + fm.descent += VERTICAL_SPACING; + fm.bottom += VERTICAL_SPACING; + } + } +} + diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/BulletSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/BulletSpan.java new file mode 100755 index 00000000..150ab427 --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/BulletSpan.java @@ -0,0 +1,188 @@ +package io.noties.markwon.span.ext; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.os.Parcel; +import android.text.Layout; +import android.text.ParcelableSpan; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.LeadingMarginSpan; +import android.text.style.MetricAffectingSpan; + +import androidx.annotation.ColorInt; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.Px; + + +public class BulletSpan implements LeadingMarginSpan, ParcelableSpan { + // Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices. + private static final int STANDARD_BULLET_RADIUS = 6; + public static final int STANDARD_GAP_WIDTH = 2; + private static final int STANDARD_COLOR = 0; + public static final int BULLET_SPAN = 8; + @Px + private final int mGapWidth; + @Px + private final int mBulletRadius; + private Path mBulletPath = null; + @ColorInt + private final int mColor; + private final boolean mWantColor; + + /** + * Creates a {@link android.text.style.BulletSpan} with the default values. + */ + public BulletSpan() { + this(STANDARD_GAP_WIDTH, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS); + } + + /** + * Creates a {@link android.text.style.BulletSpan} based on a gap width + * + * @param gapWidth the distance, in pixels, between the bullet point and the paragraph. + */ + public BulletSpan(int gapWidth) { + this(gapWidth, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS); + } + + public BulletSpan(int gapWidth, @ColorInt int color) { + this(gapWidth, color, true, STANDARD_BULLET_RADIUS); + } + + public BulletSpan(int gapWidth, @ColorInt int color, @IntRange(from = 0) int bulletRadius) { + this(gapWidth, color, true, bulletRadius); + } + + private BulletSpan(int gapWidth, @ColorInt int color, boolean wantColor, + @IntRange(from = 0) int bulletRadius) { + mGapWidth = gapWidth; + mBulletRadius = bulletRadius; + mColor = color; + mWantColor = wantColor; + } + + public BulletSpan(@NonNull Parcel src) { + mGapWidth = src.readInt(); + mWantColor = src.readInt() != 0; + mColor = src.readInt(); + mBulletRadius = src.readInt(); + } + + @Override + public int getSpanTypeId() { + return getSpanTypeIdInternal(); + } + + + public int getSpanTypeIdInternal() { + return BULLET_SPAN; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + writeToParcelInternal(dest, flags); + } + + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { + dest.writeInt(mGapWidth); + dest.writeInt(mWantColor ? 1 : 0); + dest.writeInt(mColor); + dest.writeInt(mBulletRadius); + } + + @Override + public int getLeadingMargin(boolean first) { + return 2 * mBulletRadius + mGapWidth; + } + + /** + * Get the distance, in pixels, between the bullet point and the paragraph. + * + * @return the distance, in pixels, between the bullet point and the paragraph. + */ + public int getGapWidth() { + return mGapWidth; + } + + /** + * Get the radius, in pixels, of the bullet point. + * + * @return the radius, in pixels, of the bullet point. + */ + public int getBulletRadius() { + return mBulletRadius; + } + + /** + * Get the bullet point color. + * + * @return the bullet point color + */ + public int getColor() { + return mColor; + } + + @Override + public void drawLeadingMargin(@NonNull Canvas canvas, @NonNull Paint paint, int x, int dir, + int top, int baseline, int bottom, + @NonNull CharSequence text, int start, int end, + boolean first, @Nullable Layout layout) { + if (((Spanned) text).getSpanStart(this) == start) { + Paint.Style style = paint.getStyle(); + int oldcolor = 0; + + if (mWantColor) { + oldcolor = paint.getColor(); + paint.setColor(mColor); + } + + paint.setStyle(Paint.Style.FILL); + + if (layout != null) { + // "bottom" position might include extra space as a result of line spacing + // configuration. Subtract extra space in order to show bullet in the vertical + // center of characters. + final int line = layout.getLineForOffset(start); + + bottom = bottom - 0;//layout.getLineExtra(line); + } + + final float yPosition = (top + bottom) / 2f; + final float xPosition = x + dir * mBulletRadius; + + if (canvas.isHardwareAccelerated()) { + if (mBulletPath == null) { + mBulletPath = new Path(); + mBulletPath.addCircle(0.0f, 0.0f, mBulletRadius, Path.Direction.CW); + } + + canvas.save(); + canvas.translate(xPosition, yPosition); + canvas.drawPath(mBulletPath, paint); + canvas.restore(); + } else { + canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint); + } + + if (mWantColor) { + paint.setColor(oldcolor); + } + + paint.setStyle(style); + } + } +} diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CWDrawableSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CWDrawableSpan.java new file mode 100755 index 00000000..125afe0a --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CWDrawableSpan.java @@ -0,0 +1,31 @@ +package io.noties.markwon.span.ext; + +import android.graphics.drawable.Drawable; +import android.text.style.DynamicDrawableSpan; + +public class CWDrawableSpan extends DynamicDrawableSpan { + private Drawable drawable; + private int requiredWidth = 0; + private int requiredHeight = 0; + + public CWDrawableSpan(Drawable drawable){ + this.drawable = drawable; + } + + public CWDrawableSpan(Drawable drawable, int requiredWidth, int requiredHeight){ + this.requiredWidth = requiredWidth; + this.requiredHeight = requiredHeight; + this.drawable = drawable; + } + + @Override + public Drawable getDrawable() { +// drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + if (requiredHeight != 0 && requiredWidth != 0) { + drawable.setBounds(0, 0, requiredWidth, requiredHeight); + } else { + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + } + return drawable; + } +} diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CenteredImageSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CenteredImageSpan.java new file mode 100755 index 00000000..8ed6513f --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CenteredImageSpan.java @@ -0,0 +1,55 @@ +package io.noties.markwon.span.ext; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.style.ImageSpan; + +import java.lang.ref.WeakReference; + +public class CenteredImageSpan extends ImageSpan { + private WeakReference mDrawableRef; + + public CenteredImageSpan(Context context, final int drawableRes) { + super(context, drawableRes); + } + + public CenteredImageSpan(Drawable drawable) { + super(drawable); + } + + @Override + public int getSize(Paint paint, CharSequence text, int start, int end, + Paint.FontMetricsInt fontMetricsInt) { + Drawable drawable = getDrawable(); + Rect rect = drawable.getBounds(); + if (fontMetricsInt != null) { + Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt(); + int fontHeight = fmPaint.descent - fmPaint.ascent; + int drHeight = rect.bottom - rect.top; + int centerY = fmPaint.ascent + fontHeight / 2; + + fontMetricsInt.ascent = centerY - drHeight / 2; + fontMetricsInt.top = fontMetricsInt.ascent; + fontMetricsInt.bottom = centerY + drHeight / 2; + fontMetricsInt.descent = fontMetricsInt.bottom; + } + return rect.right; + } + @Override + public void draw(Canvas canvas, CharSequence text, int start, int end, + float x, int top, int y, int bottom, Paint paint) { + + Drawable drawable = getDrawable(); + canvas.save(); + Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt(); + int fontHeight = fmPaint.descent - fmPaint.ascent; + int centerY = y + fmPaint.descent - fontHeight / 2; + int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2; + canvas.translate(x, transY); + drawable.draw(canvas); + canvas.restore(); + } +} \ No newline at end of file diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CodeBlockSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CodeBlockSpan.java new file mode 100755 index 00000000..28cdc0cb --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/CodeBlockSpan.java @@ -0,0 +1,126 @@ +package io.noties.markwon.span.ext; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.text.Layout; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.style.LeadingMarginSpan; +import android.text.style.LineHeightSpan; +import android.text.style.MetricAffectingSpan; + +public class CodeBlockSpan extends MetricAffectingSpan implements LeadingMarginSpan, LineHeightSpan { + protected static final float CODE_DEF_TEXT_SIZE_RATIO = 1.0F; + protected static final int DEFAULT_LEADING_MARGIN = 20; + protected int BACKGROUND_COLOR; + protected int TEXT_COLOR; + + protected static final int CORNER_RADIUS = 15; + private final int VERTICAL_SPACING = 40; + + private final Rect rect = ObjectsPool.rect(); + private final Paint paint = ObjectsPool.paint(); + + public CodeBlockSpan(String theme) { + if(theme.equalsIgnoreCase("dark")){ + BACKGROUND_COLOR = Color.argb(255, 25, 26, 27); + TEXT_COLOR = Color.WHITE; + }else{ + BACKGROUND_COLOR = Color.argb(255, 246, 246, 246); + TEXT_COLOR = Color.BLACK; + } + } + + @Override + public void updateMeasureState(TextPaint p) { + apply(p); + } + + @Override + public void updateDrawState(TextPaint ds) { + apply(ds); + } + + private void apply(TextPaint p) { + final int textColor = TEXT_COLOR; + + if (textColor != 0) { + paint.setColor(textColor); + } + + paint.setTypeface(Typeface.MONOSPACE); + + final int textSize = 0; + + if (textSize > 0) { + paint.setTextSize(textSize); + } else { + // calculate default value + paint.setTextSize(paint.getTextSize() * CODE_DEF_TEXT_SIZE_RATIO); + } + } + + @Override + public int getLeadingMargin(boolean first) { + return DEFAULT_LEADING_MARGIN; + } + + @Override + public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) { + + paint.setStyle(Paint.Style.FILL); + paint.setColor(BACKGROUND_COLOR); + + final int left; + final int right; + if (dir > 0) { + left = x; + right = c.getWidth(); + } else { + left = x - c.getWidth(); + right = x; + } + + rect.set(left, top, right, bottom); + + final Spanned txt = (Spanned)text; + final int spanEnd = txt.getSpanEnd(this); + final int spanStart = txt.getSpanStart(this); + + // draw rounded corner background + if (start == spanStart) { + c.drawRoundRect(new RectF(rect), CORNER_RADIUS, CORNER_RADIUS, paint); + c.drawRect(new Rect(left, top + CORNER_RADIUS, right, bottom), paint); + } + else if (Math.abs(spanEnd - end) <= 1) { + c.drawRoundRect(new RectF(rect), CORNER_RADIUS, CORNER_RADIUS, paint); + c.drawRect(new Rect(left, top, right, bottom - CORNER_RADIUS), paint); + } + else { + c.drawRect(rect, paint); + } + } + + @Override + public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) { + final Spanned txt = (Spanned)text; + final int spanEnd = txt.getSpanEnd(this); + final int spanStart = txt.getSpanStart(this); + + // add top spacing to first line + if (start == spanStart) { + fm.ascent -= VERTICAL_SPACING; + fm.top -= VERTICAL_SPACING; + } + + // add bottom spacing to last line + if (Math.abs(spanEnd - end) <= 1) { + fm.descent += VERTICAL_SPACING; + fm.bottom += VERTICAL_SPACING; + } + } +} diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/GifDrawableSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/GifDrawableSpan.java new file mode 100755 index 00000000..817d157c --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/GifDrawableSpan.java @@ -0,0 +1,33 @@ +package io.noties.markwon.span.ext; + +import android.graphics.drawable.Drawable; +import android.text.style.DynamicDrawableSpan; + +import com.bumptech.glide.load.resource.gif.GifDrawable; + +public class GifDrawableSpan extends DynamicDrawableSpan { + private GifDrawable drawable; + private int requiredWidth = 0; + private int requiredHeight = 0; + + public GifDrawableSpan(GifDrawable drawable){ + this.drawable = drawable; + } + + public GifDrawableSpan(GifDrawable drawable, int requiredWidth, int requiredHeight){ + this.requiredWidth = requiredWidth; + this.requiredHeight = requiredHeight; + this.drawable = drawable; + } + + @Override + public Drawable getDrawable() { +// drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + if (requiredHeight != 0 && requiredWidth != 0) { + drawable.setBounds(0, 0, requiredWidth, requiredHeight); + } else { + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + } + return drawable; + } +} diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/HashtagSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/HashtagSpan.java new file mode 100644 index 00000000..eef67043 --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/HashtagSpan.java @@ -0,0 +1,48 @@ +package io.noties.markwon.span.ext; + +import android.text.TextPaint; +import android.text.style.URLSpan; +import android.view.View; + +import androidx.annotation.NonNull; + +import io.noties.markwon.LinkResolver; + +public class HashtagSpan extends URLSpan { + private final String link; + private final LinkResolver resolver; + private final int linkColor; + + public HashtagSpan( + @NonNull String link, + @NonNull int linkColor, + @NonNull LinkResolver resolver) { + super(link); + this.link = link; + this.linkColor = linkColor; + this.resolver = resolver; + } + + @Override + public void onClick(View widget) { + resolver.resolve(widget, link); + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + applyLinkStyle(ds); + } + + private void applyLinkStyle(TextPaint paint){ + paint.setUnderlineText(true); + paint.setColor(linkColor); + } + + /** + * @since 4.2.0 + */ + @NonNull + public String getLink() { + return link; + } +} diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/NumberedSpan.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/NumberedSpan.java new file mode 100644 index 00000000..c2d9cd59 --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/NumberedSpan.java @@ -0,0 +1,58 @@ +package io.noties.markwon.span.ext; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.text.Layout; +import android.text.Spanned; +import android.text.style.LeadingMarginSpan; + +class NumberedSpan implements LeadingMarginSpan { + private final int NUMBER_GAP = 14; + private final int mIndex; + private final String mTheme; + private final Paint paint = ObjectsPool.paint(); + + NumberedSpan(int index, String theme) { + mIndex = index; + mTheme = theme; + } + + @Override + public int getLeadingMargin(boolean first) { + final String numText = mIndex + "."; + final Rect textBounds = new Rect(); + paint.getTextBounds(numText, 0, numText.length(), textBounds); + return textBounds.width() + NUMBER_GAP; + } + + @Override + public void drawLeadingMargin(Canvas canvas, Paint paint, int x, int dir, int top, int baseline, + int bottom, CharSequence text, int start, int end, boolean first, + Layout layout) { + // add number to first line + if (((Spanned)text).getSpanStart(this) == start) { + // save previous paint values + final Paint.Style prevStyle = paint.getStyle(); + final int prevColor = paint.getColor(); + + // draw number + paint.setStyle(Paint.Style.FILL); + if(mTheme.equalsIgnoreCase("dark")){ + paint.setColor(Color.WHITE); + }else{ + paint.setColor(Color.BLACK); + } + final String numText = mIndex + "."; + final Rect textBounds = new Rect(); + paint.getTextBounds(numText, 0, numText.length(), textBounds); + final float yVal = (top + bottom + textBounds.height()) / 2f; + canvas.drawText(numText, 0, numText.length(), x, yVal, paint); + + // reset modified paint values + paint.setStyle(prevStyle); + paint.setColor(prevColor); + } + } +} diff --git a/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/ObjectsPool.java b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/ObjectsPool.java new file mode 100755 index 00000000..ce7945d8 --- /dev/null +++ b/markwon-span-ext/src/main/java/io/noties/markwon/span/ext/ObjectsPool.java @@ -0,0 +1,33 @@ +package io.noties.markwon.span.ext; + +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; + +abstract class ObjectsPool { + + // maybe it's premature optimization, but as all the drawing is done in one thread + // and we apply needed values before actual drawing it's (I assume) safe to reuse some frequently used objects + + // if one of the spans need some really specific handling for Paint object (like colorFilters, masks, etc) + // it should instantiate own instance of it + + private static final Rect RECT = new Rect(); + private static final RectF RECT_F = new RectF(); + private static final Paint PAINT = new Paint(Paint.ANTI_ALIAS_FLAG); + + static Rect rect() { + return RECT; + } + + static RectF rectF() { + return RECT_F; + } + + static Paint paint() { + return PAINT; + } + + private ObjectsPool() { + } +} diff --git a/markwon-span-ext/src/test/java/io/noties/markwon/span/ext/ExampleUnitTest.java b/markwon-span-ext/src/test/java/io/noties/markwon/span/ext/ExampleUnitTest.java new file mode 100644 index 00000000..8aa28c02 --- /dev/null +++ b/markwon-span-ext/src/test/java/io/noties/markwon/span/ext/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package io.noties.markwon.span.ext; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file