Preparing for release, started README and javadocs
This commit is contained in:
		
							parent
							
								
									58c2f26491
								
							
						
					
					
						commit
						0f1b6c4cf7
					
				| @ -1,7 +1,7 @@ | ||||
| # Markwon | ||||
| 
 | ||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22) | ||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22) | ||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22) | ||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22) | ||||
| 
 | ||||
| Android library for rendering markdown as system-native Spannables. Based on [commonmark-java][commonmark-java] | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										97
									
								
								library/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								library/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| # Markwon | ||||
| 
 | ||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22) | ||||
| 
 | ||||
| 
 | ||||
| ## Installation | ||||
| ```groovy | ||||
| compile 'ru.noties:markwon:1.0.0' | ||||
| ``` | ||||
| 
 | ||||
| ## Intoduction | ||||
| 
 | ||||
| The aim for this library is to render markdown as first class citizen on Android - Spannables. It has reasonable defaults to display markdown, but also gives ability to customize almost every detail for your liking. | ||||
| 
 | ||||
| The most basic example would be: | ||||
| ```java | ||||
| Markwon.setMarkdown(textView, "**Hello *there*!!**") | ||||
| ``` | ||||
| 
 | ||||
| ## Images | ||||
| 
 | ||||
| By default this library does not render any of the images. It's done to simplify rendering of text-based markdown. But if images must be supported, then the `AsyncDrawable.Loader` can be specified whilst building a `SpannableConfiguration` instance: | ||||
| 
 | ||||
