diff --git a/build.gradle b/build.gradle index f06fea38..d45c2466 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ ext { // Dependencies final def supportVersion = '25.3.1' SUPPORT_ANNOTATIONS = "com.android.support:support-annotations:$supportVersion" + SUPPORT_APP_COMPAT = "com.android.support:appcompat-v7:$supportVersion" final def commonMarkVersion = '0.9.0' COMMON_MARK = "com.atlassian.commonmark:commonmark:$commonMarkVersion" diff --git a/library-view/build.gradle b/library-view/build.gradle new file mode 100644 index 00000000..c0675588 --- /dev/null +++ b/library-view/build.gradle @@ -0,0 +1,23 @@ +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 project(':library') + provided SUPPORT_APP_COMPAT +} + +if (project.hasProperty('release')) { + apply from: 'https://raw.githubusercontent.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle' +} diff --git a/library-view/src/debug/java/ru/noties/markwon/view/debug/DebugConfigurationProvider.java b/library-view/src/debug/java/ru/noties/markwon/view/debug/DebugConfigurationProvider.java new file mode 100644 index 00000000..e530bab5 --- /dev/null +++ b/library-view/src/debug/java/ru/noties/markwon/view/debug/DebugConfigurationProvider.java @@ -0,0 +1,31 @@ +package ru.noties.markwon.view.debug; + +import android.content.Context; +import android.support.annotation.NonNull; + +import ru.noties.markwon.SpannableConfiguration; +import ru.noties.markwon.spans.SpannableTheme; +import ru.noties.markwon.view.IMarkwonView; + +public class DebugConfigurationProvider implements IMarkwonView.ConfigurationProvider { + + private SpannableConfiguration cached; + + @NonNull + @Override + public SpannableConfiguration provide(@NonNull Context context) { + if (cached == null) { + cached = SpannableConfiguration.builder(context) + .theme(debugTheme(context)) + .build(); + } + return cached; + } + + private static SpannableTheme debugTheme(@NonNull Context context) { + return SpannableTheme.builderWithDefaults(context) + .blockQuoteColor(0xFFff0000) + .codeBackgroundColor(0x40FF0000) + .build(); + } +} diff --git a/library-view/src/debug/res/layout/debug_markwon_preview.xml b/library-view/src/debug/res/layout/debug_markwon_preview.xml new file mode 100644 index 00000000..ecc7bd3d --- /dev/null +++ b/library-view/src/debug/res/layout/debug_markwon_preview.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/library-view/src/debug/res/layout/debug_markwon_preview_compat.xml b/library-view/src/debug/res/layout/debug_markwon_preview_compat.xml new file mode 100644 index 00000000..092dbf00 --- /dev/null +++ b/library-view/src/debug/res/layout/debug_markwon_preview_compat.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/library-view/src/debug/res/values/debug_strings.xml b/library-view/src/debug/res/values/debug_strings.xml new file mode 100644 index 00000000..e7b87ab2 --- /dev/null +++ b/library-view/src/debug/res/values/debug_strings.xml @@ -0,0 +1,39 @@ + + + + + Quote + \n + >> Quote #2 + \n + >>> Quote `#3` + \n + --- + \n + ``` + \n + // this is some amazing code block + \n + ``` + \n + * First + \n + * Second + \n + * * Second-First + \n + * * Second-Second + \n + * * * Second-Second-First + \n + * And out of blue - Third + ]]> + + + \ No newline at end of file diff --git a/library-view/src/main/AndroidManifest.xml b/library-view/src/main/AndroidManifest.xml new file mode 100644 index 00000000..6340e29d --- /dev/null +++ b/library-view/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/library-view/src/main/java/ru/noties/markwon/view/IMarkwonView.java b/library-view/src/main/java/ru/noties/markwon/view/IMarkwonView.java new file mode 100644 index 00000000..4db6d2c7 --- /dev/null +++ b/library-view/src/main/java/ru/noties/markwon/view/IMarkwonView.java @@ -0,0 +1,23 @@ +package ru.noties.markwon.view; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import ru.noties.markwon.SpannableConfiguration; + +public interface IMarkwonView { + + interface ConfigurationProvider { + @NonNull + SpannableConfiguration provide(@NonNull Context context); + } + + void setConfigurationProvider(@NonNull ConfigurationProvider provider); + + void setMarkdown(@Nullable String markdown); + void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown); + + @Nullable + String getMarkdown(); +} diff --git a/library-view/src/main/java/ru/noties/markwon/view/MarkwonView.java b/library-view/src/main/java/ru/noties/markwon/view/MarkwonView.java new file mode 100644 index 00000000..19ab09b0 --- /dev/null +++ b/library-view/src/main/java/ru/noties/markwon/view/MarkwonView.java @@ -0,0 +1,50 @@ +package ru.noties.markwon.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.widget.TextView; + +import ru.noties.markwon.SpannableConfiguration; + +@SuppressLint("AppCompatCustomView") +public class MarkwonView extends TextView implements IMarkwonView { + + private MarkwonViewHelper helper; + + public MarkwonView(Context context) { + super(context); + init(context, null); + } + + public MarkwonView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + private void init(Context context, AttributeSet attributeSet) { + helper = MarkwonViewHelper.create(this); + helper.init(context, attributeSet); + } + + @Override + public void setConfigurationProvider(@NonNull ConfigurationProvider provider) { + helper.setConfigurationProvider(provider); + } + + public void setMarkdown(@Nullable String markdown) { + helper.setMarkdown(markdown); + } + + public void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown) { + helper.setMarkdown(configuration, markdown); + } + + @Nullable + @Override + public String getMarkdown() { + return helper.getMarkdown(); + } +} diff --git a/library-view/src/main/java/ru/noties/markwon/view/MarkwonViewCompat.java b/library-view/src/main/java/ru/noties/markwon/view/MarkwonViewCompat.java new file mode 100644 index 00000000..da5c1934 --- /dev/null +++ b/library-view/src/main/java/ru/noties/markwon/view/MarkwonViewCompat.java @@ -0,0 +1,50 @@ +package ru.noties.markwon.view; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.AppCompatTextView; +import android.util.AttributeSet; + +import ru.noties.markwon.SpannableConfiguration; + +public class MarkwonViewCompat extends AppCompatTextView implements IMarkwonView { + + private MarkwonViewHelper helper; + + public MarkwonViewCompat(Context context) { + super(context); + init(context, null); + } + + public MarkwonViewCompat(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + private void init(Context context, AttributeSet attributeSet) { + helper = MarkwonViewHelper.create(this); + helper.init(context, attributeSet); + } + + @Override + public void setConfigurationProvider(@NonNull ConfigurationProvider provider) { + helper.setConfigurationProvider(provider); + } + + @Override + public void setMarkdown(@Nullable String markdown) { + helper.setMarkdown(markdown); + } + + @Override + public void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown) { + helper.setMarkdown(configuration, markdown); + } + + @Nullable + @Override + public String getMarkdown() { + return helper.getMarkdown(); + } +} diff --git a/library-view/src/main/java/ru/noties/markwon/view/MarkwonViewHelper.java b/library-view/src/main/java/ru/noties/markwon/view/MarkwonViewHelper.java new file mode 100644 index 00000000..c8c813f6 --- /dev/null +++ b/library-view/src/main/java/ru/noties/markwon/view/MarkwonViewHelper.java @@ -0,0 +1,105 @@ +package ru.noties.markwon.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.TextView; + +import ru.noties.markwon.Markwon; +import ru.noties.markwon.SpannableConfiguration; + +public class MarkwonViewHelper implements IMarkwonView { + + public static MarkwonViewHelper create(@NonNull V view) { + return new MarkwonViewHelper(view); + } + + private final TextView textView; + + private ConfigurationProvider provider; + + private SpannableConfiguration configuration; + private String markdown; + + private MarkwonViewHelper(@NonNull TextView textView) { + this.textView = textView; + } + + public void init(Context context, AttributeSet attributeSet) { + + if (attributeSet != null) { + final TypedArray array = context.obtainStyledAttributes(attributeSet, R.styleable.MarkwonView); + try { + + final String configurationProvider = array.getString(R.styleable.MarkwonView_mv_configurationProvider); + final ConfigurationProvider provider; + if (!TextUtils.isEmpty(configurationProvider)) { + provider = MarkwonViewHelper.obtainProvider(configurationProvider); + } else { + provider = null; + } + if (provider != null) { + setConfigurationProvider(provider); + } + + final String markdown = array.getString(R.styleable.MarkwonView_mv_markdown); + if (!TextUtils.isEmpty(markdown)) { + setMarkdown(markdown); + } + } finally { + array.recycle(); + } + } + } + + @Override + public void setConfigurationProvider(@NonNull ConfigurationProvider provider) { + this.provider = provider; + this.configuration = provider.provide(textView.getContext()); + if (!TextUtils.isEmpty(markdown)) { + // invalidate rendered markdown + setMarkdown(markdown); + } + } + + @Override + public void setMarkdown(@Nullable String markdown) { + setMarkdown(null, markdown); + } + + @Override + public void setMarkdown(@Nullable SpannableConfiguration configuration, @Nullable String markdown) { + this.markdown = markdown; + if (configuration == null) { + if (this.configuration == null) { + if (provider != null) { + this.configuration = provider.provide(textView.getContext()); + } else { + this.configuration = SpannableConfiguration.create(textView.getContext()); + } + } + configuration = this.configuration; + } + Markwon.setMarkdown(textView, configuration, markdown); + } + + @Nullable + @Override + public String getMarkdown() { + return markdown; + } + + @Nullable + public static IMarkwonView.ConfigurationProvider obtainProvider(@NonNull String className) { + try { + final Class cl = Class.forName(className); + return (IMarkwonView.ConfigurationProvider) cl.newInstance(); + } catch (Throwable t) { + t.printStackTrace(); + return null; + } + } +} diff --git a/library-view/src/main/res/values/attrs.xml b/library-view/src/main/res/values/attrs.xml new file mode 100644 index 00000000..33a532f3 --- /dev/null +++ b/library-view/src/main/res/values/attrs.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index f47f4744..59f2e834 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':library', ':library-image-loader' +include ':app', ':library', ':library-image-loader', ':library-view'