diff --git a/.gitignore b/.gitignore index 8407bc1d..af8ad612 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ ############## ### Project ## -app/src/main/res/raw/changelog.md -app/src/main/res/raw/contributors.md -app/src/main/res/raw/license.md -app/src/main/res/raw/readme.md -app/src/main/res/raw/contributors.txt +app/src/main/res/raw/changelog.* +app/src/main/res/raw/contributors.* +app/src/main/res/raw/license.* +app/src/main/res/raw/readme.* +app/src/main/res/raw/contributors.* #app/src/main/res/raw/podlist.json ############## diff --git a/CHANGELOG.md b/CHANGELOG.md index ea05afe3..2d0dde83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v1.0.1 +- Update SimpleMarkdownParser + ### v1.0.0 (2017-06-14) - Added AMOLED mode - Improve NavDrawer diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 00000000..c81b59eb --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,32 @@ + +* **[Gregor Santner](https://gsantner.github.io)**
~° Current developer of dandelion +* **[Paul Schaub](https://github.com/vanitasvitae)**
~° Development of dandelion +* **[Abhijith Balan](abhijithb21 AT openmailbox DOT org)**
~° Malayalam translation +* **[Airon90](https://diasp.eu/u/airon90)**
~° Italian translation +* **[Gaukler Faun](https://github.com/scoute-dich)**
~° Diaspora Native WebApp additions +* **[Martín Vukovic](martinvukovic AT protonmail DOT com)**
~° Diaspora Native WebApp +* **[Nacho Fernández](nacho_f AT joindiaspora DOT com)**
~° Spanish translation +* **[Naofumi Fukue](https://github.com/naofum)**
~° Japanese translation +* **[pskosinski](email AT pskosinski DOT pl)**
~° Polish translation +* **[SansPseudoFix](https://github.com/SansPseudoFix)**
~° French translation +* **[secitem](secitem AT tuta DOT io)**
~° Czech translation +* **[Zsolt Szakács](maxigaz AT diaspora DOT zone)**
~° Hungarian translation +* **[Danilo Raffaelli](https://crowdin.com/profile/Daraf)**
~° Italian translation +* **[O'Loubám](loubam AT diasp DOT org)**
~° Galician translation +* **[transifex3](https://crowdin.com/profile/transifex3)**
~° Korean translation +* **[Âng Iōngchun](https://pubpod.alqualonde.org/u/iongchun)**
~° Chinese traditional translation diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt deleted file mode 100644 index 7905a35a..00000000 --- a/CONTRIBUTORS.txt +++ /dev/null @@ -1,24 +0,0 @@ -00l>> This file contains references to people who contributed to the app. -01l>> If you helped by translating the app, please send a message on Crowdin. -02l>> You can also send a mail to [gdev AT live DOT de](https://gsantner.github.io/about/email/) to get included. -03l>> -04l>> Schemes: -05l>> Firstname Lastname (Link): Text -06l>> Firstname Lastname (E-Mail): Text -07l>> Username (Link): Text -08l>> Username (E-Mail): Text -## 99l CONTRIBUTORS -Abhijith Balan (abhijithb21 AT openmailbox DOT org): Malayalam translation -Airon90 (https://diasp.eu/u/airon90): Italian translation -Gaukler Faun (https://github.com/scoute-dich): Diaspora Native WebApp additions -Martín Vukovic (martinvukovic AT protonmail DOT com): Diaspora Native WebApp -Nacho Fernández (nacho_f AT joindiaspora DOT com): Spanish translation -Naofumi Fukue (https://github.com/naofum): Japanese translation -pskosinski (email AT pskosinski DOT pl): Polish translation -SansPseudoFix (https://github.com/SansPseudoFix): French translation -secitem (secitem AT tuta DOT io): Czech translation -Zsolt Szakács (maxigaz AT diaspora DOT zone): Hungarian translation -Danilo Raffaelli (https://crowdin.com/profile/Daraf): Italian translation -O'Loubám (loubam AT diasp DOT org): Galician translation -transifex3 (https://crowdin.com/profile/transifex3): Korean translation -Âng Iōngchun (https://pubpod.alqualonde.org/u/iongchun): Chinese traditional translation diff --git a/app/build.gradle b/app/build.gradle index 2fafc546..e961ecab 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { minSdkVersion 17 targetSdkVersion 24 - versionCode 20 - versionName "1.0.0" + versionCode 21 + versionName "1.0.1-dev" applicationId "com.github.dfa.diaspora_android" resValue 'string', 'app_name', "dandelion*" @@ -86,7 +86,7 @@ dependencies { // Groovy Coding Area // ##################### final String RAW_DIR = "app/src/main/res/raw" -final String[] ROOT_TO_RAW_COPYFILES = ["README.md", "LICENSE.md", "CHANGELOG.md", "CONTRIBUTORS.txt"] +final String[] ROOT_TO_RAW_COPYFILES = ["README.md", "LICENSE.md", "CHANGELOG.md", "CONTRIBUTORS.md"] // Called before building task copyRepoFiles(type: Copy) { @@ -97,16 +97,5 @@ task copyRepoFiles(type: Copy) { rename { String fileName -> fileName.replace(fileName, fileName.toLowerCase()) } - - // Filter Contributors file - from(rootProject.file("CONTRIBUTORS.txt")) { - into '.' // Target already changed to 'src/main/res/raw' - rename { String fileName -> - fileName.replace(fileName, fileName.toLowerCase()) - } - filter { line -> - (line.toString().matches("..l>>.*") || line.toString().startsWith("## 99l CONTRIBUTORS")) ? null : line.toString().trim().replaceAll(" \\(.*\\)", "") - } - } } tasks.copyRepoFiles.execute() diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java index e46c73e6..9900d259 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/AboutActivity.java @@ -268,7 +268,7 @@ public class AboutActivity extends ThemedActivity maintainers.setTextFormatted(getString(R.string.fragment_license__maintainers_text, Helpers.get().loadMarkdownForTextViewFromRaw(R.raw.maintainers, ""))); contributors.setTextFormatted(getString(R.string.fragment_license__contributors_thank_you, - Helpers.get().loadMarkdownForTextViewFromRaw(R.raw.contributors, "* "))); + Helpers.get().loadMarkdownForTextViewFromRaw(R.raw.contributors, ""))); thirdPartyLibs.setTextFormatted( Helpers.get().loadMarkdownForTextViewFromRaw(R.raw.license_third_party, "")); return rootView; diff --git a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java index 7dbd14d9..55801b63 100644 --- a/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java +++ b/app/src/main/java/com/github/dfa/diaspora_android/activity/MainActivity.java @@ -221,22 +221,20 @@ public class MainActivity extends ThemedActivity // Show first start dialog try { + SimpleMarkdownParser mdParser = SimpleMarkdownParser.get().setDefaultSmpFilter(SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW); if (appSettings.isAppFirstStart()) { - SimpleMarkdownParser smp = new SimpleMarkdownParser().parse( - getResources().openRawResource(R.raw.license), - SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW, ""); - String html = smp.getHtml() + mdParser.parse( + getResources().openRawResource(R.raw.license), ""); + String html = mdParser.getHtml() + "


" + "

" + getString(R.string.fragment_license__thirdparty_libs) + "

" - + smp.parse(getResources().openRawResource(R.raw.license_third_party), - SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW, ""); - html = smp.setHtml(html).removeMultiNewlines().getHtml(); + + mdParser.parse(getResources().openRawResource(R.raw.license_third_party), ""); + html = mdParser.setHtml(html).removeMultiNewlines().getHtml(); HelpersA.get(this).showDialogWithHtmlTextView(R.string.about_activity__title_about_license, html); appSettings.isAppCurrentVersionFirstStart(); } else if (appSettings.isAppCurrentVersionFirstStart()) { SimpleMarkdownParser smp = new SimpleMarkdownParser().parse( - getResources().openRawResource(R.raw.changelog), - SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW, ""); + getResources().openRawResource(R.raw.changelog), ""); HelpersA.get(this).showDialogWithHtmlTextView(R.string.changelog, smp.getHtml()); } } catch (IOException e) { diff --git a/app/src/main/java/io/github/gsantner/opoc/util/Helpers.java b/app/src/main/java/io/github/gsantner/opoc/util/Helpers.java index 21c12492..db593f95 100644 --- a/app/src/main/java/io/github/gsantner/opoc/util/Helpers.java +++ b/app/src/main/java/io/github/gsantner/opoc/util/Helpers.java @@ -5,7 +5,7 @@ * worth it, you can buy me a coke in return. Provided as is without any kind * of warranty. No attribution required. - Gregor Santner * - * License: Creative Commons Zero (CC0 1.0) + * License of this file: Creative Commons Zero (CC0 1.0) * http://creativecommons.org/publicdomain/zero/1.0/ * ---------------------------------------------------------------------------- */ @@ -32,19 +32,25 @@ import android.support.annotation.StringRes; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatButton; +import android.text.Html; +import android.text.SpannableString; +import android.text.Spanned; import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.text.util.Linkify; import android.util.DisplayMetrics; import android.webkit.WebView; - -import com.github.dfa.diaspora_android.App; -import com.github.dfa.diaspora_android.BuildConfig; -import com.github.dfa.diaspora_android.R; +import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Locale; +import com.github.dfa.diaspora_android.App; +import com.github.dfa.diaspora_android.BuildConfig; +import com.github.dfa.diaspora_android.R; + @SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue"}) public class Helpers { protected Context context; @@ -177,7 +183,7 @@ public class Helpers { try { return new SimpleMarkdownParser() .parse(context.getResources().openRawResource(rawMdFile), - SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW, prepend) + prepend, SimpleMarkdownParser.FILTER_ANDROID_TEXTVIEW) .replaceColor("#000001", color(R.color.accent)) .removeMultiNewlines().replaceBulletCharacter("*").getHtml(); } catch (IOException e) { @@ -186,6 +192,17 @@ public class Helpers { } } + public void setHtmlToTextView(TextView textView, String html) { + Spanned spanned; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + spanned = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); + } else { + spanned = Html.fromHtml(html); + } + textView.setMovementMethod(LinkMovementMethod.getInstance()); + textView.setText(new SpannableString(spanned)); + } + public double getEstimatedScreenSizeInches() { DisplayMetrics dm = context.getResources().getDisplayMetrics(); diff --git a/app/src/main/java/io/github/gsantner/opoc/util/HelpersA.java b/app/src/main/java/io/github/gsantner/opoc/util/HelpersA.java index 5989138f..ff747a84 100644 --- a/app/src/main/java/io/github/gsantner/opoc/util/HelpersA.java +++ b/app/src/main/java/io/github/gsantner/opoc/util/HelpersA.java @@ -5,7 +5,7 @@ * worth it, you can buy me a coke in return. Provided as is without any kind * of warranty. No attribution required. - Gregor Santner * - * License: Creative Commons Zero (CC0 1.0) + * License of this file: Creative Commons Zero (CC0 1.0) * http://creativecommons.org/publicdomain/zero/1.0/ * ---------------------------------------------------------------------------- */ @@ -20,7 +20,7 @@ import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatTextView; import android.text.Html; import android.text.SpannableString; -import android.text.method.ScrollingMovementMethod; +import android.text.method.LinkMovementMethod; import android.util.TypedValue; import android.view.inputmethod.InputMethodManager; @@ -91,17 +91,17 @@ public class HelpersA extends Helpers { } public void showDialogWithHtmlTextView(@StringRes int resTitleId, String html) { - showDialogWithHtmlTextView(resTitleId, html, null); + showDialogWithHtmlTextView(resTitleId, html, true, null); } - public void showDialogWithHtmlTextView(@StringRes int resTitleId, String html, DialogInterface.OnDismissListener dismissedListener) { + public void showDialogWithHtmlTextView(@StringRes int resTitleId, String text, boolean isHtml, DialogInterface.OnDismissListener dismissedListener) { AppCompatTextView textView = new AppCompatTextView(context); int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, context.getResources().getDisplayMetrics()); - textView.setMovementMethod(new ScrollingMovementMethod()); + textView.setMovementMethod(new LinkMovementMethod()); textView.setPadding(padding, 0, padding, 0); - textView.setText(new SpannableString(Html.fromHtml(html))); + textView.setText(isHtml ? new SpannableString(Html.fromHtml(text)) : text); AlertDialog.Builder dialog = new AlertDialog.Builder(context) .setPositiveButton(android.R.string.ok, null) .setOnDismissListener(dismissedListener) diff --git a/app/src/main/java/io/github/gsantner/opoc/util/SimpleMarkdownParser.java b/app/src/main/java/io/github/gsantner/opoc/util/SimpleMarkdownParser.java index 0cdebcbb..17ed4ea7 100644 --- a/app/src/main/java/io/github/gsantner/opoc/util/SimpleMarkdownParser.java +++ b/app/src/main/java/io/github/gsantner/opoc/util/SimpleMarkdownParser.java @@ -5,7 +5,7 @@ * worth it, you can buy me a coke in return. Provided as is without any kind * of warranty. No attribution required. - Gregor Santner * - * License: Creative Commons Zero (CC0 1.0) + * License of this file: Creative Commons Zero (CC0 1.0) * http://creativecommons.org/publicdomain/zero/1.0/ * ---------------------------------------------------------------------------- */ @@ -22,9 +22,9 @@ * * FILTER_ANDROID_TEXTVIEW output is intended to be used at simple Android TextViews, * were a limited set of html tags is supported. This allow to still display e.g. a simple - * CHANGELOG.md file without inlcuding a WebView for showing HTML, or other additional UI-libraries. + * CHANGELOG.md file without including a WebView for showing HTML, or other additional UI-libraries. * - * FILTER_HTMLPART is intended to be used at engines understanding most common HTML tags. + * FILTER_WEB is intended to be used at engines understanding most common HTML tags. */ package io.github.gsantner.opoc.util; @@ -38,72 +38,105 @@ import java.io.InputStreamReader; /** * Simple Markdown Parser */ -@SuppressWarnings({"WeakerAccess", "CaughtExceptionImmediatelyRethrown"}) +@SuppressWarnings({"WeakerAccess", "CaughtExceptionImmediatelyRethrown", "SameParameterValue", "unused", "SpellCheckingInspection", "RepeatedSpace", "SingleCharAlternation"}) public class SimpleMarkdownParser { - public interface SimpleLineFilter { - String filterLine(String line); + private static SimpleMarkdownParser instance; + + public static SimpleMarkdownParser get() { + if (instance == null) { + instance = new SimpleMarkdownParser(); + } + return instance; } - public static final SimpleLineFilter FILTER_ANDROID_TEXTVIEW = new SimpleLineFilter() { + public interface SmpFilter { + String filter(String text); + } + + public final static SmpFilter FILTER_ANDROID_TEXTVIEW = new SmpFilter() { @Override - public String filterLine(String line) { + public String filter(String text) { // TextView supports a limited set of html tags, most notably // a href, b, big, font size&color, i, li, small, u - line = line + + // Don't start new line if 2 empty lines and heading + while (text.contains("\n\n#")) { + text = text.replace("\n\n#", "\n#"); + } + + return text + .replaceAll("(?s)", "") // HTML comments + .replace("\n\n", "\n
\n") // Start new line if 2 empty lines .replace("~°", "  ") // double space/half tab - .replaceAll("^### ([^<]*)", "
$1 ") // h3 - .replaceAll("^## ([^<]*)", "
$1
") // h2 (DEP: h3) - .replaceAll("^# ([^<]*)", "
$1
") // h1 (DEP: h2,h3) + .replaceAll("(?m)^### (.*)$", "
$1
") // h3 + .replaceAll("(?m)^## (.*)$", "
$1

") // h2 (DEP: h3) + .replaceAll("(?m)^# (.*)$", "
$1

") // h1 (DEP: h2,h3) .replaceAll("!\\[(.*?)\\]\\((.*?)\\)", "$1") // img .replaceAll("\\[(.*?)\\]\\((.*?)\\)", "$1") // a href (DEP: img) .replaceAll("<(http|https):\\/\\/(.*)>", "$1://$2") // a href (DEP: img) - .replaceAll("^(-|\\*) ([^<]*)", " $2 ") // unordered list + end line - .replaceAll("^ (-|\\*) ([^<]*)", "   $2 ") // unordered list2 + end line + .replaceAll("(?m)^([-*] )(.*)$", " $2
") // unordered list + end line + .replaceAll("(?m)^ (-|\\*) ([^<]*)$", "   $2
") // unordered list2 + end line .replaceAll("`([^<]*)`", "$1") // code .replace("\\*", "●") // temporary replace escaped star symbol - .replaceAll("\\*\\*([^<]*)\\*\\*", "$1") // bold (DEP: temp star) - .replaceAll("\\*([^<]*)\\*", "$1") // italic (DEP: temp star code) + .replaceAll("(?m)\\*\\*(.*)\\*\\*", "$1") // bold (DEP: temp star) + .replaceAll("(?m)\\*(.*)\\*", "$1") // italic (DEP: temp star code) .replace("●", "*") // restore escaped star symbol (DEP: b,i) - .replaceAll(" $", "
") // new line (DEP: ul) - ; - return line.isEmpty() ? line + "
" : line; + .replaceAll("(?m) $", "
") // new line (DEP: ul) + ; } }; - public static final SimpleLineFilter FILTER_HTMLPART = new SimpleLineFilter() { + public final static SmpFilter FILTER_WEB = new SmpFilter() { @Override - public String filterLine(String line) { - line = line + public String filter(String text) { + // Don't start new line if 2 empty lines and heading + while (text.contains("\n\n#")) { + text = text.replace("\n\n#", "\n#"); + } + + text = text + .replaceAll("(?s)", "") // HTML comments + .replace("\n\n", "\n
\n") // Start new line if 2 empty lines .replaceAll("~°", "  ") // double space/half tab - .replaceAll("^### ([^<]*)", "

$1

") // h3 - .replaceAll("^## ([^<]*)", "

$1

") /// h2 (DEP: h3) - .replaceAll("^# ([^<]*)", "

$1

") // h1 (DEP: h2,h3) + .replaceAll("(?m)^### (.*)$", "

$1

") // h3 + .replaceAll("(?m)^## (.*)$", "

$1

") /// h2 (DEP: h3) + .replaceAll("(?m)^# (.*)$", "

$1

") // h1 (DEP: h2,h3) .replaceAll("!\\[(.*?)\\]\\((.*?)\\)", "$1") // img .replaceAll("<(http|https):\\/\\/(.*)>", "$1://$2") // a href (DEP: img) .replaceAll("\\[(.*?)\\]\\((.*?)\\)", "$1") // a href (DEP: img) - .replaceAll("^(-|\\*) ([^<]*)", " $2 ") // unordered list + end line - .replaceAll("^ (-|\\*) ([^<]*)", "   $2 ") // unordered list2 + end line + .replaceAll("(?m)^([-*] )(.*)$", " $2 ") // unordered list + end line + .replaceAll("(?m)^ (-|\\*) ([^<]*)$", "   $2 ") // unordered list2 + end line .replaceAll("`([^<]*)`", "$1") // code .replace("\\*", "●") // temporary replace escaped star symbol - .replaceAll("\\*\\*([^<]*)\\*\\*", "$1") // bold (DEP: temp star) - .replaceAll("\\*([^<]*)\\*", "$1") // italic (DEP: temp star) + .replaceAll("(?m)\\*\\*(.*)\\*\\*", "$1") // bold (DEP: temp star) + .replaceAll("(?m)\\*(.*)\\*", "$1") // italic (DEP: temp star code) .replace("●", "*") // restore escaped star symbol (DEP: b,i) - .replaceAll(" $", "
") // new line (DEP: ul) + .replaceAll("(?m) $", "
") // new line (DEP: ul) ; - return line.isEmpty() ? line + "
" : line; + return text; } }; //######################## //## Members //######################## + private SmpFilter defaultSmpFilter; private String html; - public SimpleMarkdownParser parse(String filepath, SimpleLineFilter simpleLineFilter) throws IOException { - return parse(new FileInputStream(filepath), simpleLineFilter, ""); + public SimpleMarkdownParser() { + setDefaultSmpFilter(FILTER_WEB); } - public SimpleMarkdownParser parse(InputStream inputStream, SimpleLineFilter simpleLineFilter, String lineMdPrefix) throws IOException { + public SimpleMarkdownParser setDefaultSmpFilter(SmpFilter defaultSmpFilter) { + this.defaultSmpFilter = defaultSmpFilter; + return this; + } + + public SimpleMarkdownParser parse(String filepath, SmpFilter... smpFilters) throws IOException { + return parse(new FileInputStream(filepath), "", smpFilters); + } + + public SimpleMarkdownParser parse(InputStream inputStream, String lineMdPrefix, SmpFilter... smpFilters) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader br = null; String line; @@ -111,7 +144,8 @@ public class SimpleMarkdownParser { try { br = new BufferedReader(new InputStreamReader(inputStream)); while ((line = br.readLine()) != null) { - sb.append(simpleLineFilter.filterLine(lineMdPrefix + line)); + sb.append(lineMdPrefix); + sb.append(line); sb.append("\n"); } } catch (IOException rethrow) { @@ -125,7 +159,18 @@ public class SimpleMarkdownParser { } } } - html = sb.toString().trim(); + html = parse(sb.toString(), "", smpFilters).getHtml(); + return this; + } + + public SimpleMarkdownParser parse(String markdown, String lineMdPrefix, SmpFilter... smpFilters) throws IOException { + html = markdown; + if (smpFilters.length == 0) { + smpFilters = new SmpFilter[]{defaultSmpFilter}; + } + for (SmpFilter smpFilter : smpFilters) { + html = smpFilter.filter(html).trim(); + } return this; }