diff --git a/README.md b/README.md index 876af5e9..14b41586 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ # 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-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) diff --git a/app/build.gradle b/app/build.gradle index bc2bca96..7e6606e9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,4 +36,6 @@ dependencies { compile 'com.google.dagger:dagger:2.10' annotationProcessor 'com.google.dagger:dagger-compiler:2.10' + + implementation "com.android.support:appcompat-v7:27.0.2" } diff --git a/app/proguard.pro b/app/proguard.pro index 9eee5753..e69de29b 100644 --- a/app/proguard.pro +++ b/app/proguard.pro @@ -1,5 +0,0 @@ --dontwarn okhttp3.** --dontwarn okio.** - --keep class com.caverock.androidsvg.** { *; } --dontwarn com.caverock.androidsvg.** \ No newline at end of file diff --git a/app/src/main/java/ru/noties/markwon/AppModule.java b/app/src/main/java/ru/noties/markwon/AppModule.java index 09c5e230..70c40707 100644 --- a/app/src/main/java/ru/noties/markwon/AppModule.java +++ b/app/src/main/java/ru/noties/markwon/AppModule.java @@ -16,6 +16,9 @@ import okhttp3.Cache; import okhttp3.OkHttpClient; import ru.noties.markwon.il.AsyncDrawableLoader; 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 class AppModule { diff --git a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java index f276dfe2..119ba82c 100644 --- a/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java +++ b/app/src/main/java/ru/noties/markwon/MarkdownRenderer.java @@ -1,11 +1,15 @@ package ru.noties.markwon; import android.content.Context; +import android.graphics.Color; +import android.graphics.Typeface; import android.net.Uri; import android.os.Handler; import android.os.SystemClock; +import android.support.annotation.FontRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.content.res.ResourcesCompat; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -14,13 +18,14 @@ import javax.inject.Inject; import ru.noties.debug.Debug; 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 public class MarkdownRenderer { - interface MarkdownReadyListener { - void onMarkdownReady(CharSequence markdown); - } + private final Context context; @Inject AsyncDrawable.Loader loader; @@ -34,14 +39,15 @@ public class MarkdownRenderer { private Future task; @Inject - MarkdownRenderer() { + MarkdownRenderer(Context context) { + this.context = context; } public void render( - @NonNull final Context context, - @Nullable final Uri uri, - @NonNull final String markdown, - @NonNull final MarkdownReadyListener listener) { + @NonNull final Context context, + @Nullable final Uri uri, + @NonNull final String markdown, + @NonNull final MarkdownReadyListener listener) { cancel(); task = service.submit(new Runnable() { @Override @@ -55,9 +61,10 @@ public class MarkdownRenderer { } final SpannableConfiguration configuration = SpannableConfiguration.builder(context) - .asyncDrawableLoader(loader) - .urlProcessor(urlProcessor) - .build(); + .theme(getSpannableTheme()) + .asyncDrawableLoader(loader) + .urlProcessor(urlProcessor) + .build(); final long start = SystemClock.uptimeMillis(); @@ -92,4 +99,25 @@ public class MarkdownRenderer { private boolean 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); + } } diff --git a/app/src/main/res/font/app_font.xml b/app/src/main/res/font/app_font.xml new file mode 100644 index 00000000..ddf98b6b --- /dev/null +++ b/app/src/main/res/font/app_font.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/opensans_regular.ttf b/app/src/main/res/font/opensans_regular.ttf new file mode 100755 index 00000000..db433349 Binary files /dev/null and b/app/src/main/res/font/opensans_regular.ttf differ diff --git a/app/src/main/res/font/opensans_semibold.ttf b/app/src/main/res/font/opensans_semibold.ttf new file mode 100644 index 00000000..1a7679e3 Binary files /dev/null and b/app/src/main/res/font/opensans_semibold.ttf differ diff --git a/artifactory-mvn-push.gradle b/artifactory-mvn-push.gradle new file mode 100644 index 00000000..934ddba3 --- /dev/null +++ b/artifactory-mvn-push.gradle @@ -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' + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 036e147b..5e37b3a8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,14 @@ buildscript { + ext.kotlin_version = '1.2.10' repositories { jcenter() google() } 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 { - // Config - BUILD_TOOLS = '26.0.3' + BUILD_TOOLS = '27.0.1' TARGET_SDK = 26 MIN_SDK = 16 // Dependencies - final def supportVersion = '26.1.0' + final def supportVersion = '27.0.2' SUPPORT_ANNOTATIONS = "com.android.support:support-annotations:$supportVersion" SUPPORT_APP_COMPAT = "com.android.support:appcompat-v7:$supportVersion" diff --git a/library-image-loader/build.gradle b/library-image-loader/build.gradle index 3a909101..a7fd1266 100644 --- a/library-image-loader/build.gradle +++ b/library-image-loader/build.gradle @@ -10,6 +10,8 @@ android { targetSdkVersion TARGET_SDK versionCode 1 versionName version + + consumerProguardFiles 'lib-proguard-rules.pro' } lintOptions { @@ -19,12 +21,10 @@ android { } dependencies { - compile project(':library') - compile ANDROID_SVG - compile ANDROID_GIF - compile OK_HTTP + implementation project(':library') + implementation ANDROID_SVG + implementation ANDROID_GIF + implementation OK_HTTP } -if (project.hasProperty('release')) { - apply from: 'https://raw.githubusercontent.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle' -} \ No newline at end of file +apply from: '../artifactory-mvn-push.gradle' \ No newline at end of file diff --git a/library-image-loader/lib-proguard-rules.pro b/library-image-loader/lib-proguard-rules.pro new file mode 100644 index 00000000..9eee5753 --- /dev/null +++ b/library-image-loader/lib-proguard-rules.pro @@ -0,0 +1,5 @@ +-dontwarn okhttp3.** +-dontwarn okio.** + +-keep class com.caverock.androidsvg.** { *; } +-dontwarn com.caverock.androidsvg.** \ No newline at end of file diff --git a/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java b/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java index a9574d13..03530899 100644 --- a/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java +++ b/library-image-loader/src/main/java/ru/noties/markwon/il/AsyncDrawableLoader.java @@ -36,6 +36,7 @@ import okhttp3.Response; import okhttp3.ResponseBody; import pl.droidsonroids.gif.GifDrawable; import ru.noties.markwon.spans.AsyncDrawable; +import ru.noties.markwon.spans.configuration.image.ImageConfig; public class AsyncDrawableLoader implements AsyncDrawable.Loader { @@ -58,6 +59,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { private final ExecutorService executorService; private final Handler mainThread; private final Drawable errorDrawable; + private final ImageConfig imageConfig; private final Map> requests; @@ -67,6 +69,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { this.executorService = builder.executorService; this.mainThread = new Handler(Looper.getMainLooper()); this.errorDrawable = builder.errorDrawable; + this.imageConfig = builder.imageConfig; this.requests = new HashMap<>(3); } @@ -146,7 +149,7 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { public void run() { final AsyncDrawable asyncDrawable = reference.get(); 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 ExecutorService executorService; private Drawable errorDrawable; + private ImageConfig imageConfig; public Builder client(@NonNull OkHttpClient client) { this.client = client; @@ -366,6 +370,11 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { return this; } + public Builder imageConfig(ImageConfig imageConfig) { + this.imageConfig = imageConfig; + return this; + } + public AsyncDrawableLoader build() { if (client == null) { client = new OkHttpClient(); @@ -377,6 +386,9 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader { // we will use executor from okHttp executorService = client.dispatcher().executorService(); } + if(imageConfig == null){ + imageConfig = new ImageConfig(); + } return new AsyncDrawableLoader(this); } } diff --git a/library/build.gradle b/library/build.gradle index c384c1b6..28d4c12d 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { @@ -11,15 +12,24 @@ android { versionCode 1 versionName version } + + libraryVariants.all { variant -> + variant.outputs.all { + outputFileName = "${POM_ARTIFACT_ID}-release.aar" + } + } } dependencies { - compile SUPPORT_ANNOTATIONS - compile COMMON_MARK - compile COMMON_MARK_STRIKETHROUGHT - compile COMMON_MARK_TABLE + api SUPPORT_ANNOTATIONS + implementation COMMON_MARK + implementation COMMON_MARK_STRIKETHROUGHT + implementation COMMON_MARK_TABLE + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } -if (project.hasProperty('release')) { - apply from: 'https://raw.githubusercontent.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle' +repositories { + mavenCentral() } + +apply from: '../artifactory-mvn-push.gradle' \ No newline at end of file diff --git a/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java b/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java index 2253a5ba..42dce54a 100644 --- a/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java +++ b/library/src/main/java/ru/noties/markwon/spans/AsyncDrawable.java @@ -12,25 +12,26 @@ import android.support.annotation.Nullable; import ru.noties.markwon.renderer.html.ImageSize; 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 interface Loader { - - void load(@NonNull String destination, @NonNull AsyncDrawable drawable); - - void cancel(@NonNull String destination); - } - private final String destination; + private final Loader loader; + private final ImageSize imageSize; + private final ImageSizeResolver imageSizeResolver; private Drawable result; + private Callback callback; private int canvasWidth; + private float textSize; public AsyncDrawable(@NonNull String destination, @NonNull Loader loader) { @@ -41,10 +42,10 @@ public class AsyncDrawable extends Drawable { * @since 1.0.1 */ public AsyncDrawable( - @NonNull String destination, - @NonNull Loader loader, - @Nullable ImageSizeResolver imageSizeResolver, - @Nullable ImageSize imageSize + @NonNull String destination, + @NonNull Loader loader, + @Nullable ImageSizeResolver imageSizeResolver, + @Nullable ImageSize imageSize ) { this.destination = destination; 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 (this.result != null) { @@ -101,7 +102,7 @@ public class AsyncDrawable extends Drawable { this.result = result; this.result.setCallback(callback); - final Rect bounds = resolveBounds(); + final Rect bounds = resolveBounds(imageConfig); result.setBounds(bounds); setBounds(bounds); @@ -168,17 +169,55 @@ public class AsyncDrawable extends Drawable { } /** + * @param imageConfig * @since 1.0.1 */ @NonNull - private Rect resolveBounds() { + private Rect resolveBounds(@NonNull ImageConfig imageConfig) { final Rect rect; if (imageSizeResolver == null - || imageSize == null) { + || imageSize == null) { rect = result.getBounds(); } else { 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); } } diff --git a/library/src/main/java/ru/noties/markwon/spans/HeadingSpan.java b/library/src/main/java/ru/noties/markwon/spans/HeadingSpan.java index 6293d554..85b339c8 100644 --- a/library/src/main/java/ru/noties/markwon/spans/HeadingSpan.java +++ b/library/src/main/java/ru/noties/markwon/spans/HeadingSpan.java @@ -10,12 +10,14 @@ import android.text.TextPaint; import android.text.style.LeadingMarginSpan; import android.text.style.MetricAffectingSpan; +import ru.noties.markwon.spans.configuration.heading.HeadingType; + 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; + @HeadingType private final int level; public HeadingSpan(@NonNull SpannableTheme theme, @IntRange(from = 1, to = 6) int level) { this.theme = theme; diff --git a/library/src/main/java/ru/noties/markwon/spans/SpannableTheme.java b/library/src/main/java/ru/noties/markwon/spans/SpannableTheme.java index c402e24f..39b85a06 100644 --- a/library/src/main/java/ru/noties/markwon/spans/SpannableTheme.java +++ b/library/src/main/java/ru/noties/markwon/spans/SpannableTheme.java @@ -9,12 +9,15 @@ import android.support.annotation.AttrRes; import android.support.annotation.ColorInt; import android.support.annotation.Dimension; import android.support.annotation.FloatRange; -import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextPaint; 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") public class SpannableTheme { @@ -78,16 +81,16 @@ public class SpannableTheme { final Dip dip = new Dip(context); return new Builder() - .linkColor(linkColor) - .codeMultilineMargin(dip.toPx(8)) - .blockMargin(dip.toPx(24)) - .blockQuoteWidth(dip.toPx(4)) - .bulletListItemStrokeWidth(dip.toPx(1)) - .headingBreakHeight(dip.toPx(1)) - .thematicBreakHeight(dip.toPx(4)) - .tableCellPadding(dip.toPx(4)) - .tableBorderWidth(dip.toPx(1)) - .taskListDrawable(new TaskListDrawable(linkColor, linkColor, backgroundColor)); + .linkColor(linkColor) + .codeMultilineMargin(dip.toPx(8)) + .blockMargin(dip.toPx(24)) + .blockQuoteWidth(dip.toPx(4)) + .bulletListItemStrokeWidth(dip.toPx(1)) + .headingConfig(new HeadingConfig(), dip.density) + .thematicBreakHeight(dip.toPx(4)) + .tableCellPadding(dip.toPx(4)) + .tableBorderWidth(dip.toPx(1)) + .taskListDrawable(new TaskListDrawable(linkColor, linkColor, backgroundColor)); } 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 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, + private static final float[] HEADING_SCALES = { + 2.F, 1.5F, 1.17F, 1.F, .83F, .67F, }; 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 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; + protected final HeadingConfig headingConfig; // by default `SCRIPT_DEF_TEXT_SIZE_RATIO` protected final float scriptTextSizeRatio; @@ -202,8 +202,7 @@ public class SpannableTheme { this.codeMultilineMargin = builder.codeMultilineMargin; this.codeTypeface = builder.codeTypeface; this.codeTextSize = builder.codeTextSize; - this.headingBreakHeight = builder.headingBreakHeight; - this.headingBreakColor = builder.headingBreakColor; + this.headingConfig = builder.headingConfig; this.scriptTextSizeRatio = builder.scriptTextSizeRatio; this.thematicBreakColor = builder.thematicBreakColor; this.thematicBreakHeight = builder.thematicBreakHeight; @@ -214,7 +213,6 @@ public class SpannableTheme { this.taskListDrawable = builder.taskListDrawable; } - public void applyLinkStyle(@NonNull Paint paint) { paint.setUnderlineText(true); if (linkColor != 0) { @@ -269,7 +267,7 @@ public class SpannableTheme { final int width; if (bulletWidth == 0 - || bulletWidth > min) { + || bulletWidth > min) { width = min; } else { width = bulletWidth; @@ -322,13 +320,51 @@ public class SpannableTheme { 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.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) { final int color; + final int headingBreakColor = headingConfig.getHeadingBreakConfig().getHeadingBreakColor(); if (headingBreakColor != 0) { color = headingBreakColor; } else { @@ -336,6 +372,7 @@ public class SpannableTheme { } paint.setColor(color); paint.setStyle(Paint.Style.FILL); + final float headingBreakHeight = headingConfig.getHeadingBreakConfig().getHeadingBreakStrokeWidth(); if (headingBreakHeight >= 0) { //noinspection SuspiciousNameCombination paint.setStrokeWidth(headingBreakHeight); @@ -441,8 +478,7 @@ public class SpannableTheme { private int codeMultilineMargin; private Typeface codeTypeface; private int codeTextSize; - private int headingBreakHeight = -1; - private int headingBreakColor; + private HeadingConfig headingConfig; private float scriptTextSizeRatio; private int thematicBreakColor; private int thematicBreakHeight = -1; @@ -468,8 +504,7 @@ public class SpannableTheme { this.codeMultilineMargin = theme.codeMultilineMargin; this.codeTypeface = theme.codeTypeface; this.codeTextSize = theme.codeTextSize; - this.headingBreakHeight = theme.headingBreakHeight; - this.headingBreakColor = theme.headingBreakColor; + this.headingConfig = theme.headingConfig; this.scriptTextSizeRatio = theme.scriptTextSizeRatio; this.thematicBreakColor = theme.thematicBreakColor; this.thematicBreakHeight = theme.thematicBreakHeight; @@ -553,14 +588,9 @@ public class SpannableTheme { } @NonNull - public Builder headingBreakHeight(@Dimension int headingBreakHeight) { - this.headingBreakHeight = headingBreakHeight; - return this; - } - - @NonNull - public Builder headingBreakColor(@ColorInt int headingBreakColor) { - this.headingBreakColor = headingBreakColor; + public Builder headingConfig(HeadingConfig headingConfig, float density) { + headingConfig.setDensityFactor(density); + this.headingConfig = headingConfig; return this; } @@ -630,7 +660,7 @@ public class SpannableTheme { private static class Dip { - private final float density; + protected final float density; Dip(@NonNull Context context) { this.density = context.getResources().getDisplayMetrics().density; diff --git a/library/src/main/java/ru/noties/markwon/spans/configuration/heading/HeadingConfig.kt b/library/src/main/java/ru/noties/markwon/spans/configuration/heading/HeadingConfig.kt new file mode 100644 index 00000000..7b58feb6 --- /dev/null +++ b/library/src/main/java/ru/noties/markwon/spans/configuration/heading/HeadingConfig.kt @@ -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 +} \ No newline at end of file diff --git a/library/src/main/java/ru/noties/markwon/spans/configuration/heading/HeadingType.java b/library/src/main/java/ru/noties/markwon/spans/configuration/heading/HeadingType.java new file mode 100644 index 00000000..69e151e1 --- /dev/null +++ b/library/src/main/java/ru/noties/markwon/spans/configuration/heading/HeadingType.java @@ -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; +} diff --git a/library/src/main/java/ru/noties/markwon/spans/configuration/image/ImageConfig.kt b/library/src/main/java/ru/noties/markwon/spans/configuration/image/ImageConfig.kt new file mode 100644 index 00000000..b46bfe41 --- /dev/null +++ b/library/src/main/java/ru/noties/markwon/spans/configuration/image/ImageConfig.kt @@ -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 +} \ No newline at end of file