| ```java | ||||
| final AsyncDrawable.Loader loader = new AsyncDrawable.Loader() { | ||||
|     @Override | ||||
|     public void load(@NonNull String destination, @NonNull final AsyncDrawable drawable) { | ||||
|         // `download` method is here for demonstration purposes, it's not included in this interface | ||||
|         download(destination, new Callback() { | ||||
|             @Override | ||||
|             public void onDownloaded(Drawable d) { | ||||
|                 drawable.setResult(d); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void cancel(@NonNull String destination) { | ||||
|         // cancel download here | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // `this` here referrs to a Context instance | ||||
| final SpannableConfiguration configuration = SpannableConfiguration.builder(this) | ||||
|         .asyncDrawableLoader(loader) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| There is also standalone artifact that supports image loading *out-of-box* (including support for **SVG** & **GIF**), but provides little to none configuration and could be somewhat not optimal. Please refer to the [README][mil-README] of the module. | ||||
| 
 | ||||
| 
 | ||||
| ## Tables | ||||
| 
 | ||||
| Tables are supported but with some limitations. First of all: table will always take the full width of the TextView Canvas. Second: each column will have the same width (we do not calculate the weight of column) - so, a column width will be: `totalWidth / columnsNumber`. | ||||
| 
 | ||||
| 
 | ||||
| ## Syntax highlight | ||||
| This library does not provide ready-to-be-used implementation of syntax highlight, but it can be easily added via `SyntaxHighlight` interface whilst building `SpannableConfiguration`: | ||||
| 
 | ||||
| ```java | ||||
| final SyntaxHighlight syntaxHighlight = new SyntaxHighlight() { | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public CharSequence highlight(@Nullable String info, @NonNull String code) { | ||||
|         // create Spanned of highlight here | ||||
|         return null; // must not return `null` here | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| final SpannableConfiguration configuration = SpannableConfiguration.builder(this) | ||||
|         .syntaxHighlight(syntaxHighlight) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| ## Url processing | ||||
| If you wish to process urls (links & images) that markdown contains, the `UrlProcessor` can be used: | ||||
| ```java | ||||
| final UrlProcessor urlProcessor = new UrlProcessor() { | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public String process(@NonNull String destination) { | ||||
|         // modify the `destination` or return as-is | ||||
|         return null; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| final SpannableConfiguration configuration = SpannableConfiguration.builder(this) | ||||
|         .urlProcessor(urlProcessor) | ||||
|         .build(); | ||||
| ``` | ||||
| The primary goal of additing this abstraction is to give ability to convert relative urls to absolute ones. If it fits your purpose, then `UrlProcessorRelativeToAbsolute` can be used: | ||||
| ```java | ||||
| final UrlProcessor urlProcessor = new UrlProcessorRelativeToAbsolute("https://this-is-base.org"); | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| [mil-README]: https://github.com/noties/Markwon/blob/master/library-image-loader/README.md | ||||
| @ -14,6 +14,7 @@ import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import ru.noties.markwon.renderer.R; | ||||
| import ru.noties.markwon.spans.AsyncDrawable; | ||||
| import ru.noties.markwon.spans.AsyncDrawableSpan; | ||||
| 
 | ||||
| @ -23,18 +24,24 @@ abstract class DrawablesScheduler { | ||||
| 
 | ||||
|         final List<AsyncDrawable> list = extract(textView); | ||||
|         if (list.size() > 0) { | ||||
|             textView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { | ||||
|                 @Override | ||||
|                 public void onViewAttachedToWindow(View v) { | ||||
| 
 | ||||
|                 } | ||||
|             if (textView.getTag(R.id.markwon_drawables_scheduler) == null) { | ||||
|                 final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() { | ||||
|                     @Override | ||||
|                     public void onViewAttachedToWindow(View v) { | ||||
| 
 | ||||
|                 @Override | ||||
|                 public void onViewDetachedFromWindow(View v) { | ||||
|                     // we obtain a new list in case text was changed | ||||
|                     unschedule(textView); | ||||
|                 } | ||||
|             }); | ||||
|                     } | ||||
| 
 | ||||
|                     @Override | ||||
|                     public void onViewDetachedFromWindow(View v) { | ||||
|                         unschedule(textView); | ||||
|                         v.removeOnAttachStateChangeListener(this); | ||||
|                         v.setTag(R.id.markwon_drawables_scheduler, null); | ||||
|                     } | ||||
|                 }; | ||||
|                 textView.addOnAttachStateChangeListener(listener); | ||||
|                 textView.setTag(R.id.markwon_drawables_scheduler, listener); | ||||
|             } | ||||
| 
 | ||||
|             for (AsyncDrawable drawable : list) { | ||||
|                 drawable.setCallback2(new DrawableCallbackImpl(textView, drawable.getBounds())); | ||||
|  | ||||
| @ -19,16 +19,38 @@ import ru.noties.markwon.renderer.SpannableRenderer; | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public abstract class Markwon { | ||||
| 
 | ||||
|     /** | ||||
|      * Helper method to obtain a {@link Parser} with registered strike-through & table extensions | ||||
|      * | ||||
|      * @return a {@link Parser} instance that is supported by this library | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static Parser createParser() { | ||||
|         return new Parser.Builder() | ||||
|                 .extensions(Arrays.asList(StrikethroughExtension.create(), TablesExtension.create())) | ||||
|                 .build(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @see #setMarkdown(TextView, SpannableConfiguration, String) | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static void setMarkdown(@NonNull TextView view, @NonNull String markdown) { | ||||
|         setMarkdown(view, SpannableConfiguration.create(view.getContext()), markdown); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Parses submitted raw markdown, converts it to CharSequence (with Spannables) | ||||
|      * and applies it to view | ||||
|      * | ||||
|      * @param view          {@link TextView} to set markdown into | ||||
|      * @param configuration a {@link SpannableConfiguration} instance | ||||
|      * @param markdown      raw markdown String (for example: {@code `**Hello**`}) | ||||
|      * @see #markdown(SpannableConfiguration, String) | ||||
|      * @see #setText(TextView, CharSequence) | ||||
|      * @see SpannableConfiguration | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static void setMarkdown( | ||||
|             @NonNull TextView view, | ||||
|             @NonNull SpannableConfiguration configuration, | ||||
| @ -38,6 +60,15 @@ public abstract class Markwon { | ||||
|         setText(view, markdown(configuration, markdown)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Helper method to apply parsed markdown. Please note, that if images or tables are used | ||||
|      * | ||||
|      * @param view {@link TextView} to set markdown into | ||||
|      * @param text parsed markdown | ||||
|      * @see #scheduleDrawables(TextView) | ||||
|      * @see #scheduleTableRows(TextView) | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static void setText(@NonNull TextView view, CharSequence text) { | ||||
| 
 | ||||
|         unscheduleDrawables(view); | ||||
| @ -52,7 +83,14 @@ public abstract class Markwon { | ||||
|         scheduleTableRows(view); | ||||
|     } | ||||
| 
 | ||||
|     // with default configuration | ||||
|     /** | ||||
|      * Returns parsed markdown with default {@link SpannableConfiguration} obtained from {@link Context} | ||||
|      * | ||||
|      * @param context  {@link Context} | ||||
|      * @param markdown raw markdown | ||||
|      * @return parsed markdown | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static CharSequence markdown(@NonNull Context context, @Nullable String markdown) { | ||||
|         final CharSequence out; | ||||
|         if (TextUtils.isEmpty(markdown)) { | ||||
| @ -64,6 +102,15 @@ public abstract class Markwon { | ||||
|         return out; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns parsed markdown with provided {@link SpannableConfiguration} | ||||
|      * | ||||
|      * @param configuration a {@link SpannableConfiguration} | ||||
|      * @param markdown      raw markdown | ||||
|      * @return parsed markdown | ||||
|      * @see SpannableConfiguration | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static CharSequence markdown(@NonNull SpannableConfiguration configuration, @Nullable String markdown) { | ||||
|         final CharSequence out; | ||||
|         if (TextUtils.isEmpty(markdown)) { | ||||
| @ -77,18 +124,64 @@ public abstract class Markwon { | ||||
|         return out; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This method adds support for {@link ru.noties.markwon.spans.AsyncDrawable} to be used. As | ||||
|      * textView seems not to support drawables that change bounds (and gives no means | ||||
|      * to update the layout), we create own {@link android.graphics.drawable.Drawable.Callback} | ||||
|      * and apply it. So, textView can display drawables, that are: async (loading from disk, network); | ||||
|      * dynamic (requires `invalidate`) - GIF, animations. | ||||
|      * Please note, that this method should be preceded with {@link #unscheduleDrawables(TextView)} | ||||
|      * in order to avoid keeping drawables in memory after they have been removed from layout | ||||
|      * | ||||
|      * @param view a {@link TextView} | ||||
|      * @see ru.noties.markwon.spans.AsyncDrawable | ||||
|      * @see ru.noties.markwon.spans.AsyncDrawableSpan | ||||
|      * @see DrawablesScheduler#schedule(TextView) | ||||
|      * @see DrawablesScheduler#unschedule(TextView) | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static void scheduleDrawables(@NonNull TextView view) { | ||||
|         DrawablesScheduler.schedule(view); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * De-references previously scheduled {@link ru.noties.markwon.spans.AsyncDrawableSpan}'s | ||||
|      * | ||||
|      * @param view a {@link TextView} | ||||
|      * @see #scheduleDrawables(TextView) | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static void unscheduleDrawables(@NonNull TextView view) { | ||||
|         DrawablesScheduler.unschedule(view); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This method is required in order to use tables. A bit of background: | ||||
|      * this library uses a {@link android.text.style.ReplacementSpan} to | ||||
|      * render tables, but the flow is not really flexible. We are required | ||||
|      * to return `size` (width) of our replacement, but we are not provided | ||||
|      * with the total one (canvas width). In order to correctly calculate height of our | ||||
|      * table cell text, we must have available width first. This method gives | ||||
|      * ability for {@link ru.noties.markwon.spans.TableRowSpan} to invalidate | ||||
|      * `view` when it encounters such a situation (when available width is not known or have changed). | ||||
|      * Precede this call with {@link #unscheduleTableRows(TextView)} in order to | ||||
|      * de-reference previously scheduled {@link ru.noties.markwon.spans.TableRowSpan}'s | ||||
|      * | ||||
|      * @param view a {@link TextView} | ||||
|      * @see #unscheduleTableRows(TextView) | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static void scheduleTableRows(@NonNull TextView view) { | ||||
|         TableRowsScheduler.schedule(view); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * De-references previously scheduled {@link ru.noties.markwon.spans.TableRowSpan}'s | ||||
|      * | ||||
|      * @param view a {@link TextView} | ||||
|      * @see #scheduleTableRows(TextView) | ||||
|      * @since 1.0.0 | ||||
|      */ | ||||
|     public static void unscheduleTableRows(@NonNull TextView view) { | ||||
|         TableRowsScheduler.unschedule(view); | ||||
|     } | ||||
|  | ||||
| @ -6,6 +6,7 @@ import android.text.TextUtils; | ||||
| import android.view.View; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import ru.noties.markwon.renderer.R; | ||||
| import ru.noties.markwon.spans.TableRowSpan; | ||||
| 
 | ||||
| abstract class TableRowsScheduler { | ||||
| @ -14,24 +15,32 @@ abstract class TableRowsScheduler { | ||||
|         final Object[] spans = extract(view); | ||||
|         if (spans != null | ||||
|                 && spans.length > 0) { | ||||
|             view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { | ||||
|                 @Override | ||||
|                 public void onViewAttachedToWindow(View v) { | ||||
| 
 | ||||
|                 } | ||||
|             if (view.getTag(R.id.markwon_tables_scheduler) == null) { | ||||
|                 final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() { | ||||
|                     @Override | ||||
|                     public void onViewAttachedToWindow(View v) { | ||||
| 
 | ||||
|                     } | ||||
| 
 | ||||
|                     @Override | ||||
|                     public void onViewDetachedFromWindow(View v) { | ||||
|                         unschedule(view); | ||||
|                         view.removeOnAttachStateChangeListener(this); | ||||
|                         view.setTag(R.id.markwon_tables_scheduler, null); | ||||
|                     } | ||||
|                 }; | ||||
|                 view.addOnAttachStateChangeListener(listener); | ||||
|                 view.setTag(R.id.markwon_tables_scheduler, listener); | ||||
|             } | ||||
| 
 | ||||
|                 @Override | ||||
|                 public void onViewDetachedFromWindow(View v) { | ||||
|                     unschedule(view); | ||||
|                     view.removeOnAttachStateChangeListener(this); | ||||
|                 } | ||||
|             }); | ||||
|             final TableRowSpan.Invalidator invalidator = new TableRowSpan.Invalidator() { | ||||
|                 @Override | ||||
|                 public void invalidate() { | ||||
|                     view.setText(view.getText()); | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             for (Object span : spans) { | ||||
|                 ((TableRowSpan) span).invalidator(invalidator); | ||||
|             } | ||||
|  | ||||
							
								
								
									
										7
									
								
								library/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								library/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
| 
 | ||||
|     <item name="markwon_drawables_scheduler" type="id" /> | ||||
|     <item name="markwon_tables_scheduler" type="id" /> | ||||
| 
 | ||||
| </resources> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov