From 6f5fd08de4303d80901e6ea92b218241e0cdd58a Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Thu, 11 May 2017 17:15:39 +0300 Subject: [PATCH] Extracting functionality into library --- app/build.gradle | 28 +- app/src/main/AndroidManifest.xml | 22 +- app/src/main/assets/test.md | 14 +- .../java/ru/noties/markwon/MainActivity.java | 39 +- .../main/java/ru/noties/markwon/Markwon.java | 14 +- .../ru/noties/markwon/SpannableRenderer.java | 884 +++++++++--------- .../{spans => spans2}/BlockQuoteSpan.java | 2 +- .../markwon/{spans => spans2}/CodeSpan.java | 127 ++- .../noties/markwon/spans2/DrawableSpan.java | 97 ++ .../{spans => spans2}/DrawableSpanUtils.java | 2 +- .../noties/markwon/spans2/EmphasisSpan.java | 17 + .../{spans => spans2}/ListItemSpan.java | 2 +- .../markwon/spans2/StrongEmphasisSpan.java | 17 + .../ru/noties/markwon/spans2/SubSpan.java | 19 + .../ru/noties/markwon/spans2/SupSpan.java | 19 + .../markwon/spans2/ThematicBreakSpan.java | 25 + app/src/main/res/layout/activity_main.xml | 26 +- app/src/main/res/values-v21/styles.xml | 10 + app/src/main/res/values-w820dp/dimens.xml | 6 - app/src/main/res/values/styles.xml | 9 +- build.gradle | 9 +- gradle/wrapper/gradle-wrapper.properties | 2 +- library-renderer/build.gradle | 26 + library-renderer/src/main/AndroidManifest.xml | 1 + .../renderer/SpannableConfiguration.java | 109 +++ .../markwon/renderer/SpannableHtmlParser.java | 126 +++ .../renderer/SpannableMarkdownVisitor.java | 310 ++++++ .../markwon/renderer/SpannableRenderer.java | 28 + .../spans/ExampleInstrumentedTest.java | 26 - library-spans/src/main/AndroidManifest.xml | 11 +- .../noties/markwon/spans/BlockQuoteSpan.java | 73 ++ .../markwon/spans/BulletListItemSpan.java | 135 +++ .../ru/noties/markwon/spans/CodeSpan.java | 307 ++++++ .../ru/noties/markwon/spans/DrawableSpan.java | 1 + .../markwon/spans/DrawableSpanUtils.java | 117 +++ .../ru/noties/markwon/spans/EmphasisSpan.java | 0 .../ru/noties/markwon/spans/HeadingSpan.java | 96 ++ .../markwon/spans/StrongEmphasisSpan.java | 0 .../java/ru/noties/markwon/spans/SubSpan.java | 0 .../java/ru/noties/markwon/spans/SupSpan.java | 0 .../markwon/spans/ThematicBreakSpan.java | 0 library-spans/src/main/res/values/strings.xml | 3 - .../noties/markwon/spans/ExampleUnitTest.java | 17 - library-view/build.gradle | 19 + library-view/src/main/AndroidManifest.xml | 1 + .../noties/markwon/view/MarkdownTextView.java | 59 ++ settings.gradle | 2 +- 47 files changed, 2242 insertions(+), 615 deletions(-) rename app/src/main/java/ru/noties/markwon/{spans => spans2}/BlockQuoteSpan.java (97%) rename app/src/main/java/ru/noties/markwon/{spans => spans2}/CodeSpan.java (65%) create mode 100644 app/src/main/java/ru/noties/markwon/spans2/DrawableSpan.java rename app/src/main/java/ru/noties/markwon/{spans => spans2}/DrawableSpanUtils.java (99%) create mode 100644 app/src/main/java/ru/noties/markwon/spans2/EmphasisSpan.java rename app/src/main/java/ru/noties/markwon/{spans => spans2}/ListItemSpan.java (97%) create mode 100644 app/src/main/java/ru/noties/markwon/spans2/StrongEmphasisSpan.java create mode 100644 app/src/main/java/ru/noties/markwon/spans2/SubSpan.java create mode 100644 app/src/main/java/ru/noties/markwon/spans2/SupSpan.java create mode 100644 app/src/main/java/ru/noties/markwon/spans2/ThematicBreakSpan.java create mode 100644 app/src/main/res/values-v21/styles.xml delete mode 100644 app/src/main/res/values-w820dp/dimens.xml create mode 100644 library-renderer/build.gradle create mode 100644 library-renderer/src/main/AndroidManifest.xml create mode 100644 library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableConfiguration.java create mode 100644 library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableHtmlParser.java create mode 100644 library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java create mode 100644 library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java delete mode 100644 library-spans/src/androidTest/java/ru/noties/markwon/spans/ExampleInstrumentedTest.java create mode 100644 library-spans/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java create mode 100644 library-spans/src/main/java/ru/noties/markwon/spans/BulletListItemSpan.java create mode 100644 library-spans/src/main/java/ru/noties/markwon/spans/CodeSpan.java rename {app => library-spans}/src/main/java/ru/noties/markwon/spans/DrawableSpan.java (98%) create mode 100644 library-spans/src/main/java/ru/noties/markwon/spans/DrawableSpanUtils.java rename {app => library-spans}/src/main/java/ru/noties/markwon/spans/EmphasisSpan.java (100%) create mode 100644 library-spans/src/main/java/ru/noties/markwon/spans/HeadingSpan.java rename {app => library-spans}/src/main/java/ru/noties/markwon/spans/StrongEmphasisSpan.java (100%) rename {app => library-spans}/src/main/java/ru/noties/markwon/spans/SubSpan.java (100%) rename {app => library-spans}/src/main/java/ru/noties/markwon/spans/SupSpan.java (100%) rename {app => library-spans}/src/main/java/ru/noties/markwon/spans/ThematicBreakSpan.java (100%) delete mode 100644 library-spans/src/main/res/values/strings.xml delete mode 100644 library-spans/src/test/java/ru/noties/markwon/spans/ExampleUnitTest.java create mode 100644 library-view/build.gradle create mode 100644 library-view/src/main/AndroidManifest.xml create mode 100644 library-view/src/main/java/ru/noties/markwon/view/MarkdownTextView.java diff --git a/app/build.gradle b/app/build.gradle index fbb16176..6003452c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,32 +1,20 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + + compileSdkVersion TARGET_SDK + buildToolsVersion BUILD_TOOLS + defaultConfig { applicationId "ru.noties.markwon" - minSdkVersion 15 - targetSdkVersion 25 + minSdkVersion MIN_SDK + targetSdkVersion TARGET_SDK versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } + versionName version } } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.atlassian.commonmark:commonmark:0.9.0' - compile 'com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:0.9.0' + compile project(':library-renderer') compile 'ru.noties:debug:3.0.0@jar' - testCompile 'junit:junit:4.12' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 815ca533..8bb5b207 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,20 +1,22 @@ - + + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + - - - + + + \ No newline at end of file diff --git a/app/src/main/assets/test.md b/app/src/main/assets/test.md index 8adf2d45..55aaab47 100644 --- a/app/src/main/assets/test.md +++ b/app/src/main/assets/test.md @@ -8,7 +8,13 @@ >> Second Quote >>> Third one, yuhuu! -`can a code have **markdown?**` so go it doesn't +`can a code have **markdown?**` so good it doesn't + +

