text-added-listener for core-plugin and linkify module
This commit is contained in:
parent
2e35ef53bb
commit
cedb3971a0
@ -4,3 +4,6 @@
|
|||||||
also accepts a ExecutorService (optional, by default cachedThreadPool is used)
|
also accepts a ExecutorService (optional, by default cachedThreadPool is used)
|
||||||
* AsyncDrawableScheduler now can be called by multiple plugins without penalty
|
* AsyncDrawableScheduler now can be called by multiple plugins without penalty
|
||||||
internally caches latest state and skips scheduling if drawables are already processed
|
internally caches latest state and skips scheduling if drawables are already processed
|
||||||
|
* configure with registry
|
||||||
|
* removed priority
|
||||||
|
* images-plugin moved to standalone again
|
@ -34,6 +34,7 @@ dependencies {
|
|||||||
implementation project(':markwon-ext-tasklist')
|
implementation project(':markwon-ext-tasklist')
|
||||||
implementation project(':markwon-html')
|
implementation project(':markwon-html')
|
||||||
implementation project(':markwon-image')
|
implementation project(':markwon-image')
|
||||||
|
implementation project(':markwon-linkify')
|
||||||
implementation project(':markwon-syntax-highlight')
|
implementation project(':markwon-syntax-highlight')
|
||||||
|
|
||||||
deps.with {
|
deps.with {
|
||||||
|
@ -25,6 +25,7 @@ import ru.noties.markwon.image.file.FileSchemeHandler;
|
|||||||
import ru.noties.markwon.image.gif.GifMediaDecoder;
|
import ru.noties.markwon.image.gif.GifMediaDecoder;
|
||||||
import ru.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
|
import ru.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
|
||||||
import ru.noties.markwon.image.svg.SvgMediaDecoder;
|
import ru.noties.markwon.image.svg.SvgMediaDecoder;
|
||||||
|
import ru.noties.markwon.linkify.LinkifyPlugin;
|
||||||
import ru.noties.markwon.syntax.Prism4jTheme;
|
import ru.noties.markwon.syntax.Prism4jTheme;
|
||||||
import ru.noties.markwon.syntax.Prism4jThemeDarkula;
|
import ru.noties.markwon.syntax.Prism4jThemeDarkula;
|
||||||
import ru.noties.markwon.syntax.Prism4jThemeDefault;
|
import ru.noties.markwon.syntax.Prism4jThemeDefault;
|
||||||
@ -107,6 +108,7 @@ public class MarkdownRenderer {
|
|||||||
.addMediaDecoder(SvgMediaDecoder.create());
|
.addMediaDecoder(SvgMediaDecoder.create());
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.usePlugin(LinkifyPlugin.create())
|
||||||
.usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme))
|
.usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme))
|
||||||
.usePlugin(GifAwarePlugin.create(context))
|
.usePlugin(GifAwarePlugin.create(context))
|
||||||
.usePlugin(TablePlugin.create(context))
|
.usePlugin(TablePlugin.create(context))
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
android:id="@+id/scroll_view"
|
android:id="@+id/scroll_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="16dip"
|
android:layout_marginTop="?android:attr/actionBarSize"
|
||||||
android:clipToPadding="false"
|
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:scrollbarStyle="outsideOverlay"
|
android:clipToPadding="false"
|
||||||
android:layout_marginTop="?android:attr/actionBarSize">
|
android:padding="16dip"
|
||||||
|
android:scrollbarStyle="outsideOverlay">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
|
@ -27,6 +27,9 @@ import org.commonmark.node.StrongEmphasis;
|
|||||||
import org.commonmark.node.Text;
|
import org.commonmark.node.Text;
|
||||||
import org.commonmark.node.ThematicBreak;
|
import org.commonmark.node.ThematicBreak;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import ru.noties.markwon.AbstractMarkwonPlugin;
|
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||||
import ru.noties.markwon.MarkwonConfiguration;
|
import ru.noties.markwon.MarkwonConfiguration;
|
||||||
import ru.noties.markwon.MarkwonSpansFactory;
|
import ru.noties.markwon.MarkwonSpansFactory;
|
||||||
@ -51,14 +54,57 @@ import ru.noties.markwon.image.ImageProps;
|
|||||||
*/
|
*/
|
||||||
public class CorePlugin extends AbstractMarkwonPlugin {
|
public class CorePlugin extends AbstractMarkwonPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #addOnTextAddedListener(OnTextAddedListener)
|
||||||
|
* @since 4.0.0-SNAPSHOT
|
||||||
|
*/
|
||||||
|
public interface OnTextAddedListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will be called when new text is added to resulting {@link ru.noties.markwon.SpannableBuilder}.
|
||||||
|
* Please note that only text represented by {@link Text} node will trigger this callback
|
||||||
|
* (text inside code and code-blocks won\'t trigger it).
|
||||||
|
* <p>
|
||||||
|
* Please note that if you wish to add spans you must use {@code start} parameter
|
||||||
|
* in order to place spans correctly ({@code start} represents the index at which {@code text}
|
||||||
|
* was added). So, to set a span for the whole length of the text added one should use:
|
||||||
|
* <p>
|
||||||
|
* {@code
|
||||||
|
* visitor.builder().setSpan(new MySpan(), start, start + text.length(), 0);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param visitor {@link MarkwonVisitor}
|
||||||
|
* @param text literal that had been added
|
||||||
|
* @param start index in {@code visitor} as which text had been added
|
||||||
|
* @see #addOnTextAddedListener(OnTextAddedListener)
|
||||||
|
*/
|
||||||
|
void onTextAdded(@NonNull MarkwonVisitor visitor, @NonNull String text, int start);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static CorePlugin create() {
|
public static CorePlugin create() {
|
||||||
return new CorePlugin();
|
return new CorePlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @since 4.0.0-SNAPSHOT
|
||||||
|
private final List<OnTextAddedListener> onTextAddedListeners = new ArrayList<>(0);
|
||||||
|
|
||||||
protected CorePlugin() {
|
protected CorePlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be useful to post-process text added. For example for auto-linking capabilities.
|
||||||
|
*
|
||||||
|
* @see OnTextAddedListener
|
||||||
|
* @since 4.0.0-SNAPSHOT
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
|
@NonNull
|
||||||
|
public CorePlugin addOnTextAddedListener(@NonNull OnTextAddedListener onTextAddedListener) {
|
||||||
|
onTextAddedListeners.add(onTextAddedListener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
text(builder);
|
text(builder);
|
||||||
@ -114,11 +160,20 @@ public class CorePlugin extends AbstractMarkwonPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void text(@NonNull MarkwonVisitor.Builder builder) {
|
private void text(@NonNull MarkwonVisitor.Builder builder) {
|
||||||
builder.on(Text.class, new MarkwonVisitor.NodeVisitor<Text>() {
|
builder.on(Text.class, new MarkwonVisitor.NodeVisitor<Text>() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) {
|
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Text text) {
|
||||||
visitor.builder().append(text.getLiteral());
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
final String literal = text.getLiteral();
|
||||||
|
|
||||||
|
visitor.builder().append(literal);
|
||||||
|
|
||||||
|
// @since 4.0.0-SNAPSHOT
|
||||||
|
for (OnTextAddedListener onTextAddedListener : onTextAddedListeners) {
|
||||||
|
onTextAddedListener.onTextAdded(visitor, literal, length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
20
markwon-linkify/build.gradle
Normal file
20
markwon-linkify/build.gradle
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
|
||||||
|
compileSdkVersion config['compile-sdk']
|
||||||
|
buildToolsVersion config['build-tools']
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion config['min-sdk']
|
||||||
|
targetSdkVersion config['target-sdk']
|
||||||
|
versionCode 1
|
||||||
|
versionName version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':markwon-core')
|
||||||
|
}
|
||||||
|
|
||||||
|
registerArtifact(this)
|
4
markwon-linkify/gradle.properties
Normal file
4
markwon-linkify/gradle.properties
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
POM_NAME=Linkify
|
||||||
|
POM_ARTIFACT_ID=linkify
|
||||||
|
POM_DESCRIPTION=Markwon plugin to linkify text (based on Android Linkify)
|
||||||
|
POM_PACKAGING=aar
|
1
markwon-linkify/src/main/AndroidManifest.xml
Normal file
1
markwon-linkify/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="ru.noties.markwon.linkify" />
|
@ -0,0 +1,91 @@
|
|||||||
|
package ru.noties.markwon.linkify;
|
||||||
|
|
||||||
|
import android.support.annotation.IntDef;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.util.Linkify;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import ru.noties.markwon.AbstractMarkwonPlugin;
|
||||||
|
import ru.noties.markwon.MarkwonVisitor;
|
||||||
|
import ru.noties.markwon.SpannableBuilder;
|
||||||
|
import ru.noties.markwon.core.CorePlugin;
|
||||||
|
|
||||||
|
public class LinkifyPlugin extends AbstractMarkwonPlugin {
|
||||||
|
|
||||||
|
@IntDef(flag = true, value = {
|
||||||
|
Linkify.EMAIL_ADDRESSES,
|
||||||
|
Linkify.PHONE_NUMBERS,
|
||||||
|
Linkify.WEB_URLS,
|
||||||
|
Linkify.ALL
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@interface LinkifyMask {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static LinkifyPlugin create() {
|
||||||
|
return create(Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static LinkifyPlugin create(@LinkifyMask int mask) {
|
||||||
|
return new LinkifyPlugin(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int mask;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
LinkifyPlugin(@LinkifyMask int mask) {
|
||||||
|
this.mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(@NonNull Registry registry) {
|
||||||
|
registry.require(CorePlugin.class, new Action<CorePlugin>() {
|
||||||
|
@Override
|
||||||
|
public void apply(@NonNull CorePlugin corePlugin) {
|
||||||
|
corePlugin.addOnTextAddedListener(new LinkifyTextAddedListener(mask));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LinkifyTextAddedListener implements CorePlugin.OnTextAddedListener {
|
||||||
|
|
||||||
|
private final int mask;
|
||||||
|
private final SpannableStringBuilder builder;
|
||||||
|
|
||||||
|
LinkifyTextAddedListener(int mask) {
|
||||||
|
this.mask = mask;
|
||||||
|
this.builder = new SpannableStringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextAdded(@NonNull MarkwonVisitor visitor, @NonNull String text, int start) {
|
||||||
|
|
||||||
|
// clear previous state
|
||||||
|
builder.clear();
|
||||||
|
builder.clearSpans();
|
||||||
|
|
||||||
|
// append text to process
|
||||||
|
builder.append(text);
|
||||||
|
|
||||||
|
if (Linkify.addLinks(builder, mask)) {
|
||||||
|
final Object[] spans = builder.getSpans(0, builder.length(), Object.class);
|
||||||
|
if (spans != null
|
||||||
|
&& spans.length > 0) {
|
||||||
|
final SpannableBuilder spannableBuilder = visitor.builder();
|
||||||
|
for (Object span : spans) {
|
||||||
|
spannableBuilder.setSpan(
|
||||||
|
span,
|
||||||
|
start + builder.getSpanStart(span),
|
||||||
|
start + builder.getSpanEnd(span),
|
||||||
|
builder.getSpanFlags(span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ include ':app', ':sample',
|
|||||||
':markwon-image',
|
':markwon-image',
|
||||||
':markwon-image-glide',
|
':markwon-image-glide',
|
||||||
':markwon-image-picasso',
|
':markwon-image-picasso',
|
||||||
|
':markwon-linkify',
|
||||||
':markwon-recycler',
|
':markwon-recycler',
|
||||||
':markwon-recycler-table',
|
':markwon-recycler-table',
|
||||||
':markwon-syntax-highlight',
|
':markwon-syntax-highlight',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user