193 lines
7.0 KiB
Java
193 lines
7.0 KiB
Java
package ru.noties.markwon;
|
|
|
|
import android.content.Context;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.annotation.Nullable;
|
|
import android.text.TextUtils;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.widget.TextView;
|
|
|
|
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
|
|
import org.commonmark.ext.gfm.tables.TablesExtension;
|
|
import org.commonmark.node.Node;
|
|
import org.commonmark.parser.Parser;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import ru.noties.markwon.renderer.SpannableRenderer;
|
|
import ru.noties.markwon.tasklist.TaskListExtension;
|
|
|
|
@SuppressWarnings("WeakerAccess")
|
|
public abstract class Markwon {
|
|
|
|
/**
|
|
* Helper method to obtain a Parser with registered strike-through & table extensions
|
|
*
|
|
* @return a 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(), TaskListExtension.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,
|
|
@Nullable String markdown
|
|
) {
|
|
|
|
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);
|
|
unscheduleTableRows(view);
|
|
|
|
// update movement method (for links to be clickable)
|
|
view.setMovementMethod(LinkMovementMethod.getInstance());
|
|
view.setText(text);
|
|
|
|
// schedule drawables (dynamic drawables that can change bounds/animate will be correctly updated)
|
|
scheduleDrawables(view);
|
|
scheduleTableRows(view);
|
|
}
|
|
|
|
/**
|
|
* 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)) {
|
|
out = null;
|
|
} else {
|
|
final SpannableConfiguration configuration = SpannableConfiguration.create(context);
|
|
out = markdown(configuration, markdown);
|
|
}
|
|
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)) {
|
|
out = null;
|
|
} else {
|
|
final Parser parser = createParser();
|
|
final Node node = parser.parse(markdown);
|
|
final SpannableRenderer renderer = new SpannableRenderer();
|
|
out = renderer.render(configuration, node);
|
|
}
|
|
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);
|
|
}
|
|
|
|
private Markwon() {
|
|
}
|
|
}
|