Create additional spans
This commit is contained in:
		
							parent
							
								
									6006812f56
								
							
						
					
					
						commit
						c928750ff0
					
				
							
								
								
									
										1
									
								
								markwon-ext-spans/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								markwon-ext-spans/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
/build
 | 
			
		||||
							
								
								
									
										37
									
								
								markwon-ext-spans/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								markwon-ext-spans/build.gradle
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
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 {
 | 
			
		||||
 | 
			
		||||
    implementation 'androidx.appcompat:appcompat:1.3.1'
 | 
			
		||||
    implementation 'com.google.android.material:material:1.4.0'
 | 
			
		||||
    testImplementation 'junit:junit:4.+'
 | 
			
		||||
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
 | 
			
		||||
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								markwon-ext-spans/consumer-rules.pro
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								markwon-ext-spans/consumer-rules.pro
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								markwon-ext-spans/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								markwon-ext-spans/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
# Add project specific ProGuard rules here.
 | 
			
		||||
# You can control the set of applied configuration files using the
 | 
			
		||||
# proguardFiles setting in build.gradle.
 | 
			
		||||
#
 | 
			
		||||
# For more details, see
 | 
			
		||||
#   http://developer.android.com/guide/developing/tools/proguard.html
 | 
			
		||||
 | 
			
		||||
# If your project uses WebView with JS, uncomment the following
 | 
			
		||||
# and specify the fully qualified class name to the JavaScript interface
 | 
			
		||||
# class:
 | 
			
		||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 | 
			
		||||
#   public *;
 | 
			
		||||
#}
 | 
			
		||||
 | 
			
		||||
# Uncomment this to preserve the line number information for
 | 
			
		||||
# debugging stack traces.
 | 
			
		||||
#-keepattributes SourceFile,LineNumberTable
 | 
			
		||||
 | 
			
		||||
# If you keep the line number information, uncomment this to
 | 
			
		||||
# hide the original source file name.
 | 
			
		||||
#-renamesourcefileattribute SourceFile
 | 
			
		||||
@ -0,0 +1,26 @@
 | 
			
		||||
package io.noties.markwon.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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
 | 
			
		||||
 */
 | 
			
		||||
@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.ext.test", appContext.getPackageName());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								markwon-ext-spans/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								markwon-ext-spans/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="io.noties.markwon.ext">
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
							
								
								
									
										82
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/BlockQuoteSpan.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										82
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/BlockQuoteSpan.java
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,82 @@
 | 
			
		||||
package io.noties.markwon.;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										190
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/BulletSpan.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										190
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/BulletSpan.java
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,190 @@
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
import com.campuswire.android.messenger.model.THEMETYPE;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,28 @@
 | 
			
		||||
package io.noties.markwon.span.ext;
 | 
			
		||||
 | 
			
		||||
import android.text.TextPaint;
 | 
			
		||||
import android.text.style.ClickableSpan;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import com.campuswire.android.messenger.interfaces.MessageItemTapListener;
 | 
			
		||||
 | 
			
		||||
public class CWClickableSpan extends ClickableSpan {
 | 
			
		||||
    String userId;
 | 
			
		||||
    MessageItemTapListener messageItemTapListener;
 | 
			
		||||
    public CWClickableSpan(String text, MessageItemTapListener listener) {
 | 
			
		||||
        super();
 | 
			
		||||
        userId = text;
 | 
			
		||||
        messageItemTapListener = listener;
 | 
			
		||||
    }
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onClick(@NonNull View view) {
 | 
			
		||||
        messageItemTapListener.onClickUser(userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateDrawState(@NonNull TextPaint ds) {
 | 
			
		||||
        ds.setUnderlineText(false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/CWDrawableSpan.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										31
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/CWDrawableSpan.java
									
									
									
									
									
										Executable file
									
								
							@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/CenteredImageSpan.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										57
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/CenteredImageSpan.java
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
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 androidx.annotation.NonNull;
 | 
			
		||||
import android.text.style.DynamicDrawableSpan;
 | 
			
		||||
import android.text.style.ImageSpan;
 | 
			
		||||
 | 
			
		||||
import java.lang.ref.WeakReference;
 | 
			
		||||
 | 
			
		||||
public class CenteredImageSpan extends ImageSpan {
 | 
			
		||||
    private WeakReference<Drawable> 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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										133
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/CodeBlockSpan.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										133
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/CodeBlockSpan.java
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,133 @@
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import com.campuswire.android.messenger.model.THEMETYPE;
 | 
			
		||||
 | 
			
		||||
import io.noties.markwon.core.MarkwonTheme;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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(THEMETYPE.DARK.toString())){
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/GifDrawableSpan.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										33
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/GifDrawableSpan.java
									
									
									
									
									
										Executable file
									
								
							@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,60 @@
 | 
			
		||||
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 com.campuswire.android.messenger.model.THEMETYPE;
 | 
			
		||||
 | 
			
		||||
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(THEMETYPE.DARK.toString())){
 | 
			
		||||
                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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/ObjectsPool.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										33
									
								
								markwon-ext-spans/src/main/java/io/noties/markwon/ext/ObjectsPool.java
									
									
									
									
									
										Executable file
									
								
							@ -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() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
package io.noties.markwon.ext;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Example local unit test, which will execute on the development machine (host).
 | 
			
		||||
 *
 | 
			
		||||
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 | 
			
		||||
 */
 | 
			
		||||
public class ExampleUnitTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void addition_isCorrect() {
 | 
			
		||||
        assertEquals(4, 2 + 2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user