Yo! +Omg + +ddffdg +

## Unordered list @@ -17,6 +23,9 @@ * second * * second first * * second __second__ jks8feif fdsuif yuweru sdfoisdfu wutwe iower wtew ruweir weoir wutywr wer woeirwr wieyriow eryowe rwyeor oweryower o +* * * hm, is it actually a thing? +* * * * and this?! +* * * * * omg * third `and some code` @@ -59,5 +68,4 @@ To compare~~13~~ RED -**PS** additional text to check if this view scrolls gracefully, sofihweo fwfw fuwf weyf pwefiowef twe weuifphw efwepfuwoefh wfypiwe fuwoef wiefg wtefw uf ywfyw fweouf wpfyw fwfe# Hello! - +**PS** additional text to check if this view scrolls gracefully, sofihweo fwfw fuwf weyf pwefiowef twe weuifphw efwepfuwoefh wfypiwe fuwoef wiefg wtefw uf ywfyw fweouf wpfyw fwfe# \ No newline at end of file diff --git a/app/src/main/java/ru/noties/markwon/MainActivity.java b/app/src/main/java/ru/noties/markwon/MainActivity.java index 473b8607..4397f6bf 100644 --- a/app/src/main/java/ru/noties/markwon/MainActivity.java +++ b/app/src/main/java/ru/noties/markwon/MainActivity.java @@ -1,26 +1,26 @@ package ru.noties.markwon; -import android.graphics.drawable.Drawable; +import android.app.Activity; import android.os.Bundle; import android.os.SystemClock; -import android.support.v7.app.AppCompatActivity; import android.text.method.LinkMovementMethod; -import android.text.style.StrikethroughSpan; import android.widget.TextView; import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension; import org.commonmark.node.Node; import org.commonmark.parser.Parser; -import ru.noties.debug.AndroidLogDebugOutput; -import ru.noties.debug.Debug; -import ru.noties.markwon.spans.DrawableSpanUtils; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Scanner; -public class MainActivity extends AppCompatActivity { +import ru.noties.debug.AndroidLogDebugOutput; +import ru.noties.debug.Debug; +import ru.noties.markwon.renderer.*; +import ru.noties.markwon.spans.DrawableSpanUtils; + +public class MainActivity extends Activity { static { Debug.init(new AndroidLogDebugOutput(true)); @@ -62,21 +62,28 @@ public class MainActivity extends AppCompatActivity { .extensions(Arrays.asList(StrikethroughExtension.create())) .build(); final Node node = parser.parse(md); - final CharSequence text = new SpannableRenderer()._render(node/*, new Runnable() { - @Override - public void run() { - textView.setText(textView.getText()); - final Drawable drawable = null; - drawable.setCallback(textView); - } - }*/); + + final CharSequence text = new ru.noties.markwon.renderer.SpannableRenderer().render( + SpannableConfiguration.create(MainActivity.this), + node + ); + +// final CharSequence text = new SpannableRenderer()._render(node/*, new Runnable() { +// @Override +// public void run() { +// textView.setText(textView.getText()); +// final Drawable drawable = null; +// drawable.setCallback(textView); +// } +// }*/); final long end = SystemClock.uptimeMillis(); Debug.i("Rendered: %d ms, length: %d", end - start, text.length()); +// Debug.i(text); textView.post(new Runnable() { @Override public void run() { // NB! LinkMovementMethod forces frequent updates... -// textView.setMovementMethod(LinkMovementMethod.getInstance()); + textView.setMovementMethod(LinkMovementMethod.getInstance()); textView.setText(text); DrawableSpanUtils.scheduleDrawables(textView); } diff --git a/app/src/main/java/ru/noties/markwon/Markwon.java b/app/src/main/java/ru/noties/markwon/Markwon.java index 285a4714..2a76c84c 100644 --- a/app/src/main/java/ru/noties/markwon/Markwon.java +++ b/app/src/main/java/ru/noties/markwon/Markwon.java @@ -1,7 +1,7 @@ -package ru.noties.markwon; - -public class Markwon { - - // todo, annotation processor to PRE_COMPILE markdown!! no... multiple lnguages and you are out, forget about it - // view for debugging (to view in preview) x3! -} +//package ru.noties.markwon; +// +//public class Markwon { +// +// // todo, annotation processor to PRE_COMPILE markdown!! no... multiple lnguages and you are out, forget about it +// // view for debugging (to view in preview) x3! +//} diff --git a/app/src/main/java/ru/noties/markwon/SpannableRenderer.java b/app/src/main/java/ru/noties/markwon/SpannableRenderer.java index 5db373da..e75d7071 100644 --- a/app/src/main/java/ru/noties/markwon/SpannableRenderer.java +++ b/app/src/main/java/ru/noties/markwon/SpannableRenderer.java @@ -1,442 +1,444 @@ -package ru.noties.markwon; - -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.Html; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.AbsoluteSizeSpan; -import android.text.style.StrikethroughSpan; -import android.text.style.URLSpan; - -import org.commonmark.ext.gfm.strikethrough.Strikethrough; -import org.commonmark.node.AbstractVisitor; -import org.commonmark.node.BlockQuote; -import org.commonmark.node.BulletList; -import org.commonmark.node.Code; -import org.commonmark.node.CustomBlock; -import org.commonmark.node.CustomNode; -import org.commonmark.node.Document; -import org.commonmark.node.Emphasis; -import org.commonmark.node.FencedCodeBlock; -import org.commonmark.node.HardLineBreak; -import org.commonmark.node.Heading; -import org.commonmark.node.HtmlBlock; -import org.commonmark.node.HtmlInline; -import org.commonmark.node.Image; -import org.commonmark.node.IndentedCodeBlock; -import org.commonmark.node.Link; -import org.commonmark.node.ListItem; -import org.commonmark.node.Node; -import org.commonmark.node.OrderedList; -import org.commonmark.node.Paragraph; -import org.commonmark.node.SoftLineBreak; -import org.commonmark.node.StrongEmphasis; -import org.commonmark.node.Text; -import org.commonmark.node.ThematicBreak; -import org.commonmark.renderer.Renderer; - -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Deque; - -import ru.noties.debug.Debug; -import ru.noties.markwon.spans.BlockQuoteSpan; -import ru.noties.markwon.spans.CodeSpan; -import ru.noties.markwon.spans.DrawableSpan; -import ru.noties.markwon.spans.EmphasisSpan; -import ru.noties.markwon.spans.ListItemSpan; -import ru.noties.markwon.spans.StrongEmphasisSpan; -import ru.noties.markwon.spans.SubSpan; -import ru.noties.markwon.spans.SupSpan; -import ru.noties.markwon.spans.ThematicBreakSpan; - -public class SpannableRenderer implements Renderer { - - // todo, util to extract all drawables and attach to textView (gif, animations, lazyLoading, etc) - - @Override - public void render(Node node, Appendable output) { - - } - - @Override - public String render(Node node) { - // hm.. doesn't make sense to render to string - throw null; - } - - public CharSequence _render(Node node) { - final SpannableStringBuilder builder = new SpannableStringBuilder(); - node.accept(new SpannableNodeRenderer(builder)); - return builder; - } - - private static class SpannableNodeRenderer extends AbstractVisitor { - -// private static final float[] HEADING_SIZES = { -// 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f, -// }; - - private final SpannableStringBuilder builder; - - private int blockQuoteIndent; - private int listLevel; - - SpannableNodeRenderer(SpannableStringBuilder builder) { - this.builder = builder; - } - - @Override - public void visit(HardLineBreak hardLineBreak) { - Debug.i(hardLineBreak); - } - - @Override - public void visit(Text text) { - Debug.i(text); - builder.append(text.getLiteral()); - } - - @Override - public void visit(StrongEmphasis strongEmphasis) { - final int length = builder.length(); - visitChildren(strongEmphasis); - builder.setSpan(new StrongEmphasisSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @Override - public void visit(Emphasis emphasis) { - final int length = builder.length(); - visitChildren(emphasis); - builder.setSpan(new EmphasisSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @Override - public void visit(IndentedCodeBlock indentedCodeBlock) { - Debug.i(indentedCodeBlock); - } - - @Override - public void visit(BlockQuote blockQuote) { - builder.append('\n'); - final int length = builder.length(); - blockQuoteIndent += 1; - visitChildren(blockQuote); - builder.setSpan(new BlockQuoteSpan(blockQuoteIndent), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - blockQuoteIndent -= 1; - } - - @Override - public void visit(Code code) { - final int length = builder.length(); - builder.append(code.getLiteral()); -// builder.setSpan(new ForegroundColorSpan(0xff00ff00), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - builder.setSpan(new CodeSpan(false, length, builder.length()), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @Override - public void visit(BulletList bulletList) { - Debug.i(bulletList, bulletList.getBulletMarker()); - visitChildren(bulletList); - } - - @Override - public void visit(ListItem listItem) { - Debug.i(listItem); +//package ru.noties.markwon; +// +//import android.graphics.Canvas; +//import android.graphics.ColorFilter; +//import android.graphics.drawable.Drawable; +//import android.os.Handler; +//import android.os.Looper; +//import android.support.annotation.IntRange; +//import android.support.annotation.NonNull; +//import android.support.annotation.Nullable; +//import android.text.Html; +//import android.text.SpannableStringBuilder; +//import android.text.Spanned; +//import android.text.style.AbsoluteSizeSpan; +//import android.text.style.StrikethroughSpan; +//import android.text.style.URLSpan; +// +//import org.commonmark.ext.gfm.strikethrough.Strikethrough; +//import org.commonmark.node.AbstractVisitor; +//import org.commonmark.node.BlockQuote; +//import org.commonmark.node.BulletList; +//import org.commonmark.node.Code; +//import org.commonmark.node.CustomBlock; +//import org.commonmark.node.CustomNode; +//import org.commonmark.node.Document; +//import org.commonmark.node.Emphasis; +//import org.commonmark.node.FencedCodeBlock; +//import org.commonmark.node.HardLineBreak; +//import org.commonmark.node.Heading; +//import org.commonmark.node.HtmlBlock; +//import org.commonmark.node.HtmlInline; +//import org.commonmark.node.Image; +//import org.commonmark.node.IndentedCodeBlock; +//import org.commonmark.node.Link; +//import org.commonmark.node.ListItem; +//import org.commonmark.node.Node; +//import org.commonmark.node.OrderedList; +//import org.commonmark.node.Paragraph; +//import org.commonmark.node.SoftLineBreak; +//import org.commonmark.node.StrongEmphasis; +//import org.commonmark.node.Text; +//import org.commonmark.node.ThematicBreak; +//import org.commonmark.renderer.Renderer; +// +//import java.util.ArrayDeque; +//import java.util.Arrays; +//import java.util.Deque; +// +//import ru.noties.debug.Debug; +//import ru.noties.markwon.spans.BlockQuoteSpan; +//import ru.noties.markwon.spans.CodeSpan; +//import ru.noties.markwon.spans.DrawableSpan; +//import ru.noties.markwon.spans.EmphasisSpan; +//import ru.noties.markwon.spans.BulletListItemSpan; +//import ru.noties.markwon.spans.StrongEmphasisSpan; +//import ru.noties.markwon.spans.SubSpan; +//import ru.noties.markwon.spans.SupSpan; +//import ru.noties.markwon.spans.ThematicBreakSpan; +// +//public class SpannableRenderer implements Renderer { +// +// // todo, util to extract all drawables and attach to textView (gif, animations, lazyLoading, etc) +// +// @Override +// public void render(Node node, Appendable output) { +// +// } +// +// @Override +// public String render(Node node) { +// // hm.. doesn't make sense to render to string +// throw null; +// } +// +// public CharSequence _render(Node node) { +// final SpannableStringBuilder builder = new SpannableStringBuilder(); +// node.accept(new SpannableNodeRenderer(builder)); +// return builder; +// } +// +// private static class SpannableNodeRenderer extends AbstractVisitor { +// +//// private static final float[] HEADING_SIZES = { +//// 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f, +//// }; +// +// private final SpannableStringBuilder builder; +// +// private int blockQuoteIndent; +// private int listLevel; +// +// SpannableNodeRenderer(SpannableStringBuilder builder) { +// this.builder = builder; +// } +// +// @Override +// public void visit(HardLineBreak hardLineBreak) { +// // todo +// Debug.i(hardLineBreak); +// } +// +// @Override +// public void visit(Text text) { +// builder.append(text.getLiteral()); +// } +// +// @Override +// public void visit(StrongEmphasis strongEmphasis) { +// final int length = builder.length(); +// visitChildren(strongEmphasis); +// builder.setSpan(new StrongEmphasisSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } +// +// @Override +// public void visit(Emphasis emphasis) { +// final int length = builder.length(); +// visitChildren(emphasis); +// builder.setSpan(new EmphasisSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } +// +// @Override +// public void visit(IndentedCodeBlock indentedCodeBlock) { +// // todo +// Debug.i(indentedCodeBlock); +// } +// +// @Override +// public void visit(BlockQuote blockQuote) { // builder.append('\n'); - if (builder.charAt(builder.length() - 1) != '\n') { - builder.append('\n'); - } - final int length = builder.length(); - blockQuoteIndent += 1; - listLevel += 1; - visitChildren(listItem); -// builder.setSpan(new BulletSpan(4, 0xff0000ff), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - builder.setSpan(new ListItemSpan(blockQuoteIndent, listLevel > 1, length), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - blockQuoteIndent -= 1; - listLevel -= 1; - } - - @Override - public void visit(ThematicBreak thematicBreak) { - final int length = builder.length(); - builder.append('\n') - .append(' '); // without space it won't render - builder.setSpan(new ThematicBreakSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - builder.append('\n'); - } - - @Override - public void visit(OrderedList orderedList) { - Debug.i(orderedList, orderedList.getDelimiter(), orderedList.getStartNumber()); - // todo, ordering numbers - super.visit(orderedList); - } - - @Override - public void visit(SoftLineBreak softLineBreak) { - Debug.i(softLineBreak); - } - - @Override - public void visit(Heading heading) { - Debug.i(heading); - if (builder.length() != 0 && builder.charAt(builder.length() - 1) != '\n') { - builder.append('\n'); - } - final int length = builder.length(); - visitChildren(heading); - final int max = 120; - final int one = 20; // total is 6 - final int size = max - ((heading.getLevel() - 1) * one); - builder.setSpan(new AbsoluteSizeSpan(size), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - builder.append('\n'); - } - - @Override - public void visit(FencedCodeBlock fencedCodeBlock) { - builder.append('\n'); - final int length = builder.length(); - builder.append(fencedCodeBlock.getLiteral()); - builder.setSpan(new CodeSpan(true, length, builder.length() - 1), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @Override - public void visit(Paragraph paragraph) { - Debug.i(paragraph); - if (listLevel == 0 - && blockQuoteIndent == 0) { - builder.append('\n') - .append('\n'); - } - visitChildren(paragraph); - - if (listLevel == 0 - && blockQuoteIndent == 0) { - builder.append('\n') - .append('\n'); - } - } - -// private int htmlStart = -1; - private final Deque htmlStack = new ArrayDeque<>(); - - private static class HtmlInlineItem { - - final int start; - final String tag; - - private HtmlInlineItem(int start, String tag) { - this.start = start; - this.tag = tag; - } - } - - @Override - public void visit(HtmlInline htmlInline) { - -// Debug.i(htmlInline, htmlStart); -// Debug.i(htmlInline.getLiteral(), htmlInline.toString()); - - // okay, it's seems that we desperately need to understand if it's opening tag or closing - - final HtmlTag tag = parseTag(htmlInline.getLiteral()); - - Debug.i(htmlInline.getLiteral(), tag); - - if (tag != null) { - Debug.i("tag: %s, closing: %s", tag.tag, tag.closing); - if (!tag.closing) { - htmlStack.push(new HtmlInlineItem(builder.length(), tag.tag)); - visitChildren(htmlInline); - } else { - final HtmlInlineItem item = htmlStack.pop(); - final int start = item.start; - final int end = builder.length(); - // here, additionally, we can render some tags ourselves (sup/sub) - if ("sup".equals(item.tag)) { - builder.setSpan(new SupSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } else if("sub".equals(item.tag)) { - builder.setSpan(new SubSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } else if("del".equals(item.tag)) { - // weird, but `Html` class does not return a spannable for `o` - // seems like a bug - builder.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } else { - final String html = "<" + item.tag + ">" + (builder.subSequence(start, end).toString()) + ""; - final Spanned spanned = Html.fromHtml(html); - final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class); - - Debug.i("html: %s, start: %d, end: %d, spans: %s", html, start, end, Arrays.toString(spans)); - - if (spans != null - && spans.length > 0) { - for (Object span: spans) { - Debug.i(span); - builder.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } - } - } else { - super.visit(htmlInline); - } - } - - private static class HtmlTag { - final String tag; - final boolean closing; - HtmlTag(String tag, boolean closing) { - this.tag = tag; - this.closing = closing; - } - @Override - public String toString() { - return "HtmlTag{" + - "tag='" + tag + '\'' + - ", closing=" + closing + - '}'; - } - } - - private static HtmlTag parseTag(String in) { - - final HtmlTag out; - - final int length = in != null - ? in.length() - : 0; - - Debug.i(in, length); - - if (length == 0 || length < 3) { - out = null; - } else { - - final boolean closing = '<' == in.charAt(0) && '/' == in.charAt(1); - final String tag = closing - ? in.substring(2, in.length() - 1) - : in.substring(1, in.length() - 1); - out = new HtmlTag(tag, closing); - } - - return out; - } - - @Override - public void visit(HtmlBlock htmlBlock) { - // interestring thing... what is it also? - Debug.i(htmlBlock); - } - - @Override - public void visit(CustomBlock customBlock) { - // not supported, what is it anyway? - Debug.i(customBlock); - } - - @Override - public void visit(Document document) { - // the whole document, no need to do anything - Debug.i(document); - super.visit(document); - } - - @Override - public void visit(Link link) { - Debug.i(link); - final int length = builder.length(); - visitChildren(link); - builder.setSpan(new URLSpan(link.getDestination()), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @Override - public void visit(Image image) { - // not supported... maybe for now? - Debug.i(image); - super.visit(image); - - final int length = builder.length(); - final TestDrawable drawable = new TestDrawable(); - final DrawableSpan span = new DrawableSpan(drawable); - builder.append(" "); - builder.setSpan(span, length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @Override - public void visit(CustomNode customNode) { - - Debug.i(customNode); - - if (customNode instanceof Strikethrough) { - final int length = builder.length(); - visitChildren(customNode); - builder.setSpan(new StrikethroughSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } else { - super.visit(customNode); - } - } - } - - - private static class TestDrawable extends Drawable { - - private final Handler handler = new Handler(Looper.getMainLooper()); - private boolean called; - - TestDrawable() { - setBounds(0, 0, 50, 50); - } - - @Override - public void draw(@NonNull final Canvas canvas) { - canvas.clipRect(getBounds()); - if (!called) { - canvas.drawColor(0xFF00ff00); - handler.removeCallbacksAndMessages(null); - handler.postDelayed(new Runnable() { - @Override - public void run() { - called = true; - setBounds(0, 0, 400, 400); - invalidateSelf(); - } - }, 2000L); - } else { - canvas.drawColor(0xFFff0000); - } - } - - @Override - public void setAlpha(@IntRange(from = 0, to = 255) int alpha) { - - } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @Override - public int getOpacity() { - return 0; - } - - @Override - public int getIntrinsicWidth() { - return called ? 400 : 50; - } - - @Override - public int getIntrinsicHeight() { - return called ? 400 : 50; - } - } -} +// final int length = builder.length(); +// blockQuoteIndent += 1; +// visitChildren(blockQuote); +// builder.setSpan(new BlockQuoteSpan(blockQuoteIndent), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// blockQuoteIndent -= 1; +// } +// +// @Override +// public void visit(Code code) { +// final int length = builder.length(); +// builder.append(code.getLiteral()); +//// builder.setSpan(new ForegroundColorSpan(0xff00ff00), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// builder.setSpan(new CodeSpan(false, length, builder.length()), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } +// +// @Override +// public void visit(BulletList bulletList) { +// Debug.i(bulletList, bulletList.getBulletMarker()); +// visitChildren(bulletList); +// } +// +// @Override +// public void visit(ListItem listItem) { +// Debug.i(listItem); +//// builder.append('\n'); +// if (builder.charAt(builder.length() - 1) != '\n') { +// builder.append('\n'); +// } +// final int length = builder.length(); +// blockQuoteIndent += 1; +// listLevel += 1; +// visitChildren(listItem); +//// builder.setSpan(new BulletSpan(4, 0xff0000ff), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// builder.setSpan(new BulletListItemSpan(blockQuoteIndent, listLevel > 1, length), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// blockQuoteIndent -= 1; +// listLevel -= 1; +// } +// +// @Override +// public void visit(ThematicBreak thematicBreak) { +// final int length = builder.length(); +// builder.append('\n') +// .append(' '); // without space it won't render +// builder.setSpan(new ThematicBreakSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// builder.append('\n'); +// } +// +// @Override +// public void visit(OrderedList orderedList) { +// Debug.i(orderedList, orderedList.getDelimiter(), orderedList.getStartNumber()); +// // todo, ordering numbers +// super.visit(orderedList); +// } +// +// @Override +// public void visit(SoftLineBreak softLineBreak) { +// Debug.i(softLineBreak); +// } +// +// @Override +// public void visit(Heading heading) { +// Debug.i(heading); +// if (builder.length() != 0 && builder.charAt(builder.length() - 1) != '\n') { +// builder.append('\n'); +// } +// final int length = builder.length(); +// visitChildren(heading); +// final int max = 120; +// final int one = 20; // total is 6 +// final int size = max - ((heading.getLevel() - 1) * one); +// builder.setSpan(new AbsoluteSizeSpan(size), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// builder.append('\n'); +// } +// +// @Override +// public void visit(FencedCodeBlock fencedCodeBlock) { +// builder.append('\n'); +// final int length = builder.length(); +// builder.append(fencedCodeBlock.getLiteral()); +// builder.setSpan(new CodeSpan(true, length, builder.length() - 1), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } +// +// @Override +// public void visit(Paragraph paragraph) { +// Debug.i(paragraph); +// if (listLevel == 0 +// && blockQuoteIndent == 0) { +// builder.append('\n') +// .append('\n'); +// } +// visitChildren(paragraph); +// +// if (listLevel == 0 +// && blockQuoteIndent == 0) { +// builder.append('\n') +// .append('\n'); +// } +// } +// +//// private int htmlStart = -1; +// private final Deque htmlStack = new ArrayDeque<>(); +// +// private static class HtmlInlineItem { +// +// final int start; +// final String tag; +// +// private HtmlInlineItem(int start, String tag) { +// this.start = start; +// this.tag = tag; +// } +// } +// +// @Override +// public void visit(HtmlInline htmlInline) { +// +//// Debug.i(htmlInline, htmlStart); +//// Debug.i(htmlInline.getLiteral(), htmlInline.toString()); +// +// // okay, it's seems that we desperately need to understand if it's opening tag or closing +// +// final HtmlTag tag = parseTag(htmlInline.getLiteral()); +// +// Debug.i(htmlInline.getLiteral(), tag); +// +// if (tag != null) { +// Debug.i("tag: %s, closing: %s", tag.tag, tag.closing); +// if (!tag.closing) { +// htmlStack.push(new HtmlInlineItem(builder.length(), tag.tag)); +// visitChildren(htmlInline); +// } else { +// final HtmlInlineItem item = htmlStack.pop(); +// final int start = item.start; +// final int end = builder.length(); +// // here, additionally, we can render some tags ourselves (sup/sub) +// if ("sup".equals(item.tag)) { +// builder.setSpan(new SupSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } else if("sub".equals(item.tag)) { +// builder.setSpan(new SubSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } else if("del".equals(item.tag)) { +// // weird, but `Html` class does not return a spannable for `o` +// // seems like a bug +// builder.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } else { +// final String html = "<" + item.tag + ">" + (builder.subSequence(start, end).toString()) + ""; +// final Spanned spanned = Html.fromHtml(html); +// final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class); +// +// Debug.i("html: %s, start: %d, end: %d, spans: %s", html, start, end, Arrays.toString(spans)); +// +// if (spans != null +// && spans.length > 0) { +// for (Object span: spans) { +// Debug.i(span); +// builder.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } +// } +// } +// } +// } else { +// super.visit(htmlInline); +// } +// } +// +// private static class HtmlTag { +// final String tag; +// final boolean closing; +// HtmlTag(String tag, boolean closing) { +// this.tag = tag; +// this.closing = closing; +// } +// @Override +// public String toString() { +// return "HtmlTag{" + +// "tag='" + tag + '\'' + +// ", closing=" + closing + +// '}'; +// } +// } +// +// private static HtmlTag parseTag(String in) { +// +// final HtmlTag out; +// +// final int length = in != null +// ? in.length() +// : 0; +// +// Debug.i(in, length); +// +// if (length == 0 || length < 3) { +// out = null; +// } else { +// +// final boolean closing = '<' == in.charAt(0) && '/' == in.charAt(1); +// final String tag = closing +// ? in.substring(2, in.length() - 1) +// : in.substring(1, in.length() - 1); +// out = new HtmlTag(tag, closing); +// } +// +// return out; +// } +// +// @Override +// public void visit(HtmlBlock htmlBlock) { +// // interestring thing... what is it also? +// Debug.i(htmlBlock); +// } +// +// @Override +// public void visit(CustomBlock customBlock) { +// // not supported, what is it anyway? +// Debug.i(customBlock); +// } +// +// @Override +// public void visit(Document document) { +// // the whole document, no need to do anything +// Debug.i(document); +// super.visit(document); +// } +// +// @Override +// public void visit(Link link) { +// Debug.i(link); +// final int length = builder.length(); +// visitChildren(link); +// builder.setSpan(new URLSpan(link.getDestination()), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } +// +// @Override +// public void visit(Image image) { +// // not supported... maybe for now? +// Debug.i(image); +// final int length = builder.length(); +// super.visit(image); +// +//// final int length = builder.length(); +// final TestDrawable drawable = new TestDrawable(); +// final DrawableSpan span = new DrawableSpan(drawable); +// builder.append(" "); +// builder.setSpan(span, length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } +// +// @Override +// public void visit(CustomNode customNode) { +// +// Debug.i(customNode); +// +// if (customNode instanceof Strikethrough) { +// final int length = builder.length(); +// visitChildren(customNode); +// builder.setSpan(new StrikethroughSpan(), length, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// } else { +// super.visit(customNode); +// } +// } +// } +// +// +// private static class TestDrawable extends Drawable { +// +// private final Handler handler = new Handler(Looper.getMainLooper()); +// private boolean called; +// +// TestDrawable() { +// setBounds(0, 0, 50, 50); +// } +// +// @Override +// public void draw(@NonNull final Canvas canvas) { +// canvas.clipRect(getBounds()); +// if (!called) { +// canvas.drawColor(0xFF00ff00); +// handler.removeCallbacksAndMessages(null); +// handler.postDelayed(new Runnable() { +// @Override +// public void run() { +// called = true; +// setBounds(0, 0, 400, 400); +// invalidateSelf(); +// } +// }, 2000L); +// } else { +// canvas.drawColor(0xFFff0000); +// } +// } +// +// @Override +// public void setAlpha(@IntRange(from = 0, to = 255) int alpha) { +// +// } +// +// @Override +// public void setColorFilter(@Nullable ColorFilter colorFilter) { +// +// } +// +// @Override +// public int getOpacity() { +// return 0; +// } +// +// @Override +// public int getIntrinsicWidth() { +// return called ? 400 : 50; +// } +// +// @Override +// public int getIntrinsicHeight() { +// return called ? 400 : 50; +// } +// } +//} diff --git a/app/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java b/app/src/main/java/ru/noties/markwon/spans2/BlockQuoteSpan.java similarity index 97% rename from app/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java rename to app/src/main/java/ru/noties/markwon/spans2/BlockQuoteSpan.java index bfe76eec..68937347 100644 --- a/app/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java +++ b/app/src/main/java/ru/noties/markwon/spans2/BlockQuoteSpan.java @@ -1,4 +1,4 @@ -package ru.noties.markwon.spans; +package ru.noties.markwon.spans2; import android.graphics.Canvas; import android.graphics.Paint; diff --git a/app/src/main/java/ru/noties/markwon/spans/CodeSpan.java b/app/src/main/java/ru/noties/markwon/spans2/CodeSpan.java similarity index 65% rename from app/src/main/java/ru/noties/markwon/spans/CodeSpan.java rename to app/src/main/java/ru/noties/markwon/spans2/CodeSpan.java index 2c22af23..485592b3 100644 --- a/app/src/main/java/ru/noties/markwon/spans/CodeSpan.java +++ b/app/src/main/java/ru/noties/markwon/spans2/CodeSpan.java @@ -1,4 +1,4 @@ -package ru.noties.markwon.spans; +package ru.noties.markwon.spans2; import android.graphics.Canvas; import android.graphics.Paint; @@ -6,12 +6,6 @@ import android.graphics.Rect; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.text.Layout; -import android.text.StaticLayout; -import android.text.TextPaint; -import android.text.style.LeadingMarginSpan; -import android.text.style.LineHeightSpan; -import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; import ru.noties.debug.Debug; @@ -24,10 +18,16 @@ public class CodeSpan extends ReplacementSpan/* implements LeadingMarginSpan*/ { private final int start; private final int end; + private final Rect rect = new Rect(); + private final Rect borderRect = new Rect(); + private final Paint paint = new Paint(); + public CodeSpan(boolean multiline, int start, int end) { this.multiline = multiline; this.start = start; this.end = end; + + paint.setStyle(Paint.Style.FILL); } @Override @@ -74,33 +74,104 @@ public class CodeSpan extends ReplacementSpan/* implements LeadingMarginSpan*/ { @NonNull Paint paint ) { - Debug.i("text: %s, x: %s, top: %s, y: %s, bottom: %s", text.subSequence(start, end), x, top, y, bottom); - - final CharSequence cs = text.subSequence(start, end); - - final int width = 32 + (int) (paint.measureText(cs, 0, cs.length()) + .5F); - final int left = (int) (x + .5F); - final int right = multiline - ? canvas.getWidth() - : left + width; - final Rect rect = new Rect( - left, - top, - right, - bottom - ); + final int right; + if (multiline) { + right = canvas.getWidth(); + } else { + final int width = (16 * 2) + (int) (paint.measureText(text, start, end) + .5F); + right = left + width; + } - final Paint p = new Paint(); - p.setStyle(Paint.Style.FILL); - p.setColor(0x80ff0000); - canvas.drawRect(rect, p); + rect.set(left, top, right, bottom); + + // okay, draw background first + drawBackground(canvas); + + // then, if any, draw borders + drawBorders(canvas, this.start == start, this.end == end); + + // draw text // y center position final int b = bottom - ((bottom - top) / 2) - (int) ((paint.descent() + paint.ascent()) / 2); - p.setColor(0xFF000000); - canvas.drawText(cs, 0, cs.length(), x + 16, b, paint); +// if (config.textColor != 0) { +// // we will use Paint object that is used to draw all the text (textSize, textColor, typeface, etc) +// paint.setColor(config.textColor); +// } + canvas.drawText(text, start, end, x + 16, b, paint); + + +// Debug.i("text: %s, x: %s, top: %s, y: %s, bottom: %s", text.subSequence(start, end), x, top, y, bottom); +// +// final CharSequence cs = text.subSequence(start, end); +// +// final int width = 32 + (int) (paint.measureText(cs, 0, cs.length()) + .5F); +// +// final int left = (int) (x + .5F); +// final int right = multiline +// ? canvas.getWidth() +// : left + width; +// +// final Rect rect = new Rect( +// left, +// top, +// right, +// bottom +// ); +// +// final Paint p = new Paint(); +// p.setStyle(Paint.Style.FILL); +// p.setColor(0x80ff0000); +// canvas.drawRect(rect, p); +// +// // y center position +// final int b = bottom - ((bottom - top) / 2) - (int) ((paint.descent() + paint.ascent()) / 2); +// p.setColor(0xFF000000); +// canvas.drawText(cs, 0, cs.length(), x + 16, b, paint); + } + + private void drawBackground(Canvas canvas) { +// final int color = config.backgroundColor; +// if (color != 0) { + paint.setColor(0x40ff0000); + canvas.drawRect(rect, paint); +// } + } + + private void drawBorders(Canvas canvas, boolean top, boolean bottom) { + + final int color = 0xFFff0000; + final int width = 4; +// if (color == 0 +// || width == 0) { +// return; +// } + + paint.setColor(color); + + // left and right are always drawn + + // LEFT + borderRect.set(rect.left, rect.top, rect.left + width, rect.bottom); + canvas.drawRect(borderRect, paint); + + // RIGHT + borderRect.set(rect.right - width, rect.top, rect.right, rect.bottom); + canvas.drawRect(borderRect, paint); + + // TOP + if (top) { + borderRect.set(rect.left, rect.top, rect.right, rect.top + width); + canvas.drawRect(borderRect, paint); + } + + // BOTTOM + if (bottom) { + borderRect.set(rect.left, rect.bottom - width, rect.right, rect.bottom); + canvas.drawRect(borderRect, paint); + } } diff --git a/app/src/main/java/ru/noties/markwon/spans2/DrawableSpan.java b/app/src/main/java/ru/noties/markwon/spans2/DrawableSpan.java new file mode 100644 index 00000000..5da2fbce --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/spans2/DrawableSpan.java @@ -0,0 +1,97 @@ +package ru.noties.markwon.spans2; + +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.IntRange; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.style.ReplacementSpan; + +public class DrawableSpan extends ReplacementSpan { + + @IntDef({ ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER }) + @interface Alignment {} + + public static final int ALIGN_BOTTOM = 0; + public static final int ALIGN_BASELINE = 1; + public static final int ALIGN_CENTER = 2; + + private final Drawable drawable; + private final int alignment; + + public DrawableSpan(@NonNull Drawable drawable) { + this(drawable, ALIGN_BOTTOM); + } + + public DrawableSpan(@NonNull Drawable drawable, @Alignment int alignment) { + this.drawable = drawable; + this.alignment = alignment; + + // additionally set intrinsic bounds if empty + final Rect rect = drawable.getBounds(); + if (rect.isEmpty()) { + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + } + } + + @Override + public int getSize( + @NonNull Paint paint, + CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) 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, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + float x, + int top, + int y, + int bottom, + @NonNull Paint paint) { + + final Drawable drawable = this.drawable; + + final int b = bottom - drawable.getBounds().bottom; + + final int save = canvas.save(); + try { + final int translationY; + if (ALIGN_CENTER == alignment) { + translationY = (int) (b / 2.F + .5F); + } else if (ALIGN_BASELINE == alignment) { + translationY = b - paint.getFontMetricsInt().descent; + } else { + translationY = b; + } + canvas.translate(x, translationY); + drawable.draw(canvas); + } finally { + canvas.restoreToCount(save); + } + } + + public Drawable getDrawable() { + return drawable; + } +} diff --git a/app/src/main/java/ru/noties/markwon/spans/DrawableSpanUtils.java b/app/src/main/java/ru/noties/markwon/spans2/DrawableSpanUtils.java similarity index 99% rename from app/src/main/java/ru/noties/markwon/spans/DrawableSpanUtils.java rename to app/src/main/java/ru/noties/markwon/spans2/DrawableSpanUtils.java index 7528c8ec..8b3fe20b 100644 --- a/app/src/main/java/ru/noties/markwon/spans/DrawableSpanUtils.java +++ b/app/src/main/java/ru/noties/markwon/spans2/DrawableSpanUtils.java @@ -1,4 +1,4 @@ -package ru.noties.markwon.spans; +package ru.noties.markwon.spans2; import android.graphics.Rect; import android.graphics.drawable.Drawable; diff --git a/app/src/main/java/ru/noties/markwon/spans2/EmphasisSpan.java b/app/src/main/java/ru/noties/markwon/spans2/EmphasisSpan.java new file mode 100644 index 00000000..bcd8804b --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/spans2/EmphasisSpan.java @@ -0,0 +1,17 @@ +package ru.noties.markwon.spans2; + +import android.text.TextPaint; +import android.text.style.MetricAffectingSpan; + +public class EmphasisSpan extends MetricAffectingSpan { + + @Override + public void updateMeasureState(TextPaint p) { + p.setTextSkewX(-0.25f); + } + + @Override + public void updateDrawState(TextPaint tp) { + tp.setTextSkewX(-0.25f); + } +} diff --git a/app/src/main/java/ru/noties/markwon/spans/ListItemSpan.java b/app/src/main/java/ru/noties/markwon/spans2/ListItemSpan.java similarity index 97% rename from app/src/main/java/ru/noties/markwon/spans/ListItemSpan.java rename to app/src/main/java/ru/noties/markwon/spans2/ListItemSpan.java index 065a1b2f..5b81952d 100644 --- a/app/src/main/java/ru/noties/markwon/spans/ListItemSpan.java +++ b/app/src/main/java/ru/noties/markwon/spans2/ListItemSpan.java @@ -1,4 +1,4 @@ -package ru.noties.markwon.spans; +package ru.noties.markwon.spans2; import android.graphics.Canvas; import android.graphics.Paint; diff --git a/app/src/main/java/ru/noties/markwon/spans2/StrongEmphasisSpan.java b/app/src/main/java/ru/noties/markwon/spans2/StrongEmphasisSpan.java new file mode 100644 index 00000000..0ec3e8b7 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/spans2/StrongEmphasisSpan.java @@ -0,0 +1,17 @@ +package ru.noties.markwon.spans2; + +import android.text.TextPaint; +import android.text.style.MetricAffectingSpan; + +public class StrongEmphasisSpan extends MetricAffectingSpan { + + @Override + public void updateMeasureState(TextPaint p) { + p.setFakeBoldText(true); + } + + @Override + public void updateDrawState(TextPaint tp) { + tp.setFakeBoldText(true); + } +} diff --git a/app/src/main/java/ru/noties/markwon/spans2/SubSpan.java b/app/src/main/java/ru/noties/markwon/spans2/SubSpan.java new file mode 100644 index 00000000..991219c6 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/spans2/SubSpan.java @@ -0,0 +1,19 @@ +package ru.noties.markwon.spans2; + +import android.text.TextPaint; +import android.text.style.MetricAffectingSpan; + +public class SubSpan extends MetricAffectingSpan { + + @Override + public void updateDrawState(TextPaint tp) { + tp.setTextSize(tp.getTextSize() * .75F); + tp.baselineShift -= (int) (tp.ascent() / 2); + } + + @Override + public void updateMeasureState(TextPaint tp) { + tp.setTextSize(tp.getTextSize() * .75F); + tp.baselineShift -= (int) (tp.ascent() / 2); + } +} diff --git a/app/src/main/java/ru/noties/markwon/spans2/SupSpan.java b/app/src/main/java/ru/noties/markwon/spans2/SupSpan.java new file mode 100644 index 00000000..044efc70 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/spans2/SupSpan.java @@ -0,0 +1,19 @@ +package ru.noties.markwon.spans2; + +import android.text.TextPaint; +import android.text.style.MetricAffectingSpan; + +public class SupSpan extends MetricAffectingSpan { + + @Override + public void updateDrawState(TextPaint tp) { + tp.setTextSize(tp.getTextSize() * .75F); + tp.baselineShift += (int) (tp.ascent() / 2); + } + + @Override + public void updateMeasureState(TextPaint tp) { + tp.setTextSize(tp.getTextSize() * .75F); + tp.baselineShift += (int) (tp.ascent() / 2); + } +} diff --git a/app/src/main/java/ru/noties/markwon/spans2/ThematicBreakSpan.java b/app/src/main/java/ru/noties/markwon/spans2/ThematicBreakSpan.java new file mode 100644 index 00000000..731c9628 --- /dev/null +++ b/app/src/main/java/ru/noties/markwon/spans2/ThematicBreakSpan.java @@ -0,0 +1,25 @@ +package ru.noties.markwon.spans2; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.text.Layout; +import android.text.style.LeadingMarginSpan; + +public class ThematicBreakSpan implements LeadingMarginSpan { + + @Override + public int getLeadingMargin(boolean first) { + return 1; + } + + @Override + public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) { + final int middle = (bottom - top) / 2; + final Rect rect = new Rect(0, top + middle - 2, c.getWidth(), top + middle + 2); + final Paint paint = new Paint(); + paint.setStyle(Paint.Style.FILL); + paint.setColor(0x80000000); + c.drawRect(rect, paint); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5d4636b2..b80f26ff 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,17 +1,19 @@ + 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"> - + diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml new file mode 100644 index 00000000..4e1e75f3 --- /dev/null +++ b/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml deleted file mode 100644 index 63fc8164..00000000 --- a/app/src/main/res/values-w820dp/dimens.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - 64dp - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5885930d..5b267e56 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,11 +1,8 @@ + +