diff --git a/app/build.gradle b/app/build.gradle index 4d5a2ae1..3c747a17 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,4 +18,5 @@ dependencies { compile project(':library-renderer') compile 'ru.noties:debug:3.0.0@jar' compile 'com.squareup.picasso:picasso:2.5.2' + compile 'com.squareup.okhttp3:okhttp:3.8.0' } diff --git a/app/src/main/java/ru/noties/markwon/MainActivity.java b/app/src/main/java/ru/noties/markwon/MainActivity.java index eb178f80..8c22023a 100644 --- a/app/src/main/java/ru/noties/markwon/MainActivity.java +++ b/app/src/main/java/ru/noties/markwon/MainActivity.java @@ -30,7 +30,7 @@ import ru.noties.debug.Debug; import ru.noties.markwon.renderer.*; import ru.noties.markwon.spans.AsyncDrawable; import ru.noties.markwon.spans.CodeSpan; -import ru.noties.markwon.spans.DrawableSpanUtils; +import ru.noties.markwon.spans.AsyncDrawableSpanUtils; public class MainActivity extends Activity { @@ -101,50 +101,52 @@ public class MainActivity extends Activity { .build(); final Node node = parser.parse(md); - final SpannableConfiguration configuration = SpannableConfiguration.builder(MainActivity.this) - .setAsyncDrawableLoader(new AsyncDrawable.Loader() { - @Override - public void load(@NonNull String destination, @NonNull final AsyncDrawable drawable) { - Debug.i(destination); - final Target target = new Target() { - @Override - public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { - Debug.i(); - final Drawable d = new BitmapDrawable(getResources(), bitmap); - d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); - drawable.setResult(d); -// textView.setText(textView.getText()); - } +// final SpannableConfiguration configuration = SpannableConfiguration.builder(MainActivity.this) +// .setAsyncDrawableLoader(new AsyncDrawable.Loader() { +// @Override +// public void load(@NonNull String destination, @NonNull final AsyncDrawable drawable) { +// Debug.i(destination); +// final Target target = new Target() { +// @Override +// public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { +// Debug.i(); +// final Drawable d = new BitmapDrawable(getResources(), bitmap); +// d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); +// drawable.setResult(d); +//// textView.setText(textView.getText()); +// } +// +// @Override +// public void onBitmapFailed(Drawable errorDrawable) { +// Debug.i(); +// } +// +// @Override +// public void onPrepareLoad(Drawable placeHolderDrawable) { +// Debug.i(); +// } +// }; +// targets.add(target); +// +// picasso.load(destination) +// .tag(destination) +// .into(target); +// +// } +// +// @Override +// public void cancel(@NonNull String destination) { +// Debug.i(destination); +// picasso +// .cancelTag(destination); +// } +// }) +// .setCodeConfig(CodeSpan.Config.builder().setTextSize( +// (int) (getResources().getDisplayMetrics().density * 14 + .5F) +// ).setMultilineMargin((int) (getResources().getDisplayMetrics().density * 8 + .5F)).build()) +// .build(); - @Override - public void onBitmapFailed(Drawable errorDrawable) { - Debug.i(); - } - - @Override - public void onPrepareLoad(Drawable placeHolderDrawable) { - Debug.i(); - } - }; - targets.add(target); - - picasso.load(destination) - .tag(destination) - .into(target); - - } - - @Override - public void cancel(@NonNull String destination) { - Debug.i(destination); - picasso - .cancelTag(destination); - } - }) - .setCodeConfig(CodeSpan.Config.builder().setTextSize( - (int) (getResources().getDisplayMetrics().density * 14 + .5F) - ).setMultilineMargin((int) (getResources().getDisplayMetrics().density * 8 + .5F)).build()) - .build(); + final SpannableConfiguration configuration = SpannableConfiguration.create(MainActivity.this); final CharSequence text = new ru.noties.markwon.renderer.SpannableRenderer().render( configuration, @@ -168,7 +170,7 @@ public class MainActivity extends Activity { // NB! LinkMovementMethod forces frequent updates... textView.setMovementMethod(LinkMovementMethod.getInstance()); textView.setText(text); - DrawableSpanUtils.scheduleDrawables(textView); + AsyncDrawableSpanUtils.scheduleDrawables(textView); } }); } diff --git a/app/src/main/java/ru/noties/markwon/SpannableRenderer.java b/app/src/main/java/ru/noties/markwon/SpannableRenderer.java index 7d381fb2..aee93ac4 100644 --- a/app/src/main/java/ru/noties/markwon/SpannableRenderer.java +++ b/app/src/main/java/ru/noties/markwon/SpannableRenderer.java @@ -53,8 +53,8 @@ //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.SubScriptSpan; +//import ru.noties.markwon.spans.SuperScriptSpan; //import ru.noties.markwon.spans.ThematicBreakSpan; // //public class SpannableRenderer implements Renderer { @@ -263,9 +263,9 @@ // 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); +// builder.setSpan(new SuperScriptSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // } else if("sub".equals(item.tag)) { -// builder.setSpan(new SubSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +// builder.setSpan(new SubScriptSpan(), 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 diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fb275f3b..3dacf3f3 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - #3F51B5 - #303F9F - #FF4081 + #424242 + #212121 + #4caf50 diff --git a/build.gradle b/build.gradle index 43ca8a81..9b51f6fc 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.android.tools.build:gradle:2.3.2' } } diff --git a/library-renderer/build.gradle b/library-renderer/build.gradle index faf18fd8..0a53e40a 100644 --- a/library-renderer/build.gradle +++ b/library-renderer/build.gradle @@ -15,8 +15,6 @@ android { dependencies { - compile project(':library-spans') - compile SUPPORT_ANNOTATIONS compile COMMON_MARK compile COMMON_MARK_STRIKETHROUGHT diff --git a/library-renderer/src/main/java/ru/noties/markwon/renderer/LinkResolverDef.java b/library-renderer/src/main/java/ru/noties/markwon/renderer/LinkResolverDef.java new file mode 100644 index 00000000..a653e1bb --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/renderer/LinkResolverDef.java @@ -0,0 +1,27 @@ +package ru.noties.markwon.renderer; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.provider.Browser; +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.View; + +import ru.noties.markwon.spans.LinkSpan; + +class LinkResolverDef implements LinkSpan.Resolver { + @Override + public void resolve(View view, @NonNull String link) { + final Uri uri = Uri.parse(link); + final Context context = view.getContext(); + final Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w("LinkResolverDef", "Actvity was not found for intent, " + intent.toString()); + } + } +} diff --git a/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableConfiguration.java b/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableConfiguration.java index a483e045..32652faa 100644 --- a/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableConfiguration.java +++ b/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableConfiguration.java @@ -4,12 +4,8 @@ import android.content.Context; import android.support.annotation.NonNull; import ru.noties.markwon.spans.AsyncDrawable; -import ru.noties.markwon.spans.BlockQuoteSpan; -import ru.noties.markwon.spans.BulletListItemSpan; -import ru.noties.markwon.spans.CodeSpan; -import ru.noties.markwon.spans.HeadingSpan; -import ru.noties.markwon.spans.OrderedListItemSpan; -import ru.noties.markwon.spans.ThematicBreakSpan; +import ru.noties.markwon.spans.LinkSpan; +import ru.noties.markwon.spans.SpannableTheme; public class SpannableConfiguration { @@ -22,140 +18,81 @@ public class SpannableConfiguration { return new Builder(context); } - private final BlockQuoteSpan.Config blockQuoteConfig; - private final CodeSpan.Config codeConfig; - private final BulletListItemSpan.Config bulletListConfig; - private final HeadingSpan.Config headingConfig; - private final ThematicBreakSpan.Config thematicConfig; - private final OrderedListItemSpan.Config orderedListConfig; + private final SpannableTheme theme; private final AsyncDrawable.Loader asyncDrawableLoader; + private final SyntaxHighlight syntaxHighlight; + private final LinkSpan.Resolver linkResolver; private SpannableConfiguration(Builder builder) { - this.blockQuoteConfig = builder.blockQuoteConfig; - this.codeConfig = builder.codeConfig; - this.bulletListConfig = builder.bulletListConfig; - this.headingConfig = builder.headingConfig; - this.thematicConfig = builder.thematicConfig; - this.orderedListConfig = builder.orderedListConfig; + this.theme = builder.theme; this.asyncDrawableLoader = builder.asyncDrawableLoader; + this.syntaxHighlight = builder.syntaxHighlight; + this.linkResolver = builder.linkResolver; } - public BlockQuoteSpan.Config getBlockQuoteConfig() { - return blockQuoteConfig; + public SpannableTheme theme() { + return theme; } - public CodeSpan.Config getCodeConfig() { - return codeConfig; - } - - public BulletListItemSpan.Config getBulletListConfig() { - return bulletListConfig; - } - - public HeadingSpan.Config getHeadingConfig() { - return headingConfig; - } - - public ThematicBreakSpan.Config getThematicConfig() { - return thematicConfig; - } - - public OrderedListItemSpan.Config getOrderedListConfig() { - return orderedListConfig; - } - - public AsyncDrawable.Loader getAsyncDrawableLoader() { + public AsyncDrawable.Loader asyncDrawableLoader() { return asyncDrawableLoader; } + public SyntaxHighlight syntaxHighlight() { + return syntaxHighlight; + } + + public LinkSpan.Resolver linkResolver() { + return linkResolver; + } + public static class Builder { private final Context context; - private BlockQuoteSpan.Config blockQuoteConfig; - private CodeSpan.Config codeConfig; - private BulletListItemSpan.Config bulletListConfig; - private HeadingSpan.Config headingConfig; - private ThematicBreakSpan.Config thematicConfig; - private OrderedListItemSpan.Config orderedListConfig; + private SpannableTheme theme; private AsyncDrawable.Loader asyncDrawableLoader; + private SyntaxHighlight syntaxHighlight; + private LinkSpan.Resolver linkResolver; public Builder(Context context) { this.context = context; } - public Builder setBlockQuoteConfig(@NonNull BlockQuoteSpan.Config blockQuoteConfig) { - this.blockQuoteConfig = blockQuoteConfig; + public Builder theme(SpannableTheme theme) { + this.theme = theme; return this; } - public Builder setCodeConfig(@NonNull CodeSpan.Config codeConfig) { - this.codeConfig = codeConfig; - return this; - } - - public Builder setBulletListConfig(@NonNull BulletListItemSpan.Config bulletListConfig) { - this.bulletListConfig = bulletListConfig; - return this; - } - - public Builder setHeadingConfig(@NonNull HeadingSpan.Config headingConfig) { - this.headingConfig = headingConfig; - return this; - } - - public Builder setThematicConfig(@NonNull ThematicBreakSpan.Config thematicConfig) { - this.thematicConfig = thematicConfig; - return this; - } - - public Builder setOrderedListConfig(@NonNull OrderedListItemSpan.Config orderedListConfig) { - this.orderedListConfig = orderedListConfig; - return this; - } - - public Builder setAsyncDrawableLoader(AsyncDrawable.Loader asyncDrawableLoader) { + public Builder asyncDrawableLoader(AsyncDrawable.Loader asyncDrawableLoader) { this.asyncDrawableLoader = asyncDrawableLoader; return this; } - // todo, change to something more reliable - // todo, must mention that bullet/ordered/quote must have the same margin (or maybe we can just enforce it?) - // actually it does make sense to have `blockQuote` & `list` margins (if they are different) - // todo, maybe move defaults to configuration classes? + public Builder syntaxHighlight(SyntaxHighlight syntaxHighlight) { + this.syntaxHighlight = syntaxHighlight; + return this; + } + + public Builder linkResolver(LinkSpan.Resolver linkResolver) { + this.linkResolver = linkResolver; + return this; + } + public SpannableConfiguration build() { - if (blockQuoteConfig == null) { - blockQuoteConfig = new BlockQuoteSpan.Config( - px(24), - 0, - 0 - ); - } - if (codeConfig == null) { - codeConfig = CodeSpan.Config.builder() - .setMultilineMargin(px(8)) - .build(); - } - if (bulletListConfig == null) { - bulletListConfig = new BulletListItemSpan.Config(px(24), 0, px(8), px(1)); - } - if (headingConfig == null) { - headingConfig = new HeadingSpan.Config(px(1), 0); - } - if (thematicConfig == null) { - thematicConfig = new ThematicBreakSpan.Config(0, px(2)); - } - if (orderedListConfig == null) { - orderedListConfig = new OrderedListItemSpan.Config(px(24), 0); + if (theme == null) { + theme = SpannableTheme.create(context); } if (asyncDrawableLoader == null) { asyncDrawableLoader = new AsyncDrawableLoaderNoOp(); } + if (syntaxHighlight == null) { + syntaxHighlight = new SyntaxHighlightNoOp(); + } + if (linkResolver == null) { + linkResolver = new LinkResolverDef(); + } return new SpannableConfiguration(this); } - - private int px(int dp) { - return (int) (context.getResources().getDisplayMetrics().density * dp + .5F); - } } } diff --git a/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java b/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java index 589dc337..d13fcbca 100644 --- a/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java +++ b/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java @@ -3,7 +3,6 @@ package ru.noties.markwon.renderer; import android.support.annotation.NonNull; import android.text.SpannableStringBuilder; import android.text.Spanned; -import android.text.TextUtils; import android.text.style.StrikethroughSpan; import android.text.style.URLSpan; @@ -38,6 +37,7 @@ import ru.noties.markwon.spans.BulletListItemSpan; import ru.noties.markwon.spans.CodeSpan; import ru.noties.markwon.spans.EmphasisSpan; import ru.noties.markwon.spans.HeadingSpan; +import ru.noties.markwon.spans.LinkSpan; import ru.noties.markwon.spans.OrderedListItemSpan; import ru.noties.markwon.spans.StrongEmphasisSpan; import ru.noties.markwon.spans.ThematicBreakSpan; @@ -97,7 +97,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { visitChildren(blockQuote); setSpan(length, new BlockQuoteSpan( - configuration.getBlockQuoteConfig(), + configuration.theme(), blockQuoteIndent )); @@ -123,7 +123,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { builder.append('\u00a0'); setSpan(length, new CodeSpan( - configuration.getCodeConfig(), + configuration.theme(), false )); } @@ -131,17 +131,20 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { @Override public void visit(FencedCodeBlock fencedCodeBlock) { -// Debug.i(fencedCodeBlock); - newLine(); final int length = builder.length(); + // empty lines on top & bottom builder.append('\u00a0').append('\n'); - builder.append(fencedCodeBlock.getLiteral()); + builder.append( + configuration.syntaxHighlight() + .highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral()) + ); builder.append('\u00a0').append('\n'); + setSpan(length, new CodeSpan( - configuration.getCodeConfig(), + configuration.theme(), true )); @@ -189,7 +192,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { visitChildren(listItem); setSpan(length, new OrderedListItemSpan( - configuration.getOrderedListConfig(), + configuration.theme(), String.valueOf(start) + "." + '\u00a0', blockQuoteIndent, length @@ -204,7 +207,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { visitChildren(listItem); setSpan(length, new BulletListItemSpan( - configuration.getBulletListConfig(), + configuration.theme(), blockQuoteIndent, listLevel - 1, length @@ -226,7 +229,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { final int length = builder.length(); builder.append(' '); // without space it won't render - setSpan(length, new ThematicBreakSpan(configuration.getThematicConfig())); + setSpan(length, new ThematicBreakSpan(configuration.theme())); newLine(); builder.append('\n'); @@ -242,7 +245,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { final int length = builder.length(); visitChildren(heading); setSpan(length, new HeadingSpan( - configuration.getHeadingConfig(), + configuration.theme(), heading.getLevel(), builder.length()) ); @@ -315,7 +318,18 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { builder.append(' '); // breakable space } - setSpan(length, new AsyncDrawableSpan(new AsyncDrawable(image.getDestination(), configuration.getAsyncDrawableLoader()))); + final Node parent = image.getParent(); + final boolean link = parent != null && parent instanceof Link; + + setSpan(length, new AsyncDrawableSpan( + configuration.theme(), + new AsyncDrawable( + image.getDestination(), + configuration.asyncDrawableLoader() + ), + AsyncDrawableSpan.ALIGN_BOTTOM, + link) + ); } @Override @@ -329,7 +343,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { public void visit(Link link) { final int length = builder.length(); visitChildren(link); - setSpan(length, new URLSpan(link.getDestination())); + setSpan(length, new LinkSpan(configuration.theme(), link.getDestination(), configuration.linkResolver())); } private void setSpan(int start, @NonNull Object span) { diff --git a/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java b/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java index 69f78dcb..88265eeb 100644 --- a/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java +++ b/library-renderer/src/main/java/ru/noties/markwon/renderer/SpannableRenderer.java @@ -14,6 +14,7 @@ public class SpannableRenderer { // * Common interface for images (in markdown & inline-html) // * util method to properly copy markdown (with lists/links, etc) // * util to apply empty line height + // * transform relative urls to absolute ones... public CharSequence render(@NonNull SpannableConfiguration configuration, @Nullable Node node) { final CharSequence out; diff --git a/library-renderer/src/main/java/ru/noties/markwon/renderer/SyntaxHighlight.java b/library-renderer/src/main/java/ru/noties/markwon/renderer/SyntaxHighlight.java new file mode 100644 index 00000000..4ab0b1dd --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/renderer/SyntaxHighlight.java @@ -0,0 +1,10 @@ +package ru.noties.markwon.renderer; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public interface SyntaxHighlight { + + @NonNull + CharSequence highlight(@Nullable String info, @NonNull String code); +} diff --git a/library-renderer/src/main/java/ru/noties/markwon/renderer/SyntaxHighlightNoOp.java b/library-renderer/src/main/java/ru/noties/markwon/renderer/SyntaxHighlightNoOp.java new file mode 100644 index 00000000..4df3b68e --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/renderer/SyntaxHighlightNoOp.java @@ -0,0 +1,12 @@ +package ru.noties.markwon.renderer; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +class SyntaxHighlightNoOp implements SyntaxHighlight { + @NonNull + @Override + public CharSequence highlight(@Nullable String info, @NonNull String code) { + return code; + } +} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java similarity index 100% rename from library-spans/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java similarity index 67% rename from library-spans/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java index d81da309..637c0e95 100644 --- a/library-spans/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpan.java @@ -3,7 +3,6 @@ package ru.noties.markwon.spans; 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; @@ -13,23 +12,39 @@ import android.text.style.ReplacementSpan; @SuppressWarnings("WeakerAccess") public class AsyncDrawableSpan extends ReplacementSpan { - @IntDef({ ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER }) - @interface Alignment {} + @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; // will only center if drawable height is less than text line height + private final SpannableTheme theme; private final AsyncDrawable drawable; private final int alignment; + private final boolean replacementTextIsLink; - public AsyncDrawableSpan(@NonNull AsyncDrawable drawable) { - this(drawable, ALIGN_BOTTOM); + public AsyncDrawableSpan(@NonNull SpannableTheme theme, @NonNull AsyncDrawable drawable) { + this(theme, drawable, ALIGN_BOTTOM); } - public AsyncDrawableSpan(@NonNull AsyncDrawable drawable, @Alignment int alignment) { + public AsyncDrawableSpan( + @NonNull SpannableTheme theme, + @NonNull AsyncDrawable drawable, + @Alignment int alignment) { + this(theme, drawable, alignment, false); + } + + public AsyncDrawableSpan( + @NonNull SpannableTheme theme, + @NonNull AsyncDrawable drawable, + @Alignment int alignment, + boolean replacementTextIsLink) { + this.theme = theme; this.drawable = drawable; this.alignment = alignment; + this.replacementTextIsLink = replacementTextIsLink; // additionally set intrinsic bounds if empty final Rect rect = drawable.getBounds(); @@ -66,8 +81,13 @@ public class AsyncDrawableSpan extends ReplacementSpan { } else { - size = (int) (paint.measureText(text, start, end) + .5F); + // we will apply style here in case if theme modifies textSize or style (affects metrics) + if (replacementTextIsLink) { + theme.applyLinkStyle(paint); + } + // NB, no specific text handling (no new lines, etc) + size = (int) (paint.measureText(text, start, end) + .5F); } return size; @@ -108,7 +128,15 @@ public class AsyncDrawableSpan extends ReplacementSpan { } } else { - final int textY = (int) (bottom - ((bottom - top) / 2) - ((paint.descent() + paint.ascent()) / 2.F + .5F)); + // will it make sense to have additional background/borders for an image replacement? + // let's focus on main functionality and then think of it + + final float textY = CanvasUtils.textCenterY(top, bottom, paint); + if (replacementTextIsLink) { + theme.applyLinkStyle(paint); + } + + // NB, no specific text handling (no new lines, etc) canvas.drawText(text, start, end, x, textY, paint); } } diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/DrawableSpanUtils.java b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpanUtils.java similarity index 98% rename from library-spans/src/main/java/ru/noties/markwon/spans/DrawableSpanUtils.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpanUtils.java index b528d66b..872183e1 100644 --- a/library-spans/src/main/java/ru/noties/markwon/spans/DrawableSpanUtils.java +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/AsyncDrawableSpanUtils.java @@ -11,7 +11,7 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.List; -public class DrawableSpanUtils { +public class AsyncDrawableSpanUtils { // todo, add `unschedule` method (to be used when new text is set, so // drawables are removed from callbacks) @@ -66,7 +66,7 @@ public class DrawableSpanUtils { } } - private DrawableSpanUtils() {} + private AsyncDrawableSpanUtils() {} private static class DrawableCallbackImpl implements Drawable.Callback { diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java new file mode 100644 index 00000000..865f9cd2 --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java @@ -0,0 +1,51 @@ +package ru.noties.markwon.spans; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.annotation.NonNull; +import android.text.Layout; +import android.text.style.LeadingMarginSpan; + +public class BlockQuoteSpan implements LeadingMarginSpan { + + private final SpannableTheme theme; + private final Rect rect = ObjectsPool.rect(); + private final Paint paint = ObjectsPool.paint(); + private final int indent; + + public BlockQuoteSpan(@NonNull SpannableTheme theme, int indent) { + this.theme = theme; + this.indent = indent; + } + + @Override + public int getLeadingMargin(boolean first) { + return theme.getBlockMargin(); + } + + @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 width = theme.getBlockQuoteWidth(); + + theme.applyBlockQuoteStyle(paint); + + final int left = theme.getBlockMargin() * (indent - 1); + rect.set(left, top, left + width, bottom); + + c.drawRect(rect, paint); + } +} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/BulletListItemSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/BulletListItemSpan.java similarity index 54% rename from library-spans/src/main/java/ru/noties/markwon/spans/BulletListItemSpan.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/BulletListItemSpan.java index 1aa6b89a..d8058f25 100644 --- a/library-spans/src/main/java/ru/noties/markwon/spans/BulletListItemSpan.java +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/BulletListItemSpan.java @@ -4,7 +4,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; -import android.support.annotation.ColorInt; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.text.Layout; @@ -12,30 +11,7 @@ import android.text.style.LeadingMarginSpan; public class BulletListItemSpan implements LeadingMarginSpan { - // todo, there are 3 types of bullets: filled circle, stroke circle & filled rectangle - // also, there are ordered lists - - public static class Config { - - final int marginWidth; - final int bulletColor; // by default uses text color - final int bulletSide; - final int bulletStrokeWidth; - - // from 0 but it makes sense to provide something wider - public Config( - @IntRange(from = 0) int marginWidth, - @ColorInt int bulletColor, - @IntRange(from = 0) int bulletSide, - @IntRange(from = 0) int bulletStrokeWidth) { - this.marginWidth = marginWidth; - this.bulletColor = bulletColor; - this.bulletSide = bulletSide; - this.bulletStrokeWidth = bulletStrokeWidth; - } - } - - private final Config config; + private SpannableTheme theme; private final Paint paint = ObjectsPool.paint(); private final RectF circle = ObjectsPool.rectF(); @@ -46,11 +22,11 @@ public class BulletListItemSpan implements LeadingMarginSpan { private final int start; public BulletListItemSpan( - @NonNull Config config, + @NonNull SpannableTheme theme, @IntRange(from = 0) int blockIndent, @IntRange(from = 0) int level, @IntRange(from = 0) int start) { - this.config = config; + this.theme = theme; this.blockIndent = blockIndent; this.level = level; this.start = start; @@ -58,7 +34,7 @@ public class BulletListItemSpan implements LeadingMarginSpan { @Override public int getLeadingMargin(boolean first) { - return config.marginWidth; + return theme.getBlockMargin(); } @Override @@ -69,39 +45,17 @@ public class BulletListItemSpan implements LeadingMarginSpan { return; } - final int color; - final float stroke; + paint.set(p); - if (config.bulletColor == 0) { - color = p.getColor(); - } else { - color = config.bulletColor; - } - - if (config.bulletStrokeWidth == 0) { - stroke = p.getStrokeWidth(); - } else { - stroke = config.bulletStrokeWidth; - } - - paint.setColor(color); - paint.setStrokeWidth(stroke); + theme.applyListItemStyle(paint); final int save = c.save(); try { - // by default we use half of margin width, but if height is less than width, we calculate from it - final int width = config.marginWidth; + final int width = theme.getBlockMargin(); final int height = bottom - top; - final int min = Math.min(config.marginWidth, height) / 2; - final int side; - if (config.bulletSide == 0 - || config.bulletSide > min) { - side = min; - } else { - side = config.bulletSide; - } + final int side = theme.getBulletWidth(bottom - top); final int marginLeft = (width - side) / 2; final int marginTop = (height - side) / 2; diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/CanvasUtils.java b/library-renderer/src/main/java/ru/noties/markwon/spans/CanvasUtils.java new file mode 100644 index 00000000..b55783b8 --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/CanvasUtils.java @@ -0,0 +1,14 @@ +package ru.noties.markwon.spans; + +import android.graphics.Paint; +import android.support.annotation.NonNull; + +abstract class CanvasUtils { + + static float textCenterY(int top, int bottom, @NonNull Paint paint) { + return (int) (bottom - ((bottom - top) / 2) - ((paint.descent() + paint.ascent()) / 2.F + .5F)); + } + + private CanvasUtils() { + } +} diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/CodeSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/CodeSpan.java new file mode 100644 index 00000000..db4e0553 --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/CodeSpan.java @@ -0,0 +1,60 @@ +package ru.noties.markwon.spans; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.annotation.NonNull; +import android.text.Layout; +import android.text.TextPaint; +import android.text.style.LeadingMarginSpan; +import android.text.style.MetricAffectingSpan; + +public class CodeSpan extends MetricAffectingSpan implements LeadingMarginSpan { + + private final SpannableTheme theme; + private final Rect rect = ObjectsPool.rect(); + private final Paint paint = ObjectsPool.paint(); + + private final boolean multiline; + + public CodeSpan(@NonNull SpannableTheme theme, boolean multiline) { + this.theme = theme; + this.multiline = multiline; + } + + @Override + public void updateMeasureState(TextPaint p) { + apply(p); + } + + @Override + public void updateDrawState(TextPaint ds) { + apply(ds); + if (!multiline) { + ds.bgColor = theme.getCodeBackgroundColor(ds); + } + } + + private void apply(TextPaint p) { + theme.applyCodeTextStyle(p); + } + + @Override + public int getLeadingMargin(boolean first) { + return multiline ? theme.getCodeMultilineMargin() : 0; + } + + @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) { + + if (multiline) { + + paint.setStyle(Paint.Style.FILL); + paint.setColor(theme.getCodeBackgroundColor(p)); + + rect.set(x, top, c.getWidth(), bottom); + + c.drawRect(rect, paint); + } + } +} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/ColorUtils.java b/library-renderer/src/main/java/ru/noties/markwon/spans/ColorUtils.java similarity index 86% rename from library-spans/src/main/java/ru/noties/markwon/spans/ColorUtils.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/ColorUtils.java index c59b01fd..4739d531 100644 --- a/library-spans/src/main/java/ru/noties/markwon/spans/ColorUtils.java +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/ColorUtils.java @@ -1,6 +1,6 @@ package ru.noties.markwon.spans; -class ColorUtils { +abstract class ColorUtils { static int applyAlpha(int color, int alpha) { return (color & 0x00FFFFFF) | (alpha << 24); diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/EmphasisSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/EmphasisSpan.java similarity index 100% rename from library-spans/src/main/java/ru/noties/markwon/spans/EmphasisSpan.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/EmphasisSpan.java diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/HeadingSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/HeadingSpan.java new file mode 100644 index 00000000..7ec9c10a --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/HeadingSpan.java @@ -0,0 +1,67 @@ +package ru.noties.markwon.spans; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.text.Layout; +import android.text.TextPaint; +import android.text.style.LeadingMarginSpan; +import android.text.style.MetricAffectingSpan; + +public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpan { + + private final SpannableTheme theme; + private final Rect rect = ObjectsPool.rect(); + private final Paint paint = ObjectsPool.paint(); + private final int level; + private final int end; + + public HeadingSpan(@NonNull SpannableTheme theme, @IntRange(from = 1, to = 6) int level, @IntRange(from = 0) int end) { + this.theme = theme; + this.level = level; + this.end = end; + } + + @Override + public void updateMeasureState(TextPaint p) { + apply(p); + } + + @Override + public void updateDrawState(TextPaint tp) { + apply(tp); + } + + private void apply(TextPaint paint) { + theme.applyHeadingTextStyle(paint, level); + } + + @Override + public int getLeadingMargin(boolean first) { + // no margin actually, but we need to access Canvas to draw break + return 0; + } + + @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) { + + if (level == 1 + || level == 2) { + + if (this.end == end) { + + paint.set(p); + + theme.applyHeadingBreakStyle(paint); + + final float height = paint.getStrokeWidth(); + final int b = (int) (bottom - height + .5F); + + rect.set(x, b, c.getWidth(), bottom); + c.drawRect(rect, paint); + } + } + } +} diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/LinkSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/LinkSpan.java new file mode 100644 index 00000000..b96f4681 --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/LinkSpan.java @@ -0,0 +1,33 @@ +package ru.noties.markwon.spans; + +import android.support.annotation.NonNull; +import android.text.TextPaint; +import android.text.style.ClickableSpan; +import android.view.View; + +public class LinkSpan extends ClickableSpan { + + public interface Resolver { + void resolve(View view, @NonNull String link); + } + + private final SpannableTheme theme; + private final String link; + private final Resolver resolver; + + public LinkSpan(@NonNull SpannableTheme theme, @NonNull String link, @NonNull Resolver resolver) { + this.theme = theme; + this.link = link; + this.resolver = resolver; + } + + @Override + public void onClick(View widget) { + resolver.resolve(widget, link); + } + + @Override + public void updateDrawState(TextPaint ds) { + theme.applyLinkStyle(ds); + } +} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/ObjectsPool.java b/library-renderer/src/main/java/ru/noties/markwon/spans/ObjectsPool.java similarity index 100% rename from library-spans/src/main/java/ru/noties/markwon/spans/ObjectsPool.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/ObjectsPool.java diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/OrderedListItemSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/OrderedListItemSpan.java similarity index 62% rename from library-spans/src/main/java/ru/noties/markwon/spans/OrderedListItemSpan.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/OrderedListItemSpan.java index 4d1e0451..0cbd1fac 100644 --- a/library-spans/src/main/java/ru/noties/markwon/spans/OrderedListItemSpan.java +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/OrderedListItemSpan.java @@ -2,7 +2,6 @@ package ru.noties.markwon.spans; import android.graphics.Canvas; import android.graphics.Paint; -import android.support.annotation.ColorInt; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.text.Layout; @@ -10,29 +9,18 @@ import android.text.style.LeadingMarginSpan; public class OrderedListItemSpan implements LeadingMarginSpan { - public static class Config { - - final int marginWidth; // by default 0 - final int numberColor; // by default color of the main text - - public Config(@IntRange(from = 0) int marginWidth, @ColorInt int numberColor) { - this.marginWidth = marginWidth; - this.numberColor = numberColor; - } - } - - private final Config config; + private final SpannableTheme theme; private final String number; private final int blockIndent; private final int start; public OrderedListItemSpan( - @NonNull Config config, + @NonNull SpannableTheme theme, @NonNull String number, @IntRange(from = 0) int blockIndent, @IntRange(from = 0) int start ) { - this.config = config; + this.theme = theme; this.number = number; this.blockIndent = blockIndent; this.start = start; @@ -40,7 +28,7 @@ public class OrderedListItemSpan implements LeadingMarginSpan { @Override public int getLeadingMargin(boolean first) { - return config.marginWidth; + return theme.getBlockMargin(); } @Override @@ -51,15 +39,13 @@ public class OrderedListItemSpan implements LeadingMarginSpan { return; } - if (config.numberColor != 0) { - p.setColor(config.numberColor); - } + theme.applyListItemStyle(p); - final int width = config.marginWidth; + final int width = theme.getBlockMargin(); final int numberWidth = (int) (p.measureText(number) + .5F); final int numberX = (width * blockIndent) - numberWidth; - final int numberY = bottom - ((bottom - top) / 2) - (int) ((p.descent() + p.ascent()) / 2); + final float numberY = CanvasUtils.textCenterY(top, bottom, p); c.drawText(number, numberX, numberY, p); } diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/SpannableTheme.java b/library-renderer/src/main/java/ru/noties/markwon/spans/SpannableTheme.java new file mode 100644 index 00000000..c25878ce --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/SpannableTheme.java @@ -0,0 +1,456 @@ +package ru.noties.markwon.spans; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.support.annotation.AttrRes; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.text.TextPaint; +import android.util.TypedValue; +import android.widget.TextView; + +@SuppressWarnings("WeakerAccess") +public class SpannableTheme { + + // this method should be used if TextView is known beforehand + // it will correctly measure the `space` char and set it as `codeMultilineMargin` + // otherwise this value must be set explicitly ( + public static SpannableTheme create(@NonNull TextView textView) { + return builderWithDefaults(textView.getContext()) + .codeMultilineMargin((int) (textView.getPaint().measureText("\u00a0") + .5F)) + .build(); + } + + // this create default theme (except for `codeMultilineMargin` property) + public static SpannableTheme create(@NonNull Context context) { + return builderWithDefaults(context).build(); + } + + public static Builder builder() { + return new Builder(); + } + + public static Builder builder(@NonNull SpannableTheme copyFrom) { + return new Builder(copyFrom); + } + + public static Builder builderWithDefaults(@NonNull Context context) { + final Px px = new Px(context); + return new Builder() + .linkColor(resolve(context, android.R.attr.textColorLink)) + .codeMultilineMargin(px.px(8)) + .blockMargin(px.px(24)) + .bulletListItemStrokeWidth(px.px(1)) + .headingBreakHeight(px.px(1)) + .thematicBreakHeight(px.px(2)); + } + + private static int resolve(Context context, @AttrRes int attr) { + final TypedValue typedValue = new TypedValue(); + final int attrs[] = new int[]{attr}; + final TypedArray typedArray = context.obtainStyledAttributes(typedValue.data, attrs); + try { + return typedArray.getColor(0, 0); + } finally { + typedArray.recycle(); + } + } + + protected static final int BLOCK_QUOTE_DEF_COLOR_ALPHA = 50; + + protected static final int CODE_DEF_BACKGROUND_COLOR_ALPHA = 25; + protected static final float CODE_DEF_TEXT_SIZE_RATIO = .87F; + + protected static final int HEADING_DEF_BREAK_COLOR_ALPHA = 75; + + // taken from html spec (most browsers render headings like that) + // is not exposed via protected modifier in order to disallow modification + private static final float[] HEADING_SIZES = { + 2.F, 1.5F, 1.17F, 1.F, .83F, .67F, + }; + + protected static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F; + + protected static final int THEMATIC_BREAK_DEF_ALPHA = 75; + + protected final int linkColor; + + // used in quote, lists + protected final int blockMargin; + + // by default it's 1/4th of `blockMargin` + protected final int blockQuoteWidth; + + // by default it's text color with `BLOCK_QUOTE_DEF_COLOR_ALPHA` applied alpha + protected final int blockQuoteColor; + + // by default uses text color (applied for un-ordered lists & ordered (bullets & numbers) + protected final int listItemColor; + + // by default the stroke color of a paint object + protected final int bulletListItemStrokeWidth; + + // width of bullet, by default min(blockMargin, height) / 2 + protected final int bulletWidth; + + // by default - main text color + protected final int codeTextColor; + + // by default 0.1 alpha of textColor/codeTextColor + protected final int codeBackgroundColor; + + // by default `width` of a space char... it's fun and games, but span doesn't have access to paint in `getLeadingMargin` + // so, we need to set this value explicitly (think of an utility method, that takes TextView/TextPaint and measures space char) + protected final int codeMultilineMargin; + + // by default Typeface.MONOSPACE + protected final Typeface codeTypeface; + + // by default a bit (how much?!) smaller than normal text + // applied ONLY if default typeface was used, otherwise, not applied + protected final int codeTextSize; + + // by default paint.getStrokeWidth + protected final int headingBreakHeight; + + // by default, text color with `HEADING_DEF_BREAK_COLOR_ALPHA` applied alpha + protected final int headingBreakColor; + + // by default `SCRIPT_DEF_TEXT_SIZE_RATIO` + protected final float scriptTextSizeRatio; + + // by default textColor with `THEMATIC_BREAK_DEF_ALPHA` applied alpha + protected final int thematicBreakColor; + + // by default paint.strokeWidth + protected final int thematicBreakHeight; + + protected SpannableTheme(@NonNull Builder builder) { + this.linkColor = builder.linkColor; + this.blockMargin = builder.blockMargin; + this.blockQuoteWidth = builder.blockQuoteWidth; + this.blockQuoteColor = builder.blockQuoteColor; + this.listItemColor = builder.listItemColor; + this.bulletListItemStrokeWidth = builder.bulletListItemStrokeWidth; + this.bulletWidth = builder.bulletWidth; + this.codeTextColor = builder.codeTextColor; + this.codeBackgroundColor = builder.codeBackgroundColor; + this.codeMultilineMargin = builder.codeMultilineMargin; + this.codeTypeface = builder.codeTypeface; + this.codeTextSize = builder.codeTextSize; + this.headingBreakHeight = builder.headingBreakHeight; + this.headingBreakColor = builder.headingBreakColor; + this.scriptTextSizeRatio = builder.scriptTextSizeRatio; + this.thematicBreakColor = builder.thematicBreakColor; + this.thematicBreakHeight = builder.thematicBreakHeight; + } + + + public void applyLinkStyle(@NonNull Paint paint) { + paint.setUnderlineText(true); + if (linkColor != 0) { + // by default we will be using text color + paint.setColor(linkColor); + } + } + + public void applyBlockQuoteStyle(@NonNull Paint paint) { + final int color; + if (blockQuoteColor == 0) { + color = ColorUtils.applyAlpha(paint.getColor(), BLOCK_QUOTE_DEF_COLOR_ALPHA); + } else { + color = blockQuoteColor; + } + paint.setStyle(Paint.Style.FILL); + paint.setColor(color); + } + + public int getBlockMargin() { + return blockMargin; + } + + public int getBlockQuoteWidth() { + final int out; + if (blockQuoteWidth == 0) { + out = (int) (blockMargin * .25F + .5F); + } else { + out = blockQuoteWidth; + } + return out; + } + + public void applyListItemStyle(@NonNull Paint paint) { + + final int color; + if (listItemColor != 0) { + color = listItemColor; + } else { + color = paint.getColor(); + } + paint.setColor(color); + + if (bulletListItemStrokeWidth != 0) { + paint.setStrokeWidth(bulletListItemStrokeWidth); + } + } + + public int getBulletWidth(int height) { + + final int min = Math.min(blockMargin, height) / 2; + + final int width; + if (bulletWidth == 0 + || bulletWidth > min) { + width = min; + } else { + width = bulletWidth; + } + + return width; + } + + public void applyCodeTextStyle(@NonNull Paint paint) { + + if (codeTextColor != 0) { + paint.setColor(codeTextColor); + } + + // custom typeface was set + if (codeTypeface != null) { + paint.setTypeface(codeTypeface); + if (codeTextSize != 0) { + paint.setTextSize(codeTextSize); + } + } else { + paint.setTypeface(Typeface.MONOSPACE); + final float textSize; + if (codeTextSize != 0) { + textSize = codeTextSize; + } else { + textSize = paint.getTextSize() * CODE_DEF_TEXT_SIZE_RATIO; + } + paint.setTextSize(textSize); + } + } + + public int getCodeMultilineMargin() { + return codeMultilineMargin; + } + + public int getCodeBackgroundColor(@NonNull Paint paint) { + final int color; + if (codeBackgroundColor != 0) { + color = codeBackgroundColor; + } else { + color = ColorUtils.applyAlpha(paint.getColor(), CODE_DEF_BACKGROUND_COLOR_ALPHA); + } + return color; + } + + public void applyHeadingTextStyle(@NonNull Paint paint, @IntRange(from = 1, to = 6) int level) { + paint.setFakeBoldText(true); + paint.setTextSize(paint.getTextSize() * HEADING_SIZES[level - 1]); + } + + public void applyHeadingBreakStyle(@NonNull Paint paint) { + final int color; + if (headingBreakColor != 0) { + color = headingBreakColor; + } else { + color = ColorUtils.applyAlpha(paint.getColor(), HEADING_DEF_BREAK_COLOR_ALPHA); + } + paint.setColor(color); + paint.setStyle(Paint.Style.FILL); + if (headingBreakHeight != 0) { + //noinspection SuspiciousNameCombination + paint.setStrokeWidth(headingBreakHeight); + } + } + + public void applySuperScriptStyle(@NonNull TextPaint paint) { + final float ratio; + if (Float.compare(scriptTextSizeRatio, .0F) == 0) { + ratio = SCRIPT_DEF_TEXT_SIZE_RATIO; + } else { + ratio = scriptTextSizeRatio; + } + paint.setTextSize(paint.getTextSize() * ratio); + paint.baselineShift += (int) (paint.ascent() / 2); + } + + public void applySubScriptStyle(@NonNull TextPaint paint) { + final float ratio; + if (Float.compare(scriptTextSizeRatio, .0F) == 0) { + ratio = SCRIPT_DEF_TEXT_SIZE_RATIO; + } else { + ratio = scriptTextSizeRatio; + } + paint.setTextSize(paint.getTextSize() * ratio); + paint.baselineShift -= (int) (paint.ascent() / 2); + } + + public void applyThematicBreakStyle(@NonNull Paint paint) { + final int color; + if (thematicBreakColor != 0) { + color = thematicBreakColor; + } else { + color = ColorUtils.applyAlpha(paint.getColor(), THEMATIC_BREAK_DEF_ALPHA); + } + paint.setColor(color); + paint.setStyle(Paint.Style.FILL); + + if (thematicBreakHeight != 0) { + //noinspection SuspiciousNameCombination + paint.setStrokeWidth(thematicBreakHeight); + } + } + + public static class Builder { + + private int linkColor; + private int blockMargin; + private int blockQuoteWidth; + private int blockQuoteColor; + private int listItemColor; + private int bulletListItemStrokeWidth; + private int bulletWidth; + private int codeTextColor; + private int codeBackgroundColor; + private int codeMultilineMargin; + private Typeface codeTypeface; + private int codeTextSize; + private int headingBreakHeight; + private int headingBreakColor; + private float scriptTextSizeRatio; + private int thematicBreakColor; + private int thematicBreakHeight; + + Builder() { + + } + + Builder(@NonNull SpannableTheme theme) { + + this.linkColor = theme.linkColor; + this.blockMargin = theme.blockMargin; + this.blockQuoteWidth = theme.blockQuoteWidth; + this.blockQuoteColor = theme.blockQuoteColor; + this.listItemColor = theme.listItemColor; + this.bulletListItemStrokeWidth = theme.bulletListItemStrokeWidth; + this.bulletWidth = theme.bulletWidth; + this.codeTextColor = theme.codeTextColor; + this.codeBackgroundColor = theme.codeBackgroundColor; + this.codeMultilineMargin = theme.codeMultilineMargin; + this.codeTypeface = theme.codeTypeface; + this.codeTextSize = theme.codeTextSize; + this.headingBreakHeight = theme.headingBreakHeight; + this.headingBreakColor = theme.headingBreakColor; + this.scriptTextSizeRatio = theme.scriptTextSizeRatio; + this.thematicBreakColor = theme.thematicBreakColor; + this.thematicBreakHeight = theme.thematicBreakHeight; + } + + public Builder linkColor(int linkColor) { + this.linkColor = linkColor; + return this; + } + + public Builder blockMargin(int blockMargin) { + this.blockMargin = blockMargin; + return this; + } + + public Builder blockQuoteWidth(int blockQuoteWidth) { + this.blockQuoteWidth = blockQuoteWidth; + return this; + } + + public Builder blockQuoteColor(int blockQuoteColor) { + this.blockQuoteColor = blockQuoteColor; + return this; + } + + public Builder listItemColor(int listItemColor) { + this.listItemColor = listItemColor; + return this; + } + + public Builder bulletListItemStrokeWidth(int bulletListItemStrokeWidth) { + this.bulletListItemStrokeWidth = bulletListItemStrokeWidth; + return this; + } + + public Builder bulletWidth(int bulletWidth) { + this.bulletWidth = bulletWidth; + return this; + } + + public Builder codeTextColor(int codeTextColor) { + this.codeTextColor = codeTextColor; + return this; + } + + public Builder codeBackgroundColor(int codeBackgroundColor) { + this.codeBackgroundColor = codeBackgroundColor; + return this; + } + + public Builder codeMultilineMargin(int codeMultilineMargin) { + this.codeMultilineMargin = codeMultilineMargin; + return this; + } + + public Builder codeTypeface(Typeface codeTypeface) { + this.codeTypeface = codeTypeface; + return this; + } + + public Builder codeTextSize(int codeTextSize) { + this.codeTextSize = codeTextSize; + return this; + } + + public Builder headingBreakHeight(int headingBreakHeight) { + this.headingBreakHeight = headingBreakHeight; + return this; + } + + public Builder headingBreakColor(int headingBreakColor) { + this.headingBreakColor = headingBreakColor; + return this; + } + + public Builder scriptTextSizeRatio(float scriptTextSizeRatio) { + this.scriptTextSizeRatio = scriptTextSizeRatio; + return this; + } + + public Builder thematicBreakColor(int thematicBreakColor) { + this.thematicBreakColor = thematicBreakColor; + return this; + } + + public Builder thematicBreakHeight(int thematicBreakHeight) { + this.thematicBreakHeight = thematicBreakHeight; + return this; + } + + public SpannableTheme build() { + return new SpannableTheme(this); + } + } + + private static class Px { + private final float density; + + Px(@NonNull Context context) { + this.density = context.getResources().getDisplayMetrics().density; + } + + int px(int dp) { + return (int) (dp * density + .5F); + } + } +} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/StrongEmphasisSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/StrongEmphasisSpan.java similarity index 100% rename from library-spans/src/main/java/ru/noties/markwon/spans/StrongEmphasisSpan.java rename to library-renderer/src/main/java/ru/noties/markwon/spans/StrongEmphasisSpan.java diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/SubScriptSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/SubScriptSpan.java new file mode 100644 index 00000000..4386613d --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/SubScriptSpan.java @@ -0,0 +1,28 @@ +package ru.noties.markwon.spans; + +import android.support.annotation.NonNull; +import android.text.TextPaint; +import android.text.style.MetricAffectingSpan; + +public class SubScriptSpan extends MetricAffectingSpan { + + private final SpannableTheme theme; + + public SubScriptSpan(@NonNull SpannableTheme theme) { + this.theme = theme; + } + + @Override + public void updateDrawState(TextPaint tp) { + apply(tp); + } + + @Override + public void updateMeasureState(TextPaint tp) { + apply(tp); + } + + private void apply(TextPaint paint) { + theme.applySubScriptStyle(paint); + } +} diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/SuperScriptSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/SuperScriptSpan.java new file mode 100644 index 00000000..4b8151ec --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/SuperScriptSpan.java @@ -0,0 +1,28 @@ +package ru.noties.markwon.spans; + +import android.support.annotation.NonNull; +import android.text.TextPaint; +import android.text.style.MetricAffectingSpan; + +public class SuperScriptSpan extends MetricAffectingSpan { + + private final SpannableTheme theme; + + public SuperScriptSpan(@NonNull SpannableTheme theme) { + this.theme = theme; + } + + @Override + public void updateDrawState(TextPaint tp) { + apply(tp); + } + + @Override + public void updateMeasureState(TextPaint tp) { + apply(tp); + } + + private void apply(TextPaint paint) { + theme.applySuperScriptStyle(paint); + } +} diff --git a/library-renderer/src/main/java/ru/noties/markwon/spans/ThematicBreakSpan.java b/library-renderer/src/main/java/ru/noties/markwon/spans/ThematicBreakSpan.java new file mode 100644 index 00000000..1c5355af --- /dev/null +++ b/library-renderer/src/main/java/ru/noties/markwon/spans/ThematicBreakSpan.java @@ -0,0 +1,39 @@ +package ru.noties.markwon.spans; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.annotation.NonNull; +import android.text.Layout; +import android.text.style.LeadingMarginSpan; + +public class ThematicBreakSpan implements LeadingMarginSpan { + + private final SpannableTheme theme; + private final Rect rect = ObjectsPool.rect(); + private final Paint paint = ObjectsPool.paint(); + + public ThematicBreakSpan(@NonNull SpannableTheme theme) { + this.theme = theme; + } + + @Override + public int getLeadingMargin(boolean first) { + return 0; + } + + @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 = top + ((bottom - top) / 2); + + paint.set(p); + theme.applyThematicBreakStyle(paint); + + final int height = (int) (paint.getStrokeWidth() + .5F); + final int halfHeight = (int) (height / 2.F + .5F); + + rect.set(x, middle - halfHeight, c.getWidth(), middle + halfHeight); + c.drawRect(rect, paint); + } +} diff --git a/library-spans/build.gradle b/library-spans/build.gradle deleted file mode 100644 index c07ebc94..00000000 --- a/library-spans/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -apply plugin: 'com.android.library' - -android { - - compileSdkVersion TARGET_SDK - buildToolsVersion BUILD_TOOLS - - defaultConfig { - minSdkVersion MIN_SDK - targetSdkVersion TARGET_SDK - versionCode 1 - versionName version - } -} - -dependencies { - compile SUPPORT_ANNOTATIONS -} diff --git a/library-spans/src/main/AndroidManifest.xml b/library-spans/src/main/AndroidManifest.xml deleted file mode 100644 index 84de88b7..00000000 --- a/library-spans/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java b/library-spans/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java deleted file mode 100644 index 81a82b59..00000000 --- a/library-spans/src/main/java/ru/noties/markwon/spans/BlockQuoteSpan.java +++ /dev/null @@ -1,84 +0,0 @@ -package ru.noties.markwon.spans; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.support.annotation.ColorInt; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.text.Layout; -import android.text.style.LeadingMarginSpan; - -public class BlockQuoteSpan implements LeadingMarginSpan { - - private static final int DEF_COLOR_ALPHA = 50; - - @SuppressWarnings("WeakerAccess") - public static class Config { - - final int totalWidth; - final int quoteWidth; // by default 1/4 of width - final int quoteColor; // by default textColor with 0.2 alpha - - public Config( - @IntRange(from = 1) int totalWidth, - @IntRange(from = 0) int quoteWidth, - @ColorInt int quoteColor) { - this.totalWidth = totalWidth; - this.quoteWidth = quoteWidth; - this.quoteColor = quoteColor; - } - } - - private final Config config; - private final Rect rect = ObjectsPool.rect(); - private final Paint paint = ObjectsPool.paint(); - private final int indent; - - public BlockQuoteSpan(@NonNull Config config, int indent) { - this.config = config; - this.indent = indent; - } - - @Override - public int getLeadingMargin(boolean first) { - return config.totalWidth; - } - - @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 width; - if (config.quoteWidth == 0) { - width = (int) (config.totalWidth / 4.F + .5F); - } else { - width = config.quoteWidth; - } - - final int color; - if (config.quoteColor != 0) { - color = config.quoteColor; - } else { - color = ColorUtils.applyAlpha(p.getColor(), DEF_COLOR_ALPHA); - } - paint.setStyle(Paint.Style.FILL); - paint.setColor(color); - - final int left = config.totalWidth * (indent - 1); - rect.set(left, top, left + width, bottom); - - c.drawRect(rect, paint); - } -} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/CodeSpan.java b/library-spans/src/main/java/ru/noties/markwon/spans/CodeSpan.java deleted file mode 100644 index 7bb29632..00000000 --- a/library-spans/src/main/java/ru/noties/markwon/spans/CodeSpan.java +++ /dev/null @@ -1,146 +0,0 @@ -package ru.noties.markwon.spans; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.support.annotation.ColorInt; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.text.Layout; -import android.text.TextPaint; -import android.text.style.LeadingMarginSpan; -import android.text.style.MetricAffectingSpan; - -public class CodeSpan extends MetricAffectingSpan implements LeadingMarginSpan { - - private static final int DEF_COLOR_ALPHA = 25; - - @SuppressWarnings("WeakerAccess") - public static class Config { - - public static Builder builder() { - return new Builder(); - } - - final int textColor; // by default the same as main text - final int backgroundColor; // by default textColor with 0.1 alpha - final int multilineMargin; // by default 0 - final int textSize; // by default the same as main text - final Typeface typeface; // by default Typeface.MONOSPACE - - private Config(Builder builder) { - this.textColor = builder.textColor; - this.backgroundColor = builder.backgroundColor; - this.multilineMargin = builder.multilineMargin; - this.textSize = builder.textSize; - this.typeface = builder.typeface; - } - - public static class Builder { - - int textColor; - int backgroundColor; - int multilineMargin; - int textSize; - Typeface typeface; - - public Builder setTextColor(@ColorInt int textColor) { - this.textColor = textColor; - return this; - } - - public Builder setBackgroundColor(@ColorInt int backgroundColor) { - this.backgroundColor = backgroundColor; - return this; - } - - public Builder setMultilineMargin(int multilineMargin) { - this.multilineMargin = multilineMargin; - return this; - } - - public Builder setTextSize(@IntRange(from = 0) int textSize) { - this.textSize = textSize; - return this; - } - - public Builder setTypeface(@NonNull Typeface typeface) { - this.typeface = typeface; - return this; - } - - public Config build() { - if (typeface == null) { - typeface = Typeface.MONOSPACE; - } - return new Config(this); - } - } - } - - private final Config config; - private final Rect rect = ObjectsPool.rect(); - private final Paint paint = ObjectsPool.paint(); - - private final boolean multiline; - - public CodeSpan(@NonNull Config config, boolean multiline) { - this.config = config; - this.multiline = multiline; - } - - @Override - public void updateMeasureState(TextPaint p) { - apply(p); - } - - @Override - public void updateDrawState(TextPaint ds) { - apply(ds); - if (!multiline) { - final int color; - if (config.backgroundColor == 0) { - color = ColorUtils.applyAlpha(ds.getColor(), DEF_COLOR_ALPHA); - } else { - color = config.backgroundColor; - } - ds.bgColor = color; - } - } - - private void apply(TextPaint p) { - p.setTypeface(config.typeface); - if (config.textSize > 0) { - p.setTextSize(config.textSize); - } - if (config.textColor != 0) { - p.setColor(config.textColor); - } - } - - @Override - public int getLeadingMargin(boolean first) { - return multiline ? config.multilineMargin : 0; - } - - @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) { - - if (multiline) { - - final int color; - if (config.backgroundColor == 0) { - color = ColorUtils.applyAlpha(p.getColor(), DEF_COLOR_ALPHA); - } else { - color = config.backgroundColor; - } - paint.setStyle(Paint.Style.FILL); - paint.setColor(color); - - rect.set(x, top, c.getWidth(), bottom); - - c.drawRect(rect, paint); - } - } -} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/HeadingSpan.java b/library-spans/src/main/java/ru/noties/markwon/spans/HeadingSpan.java deleted file mode 100644 index c0675d8f..00000000 --- a/library-spans/src/main/java/ru/noties/markwon/spans/HeadingSpan.java +++ /dev/null @@ -1,97 +0,0 @@ -package ru.noties.markwon.spans; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.support.annotation.ColorInt; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.text.Layout; -import android.text.TextPaint; -import android.text.style.LeadingMarginSpan; -import android.text.style.MetricAffectingSpan; - -public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpan { - - // taken from html spec (most browsers render headings like that) - private static final float[] HEADING_SIZES = { - 2.F, 1.5F, 1.17F, 1.F, .83F, .67F, - }; - - private static final int DEF_BREAK_COLOR_ALPHA = 127; - - public static class Config { - - final int breakHeight; // by default stroke width - final int breakColor; // by default -> textColor with 0.5 alpha - - public Config(@IntRange(from = 0) int breakHeight, @ColorInt int breakColor) { - this.breakHeight = breakHeight; - this.breakColor = breakColor; - } - } - - private final Config config; - private final Rect rect = ObjectsPool.rect(); - private final Paint paint = ObjectsPool.paint(); - private final int level; - private final int end; - - public HeadingSpan(@NonNull Config config, @IntRange(from = 1, to = 6) int level, @IntRange(from = 0) int end) { - this.config = config; - this.level = level - 1; - this.end = end; - } - - @Override - public void updateMeasureState(TextPaint p) { - apply(p); - } - - @Override - public void updateDrawState(TextPaint tp) { - apply(tp); - } - - private void apply(TextPaint paint) { - paint.setTextSize(paint.getTextSize() * HEADING_SIZES[level]); - paint.setFakeBoldText(true); - } - - @Override - public int getLeadingMargin(boolean first) { - // no margin actually, but we need to access Canvas to draw break - return 0; - } - - @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) { - - // if we are configured to draw underlines, draw them here - - if (level == 0 - || level == 1) { - - if (this.end == end) { - - final int color; - final int breakHeight; - if (config.breakColor == 0) { - color = ColorUtils.applyAlpha(p.getColor(), DEF_BREAK_COLOR_ALPHA); - } else { - color = config.breakColor; - } - if (config.breakHeight == 0) { - breakHeight = (int) (p.getStrokeWidth() + .5F); - } else { - breakHeight = config.breakHeight; - } - paint.setStyle(Paint.Style.FILL); - paint.setColor(color); - - rect.set(x, bottom - breakHeight, c.getWidth(), bottom); - c.drawRect(rect, paint); - } - } - } -} diff --git a/library-spans/src/main/java/ru/noties/markwon/spans/SubSpan.java b/library-spans/src/main/java/ru/noties/markwon/spans/SubSpan.java deleted file mode 100644 index d38f75da..00000000 --- a/library-spans/src/main/java/ru/noties/markwon/spans/SubSpan.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.noties.markwon.spans; - -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/library-spans/src/main/java/ru/noties/markwon/spans/SupSpan.java b/library-spans/src/main/java/ru/noties/markwon/spans/SupSpan.java deleted file mode 100644 index 4fddd44d..00000000 --- a/library-spans/src/main/java/ru/noties/markwon/spans/SupSpan.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.noties.markwon.spans; - -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/library-spans/src/main/java/ru/noties/markwon/spans/ThematicBreakSpan.java b/library-spans/src/main/java/ru/noties/markwon/spans/ThematicBreakSpan.java deleted file mode 100644 index 27e99271..00000000 --- a/library-spans/src/main/java/ru/noties/markwon/spans/ThematicBreakSpan.java +++ /dev/null @@ -1,64 +0,0 @@ -package ru.noties.markwon.spans; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.support.annotation.ColorInt; -import android.support.annotation.IntRange; -import android.text.Layout; -import android.text.style.LeadingMarginSpan; - -public class ThematicBreakSpan implements LeadingMarginSpan { - - private static final int DEF_COLOR_ALPHA = 127; - - public static class Config { - - final int color; // by default textColor with 0.5 alpha - final int height; // by default strokeWidth of paint - - public Config(@ColorInt int color, @IntRange(from = 0) int height) { - this.color = color; - this.height = height; - } - } - - private final Config config; - private final Rect rect = ObjectsPool.rect(); - private final Paint paint = ObjectsPool.paint(); - - public ThematicBreakSpan(Config config) { - this.config = config; - } - - @Override - public int getLeadingMargin(boolean first) { - return 0; - } - - @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 = top + ((bottom - top) / 2); - - final int color; - if (config.color == 0) { - color = ColorUtils.applyAlpha(p.getColor(), DEF_COLOR_ALPHA); - } else { - color = config.color; - } - paint.setColor(color); - paint.setStyle(Paint.Style.FILL); - - final int height; - if (config.height == 0) { - height = (int) (p.getStrokeWidth() + .5F); - } else { - height = config.height; - } - final int halfHeight = (int) (height / 2.F + .5F); - - rect.set(x, middle - halfHeight, c.getWidth(), middle + halfHeight); - c.drawRect(rect, paint); - } -} diff --git a/settings.gradle b/settings.gradle index dce89cfc..330339ba 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':library-spans', ':library-renderer', ':library-view' +include ':app', ':library-renderer', ':library-view'