text-added-listener for core-plugin and linkify module
This commit is contained in:
		
							parent
							
								
									2e35ef53bb
								
							
						
					
					
						commit
						cedb3971a0
					
				| @ -3,4 +3,7 @@ | ||||
| * `JLatex` plugin now is not dependent on ImagesPlugin | ||||
|   also accepts a ExecutorService (optional, by default cachedThreadPool is used) | ||||
| * 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-html') | ||||
|     implementation project(':markwon-image') | ||||
|     implementation project(':markwon-linkify') | ||||
|     implementation project(':markwon-syntax-highlight') | ||||
| 
 | ||||
|     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.network.OkHttpNetworkSchemeHandler; | ||||
| import ru.noties.markwon.image.svg.SvgMediaDecoder; | ||||
| import ru.noties.markwon.linkify.LinkifyPlugin; | ||||
| import ru.noties.markwon.syntax.Prism4jTheme; | ||||
| import ru.noties.markwon.syntax.Prism4jThemeDarkula; | ||||
| import ru.noties.markwon.syntax.Prism4jThemeDefault; | ||||
| @ -107,6 +108,7 @@ public class MarkdownRenderer { | ||||
|                                         .addMediaDecoder(SvgMediaDecoder.create()); | ||||
|                             } | ||||
|                         })) | ||||
|                         .usePlugin(LinkifyPlugin.create()) | ||||
|                         .usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme)) | ||||
|                         .usePlugin(GifAwarePlugin.create(context)) | ||||
|                         .usePlugin(TablePlugin.create(context)) | ||||
|  | ||||
| @ -8,11 +8,11 @@ | ||||
|         android:id="@+id/scroll_view" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:padding="16dip" | ||||
|         android:clipToPadding="false" | ||||
|         android:layout_marginTop="?android:attr/actionBarSize" | ||||
|         android:clipChildren="false" | ||||
|         android:scrollbarStyle="outsideOverlay" | ||||
|         android:layout_marginTop="?android:attr/actionBarSize"> | ||||
|         android:clipToPadding="false" | ||||
|         android:padding="16dip" | ||||
|         android:scrollbarStyle="outsideOverlay"> | ||||
| 
 | ||||
|         <TextView | ||||
|             android:id="@+id/text" | ||||
|  | ||||
| @ -27,6 +27,9 @@ import org.commonmark.node.StrongEmphasis; | ||||
| import org.commonmark.node.Text; | ||||
| import org.commonmark.node.ThematicBreak; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonSpansFactory; | ||||
| @ -51,14 +54,57 @@ import ru.noties.markwon.image.ImageProps; | ||||
|  */ | ||||
| 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 | ||||
|     public static CorePlugin create() { | ||||
|         return new CorePlugin(); | ||||
|     } | ||||
| 
 | ||||
|     // @since 4.0.0-SNAPSHOT | ||||
|     private final List<OnTextAddedListener> onTextAddedListeners = new ArrayList<>(0); | ||||
| 
 | ||||
|     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 | ||||
|     public void configureVisitor(@NonNull MarkwonVisitor.Builder 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>() { | ||||
|             @Override | ||||
|             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-glide', | ||||
|         ':markwon-image-picasso', | ||||
|         ':markwon-linkify', | ||||
|         ':markwon-recycler', | ||||
|         ':markwon-recycler-table', | ||||
|         ':markwon-syntax-highlight', | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov