diff --git a/app/build.gradle b/app/build.gradle index bc2bca96..286e6bb9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,13 +27,13 @@ android { dependencies { - compile project(':library') - compile project(':library-image-loader') + implementation project(':library') + implementation project(':library-image-loader') - compile 'ru.noties:debug:3.0.0@jar' + implementation 'ru.noties:debug:3.0.0@jar' - compile OK_HTTP + implementation OK_HTTP - compile 'com.google.dagger:dagger:2.10' + implementation 'com.google.dagger:dagger:2.10' annotationProcessor 'com.google.dagger:dagger-compiler:2.10' } diff --git a/build.gradle b/build.gradle index 036e147b..ef441eaf 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.0.1' } } @@ -22,7 +22,7 @@ task clean(type: Delete) { } task wrapper(type: Wrapper) { - gradleVersion '4.3' + gradleVersion '4.5' distributionType 'all' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7a3265ee..27768f1b 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 590f0e81..610ad4c5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip diff --git a/library-image-loader/build.gradle b/library-image-loader/build.gradle index 3a909101..5c8f993b 100644 --- a/library-image-loader/build.gradle +++ b/library-image-loader/build.gradle @@ -19,10 +19,10 @@ android { } dependencies { - compile project(':library') - compile ANDROID_SVG - compile ANDROID_GIF - compile OK_HTTP + api project(':library') + api ANDROID_SVG + api ANDROID_GIF + api OK_HTTP } if (project.hasProperty('release')) { diff --git a/library-view/build.gradle b/library-view/build.gradle index c0675588..bb6073b7 100644 --- a/library-view/build.gradle +++ b/library-view/build.gradle @@ -14,8 +14,8 @@ android { } dependencies { - compile project(':library') - provided SUPPORT_APP_COMPAT + api project(':library') + compileOnly SUPPORT_APP_COMPAT } if (project.hasProperty('release')) { diff --git a/library/build.gradle b/library/build.gradle index c384c1b6..702b609b 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -14,10 +14,10 @@ android { } dependencies { - compile SUPPORT_ANNOTATIONS - compile COMMON_MARK - compile COMMON_MARK_STRIKETHROUGHT - compile COMMON_MARK_TABLE + api SUPPORT_ANNOTATIONS + api COMMON_MARK + api COMMON_MARK_STRIKETHROUGHT + api COMMON_MARK_TABLE } if (project.hasProperty('release')) { diff --git a/sample-custom-extension/README.md b/sample-custom-extension/README.md new file mode 100644 index 00000000..194557ab --- /dev/null +++ b/sample-custom-extension/README.md @@ -0,0 +1,26 @@ +# Custom extension + +This module provides a simple implementation for icons that are bundled in your application resources using custom `DelimiterProcessor`. It can be used as a reference when dealing with new functionality based on _delimiters_. + +```markdown +# Hello @ic-android-black-24 + +**Please** click @ic-home-green-24 (home icon) if you want to go home. +``` + +Here we will substitute elements starting with `@ic-` for icons that we have in our resources: + +* `@ic-android-black-24` -> `R.drawable.ic_android_black_24dp` +* `@ic-home-green-24` -> `R.drawable.ic_home_green_24dp` + +In order to provide reliable parsing we need to have delimiters _around_ desired content. So, `@ic-home-green-24` would become `@ic-home-green-24@`. This is current limitation of [commonmark-java](https://github.com/atlassian/commonmark-java) library that Markwon uses underneath. There is an ongoing [issue](https://github.com/atlassian/commonmark-java/issues/113) that might change this in future thought. + +But as we known the pattern beforehand it's pretty easy to pre-process raw markdown and make it the way we want it. Please refer to `IconProcessor#process` method for the reference. + +So, the our steps would be: + +* prepare raw markdown (wrap icons with `@` if it's not already) +* construct a Parser with our registered delimiter processor +* parse markdown and obtain a `Node` +* create a node visitor that will additionally visit custom node (`IconNode`) +* use markdown diff --git a/sample-custom-extension/build.gradle b/sample-custom-extension/build.gradle new file mode 100644 index 00000000..baa37484 --- /dev/null +++ b/sample-custom-extension/build.gradle @@ -0,0 +1,22 @@ +apply plugin: 'com.android.application' + +android { + + compileSdkVersion TARGET_SDK + buildToolsVersion BUILD_TOOLS + + defaultConfig { + + applicationId "ru.noties.markwon.sample.extension" + + // using 21 as minimum only to be able to vector assets + minSdkVersion 21 + targetSdkVersion TARGET_SDK + versionCode 1 + versionName version + } +} + +dependencies { + implementation project(':library') +} diff --git a/sample-custom-extension/src/main/AndroidManifest.xml b/sample-custom-extension/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1553a0a6 --- /dev/null +++ b/sample-custom-extension/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="ru.noties.markwon.sample.extension"> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> \ No newline at end of file diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconGroupNode.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconGroupNode.java new file mode 100644 index 00000000..193b33b8 --- /dev/null +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconGroupNode.java @@ -0,0 +1,8 @@ +package ru.noties.markwon.sample.extension; + +import org.commonmark.node.CustomNode; + +@SuppressWarnings("WeakerAccess") +public class IconGroupNode extends CustomNode { + +} diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconNode.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconNode.java new file mode 100644 index 00000000..3d40ec2f --- /dev/null +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconNode.java @@ -0,0 +1,61 @@ +package ru.noties.markwon.sample.extension; + +import android.support.annotation.NonNull; + +import org.commonmark.node.CustomNode; +import org.commonmark.node.Delimited; + +@SuppressWarnings("WeakerAccess") +public class IconNode extends CustomNode implements Delimited { + + public static final char DELIMITER = '@'; + + public static final String DELIMITER_STRING = "" + DELIMITER; + + + private final String name; + + private final String color; + + private final String size; + + public IconNode(@NonNull String name, @NonNull String color, @NonNull String size) { + this.name = name; + this.color = color; + this.size = size; + } + + @NonNull + public String name() { + return name; + } + + @NonNull + public String color() { + return color; + } + + @NonNull + public String size() { + return size; + } + + @Override + public String getOpeningDelimiter() { + return DELIMITER_STRING; + } + + @Override + public String getClosingDelimiter() { + return DELIMITER_STRING; + } + + @Override + public String toString() { + return "IconNode{" + + "name='" + name + '\'' + + ", color='" + color + '\'' + + ", size='" + size + '\'' + + '}'; + } +} diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconProcessor.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconProcessor.java new file mode 100644 index 00000000..500726e0 --- /dev/null +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconProcessor.java @@ -0,0 +1,160 @@ +package ru.noties.markwon.sample.extension; + +import android.support.annotation.NonNull; +import android.text.TextUtils; + +import org.commonmark.node.Node; +import org.commonmark.node.Text; +import org.commonmark.parser.delimiter.DelimiterProcessor; +import org.commonmark.parser.delimiter.DelimiterRun; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@SuppressWarnings("WeakerAccess") +public class IconProcessor implements DelimiterProcessor { + + @NonNull + public static IconProcessor create() { + return new IconProcessor(); + } + + // ic-home-black-24 + private static final Pattern PATTERN = Pattern.compile("ic-(\\w+)-(\\w+)-(\\d+)"); + + private static final String TO_FIND = IconNode.DELIMITER_STRING + "ic-"; + + /** + * Should be used when input string does not wrap icon definition with `@` from both ends. + * So, `@ic-home-white-24` would become `@ic-home-white-24@`. This way parsing is easier + * and more predictable (cannot specify multiple ending delimiters, as we would require them: + * space, newline, end of a document, and a lot of more) + * + * @param input to process + * @return processed string + * @see #prepare(StringBuilder) + */ + @NonNull + public static String prepare(@NonNull String input) { + final StringBuilder builder = new StringBuilder(input); + prepare(builder); + return builder.toString(); + } + + public static void prepare(@NonNull StringBuilder builder) { + + int start = builder.indexOf(TO_FIND); + int end; + + while (start > -1) { + + end = iconDefinitionEnd(start + TO_FIND.length(), builder); + + // if we match our pattern, append `@` else ignore + if (iconDefinitionValid(builder.subSequence(start + 1, end))) { + builder.insert(end, '@'); + } + + // move to next + start = builder.indexOf(TO_FIND, end); + } + } + + @Override + public char getOpeningCharacter() { + return IconNode.DELIMITER; + } + + @Override + public char getClosingCharacter() { + return IconNode.DELIMITER; + } + + @Override + public int getMinLength() { + return 1; + } + + @Override + public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) { + return opener.length() >= 1 && closer.length() >= 1 ? 1 : 0; + } + + @Override + public void process(Text opener, Text closer, int delimiterUse) { + + final IconGroupNode iconGroupNode = new IconGroupNode(); + + final Node next = opener.getNext(); + + boolean handled = false; + + // process only if we have exactly one Text node + if (next instanceof Text && next.getNext() == closer) { + + final String text = ((Text) next).getLiteral(); + + if (!TextUtils.isEmpty(text)) { + + // attempt to match + final Matcher matcher = PATTERN.matcher(text); + if (matcher.matches()) { + final IconNode iconNode = new IconNode( + matcher.group(1), + matcher.group(2), + matcher.group(3) + ); + iconGroupNode.appendChild(iconNode); + next.unlink(); + handled = true; + } + } + } + + if (!handled) { + + // restore delimiters if we didn't match + + iconGroupNode.appendChild(new Text(IconNode.DELIMITER_STRING)); + + Node node; + for (Node tmp = opener.getNext(); tmp != null && tmp != closer; tmp = node) { + node = tmp.getNext(); + // append a child anyway + iconGroupNode.appendChild(tmp); + } + + iconGroupNode.appendChild(new Text(IconNode.DELIMITER_STRING)); + } + + opener.insertBefore(iconGroupNode); + } + + private static int iconDefinitionEnd(int index, @NonNull StringBuilder builder) { + + // all spaces, new lines, non-words or digits, + + char c; + + int end = -1; + for (int i = index; i < builder.length(); i++) { + c = builder.charAt(i); + if (Character.isWhitespace(c) + || !(Character.isLetterOrDigit(c) || c == '-' || c == '_')) { + end = i; + break; + } + } + + if (end == -1) { + end = builder.length(); + } + + return end; + } + + private static boolean iconDefinitionValid(@NonNull CharSequence cs) { + final Matcher matcher = PATTERN.matcher(cs); + return matcher.matches(); + } +} diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconSpan.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconSpan.java new file mode 100644 index 00000000..690ba5b2 --- /dev/null +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconSpan.java @@ -0,0 +1,77 @@ +package ru.noties.markwon.sample.extension; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.style.ReplacementSpan; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@SuppressWarnings("WeakerAccess") +public class IconSpan extends ReplacementSpan { + + @IntDef({ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER}) + @Retention(RetentionPolicy.CLASS) + @interface Alignment { + } + + public static final int ALIGN_BOTTOM = 0; + public static final int ALIGN_BASELINE = 1; + public static final int ALIGN_CENTER = 2; // will only center if drawable height is less than text line height + + + private final Drawable drawable; + + private final int alignment; + + public IconSpan(@NonNull Drawable drawable, @Alignment int alignment) { + this.drawable = drawable; + this.alignment = alignment; + if (drawable.getBounds().isEmpty()) { + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + } + } + + @Override + public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { + + final Rect rect = drawable.getBounds(); + + if (fm != null) { + fm.ascent = -rect.bottom; + fm.descent = 0; + + fm.top = fm.ascent; + fm.bottom = 0; + } + + return rect.right; + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + + final int b = bottom - drawable.getBounds().bottom; + + final int save = canvas.save(); + try { + final int translationY; + if (ALIGN_CENTER == alignment) { + translationY = b - ((bottom - top - drawable.getBounds().height()) / 2); + } else if (ALIGN_BASELINE == alignment) { + translationY = b - paint.getFontMetricsInt().descent; + } else { + translationY = b; + } + canvas.translate(x, translationY); + drawable.draw(canvas); + } finally { + canvas.restoreToCount(save); + } + } +} diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconSpanProvider.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconSpanProvider.java new file mode 100644 index 00000000..cd6e3bf3 --- /dev/null +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconSpanProvider.java @@ -0,0 +1,67 @@ +package ru.noties.markwon.sample.extension; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; + +@SuppressWarnings("WeakerAccess") +public abstract class IconSpanProvider { + + @SuppressWarnings("SameParameterValue") + @NonNull + public static IconSpanProvider create(@NonNull Context context, @DrawableRes int fallBack) { + return new Impl(context, fallBack); + } + + + @NonNull + public abstract IconSpan provide(@NonNull String name, @NonNull String color, @NonNull String size); + + + private static class Impl extends IconSpanProvider { + + private static final boolean IS_L = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + + private final Context context; + private final Resources resources; + private final int fallBack; + + Impl(@NonNull Context context, @DrawableRes int fallBack) { + this.context = context; + this.resources = context.getResources(); + this.fallBack = fallBack; + } + + @NonNull + @Override + public IconSpan provide(@NonNull String name, @NonNull String color, @NonNull String size) { + final String resName = iconName(name, color, size); + int resId = resources.getIdentifier(resName, "drawable", context.getPackageName()); + if (resId == 0) { + resId = fallBack; + } + return new IconSpan(getDrawable(resId), IconSpan.ALIGN_CENTER); + } + + + @NonNull + private static String iconName(@NonNull String name, @NonNull String color, @NonNull String size) { + return "ic_" + name + "_" + color + "_" + size + "dp"; + } + + @NonNull + private Drawable getDrawable(int resId) { + final Drawable drawable; + if (IS_L) { + drawable = context.getDrawable(resId); + } else { + drawable = resources.getDrawable(resId); + } + //noinspection ConstantConditions + return drawable; + } + } +} diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconVisitor.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconVisitor.java new file mode 100644 index 00000000..d373ff75 --- /dev/null +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/IconVisitor.java @@ -0,0 +1,63 @@ +package ru.noties.markwon.sample.extension; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.widget.TextView; + +import org.commonmark.node.CustomNode; + +import ru.noties.markwon.SpannableBuilder; +import ru.noties.markwon.SpannableConfiguration; +import ru.noties.markwon.renderer.SpannableMarkdownVisitor; + +@SuppressWarnings("WeakerAccess") +public class IconVisitor extends SpannableMarkdownVisitor { + + private final SpannableBuilder builder; + + private final IconSpanProvider iconSpanProvider; + + public IconVisitor( + @NonNull SpannableConfiguration configuration, + @NonNull SpannableBuilder builder, + @NonNull IconSpanProvider iconSpanProvider + ) { + super(configuration, builder); + this.builder = builder; + this.iconSpanProvider = iconSpanProvider; + } + + @Override + public void visit(CustomNode customNode) { + if (!visitIconNode(customNode)) { + super.visit(customNode); + } + } + + private boolean visitIconNode(@NonNull CustomNode customNode) { + + if (customNode instanceof IconNode) { + + final IconNode node = (IconNode) customNode; + + final String name = node.name(); + final String color = node.color(); + final String size = node.size(); + + if (!TextUtils.isEmpty(name) + && !TextUtils.isEmpty(color) + && !TextUtils.isEmpty(size)) { + + final int length = builder.length(); + + builder.append(name); + builder.setSpan(iconSpanProvider.provide(name, color, size), length); + builder.append(' '); + + return true; + } + } + + return false; + } +} diff --git a/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/MainActivity.java b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/MainActivity.java new file mode 100644 index 00000000..a81f8eb0 --- /dev/null +++ b/sample-custom-extension/src/main/java/ru/noties/markwon/sample/extension/MainActivity.java @@ -0,0 +1,66 @@ +package ru.noties.markwon.sample.extension; + +import android.app.Activity; +import android.os.Bundle; +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.SpannableBuilder; +import ru.noties.markwon.SpannableConfiguration; +import ru.noties.markwon.tasklist.TaskListExtension; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_main); + + final TextView textView = findViewById(R.id.text_view); + + // obtain an instance of parser + final Parser parser = new Parser.Builder() + // we will register all known to Markwon extensions + .extensions(Arrays.asList( + StrikethroughExtension.create(), + TablesExtension.create(), + TaskListExtension.create() + )) + // this is the handler for custom icons + .customDelimiterProcessor(IconProcessor.create()) + .build(); + + // we process input to wrap icon definitions with `@` on both ends + // if your input already does it, there is not need for `IconProcessor.prepare()` call. + final String markdown = IconProcessor.prepare(getString(R.string.input)); + + final Node node = parser.parse(markdown); + + final SpannableBuilder builder = new SpannableBuilder(); + + // please note that here I am passing `0` as fallback it means that if markdown references + // unknown icon, it will try to load fallback one and will fail with ResourceNotFound. It's + // better to provide a valid fallback option + final IconSpanProvider spanProvider = IconSpanProvider.create(this, 0); + + // create an instance of visitor to process parsed markdown + final IconVisitor visitor = new IconVisitor( + SpannableConfiguration.create(this), + builder, + spanProvider + ); + + // trigger visit + node.accept(visitor); + + // apply + textView.setText(builder.text()); + } +} diff --git a/sample-custom-extension/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sample-custom-extension/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..c7bd21db --- /dev/null +++ b/sample-custom-extension/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + <path + android:fillType="evenOdd" + android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z" + android:strokeColor="#00000000" + android:strokeWidth="1"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="78.5885" + android:endY="90.9159" + android:startX="48.7653" + android:startY="61.0927" + android:type="linear"> + <item + android:color="#44000000" + android:offset="0.0" /> + <item + android:color="#00000000" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z" + android:strokeColor="#00000000" + android:strokeWidth="1" /> +</vector> diff --git a/sample-custom-extension/src/main/res/drawable/ic_android_black_24dp.xml b/sample-custom-extension/src/main/res/drawable/ic_android_black_24dp.xml new file mode 100644 index 00000000..401cbf63 --- /dev/null +++ b/sample-custom-extension/src/main/res/drawable/ic_android_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/> +</vector> diff --git a/sample-custom-extension/src/main/res/drawable/ic_home_black_36dp.xml b/sample-custom-extension/src/main/res/drawable/ic_home_black_36dp.xml new file mode 100644 index 00000000..c3b7e150 --- /dev/null +++ b/sample-custom-extension/src/main/res/drawable/ic_home_black_36dp.xml @@ -0,0 +1,4 @@ +<vector android:height="36dp" android:viewportHeight="24.0" + android:viewportWidth="24.0" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#FF000000" android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/> +</vector> diff --git a/sample-custom-extension/src/main/res/drawable/ic_launcher_background.xml b/sample-custom-extension/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..d5fccc53 --- /dev/null +++ b/sample-custom-extension/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + <path + android:fillColor="#26A69A" + android:pathData="M0,0h108v108h-108z" /> + <path + android:fillColor="#00000000" + android:pathData="M9,0L9,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,0L19,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M29,0L29,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M39,0L39,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M49,0L49,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M59,0L59,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M69,0L69,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M79,0L79,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M89,0L89,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M99,0L99,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,9L108,9" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,19L108,19" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,29L108,29" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,39L108,39" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,49L108,49" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,59L108,59" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,69L108,69" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,79L108,79" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,89L108,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,99L108,99" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,29L89,29" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,39L89,39" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,49L89,49" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,59L89,59" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,69L89,69" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,79L89,79" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M29,19L29,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M39,19L39,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M49,19L49,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M59,19L59,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M69,19L69,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M79,19L79,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> +</vector> diff --git a/sample-custom-extension/src/main/res/drawable/ic_memory_black_48dp.xml b/sample-custom-extension/src/main/res/drawable/ic_memory_black_48dp.xml new file mode 100644 index 00000000..88ac2954 --- /dev/null +++ b/sample-custom-extension/src/main/res/drawable/ic_memory_black_48dp.xml @@ -0,0 +1,4 @@ +<vector android:height="48dp" android:viewportHeight="24.0" + android:viewportWidth="24.0" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#FF000000" android:pathData="M15,9L9,9v6h6L15,9zM13,13h-2v-2h2v2zM21,11L21,9h-2L19,7c0,-1.1 -0.9,-2 -2,-2h-2L15,3h-2v2h-2L11,3L9,3v2L7,5c-1.1,0 -2,0.9 -2,2v2L3,9v2h2v2L3,13v2h2v2c0,1.1 0.9,2 2,2h2v2h2v-2h2v2h2v-2h2c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2zM17,17L7,17L7,7h10v10z"/> +</vector> diff --git a/sample-custom-extension/src/main/res/drawable/ic_sentiment_satisfied_red_64dp.xml b/sample-custom-extension/src/main/res/drawable/ic_sentiment_satisfied_red_64dp.xml new file mode 100644 index 00000000..f9c60705 --- /dev/null +++ b/sample-custom-extension/src/main/res/drawable/ic_sentiment_satisfied_red_64dp.xml @@ -0,0 +1,6 @@ +<vector android:height="64dp" android:viewportHeight="24.0" + android:viewportWidth="24.0" android:width="64dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#FFFF0000" android:pathData="M15.5,9.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> + <path android:fillColor="#FFFF0000" android:pathData="M8.5,9.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> + <path android:fillColor="#FFFF0000" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12,16c-1.48,0 -2.75,-0.81 -3.45,-2L6.88,14c0.8,2.05 2.79,3.5 5.12,3.5s4.32,-1.45 5.12,-3.5h-1.67c-0.7,1.19 -1.97,2 -3.45,2z"/> +</vector> diff --git a/sample-custom-extension/src/main/res/layout/activity_main.xml b/sample-custom-extension/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..439dac71 --- /dev/null +++ b/sample-custom-extension/src/main/res/layout/activity_main.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/text_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="8dip" + android:textAppearance="?android:attr/textAppearanceMedium" + tools:text="@string/input"/> + +</ScrollView> \ No newline at end of file diff --git a/sample-custom-extension/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample-custom-extension/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/sample-custom-extension/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon> \ No newline at end of file diff --git a/sample-custom-extension/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/sample-custom-extension/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/sample-custom-extension/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon> \ No newline at end of file diff --git a/sample-custom-extension/src/main/res/mipmap-hdpi/ic_launcher.png b/sample-custom-extension/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..a2f59082 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sample-custom-extension/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..1b523998 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-mdpi/ic_launcher.png b/sample-custom-extension/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..ff10afd6 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sample-custom-extension/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..115a4c76 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample-custom-extension/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..dcd3cd80 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sample-custom-extension/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..459ca609 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample-custom-extension/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..8ca12fe0 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sample-custom-extension/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..8e19b410 Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample-custom-extension/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..b824ebdd Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/sample-custom-extension/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sample-custom-extension/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..4c19a13c Binary files /dev/null and b/sample-custom-extension/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/sample-custom-extension/src/main/res/values/strings.xml b/sample-custom-extension/src/main/res/values/strings.xml new file mode 100644 index 00000000..7dc177e7 --- /dev/null +++ b/sample-custom-extension/src/main/res/values/strings.xml @@ -0,0 +1,13 @@ +<resources> + + <string name="app_name">Markwon-SampleCustomExtension</string> + + <string name="input"><![CDATA[ + # Hello! @ic-android-black-24\n\n + Home 36 black: @ic-home-black-36\n\n + Memory 48 black: @ic-memory-black-48\n\n + Sentiment Satisfied 64 red: @ic-sentiment_satisfied-red-64 + ]]> + </string> + +</resources> diff --git a/sample-custom-extension/src/main/res/values/styles.xml b/sample-custom-extension/src/main/res/values/styles.xml new file mode 100644 index 00000000..49c8cb25 --- /dev/null +++ b/sample-custom-extension/src/main/res/values/styles.xml @@ -0,0 +1,5 @@ +<resources> + + <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar"/> + +</resources> diff --git a/settings.gradle b/settings.gradle index 59f2e834..fbd8c3c4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':library', ':library-image-loader', ':library-view' +include ':app', ':library', ':library-image-loader', ':library-view', ':sample-custom-extension'