Merge 4b01d37f3098cb781bad7761f38455943585400b into 9bdcf8551c60de116d0a035fd9d812218777da1e

This commit is contained in:
Daniel Leal 2018-01-30 04:32:55 +00:00 committed by GitHub
commit 73d7e00a36
20 changed files with 420 additions and 88 deletions

View File

@ -2,6 +2,8 @@
# Markwon # Markwon
![Image1](https://content-frontend.wkda-test.de/static/consumer_app/article19_image1.png)
[![markwon](https://img.shields.io/maven-central/v/ru.noties/markwon.svg?label=markwon)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22) [![markwon](https://img.shields.io/maven-central/v/ru.noties/markwon.svg?label=markwon)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon%22)
[![markwon-image-loader](https://img.shields.io/maven-central/v/ru.noties/markwon-image-loader.svg?label=markwon-image-loader)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon-image-loader%22) [![markwon-image-loader](https://img.shields.io/maven-central/v/ru.noties/markwon-image-loader.svg?label=markwon-image-loader)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon-image-loader%22)
[![markwon-view](https://img.shields.io/maven-central/v/ru.noties/markwon-view.svg?label=markwon-view)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon-view%22) [![markwon-view](https://img.shields.io/maven-central/v/ru.noties/markwon-view.svg?label=markwon-view)](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%markwon-view%22)

View File

@ -36,4 +36,6 @@ dependencies {
compile 'com.google.dagger:dagger:2.10' compile 'com.google.dagger:dagger:2.10'
annotationProcessor 'com.google.dagger:dagger-compiler:2.10' annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
implementation "com.android.support:appcompat-v7:27.0.2"
} }

5
app/proguard.pro vendored
View File

@ -1,5 +0,0 @@
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class com.caverock.androidsvg.** { *; }
-dontwarn com.caverock.androidsvg.**

View File

@ -16,6 +16,9 @@ import okhttp3.Cache;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import ru.noties.markwon.il.AsyncDrawableLoader; import ru.noties.markwon.il.AsyncDrawableLoader;
import ru.noties.markwon.spans.AsyncDrawable; import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.configuration.image.ImageConfig;
import ru.noties.markwon.spans.configuration.image.ImageGravity;
import ru.noties.markwon.spans.configuration.image.ImageWidth;
@Module @Module
class AppModule { class AppModule {

View File

@ -1,11 +1,15 @@
package ru.noties.markwon; package ru.noties.markwon;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.FontRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -14,13 +18,14 @@ import javax.inject.Inject;
import ru.noties.debug.Debug; import ru.noties.debug.Debug;
import ru.noties.markwon.spans.AsyncDrawable; import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.SpannableTheme;
import ru.noties.markwon.spans.configuration.heading.HeadingConfig;
import ru.noties.markwon.spans.configuration.heading.HeadingTypeConfig;
@ActivityScope @ActivityScope
public class MarkdownRenderer { public class MarkdownRenderer {
interface MarkdownReadyListener { private final Context context;
void onMarkdownReady(CharSequence markdown);
}
@Inject @Inject
AsyncDrawable.Loader loader; AsyncDrawable.Loader loader;
@ -34,14 +39,15 @@ public class MarkdownRenderer {
private Future<?> task; private Future<?> task;
@Inject @Inject
MarkdownRenderer() { MarkdownRenderer(Context context) {
this.context = context;
} }
public void render( public void render(
@NonNull final Context context, @NonNull final Context context,
@Nullable final Uri uri, @Nullable final Uri uri,
@NonNull final String markdown, @NonNull final String markdown,
@NonNull final MarkdownReadyListener listener) { @NonNull final MarkdownReadyListener listener) {
cancel(); cancel();
task = service.submit(new Runnable() { task = service.submit(new Runnable() {
@Override @Override
@ -55,9 +61,10 @@ public class MarkdownRenderer {
} }
final SpannableConfiguration configuration = SpannableConfiguration.builder(context) final SpannableConfiguration configuration = SpannableConfiguration.builder(context)
.asyncDrawableLoader(loader) .theme(getSpannableTheme())
.urlProcessor(urlProcessor) .asyncDrawableLoader(loader)
.build(); .urlProcessor(urlProcessor)
.build();
final long start = SystemClock.uptimeMillis(); final long start = SystemClock.uptimeMillis();
@ -92,4 +99,25 @@ public class MarkdownRenderer {
private boolean isCancelled() { private boolean isCancelled() {
return task == null || task.isCancelled(); return task == null || task.isCancelled();
} }
private HeadingConfig getHeadingConfig() {
final HeadingTypeConfig h1 = new HeadingTypeConfig(-1, Color.RED, getTypeface(R.font.opensans_semibold));
final HeadingTypeConfig h2 = new HeadingTypeConfig(-1, Color.BLUE, getTypeface(R.font.opensans_regular));
return new HeadingConfig(h1, h2);
}
private SpannableTheme getSpannableTheme() {
return SpannableTheme.builderWithDefaults(context)
.headingConfig(getHeadingConfig(), context.getResources().getDisplayMetrics().density)
.build();
}
private Typeface getTypeface(@FontRes int font){
return ResourcesCompat.getFont(context, font);
}
interface MarkdownReadyListener {
void onMarkdownReady(CharSequence markdown);
}
} }

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- Regular -->
<font
android:font="@font/opensans_regular"
android:fontStyle="normal"
android:fontWeight="400"
app:font="@font/opensans_regular"
app:fontStyle="normal"
app:fontWeight="400"/>
<!-- Semi-bold -->
<font
android:font="@font/opensans_semibold"
android:fontStyle="normal"
android:fontWeight="700"
app:font="@font/opensans_semibold"
app:fontStyle="normal"
app:fontWeight="700"/>
</font-family>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,44 @@
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'digital.wup.android-maven-publish'
publishing {
publications {
aar(MavenPublication) {
groupId "${GROUP}.auto1"
version VERSION_NAME
artifactId POM_ARTIFACT_ID
from components.android
}
}
}
artifactory {
contextUrl = artifactory_repository_url
publish {
repository {
repoKey = artifactory_repository_name
username = artifactory_username
password = artifactory_password
}
defaults {
publications('aar')
publishArtifacts = true
properties = ['qa.level': 'basic', 'q.os': 'android', 'dev.team': 'mobile-android']
publishPom = true
}
}
}
afterEvaluate {
task cleanBuildPublish {
dependsOn 'clean'
dependsOn 'assembleRelease'
dependsOn 'artifactoryPublish'
tasks.findByName('assembleRelease').mustRunAfter 'clean'
tasks.findByName('artifactoryPublish').dependsOn 'generatePomFileForAarPublication'
tasks.findByName('artifactoryPublish').mustRunAfter 'assembleRelease'
}
}

View File

@ -1,10 +1,14 @@
buildscript { buildscript {
ext.kotlin_version = '1.2.10'
repositories { repositories {
jcenter() jcenter()
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.4.12"
classpath 'digital.wup:android-maven-publish:3.1.1'
} }
} }
@ -27,14 +31,13 @@ task wrapper(type: Wrapper) {
} }
ext { ext {
// Config // Config
BUILD_TOOLS = '26.0.3' BUILD_TOOLS = '27.0.1'
TARGET_SDK = 26 TARGET_SDK = 26
MIN_SDK = 16 MIN_SDK = 16
// Dependencies // Dependencies
final def supportVersion = '26.1.0' final def supportVersion = '27.0.2'
SUPPORT_ANNOTATIONS = "com.android.support:support-annotations:$supportVersion" SUPPORT_ANNOTATIONS = "com.android.support:support-annotations:$supportVersion"
SUPPORT_APP_COMPAT = "com.android.support:appcompat-v7:$supportVersion" SUPPORT_APP_COMPAT = "com.android.support:appcompat-v7:$supportVersion"

View File

@ -10,6 +10,8 @@ android {
targetSdkVersion TARGET_SDK targetSdkVersion TARGET_SDK
versionCode 1 versionCode 1
versionName version versionName version
consumerProguardFiles 'lib-proguard-rules.pro'
} }
lintOptions { lintOptions {
@ -19,12 +21,10 @@ android {
} }
dependencies { dependencies {
compile project(':library') implementation project(':library')
compile ANDROID_SVG implementation ANDROID_SVG
compile ANDROID_GIF implementation ANDROID_GIF
compile OK_HTTP implementation OK_HTTP
} }
if (project.hasProperty('release')) { apply from: '../artifactory-mvn-push.gradle'
apply from: 'https://raw.githubusercontent.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle'
}

View File

@ -0,0 +1,5 @@
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class com.caverock.androidsvg.** { *; }
-dontwarn com.caverock.androidsvg.**

View File

@ -36,6 +36,7 @@ import okhttp3.Response;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import pl.droidsonroids.gif.GifDrawable; import pl.droidsonroids.gif.GifDrawable;
import ru.noties.markwon.spans.AsyncDrawable; import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.configuration.image.ImageConfig;
public class AsyncDrawableLoader implements AsyncDrawable.Loader { public class AsyncDrawableLoader implements AsyncDrawable.Loader {
@ -58,6 +59,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
private final ExecutorService executorService; private final ExecutorService executorService;
private final Handler mainThread; private final Handler mainThread;
private final Drawable errorDrawable; private final Drawable errorDrawable;
private final ImageConfig imageConfig;
private final Map<String, Future<?>> requests; private final Map<String, Future<?>> requests;
@ -67,6 +69,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
this.executorService = builder.executorService; this.executorService = builder.executorService;
this.mainThread = new Handler(Looper.getMainLooper()); this.mainThread = new Handler(Looper.getMainLooper());
this.errorDrawable = builder.errorDrawable; this.errorDrawable = builder.errorDrawable;
this.imageConfig = builder.imageConfig;
this.requests = new HashMap<>(3); this.requests = new HashMap<>(3);
} }
@ -146,7 +149,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
public void run() { public void run() {
final AsyncDrawable asyncDrawable = reference.get(); final AsyncDrawable asyncDrawable = reference.get();
if (asyncDrawable != null && asyncDrawable.isAttached()) { if (asyncDrawable != null && asyncDrawable.isAttached()) {
asyncDrawable.setResult(out); asyncDrawable.setResult(out, imageConfig);
} }
} }
}); });
@ -345,6 +348,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
private Resources resources; private Resources resources;
private ExecutorService executorService; private ExecutorService executorService;
private Drawable errorDrawable; private Drawable errorDrawable;
private ImageConfig imageConfig;
public Builder client(@NonNull OkHttpClient client) { public Builder client(@NonNull OkHttpClient client) {
this.client = client; this.client = client;
@ -366,6 +370,11 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
return this; return this;
} }
public Builder imageConfig(ImageConfig imageConfig) {
this.imageConfig = imageConfig;
return this;
}
public AsyncDrawableLoader build() { public AsyncDrawableLoader build() {
if (client == null) { if (client == null) {
client = new OkHttpClient(); client = new OkHttpClient();
@ -377,6 +386,9 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
// we will use executor from okHttp // we will use executor from okHttp
executorService = client.dispatcher().executorService(); executorService = client.dispatcher().executorService();
} }
if(imageConfig == null){
imageConfig = new ImageConfig();
}
return new AsyncDrawableLoader(this); return new AsyncDrawableLoader(this);
} }
} }

View File

@ -1,4 +1,5 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android { android {
@ -11,15 +12,24 @@ android {
versionCode 1 versionCode 1
versionName version versionName version
} }
libraryVariants.all { variant ->
variant.outputs.all {
outputFileName = "${POM_ARTIFACT_ID}-release.aar"
}
}
} }
dependencies { dependencies {
compile SUPPORT_ANNOTATIONS api SUPPORT_ANNOTATIONS
compile COMMON_MARK implementation COMMON_MARK
compile COMMON_MARK_STRIKETHROUGHT implementation COMMON_MARK_STRIKETHROUGHT
compile COMMON_MARK_TABLE implementation COMMON_MARK_TABLE
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
} }
if (project.hasProperty('release')) { repositories {
apply from: 'https://raw.githubusercontent.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle' mavenCentral()
} }
apply from: '../artifactory-mvn-push.gradle'

View File

@ -12,25 +12,26 @@ import android.support.annotation.Nullable;
import ru.noties.markwon.renderer.html.ImageSize; import ru.noties.markwon.renderer.html.ImageSize;
import ru.noties.markwon.renderer.html.ImageSizeResolver; import ru.noties.markwon.renderer.html.ImageSizeResolver;
import ru.noties.markwon.spans.configuration.image.ImageConfig;
import ru.noties.markwon.spans.configuration.image.ImageGravity;
import ru.noties.markwon.spans.configuration.image.ImageWidth;
public class AsyncDrawable extends Drawable { public class AsyncDrawable extends Drawable {
public interface Loader {
void load(@NonNull String destination, @NonNull AsyncDrawable drawable);
void cancel(@NonNull String destination);
}
private final String destination; private final String destination;
private final Loader loader; private final Loader loader;
private final ImageSize imageSize; private final ImageSize imageSize;
private final ImageSizeResolver imageSizeResolver; private final ImageSizeResolver imageSizeResolver;
private Drawable result; private Drawable result;
private Callback callback; private Callback callback;
private int canvasWidth; private int canvasWidth;
private float textSize; private float textSize;
public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) { public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) {
@ -41,10 +42,10 @@ public class AsyncDrawable extends Drawable {
* @since 1.0.1 * @since 1.0.1
*/ */
public AsyncDrawable( public AsyncDrawable(
@NonNull String destination, @NonNull String destination,
@NonNull Loader loader, @NonNull Loader loader,
@Nullable ImageSizeResolver imageSizeResolver, @Nullable ImageSizeResolver imageSizeResolver,
@Nullable ImageSize imageSize @Nullable ImageSize imageSize
) { ) {
this.destination = destination; this.destination = destination;
this.loader = loader; this.loader = loader;
@ -91,7 +92,7 @@ public class AsyncDrawable extends Drawable {
} }
} }
public void setResult(@NonNull Drawable result) { public void setResult(@NonNull Drawable result, @NonNull ImageConfig imageConfig) {
// if we have previous one, detach it // if we have previous one, detach it
if (this.result != null) { if (this.result != null) {
@ -101,7 +102,7 @@ public class AsyncDrawable extends Drawable {
this.result = result; this.result = result;
this.result.setCallback(callback); this.result.setCallback(callback);
final Rect bounds = resolveBounds(); final Rect bounds = resolveBounds(imageConfig);
result.setBounds(bounds); result.setBounds(bounds);
setBounds(bounds); setBounds(bounds);
@ -168,17 +169,55 @@ public class AsyncDrawable extends Drawable {
} }
/** /**
* @param imageConfig
* @since 1.0.1 * @since 1.0.1
*/ */
@NonNull @NonNull
private Rect resolveBounds() { private Rect resolveBounds(@NonNull ImageConfig imageConfig) {
final Rect rect; final Rect rect;
if (imageSizeResolver == null if (imageSizeResolver == null
|| imageSize == null) { || imageSize == null) {
rect = result.getBounds(); rect = result.getBounds();
} else { } else {
rect = imageSizeResolver.resolveImageSize(imageSize, result.getBounds(), canvasWidth, textSize); rect = imageSizeResolver.resolveImageSize(imageSize, result.getBounds(), canvasWidth, textSize);
} }
return rect; return adjustBounds(rect, imageConfig);
}
private Rect adjustBounds(Rect bounds, ImageConfig imageConfig) {
final ImageGravity gravity = imageConfig.getGravity();
final ImageWidth imageWidth = imageConfig.getImageWidth();
if (imageWidth == ImageWidth.MatchParent) {
final float growthRatio = (float) canvasWidth / bounds.width();
bounds.left = 0;
bounds.right = canvasWidth;
bounds.bottom = (int) (bounds.top + bounds.height() * growthRatio);
} else {
switch (gravity) {
case Left:
//left is unchanged
break;
case Right:
bounds.left = canvasWidth - bounds.width();
bounds.right = canvasWidth;
break;
case Center:
final int center = canvasWidth / 2;
final int imageRadius = bounds.width() / 2;
bounds.left = center - imageRadius;
bounds.right = center + imageRadius;
break;
}
}
return bounds;
}
public interface Loader {
void load(@NonNull String destination, @NonNull AsyncDrawable drawable);
void cancel(@NonNull String destination);
} }
} }

View File

@ -10,12 +10,14 @@ import android.text.TextPaint;
import android.text.style.LeadingMarginSpan; import android.text.style.LeadingMarginSpan;
import android.text.style.MetricAffectingSpan; import android.text.style.MetricAffectingSpan;
import ru.noties.markwon.spans.configuration.heading.HeadingType;
public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpan { public class HeadingSpan extends MetricAffectingSpan implements LeadingMarginSpan {
private final SpannableTheme theme; private final SpannableTheme theme;
private final Rect rect = ObjectsPool.rect(); private final Rect rect = ObjectsPool.rect();
private final Paint paint = ObjectsPool.paint(); private final Paint paint = ObjectsPool.paint();
private final int level; @HeadingType private final int level;
public HeadingSpan(@NonNull SpannableTheme theme, @IntRange(from = 1, to = 6) int level) { public HeadingSpan(@NonNull SpannableTheme theme, @IntRange(from = 1, to = 6) int level) {
this.theme = theme; this.theme = theme;

View File

@ -9,12 +9,15 @@ import android.support.annotation.AttrRes;
import android.support.annotation.ColorInt; import android.support.annotation.ColorInt;
import android.support.annotation.Dimension; import android.support.annotation.Dimension;
import android.support.annotation.FloatRange; import android.support.annotation.FloatRange;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextPaint; import android.text.TextPaint;
import android.util.TypedValue; import android.util.TypedValue;
import ru.noties.markwon.spans.configuration.heading.HeadingConfig;
import ru.noties.markwon.spans.configuration.heading.HeadingType;
import ru.noties.markwon.spans.configuration.heading.HeadingTypeConfig;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class SpannableTheme { public class SpannableTheme {
@ -78,16 +81,16 @@ public class SpannableTheme {
final Dip dip = new Dip(context); final Dip dip = new Dip(context);
return new Builder() return new Builder()
.linkColor(linkColor) .linkColor(linkColor)
.codeMultilineMargin(dip.toPx(8)) .codeMultilineMargin(dip.toPx(8))
.blockMargin(dip.toPx(24)) .blockMargin(dip.toPx(24))
.blockQuoteWidth(dip.toPx(4)) .blockQuoteWidth(dip.toPx(4))
.bulletListItemStrokeWidth(dip.toPx(1)) .bulletListItemStrokeWidth(dip.toPx(1))
.headingBreakHeight(dip.toPx(1)) .headingConfig(new HeadingConfig(), dip.density)
.thematicBreakHeight(dip.toPx(4)) .thematicBreakHeight(dip.toPx(4))
.tableCellPadding(dip.toPx(4)) .tableCellPadding(dip.toPx(4))
.tableBorderWidth(dip.toPx(1)) .tableBorderWidth(dip.toPx(1))
.taskListDrawable(new TaskListDrawable(linkColor, linkColor, backgroundColor)); .taskListDrawable(new TaskListDrawable(linkColor, linkColor, backgroundColor));
} }
private static int resolve(Context context, @AttrRes int attr) { private static int resolve(Context context, @AttrRes int attr) {
@ -104,14 +107,15 @@ public class SpannableTheme {
protected static final int BLOCK_QUOTE_DEF_COLOR_ALPHA = 25; protected static final int BLOCK_QUOTE_DEF_COLOR_ALPHA = 25;
protected static final int CODE_DEF_BACKGROUND_COLOR_ALPHA = 25; protected static final int CODE_DEF_BACKGROUND_COLOR_ALPHA = 25;
protected static final float CODE_DEF_TEXT_SIZE_RATIO = .87F; protected static final float CODE_DEF_TEXT_SIZE_RATIO = .87F;
protected static final int HEADING_DEF_BREAK_COLOR_ALPHA = 75; protected static final int HEADING_DEF_BREAK_COLOR_ALPHA = 75;
// taken from html spec (most browsers render headings like that) // taken from html spec (most browsers render headings like that)
// is not exposed via protected modifier in order to disallow modification // is not exposed via protected modifier in order to disallow modification
private static final float[] HEADING_SIZES = { private static final float[] HEADING_SCALES = {
2.F, 1.5F, 1.17F, 1.F, .83F, .67F, 2.F, 1.5F, 1.17F, 1.F, .83F, .67F,
}; };
protected static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F; protected static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
@ -159,11 +163,7 @@ public class SpannableTheme {
// applied ONLY if default typeface was used, otherwise, not applied // applied ONLY if default typeface was used, otherwise, not applied
protected final int codeTextSize; protected final int codeTextSize;
// by default paint.getStrokeWidth protected final HeadingConfig headingConfig;
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` // by default `SCRIPT_DEF_TEXT_SIZE_RATIO`
protected final float scriptTextSizeRatio; protected final float scriptTextSizeRatio;
@ -202,8 +202,7 @@ public class SpannableTheme {
this.codeMultilineMargin = builder.codeMultilineMargin; this.codeMultilineMargin = builder.codeMultilineMargin;
this.codeTypeface = builder.codeTypeface; this.codeTypeface = builder.codeTypeface;
this.codeTextSize = builder.codeTextSize; this.codeTextSize = builder.codeTextSize;
this.headingBreakHeight = builder.headingBreakHeight; this.headingConfig = builder.headingConfig;
this.headingBreakColor = builder.headingBreakColor;
this.scriptTextSizeRatio = builder.scriptTextSizeRatio; this.scriptTextSizeRatio = builder.scriptTextSizeRatio;
this.thematicBreakColor = builder.thematicBreakColor; this.thematicBreakColor = builder.thematicBreakColor;
this.thematicBreakHeight = builder.thematicBreakHeight; this.thematicBreakHeight = builder.thematicBreakHeight;
@ -214,7 +213,6 @@ public class SpannableTheme {
this.taskListDrawable = builder.taskListDrawable; this.taskListDrawable = builder.taskListDrawable;
} }
public void applyLinkStyle(@NonNull Paint paint) { public void applyLinkStyle(@NonNull Paint paint) {
paint.setUnderlineText(true); paint.setUnderlineText(true);
if (linkColor != 0) { if (linkColor != 0) {
@ -269,7 +267,7 @@ public class SpannableTheme {
final int width; final int width;
if (bulletWidth == 0 if (bulletWidth == 0
|| bulletWidth > min) { || bulletWidth > min) {
width = min; width = min;
} else { } else {
width = bulletWidth; width = bulletWidth;
@ -322,13 +320,51 @@ public class SpannableTheme {
return color; return color;
} }
public void applyHeadingTextStyle(@NonNull Paint paint, @IntRange(from = 1, to = 6) int level) { public void applyHeadingTextStyle(@NonNull Paint paint, @HeadingType int level) {
HeadingTypeConfig headingTypeConfig;
switch (level) {
case HeadingType.H1:
headingTypeConfig = headingConfig.getH1Config();
break;
case HeadingType.H2:
headingTypeConfig = headingConfig.getH2Config();
break;
case HeadingType.H3:
headingTypeConfig = headingConfig.getH3Config();
break;
case HeadingType.H4:
headingTypeConfig = headingConfig.getH4Config();
break;
case HeadingType.H5:
headingTypeConfig = headingConfig.getH5Config();
break;
case HeadingType.H6:
headingTypeConfig = headingConfig.getH6Config();
break;
default:
headingTypeConfig = new HeadingTypeConfig();
}
paint.setFakeBoldText(true); paint.setFakeBoldText(true);
paint.setTextSize(paint.getTextSize() * HEADING_SIZES[level - 1]);
final float textSize = headingTypeConfig.getTextScale() > 0 ?
headingTypeConfig.getTextScale() : HEADING_SCALES[level - 1];
paint.setTextSize(paint.getTextSize() * textSize);
final int textColor = headingTypeConfig.getTextColor();
if (textColor != -1) {
paint.setColor(textColor);
}
final Typeface typeface = headingTypeConfig.getTypeface();
if(typeface != null){
paint.setTypeface(typeface);
}
} }
public void applyHeadingBreakStyle(@NonNull Paint paint) { public void applyHeadingBreakStyle(@NonNull Paint paint) {
final int color; final int color;
final int headingBreakColor = headingConfig.getHeadingBreakConfig().getHeadingBreakColor();
if (headingBreakColor != 0) { if (headingBreakColor != 0) {
color = headingBreakColor; color = headingBreakColor;
} else { } else {
@ -336,6 +372,7 @@ public class SpannableTheme {
} }
paint.setColor(color); paint.setColor(color);
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
final float headingBreakHeight = headingConfig.getHeadingBreakConfig().getHeadingBreakStrokeWidth();
if (headingBreakHeight >= 0) { if (headingBreakHeight >= 0) {
//noinspection SuspiciousNameCombination //noinspection SuspiciousNameCombination
paint.setStrokeWidth(headingBreakHeight); paint.setStrokeWidth(headingBreakHeight);
@ -441,8 +478,7 @@ public class SpannableTheme {
private int codeMultilineMargin; private int codeMultilineMargin;
private Typeface codeTypeface; private Typeface codeTypeface;
private int codeTextSize; private int codeTextSize;
private int headingBreakHeight = -1; private HeadingConfig headingConfig;
private int headingBreakColor;
private float scriptTextSizeRatio; private float scriptTextSizeRatio;
private int thematicBreakColor; private int thematicBreakColor;
private int thematicBreakHeight = -1; private int thematicBreakHeight = -1;
@ -468,8 +504,7 @@ public class SpannableTheme {
this.codeMultilineMargin = theme.codeMultilineMargin; this.codeMultilineMargin = theme.codeMultilineMargin;
this.codeTypeface = theme.codeTypeface; this.codeTypeface = theme.codeTypeface;
this.codeTextSize = theme.codeTextSize; this.codeTextSize = theme.codeTextSize;
this.headingBreakHeight = theme.headingBreakHeight; this.headingConfig = theme.headingConfig;
this.headingBreakColor = theme.headingBreakColor;
this.scriptTextSizeRatio = theme.scriptTextSizeRatio; this.scriptTextSizeRatio = theme.scriptTextSizeRatio;
this.thematicBreakColor = theme.thematicBreakColor; this.thematicBreakColor = theme.thematicBreakColor;
this.thematicBreakHeight = theme.thematicBreakHeight; this.thematicBreakHeight = theme.thematicBreakHeight;
@ -553,14 +588,9 @@ public class SpannableTheme {
} }
@NonNull @NonNull
public Builder headingBreakHeight(@Dimension int headingBreakHeight) { public Builder headingConfig(HeadingConfig headingConfig, float density) {
this.headingBreakHeight = headingBreakHeight; headingConfig.setDensityFactor(density);
return this; this.headingConfig = headingConfig;
}
@NonNull
public Builder headingBreakColor(@ColorInt int headingBreakColor) {
this.headingBreakColor = headingBreakColor;
return this; return this;
} }
@ -630,7 +660,7 @@ public class SpannableTheme {
private static class Dip { private static class Dip {
private final float density; protected final float density;
Dip(@NonNull Context context) { Dip(@NonNull Context context) {
this.density = context.getResources().getDisplayMetrics().density; this.density = context.getResources().getDisplayMetrics().density;

View File

@ -0,0 +1,80 @@
package ru.noties.markwon.spans.configuration.heading
import android.graphics.Typeface
import android.support.annotation.ColorInt
import android.support.annotation.Dimension
/**
* Configuration for heading type (H1, H2, ..., H6)
*
* Can define different configurations for all six types of headings,
* plus an extra configuration for the line breaks (only applies to H1 and H2)
*
* @property h1Config Config for H1 heading
* @property h2Config Config for H2 heading
* @property h3Config Config for H3 heading
* @property h4Config Config for H4 heading
* @property h5Config Config for H5 heading
* @property h6Config Config for H6 heading
* @property headingBreakConfig Config for line breaks (for H1 and H2)
*/
class HeadingConfig @JvmOverloads constructor(
val h1Config: HeadingTypeConfig = HeadingTypeConfig(),
val h2Config: HeadingTypeConfig = HeadingTypeConfig(),
val h3Config: HeadingTypeConfig = HeadingTypeConfig(),
val h4Config: HeadingTypeConfig = HeadingTypeConfig(),
val h5Config: HeadingTypeConfig = HeadingTypeConfig(),
val h6Config: HeadingTypeConfig = HeadingTypeConfig(),
val headingBreakConfig: HeadingBreakConfig = HeadingBreakConfig()
) {
fun setDensityFactor(factor: Float) {
h1Config.densityFactor = factor
h2Config.densityFactor = factor
h3Config.densityFactor = factor
h4Config.densityFactor = factor
h5Config.densityFactor = factor
h6Config.densityFactor = factor
headingBreakConfig.densityFactor = factor
}
}
/**
* Configuration for given heading type (H1, H2, ..., H6)
*
* Can set text size, text color and font (typeface)
*
* @property textScale Text scale for heading
* @property textColor Text color for heading
* @property typeface Typeface for heading
*/
class HeadingTypeConfig @JvmOverloads constructor(
//Standard sizes available at #SpannableTheme.java:HEADING_SIZES
val textScale: Float = -1F,
@ColorInt val textColor: Int = -1,
val typeface: Typeface? = null
) {
internal var densityFactor: Float = -1F
}
/**
* Configuration for given heading type (H1, H2, ..., H6)
*
* Can set text size, text color and font (typeface)
*
* @property headingBreakStrokeWidth Stroke width for heading's line break
* @property headingBreakColor Color for heading's line break
*/
class HeadingBreakConfig @JvmOverloads constructor(
// by default paint.getStrokeWidth
@Dimension headingBreakStrokeWidth: Int = -1,
// by default, text color with `HEADING_DEF_BREAK_COLOR_ALPHA` applied alpha
@ColorInt val headingBreakColor: Int = 0
) {
internal var densityFactor: Float = -1F
val headingBreakStrokeWidth: Float = headingBreakStrokeWidth.toFloat()
get() = field * densityFactor
}

View File

@ -0,0 +1,27 @@
package ru.noties.markwon.spans.configuration.heading;
import android.support.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import static ru.noties.markwon.spans.configuration.heading.HeadingType.H1;
import static ru.noties.markwon.spans.configuration.heading.HeadingType.H2;
import static ru.noties.markwon.spans.configuration.heading.HeadingType.H3;
import static ru.noties.markwon.spans.configuration.heading.HeadingType.H4;
import static ru.noties.markwon.spans.configuration.heading.HeadingType.H5;
import static ru.noties.markwon.spans.configuration.heading.HeadingType.H6;
/**
* Created by daniel.leal on 13.12.17.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({H1, H2, H3, H4, H5, H6})
public @interface HeadingType {
int H1 = 1;
int H2 = 2;
int H3 = 3;
int H4 = 4;
int H5 = 5;
int H6 = 6;
}

View File

@ -0,0 +1,25 @@
package ru.noties.markwon.spans.configuration.image
/**
* Configuration for images
*
* Can set two parameters: image width style and horizontal gravity
*
* @property imageWidth Width of the image to be displayed (WRAP_CONTENT or MATCH_PARENT)
* @property gravity Horizontal gravity (left, right or center)
*/
class ImageConfig @JvmOverloads constructor(
val imageWidth: ImageWidth = ImageWidth.Wrap,
val gravity: ImageGravity = ImageGravity.Left
)
enum class ImageGravity {
Left,
Center,
Right
}
enum class ImageWidth {
Wrap,
MatchParent
}