react-native-enriched 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +875 -0
  3. package/ReactNativeEnriched.podspec +27 -0
  4. package/android/build.gradle +101 -0
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +146 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +55 -0
  7. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ComponentDescriptors.cpp +22 -0
  8. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ComponentDescriptors.h +24 -0
  9. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.cpp +118 -0
  10. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/EventEmitters.h +95 -0
  11. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +128 -0
  12. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +577 -0
  13. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ShadowNodes.cpp +17 -0
  14. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/ShadowNodes.h +23 -0
  15. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/States.cpp +16 -0
  16. package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/States.h +20 -0
  17. package/android/gradle.properties +5 -0
  18. package/android/src/main/AndroidManifest.xml +3 -0
  19. package/android/src/main/AndroidManifestNew.xml +2 -0
  20. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +535 -0
  21. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +64 -0
  22. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +292 -0
  23. package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +19 -0
  24. package/android/src/main/java/com/swmansion/enriched/events/MentionHandler.kt +40 -0
  25. package/android/src/main/java/com/swmansion/enriched/events/OnChangeHtmlEvent.kt +28 -0
  26. package/android/src/main/java/com/swmansion/enriched/events/OnChangeSelectionEvent.kt +29 -0
  27. package/android/src/main/java/com/swmansion/enriched/events/OnChangeStateEvent.kt +24 -0
  28. package/android/src/main/java/com/swmansion/enriched/events/OnChangeTextEvent.kt +30 -0
  29. package/android/src/main/java/com/swmansion/enriched/events/OnInputBlurEvent.kt +27 -0
  30. package/android/src/main/java/com/swmansion/enriched/events/OnInputFocusEvent.kt +27 -0
  31. package/android/src/main/java/com/swmansion/enriched/events/OnLinkDetectedEvent.kt +30 -0
  32. package/android/src/main/java/com/swmansion/enriched/events/OnMentionDetectedEvent.kt +29 -0
  33. package/android/src/main/java/com/swmansion/enriched/events/OnMentionEvent.kt +33 -0
  34. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBlockQuoteSpan.kt +34 -0
  35. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedBoldSpan.kt +10 -0
  36. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +38 -0
  37. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH1Span.kt +17 -0
  38. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH2Span.kt +17 -0
  39. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedH3Span.kt +17 -0
  40. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +41 -0
  41. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedInlineCodeSpan.kt +16 -0
  42. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedItalicSpan.kt +10 -0
  43. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedLinkSpan.kt +24 -0
  44. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedMentionSpan.kt +36 -0
  45. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedOrderedListSpan.kt +71 -0
  46. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +111 -0
  47. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedStrikeThroughSpan.kt +9 -0
  48. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnderlineSpan.kt +9 -0
  49. package/android/src/main/java/com/swmansion/enriched/spans/EnrichedUnorderedListSpan.kt +49 -0
  50. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedBlockSpan.kt +4 -0
  51. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedHeadingSpan.kt +4 -0
  52. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedInlineSpan.kt +4 -0
  53. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedParagraphSpan.kt +4 -0
  54. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedSpan.kt +4 -0
  55. package/android/src/main/java/com/swmansion/enriched/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +5 -0
  56. package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +227 -0
  57. package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +146 -0
  58. package/android/src/main/java/com/swmansion/enriched/styles/ListStyles.kt +173 -0
  59. package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +186 -0
  60. package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +223 -0
  61. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +857 -0
  62. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSelection.kt +285 -0
  63. package/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +204 -0
  64. package/android/src/main/java/com/swmansion/enriched/utils/Utils.kt +91 -0
  65. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +73 -0
  66. package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +51 -0
  67. package/android/src/main/new_arch/CMakeLists.txt +56 -0
  68. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.cpp +22 -0
  69. package/android/src/main/new_arch/RNEnrichedTextInputViewSpec.h +26 -0
  70. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputComponentDescriptor.h +35 -0
  71. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +51 -0
  72. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +26 -0
  73. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +34 -0
  74. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.h +54 -0
  75. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.cpp +9 -0
  76. package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputState.h +25 -0
  77. package/ios/EnrichedTextInputView.h +33 -0
  78. package/ios/EnrichedTextInputView.mm +1190 -0
  79. package/ios/EnrichedTextInputViewManager.mm +13 -0
  80. package/ios/config/InputConfig.h +67 -0
  81. package/ios/config/InputConfig.mm +382 -0
  82. package/ios/generated/RNEnrichedTextInputViewSpec/ComponentDescriptors.cpp +22 -0
  83. package/ios/generated/RNEnrichedTextInputViewSpec/ComponentDescriptors.h +24 -0
  84. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.cpp +118 -0
  85. package/ios/generated/RNEnrichedTextInputViewSpec/EventEmitters.h +95 -0
  86. package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +128 -0
  87. package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +577 -0
  88. package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +384 -0
  89. package/ios/generated/RNEnrichedTextInputViewSpec/ShadowNodes.cpp +17 -0
  90. package/ios/generated/RNEnrichedTextInputViewSpec/ShadowNodes.h +23 -0
  91. package/ios/generated/RNEnrichedTextInputViewSpec/States.cpp +16 -0
  92. package/ios/generated/RNEnrichedTextInputViewSpec/States.h +20 -0
  93. package/ios/inputParser/InputParser.h +11 -0
  94. package/ios/inputParser/InputParser.mm +659 -0
  95. package/ios/inputTextView/InputTextView.h +6 -0
  96. package/ios/inputTextView/InputTextView.mm +115 -0
  97. package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +17 -0
  98. package/ios/internals/EnrichedTextInputViewShadowNode.h +40 -0
  99. package/ios/internals/EnrichedTextInputViewShadowNode.mm +83 -0
  100. package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
  101. package/ios/internals/EnrichedTextInputViewState.h +20 -0
  102. package/ios/styles/BlockQuoteStyle.mm +248 -0
  103. package/ios/styles/BoldStyle.mm +122 -0
  104. package/ios/styles/H1Style.mm +10 -0
  105. package/ios/styles/H2Style.mm +10 -0
  106. package/ios/styles/H3Style.mm +10 -0
  107. package/ios/styles/HeadingStyleBase.mm +144 -0
  108. package/ios/styles/InlineCodeStyle.mm +163 -0
  109. package/ios/styles/ItalicStyle.mm +110 -0
  110. package/ios/styles/LinkStyle.mm +463 -0
  111. package/ios/styles/MentionStyle.mm +476 -0
  112. package/ios/styles/OrderedListStyle.mm +225 -0
  113. package/ios/styles/StrikethroughStyle.mm +80 -0
  114. package/ios/styles/UnderlineStyle.mm +112 -0
  115. package/ios/styles/UnorderedListStyle.mm +225 -0
  116. package/ios/utils/BaseStyleProtocol.h +16 -0
  117. package/ios/utils/ColorExtension.h +6 -0
  118. package/ios/utils/ColorExtension.mm +27 -0
  119. package/ios/utils/FontExtension.h +13 -0
  120. package/ios/utils/FontExtension.mm +91 -0
  121. package/ios/utils/LayoutManagerExtension.h +6 -0
  122. package/ios/utils/LayoutManagerExtension.mm +171 -0
  123. package/ios/utils/LinkData.h +9 -0
  124. package/ios/utils/LinkData.mm +4 -0
  125. package/ios/utils/MentionParams.h +9 -0
  126. package/ios/utils/MentionParams.mm +4 -0
  127. package/ios/utils/MentionStyleProps.h +13 -0
  128. package/ios/utils/MentionStyleProps.mm +56 -0
  129. package/ios/utils/OccurenceUtils.h +37 -0
  130. package/ios/utils/OccurenceUtils.mm +124 -0
  131. package/ios/utils/ParagraphsUtils.h +7 -0
  132. package/ios/utils/ParagraphsUtils.mm +54 -0
  133. package/ios/utils/StringExtension.h +15 -0
  134. package/ios/utils/StringExtension.mm +57 -0
  135. package/ios/utils/StyleHeaders.h +74 -0
  136. package/ios/utils/StylePair.h +9 -0
  137. package/ios/utils/StylePair.mm +4 -0
  138. package/ios/utils/StyleTypeEnum.h +22 -0
  139. package/ios/utils/TextDecorationLineEnum.h +6 -0
  140. package/ios/utils/TextDecorationLineEnum.mm +4 -0
  141. package/ios/utils/TextInsertionUtils.h +6 -0
  142. package/ios/utils/TextInsertionUtils.mm +48 -0
  143. package/ios/utils/WordsUtils.h +6 -0
  144. package/ios/utils/WordsUtils.mm +88 -0
  145. package/ios/utils/ZeroWidthSpaceUtils.h +7 -0
  146. package/ios/utils/ZeroWidthSpaceUtils.mm +164 -0
  147. package/lib/module/EnrichedTextInput.js +191 -0
  148. package/lib/module/EnrichedTextInput.js.map +1 -0
  149. package/lib/module/EnrichedTextInputNativeComponent.ts +235 -0
  150. package/lib/module/index.js +4 -0
  151. package/lib/module/index.js.map +1 -0
  152. package/lib/module/normalizeHtmlStyle.js +141 -0
  153. package/lib/module/normalizeHtmlStyle.js.map +1 -0
  154. package/lib/module/package.json +1 -0
  155. package/lib/typescript/package.json +1 -0
  156. package/lib/typescript/src/EnrichedTextInput.d.ts +113 -0
  157. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -0
  158. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +160 -0
  159. package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -0
  160. package/lib/typescript/src/index.d.ts +3 -0
  161. package/lib/typescript/src/index.d.ts.map +1 -0
  162. package/lib/typescript/src/normalizeHtmlStyle.d.ts +4 -0
  163. package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -0
  164. package/package.json +172 -1
  165. package/react-native.config.js +13 -0
  166. package/src/EnrichedTextInput.tsx +358 -0
  167. package/src/EnrichedTextInputNativeComponent.ts +235 -0
  168. package/src/index.tsx +9 -0
  169. package/src/normalizeHtmlStyle.ts +188 -0
@@ -0,0 +1,857 @@
1
+ package com.swmansion.enriched.utils;
2
+
3
+ import android.graphics.drawable.Drawable;
4
+ import android.text.Editable;
5
+ import android.text.Layout;
6
+ import android.text.Spannable;
7
+ import android.text.SpannableStringBuilder;
8
+ import android.text.Spanned;
9
+ import android.text.TextUtils;
10
+ import android.text.style.AlignmentSpan;
11
+ import android.text.style.ParagraphStyle;
12
+
13
+ import com.swmansion.enriched.spans.EnrichedBlockQuoteSpan;
14
+ import com.swmansion.enriched.spans.EnrichedBoldSpan;
15
+ import com.swmansion.enriched.spans.EnrichedCodeBlockSpan;
16
+ import com.swmansion.enriched.spans.EnrichedH1Span;
17
+ import com.swmansion.enriched.spans.EnrichedH2Span;
18
+ import com.swmansion.enriched.spans.EnrichedH3Span;
19
+ import com.swmansion.enriched.spans.EnrichedImageSpan;
20
+ import com.swmansion.enriched.spans.EnrichedInlineCodeSpan;
21
+ import com.swmansion.enriched.spans.EnrichedItalicSpan;
22
+ import com.swmansion.enriched.spans.EnrichedLinkSpan;
23
+ import com.swmansion.enriched.spans.EnrichedMentionSpan;
24
+ import com.swmansion.enriched.spans.EnrichedOrderedListSpan;
25
+ import com.swmansion.enriched.spans.EnrichedStrikeThroughSpan;
26
+ import com.swmansion.enriched.spans.EnrichedUnderlineSpan;
27
+ import com.swmansion.enriched.spans.EnrichedUnorderedListSpan;
28
+ import com.swmansion.enriched.spans.interfaces.EnrichedBlockSpan;
29
+ import com.swmansion.enriched.spans.interfaces.EnrichedParagraphSpan;
30
+ import com.swmansion.enriched.spans.interfaces.EnrichedInlineSpan;
31
+ import com.swmansion.enriched.spans.interfaces.EnrichedZeroWidthSpaceSpan;
32
+ import com.swmansion.enriched.styles.HtmlStyle;
33
+
34
+ import org.ccil.cowan.tagsoup.HTMLSchema;
35
+ import org.ccil.cowan.tagsoup.Parser;
36
+ import org.xml.sax.Attributes;
37
+ import org.xml.sax.ContentHandler;
38
+ import org.xml.sax.InputSource;
39
+ import org.xml.sax.Locator;
40
+ import org.xml.sax.SAXException;
41
+ import org.xml.sax.SAXNotRecognizedException;
42
+ import org.xml.sax.SAXNotSupportedException;
43
+ import org.xml.sax.XMLReader;
44
+
45
+ import java.io.IOException;
46
+ import java.io.StringReader;
47
+ import java.util.HashMap;
48
+ import java.util.Map;
49
+
50
+ /**
51
+ * Most of the code in this file is copied from the Android source code and adjusted to our needs.
52
+ * For the reference see <a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/text/Html.java">docs</a>
53
+ */
54
+ public class EnrichedParser {
55
+ /**
56
+ * Retrieves images for HTML &lt;img&gt; tags.
57
+ */
58
+ public interface ImageGetter {
59
+ /**
60
+ * This method is called when the HTML parser encounters an
61
+ * &lt;img&gt; tag. The <code>source</code> argument is the
62
+ * string from the "src" attribute; the return value should be
63
+ * a Drawable representation of the image or <code>null</code>
64
+ * for a generic replacement image. Make sure you call
65
+ * setBounds() on your Drawable if it doesn't already have
66
+ * its bounds set.
67
+ */
68
+ Drawable getDrawable(String source);
69
+ }
70
+
71
+ private EnrichedParser() { }
72
+ /**
73
+ * Lazy initialization holder for HTML parser. This class will
74
+ * a) be preloaded by the zygote, or b) not loaded until absolutely
75
+ * necessary.
76
+ */
77
+ private static class HtmlParser {
78
+ private static final HTMLSchema schema = new HTMLSchema();
79
+ }
80
+ /**
81
+ * Returns displayable styled text from the provided HTML string. Any &lt;img&gt; tags in the
82
+ * HTML will use the specified ImageGetter to request a representation of the image (use null
83
+ * if you don't want this) and the specified TagHandler to handle unknown tags (specify null if
84
+ * you don't want this).
85
+ *
86
+ * <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
87
+ */
88
+ public static Spanned fromHtml(String source, HtmlStyle style, ImageGetter imageGetter) {
89
+ Parser parser = new Parser();
90
+ try {
91
+ parser.setProperty(Parser.schemaProperty, HtmlParser.schema);
92
+ } catch (SAXNotRecognizedException | SAXNotSupportedException e) {
93
+ // Should not happen.
94
+ throw new RuntimeException(e);
95
+ }
96
+ HtmlToSpannedConverter converter = new HtmlToSpannedConverter(source, style, imageGetter, parser);
97
+ return converter.convert();
98
+ }
99
+ public static String toHtml(Spanned text) {
100
+ StringBuilder out = new StringBuilder();
101
+ withinHtml(out, text);
102
+ String outString = out.toString();
103
+ // Codeblocks and blockquotes appends a newline character by default, so we have to remove it
104
+ String normalizedCodeBlock = outString.replaceAll("</codeblock>\\n<br>", "</codeblock>");
105
+ String normalizedBlockQuote = normalizedCodeBlock.replaceAll("</blockquote>\\n<br>", "</blockquote>");
106
+ return "<html>\n" + normalizedBlockQuote + "</html>";
107
+ }
108
+ /**
109
+ * Returns an HTML escaped representation of the given plain text.
110
+ */
111
+ public static String escapeHtml(CharSequence text) {
112
+ StringBuilder out = new StringBuilder();
113
+ withinStyle(out, text, 0, text.length());
114
+ return out.toString();
115
+ }
116
+ private static void withinHtml(StringBuilder out, Spanned text) {
117
+ withinDiv(out, text, 0, text.length());
118
+ }
119
+ private static void withinDiv(StringBuilder out, Spanned text, int start, int end) {
120
+ int next;
121
+ for (int i = start; i < end; i = next) {
122
+ next = text.nextSpanTransition(i, end, EnrichedBlockSpan.class);
123
+ EnrichedBlockSpan[] blocks = text.getSpans(i, next, EnrichedBlockSpan.class);
124
+ String tag = "unknown";
125
+ if (blocks.length > 0){
126
+ tag = blocks[0] instanceof EnrichedCodeBlockSpan ? "codeblock" : "blockquote";
127
+ }
128
+
129
+ // Each block appends a newline by default.
130
+ // If we set up a new block, we have to remove the last character.
131
+ if (out.length() >= 5 && out.substring(out.length() - 5).equals("<br>\n")) {
132
+ out.replace(out.length() - 5, out.length(), "");
133
+ }
134
+
135
+ for (EnrichedBlockSpan ignored : blocks) {
136
+ out.append("<").append(tag).append(">\n");
137
+ }
138
+ withinBlock(out, text, i, next);
139
+ for (EnrichedBlockSpan ignored : blocks) {
140
+ out.append("</").append(tag).append(">\n");
141
+ }
142
+ }
143
+ }
144
+ private static String getBlockTag(EnrichedParagraphSpan[] spans) {
145
+ for (EnrichedParagraphSpan span : spans) {
146
+ if (span instanceof EnrichedUnorderedListSpan) {
147
+ return "ul";
148
+ } else if (span instanceof EnrichedOrderedListSpan) {
149
+ return "ol";
150
+ } else if (span instanceof EnrichedH1Span) {
151
+ return "h1";
152
+ } else if (span instanceof EnrichedH2Span) {
153
+ return "h2";
154
+ } else if (span instanceof EnrichedH3Span) {
155
+ return "h3";
156
+ }
157
+ }
158
+
159
+ return "p";
160
+ }
161
+ private static void withinBlock(StringBuilder out, Spanned text, int start, int end) {
162
+ boolean isInUlList = false;
163
+ boolean isInOlList = false;
164
+ int next;
165
+ for (int i = start; i <= end; i = next) {
166
+ next = TextUtils.indexOf(text, '\n', i, end);
167
+ if (next < 0) {
168
+ next = end;
169
+ }
170
+ if (next == i) {
171
+ if (isInUlList) {
172
+ // Current paragraph is no longer a list item; close the previously opened list
173
+ isInUlList = false;
174
+ out.append("</ul>\n");
175
+ } else if (isInOlList) {
176
+ // Current paragraph is no longer a list item; close the previously opened list
177
+ isInOlList = false;
178
+ out.append("</ol>\n");
179
+ }
180
+ out.append("<br>\n");
181
+ } else {
182
+ EnrichedParagraphSpan[] paragraphStyles = text.getSpans(i, next, EnrichedParagraphSpan.class);
183
+ String tag = getBlockTag(paragraphStyles);
184
+ boolean isUlListItem = tag.equals("ul");
185
+ boolean isOlListItem = tag.equals("ol");
186
+
187
+ if (isInUlList && !isUlListItem) {
188
+ // Current paragraph is no longer a list item; close the previously opened list
189
+ isInUlList = false;
190
+ out.append("</ul>\n");
191
+ } else if (isInOlList && !isOlListItem) {
192
+ // Current paragraph is no longer a list item; close the previously opened list
193
+ isInOlList = false;
194
+ out.append("</ol>\n");
195
+ }
196
+
197
+ if (isUlListItem && !isInUlList) {
198
+ // Current paragraph is the first item in a list
199
+ isInUlList = true;
200
+ out.append("<ul").append(">\n");
201
+ } else if (isOlListItem && !isInOlList) {
202
+ // Current paragraph is the first item in a list
203
+ isInOlList = true;
204
+ out.append("<ol").append(">\n");
205
+ }
206
+
207
+ boolean isList = isUlListItem || isOlListItem;
208
+ String tagType = isList ? "li" : tag;
209
+ out.append("<");
210
+
211
+ out.append(tagType);
212
+
213
+ out.append(">");
214
+ withinParagraph(out, text, i, next);
215
+ out.append("</");
216
+ out.append(tagType);
217
+ out.append(">\n");
218
+ if (next == end && isInUlList) {
219
+ isInUlList = false;
220
+ out.append("</ul>\n");
221
+ } else if (next == end && isInOlList) {
222
+ isInOlList = false;
223
+ out.append("</ol>\n");
224
+ }
225
+ }
226
+ next++;
227
+ }
228
+ }
229
+ private static void withinParagraph(StringBuilder out, Spanned text, int start, int end) {
230
+ int next;
231
+ for (int i = start; i < end; i = next) {
232
+ next = text.nextSpanTransition(i, end, EnrichedInlineSpan.class);
233
+ EnrichedInlineSpan[] style = text.getSpans(i, next, EnrichedInlineSpan.class);
234
+ for (int j = 0; j < style.length; j++) {
235
+ if (style[j] instanceof EnrichedBoldSpan) {
236
+ out.append("<b>");
237
+ }
238
+ if (style[j] instanceof EnrichedItalicSpan) {
239
+ out.append("<i>");
240
+ }
241
+ if (style[j] instanceof EnrichedUnderlineSpan) {
242
+ out.append("<u>");
243
+ }
244
+ if (style[j] instanceof EnrichedInlineCodeSpan) {
245
+ out.append("<code>");
246
+ }
247
+ if (style[j] instanceof EnrichedStrikeThroughSpan) {
248
+ out.append("<s>");
249
+ }
250
+ if (style[j] instanceof EnrichedLinkSpan) {
251
+ out.append("<a href=\"");
252
+ out.append(((EnrichedLinkSpan) style[j]).getUrl());
253
+ out.append("\">");
254
+ }
255
+ if (style[j] instanceof EnrichedMentionSpan) {
256
+ out.append("<mention text=\"");
257
+ out.append(((EnrichedMentionSpan) style[j]).getText());
258
+ out.append("\"");
259
+
260
+ out.append(" indicator=\"");
261
+ out.append(((EnrichedMentionSpan) style[j]).getIndicator());
262
+ out.append("\"");
263
+
264
+ Map<String, String> attributes = ((EnrichedMentionSpan) style[j]).getAttributes();
265
+ for (Map.Entry<String, String> entry : attributes.entrySet()) {
266
+ out.append(" ");
267
+ out.append(entry.getKey());
268
+ out.append("=\"");
269
+ out.append(entry.getValue());
270
+ out.append("\"");
271
+ }
272
+
273
+ out.append(">");
274
+ }
275
+ if (style[j] instanceof EnrichedImageSpan) {
276
+ out.append("<img src=\"");
277
+ out.append(((EnrichedImageSpan) style[j]).getSource());
278
+ out.append("\">");
279
+ // Don't output the placeholder character underlying the image.
280
+ i = next;
281
+ }
282
+ }
283
+ withinStyle(out, text, i, next);
284
+ for (int j = style.length - 1; j >= 0; j--) {
285
+ if (style[j] instanceof EnrichedLinkSpan) {
286
+ out.append("</a>");
287
+ }
288
+ if (style[j] instanceof EnrichedMentionSpan) {
289
+ out.append("</mention>");
290
+ }
291
+ if (style[j] instanceof EnrichedStrikeThroughSpan) {
292
+ out.append("</s>");
293
+ }
294
+ if (style[j] instanceof EnrichedUnderlineSpan) {
295
+ out.append("</u>");
296
+ }
297
+ if (style[j] instanceof EnrichedInlineCodeSpan) {
298
+ out.append("</code>");
299
+ }
300
+ if (style[j] instanceof EnrichedBoldSpan) {
301
+ out.append("</b>");
302
+ }
303
+ if (style[j] instanceof EnrichedItalicSpan) {
304
+ out.append("</i>");
305
+ }
306
+ }
307
+ }
308
+ }
309
+ private static void withinStyle(StringBuilder out, CharSequence text,
310
+ int start, int end) {
311
+ for (int i = start; i < end; i++) {
312
+ char c = text.charAt(i);
313
+ if (c == '\u200B') {
314
+ // Do not output zero-width space characters.
315
+ continue;
316
+ } else if (c == '<') {
317
+ out.append("&lt;");
318
+ } else if (c == '>') {
319
+ out.append("&gt;");
320
+ } else if (c == '&') {
321
+ out.append("&amp;");
322
+ } else if (c >= 0xD800 && c <= 0xDFFF) {
323
+ if (c < 0xDC00 && i + 1 < end) {
324
+ char d = text.charAt(i + 1);
325
+ if (d >= 0xDC00 && d <= 0xDFFF) {
326
+ i++;
327
+ int codepoint = 0x010000 | (int) c - 0xD800 << 10 | (int) d - 0xDC00;
328
+ out.append("&#").append(codepoint).append(";");
329
+ }
330
+ }
331
+ } else if (c > 0x7E || c < ' ') {
332
+ out.append("&#").append((int) c).append(";");
333
+ } else if (c == ' ') {
334
+ while (i + 1 < end && text.charAt(i + 1) == ' ') {
335
+ out.append("&nbsp;");
336
+ i++;
337
+ }
338
+ out.append(' ');
339
+ } else {
340
+ out.append(c);
341
+ }
342
+ }
343
+ }
344
+ }
345
+ class HtmlToSpannedConverter implements ContentHandler {
346
+ private final HtmlStyle mStyle;
347
+ private final String mSource;
348
+ private final XMLReader mReader;
349
+ private final SpannableStringBuilder mSpannableStringBuilder;
350
+ private final EnrichedParser.ImageGetter mImageGetter;
351
+ private static Integer currentOrderedListItemIndex = 0;
352
+ private static Boolean isInOrderedList = false;
353
+
354
+ public HtmlToSpannedConverter(String source, HtmlStyle style, EnrichedParser.ImageGetter imageGetter, Parser parser) {
355
+ mStyle = style;
356
+ mSource = source;
357
+ mSpannableStringBuilder = new SpannableStringBuilder();
358
+ mImageGetter = imageGetter;
359
+ mReader = parser;
360
+ }
361
+
362
+ public Spanned convert() {
363
+ mReader.setContentHandler(this);
364
+ try {
365
+ mReader.parse(new InputSource(new StringReader(mSource)));
366
+ } catch (IOException e) {
367
+ // We are reading from a string. There should not be IO problems.
368
+ throw new RuntimeException(e);
369
+ } catch (SAXException e) {
370
+ // TagSoup doesn't throw parse exceptions.
371
+ throw new RuntimeException(e);
372
+ }
373
+ // Fix flags and range for paragraph-type markup.
374
+ Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class);
375
+ for (int i = 0; i < obj.length; i++) {
376
+ int start = mSpannableStringBuilder.getSpanStart(obj[i]);
377
+ int end = mSpannableStringBuilder.getSpanEnd(obj[i]);
378
+ // If the last line of the range is blank, back off by one.
379
+ if (end - 2 >= 0) {
380
+ if (mSpannableStringBuilder.charAt(end - 1) == '\n' &&
381
+ mSpannableStringBuilder.charAt(end - 2) == '\n') {
382
+ end--;
383
+ }
384
+ }
385
+ if (end == start) {
386
+ mSpannableStringBuilder.removeSpan(obj[i]);
387
+ } else {
388
+ // TODO: verify if Spannable.SPAN_EXCLUSIVE_EXCLUSIVE does not break anything.
389
+ // Previously it was SPAN_PARAGRAPH. I've changed that in order to fix ranges for list items.
390
+ mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
391
+ }
392
+ }
393
+
394
+ // Assign zero-width space character to the proper spans.
395
+ EnrichedZeroWidthSpaceSpan[] zeroWidthSpaceSpans = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), EnrichedZeroWidthSpaceSpan.class);
396
+ for (EnrichedZeroWidthSpaceSpan zeroWidthSpaceSpan : zeroWidthSpaceSpans) {
397
+ int start = mSpannableStringBuilder.getSpanStart(zeroWidthSpaceSpan);
398
+ int end = mSpannableStringBuilder.getSpanEnd(zeroWidthSpaceSpan);
399
+ mSpannableStringBuilder.insert(start, "\u200B");
400
+ mSpannableStringBuilder.removeSpan(zeroWidthSpaceSpan);
401
+ mSpannableStringBuilder.setSpan(zeroWidthSpaceSpan, start, end + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
402
+ }
403
+
404
+ return mSpannableStringBuilder;
405
+ }
406
+
407
+ private void handleStartTag(String tag, Attributes attributes) {
408
+ if (tag.equalsIgnoreCase("br")) {
409
+ // We don't need to handle this. TagSoup will ensure that there's a </br> for each <br>
410
+ // so we can safely emit the linebreaks when we handle the close tag.
411
+ } else if (tag.equalsIgnoreCase("p")) {
412
+ startBlockElement(mSpannableStringBuilder);
413
+ } else if (tag.equalsIgnoreCase("ul")) {
414
+ isInOrderedList = false;
415
+ startBlockElement(mSpannableStringBuilder);
416
+ } else if (tag.equalsIgnoreCase("ol")) {
417
+ isInOrderedList = true;
418
+ currentOrderedListItemIndex = 0;
419
+ startBlockElement(mSpannableStringBuilder);
420
+ } else if (tag.equalsIgnoreCase("li")) {
421
+ startLi(mSpannableStringBuilder);
422
+ } else if (tag.equalsIgnoreCase("b")) {
423
+ start(mSpannableStringBuilder, new Bold());
424
+ } else if (tag.equalsIgnoreCase("i")) {
425
+ start(mSpannableStringBuilder, new Italic());
426
+ } else if (tag.equalsIgnoreCase("blockquote")) {
427
+ startBlockquote(mSpannableStringBuilder);
428
+ } else if (tag.equalsIgnoreCase("codeblock")) {
429
+ startCodeBlock(mSpannableStringBuilder);
430
+ } else if (tag.equalsIgnoreCase("a")) {
431
+ startA(mSpannableStringBuilder, attributes);
432
+ } else if (tag.equalsIgnoreCase("u")) {
433
+ start(mSpannableStringBuilder, new Underline());
434
+ } else if (tag.equalsIgnoreCase("s")) {
435
+ start(mSpannableStringBuilder, new Strikethrough());
436
+ } else if (tag.equalsIgnoreCase("strike")) {
437
+ start(mSpannableStringBuilder, new Strikethrough());
438
+ } else if (tag.equalsIgnoreCase("h1")) {
439
+ startHeading(mSpannableStringBuilder, 1);
440
+ } else if (tag.equalsIgnoreCase("h2")) {
441
+ startHeading(mSpannableStringBuilder, 2);
442
+ } else if (tag.equalsIgnoreCase("h3")) {
443
+ startHeading(mSpannableStringBuilder, 3);
444
+ } else if (tag.equalsIgnoreCase("img")) {
445
+ startImg(mSpannableStringBuilder, attributes, mImageGetter, mStyle);
446
+ } else if (tag.equalsIgnoreCase("code")) {
447
+ start(mSpannableStringBuilder, new Code());
448
+ } else if (tag.equalsIgnoreCase("mention")) {
449
+ startMention(mSpannableStringBuilder, attributes);
450
+ }
451
+ }
452
+
453
+ private void handleEndTag(String tag) {
454
+ if (tag.equalsIgnoreCase("br")) {
455
+ handleBr(mSpannableStringBuilder);
456
+ } else if (tag.equalsIgnoreCase("p")) {
457
+ endBlockElement(mSpannableStringBuilder);
458
+ } else if (tag.equalsIgnoreCase("ul")) {
459
+ endBlockElement(mSpannableStringBuilder);
460
+ } else if (tag.equalsIgnoreCase("li")) {
461
+ endLi(mSpannableStringBuilder, mStyle);
462
+ } else if (tag.equalsIgnoreCase("b")) {
463
+ end(mSpannableStringBuilder, Bold.class, new EnrichedBoldSpan(mStyle));
464
+ } else if (tag.equalsIgnoreCase("i")) {
465
+ end(mSpannableStringBuilder, Italic.class, new EnrichedItalicSpan(mStyle));
466
+ } else if (tag.equalsIgnoreCase("blockquote")) {
467
+ endBlockquote(mSpannableStringBuilder, mStyle);
468
+ } else if (tag.equalsIgnoreCase("codeblock")) {
469
+ endCodeBlock(mSpannableStringBuilder, mStyle);
470
+ } else if (tag.equalsIgnoreCase("a")) {
471
+ endA(mSpannableStringBuilder, mStyle);
472
+ } else if (tag.equalsIgnoreCase("u")) {
473
+ end(mSpannableStringBuilder, Underline.class, new EnrichedUnderlineSpan(mStyle));
474
+ } else if (tag.equalsIgnoreCase("s")) {
475
+ end(mSpannableStringBuilder, Strikethrough.class, new EnrichedStrikeThroughSpan(mStyle));
476
+ } else if (tag.equalsIgnoreCase("h1")) {
477
+ endHeading(mSpannableStringBuilder, mStyle, 1);
478
+ } else if (tag.equalsIgnoreCase("h2")) {
479
+ endHeading(mSpannableStringBuilder, mStyle, 2);
480
+ } else if (tag.equalsIgnoreCase("h3")) {
481
+ endHeading(mSpannableStringBuilder, mStyle, 3);
482
+ } else if (tag.equalsIgnoreCase("code")) {
483
+ end(mSpannableStringBuilder, Code.class, new EnrichedInlineCodeSpan(mStyle));
484
+ } else if (tag.equalsIgnoreCase("mention")) {
485
+ endMention(mSpannableStringBuilder, mStyle);
486
+ }
487
+ }
488
+
489
+ private static void appendNewlines(Editable text, int minNewline) {
490
+ final int len = text.length();
491
+ if (len == 0) {
492
+ return;
493
+ }
494
+ int existingNewlines = 0;
495
+ for (int i = len - 1; i >= 0 && text.charAt(i) == '\n'; i--) {
496
+ existingNewlines++;
497
+ }
498
+ for (int j = existingNewlines; j < minNewline; j++) {
499
+ text.append("\n");
500
+ }
501
+ }
502
+
503
+ private static void startBlockElement(Editable text) {
504
+ appendNewlines(text, 1);
505
+ start(text, new Newline(1));
506
+ }
507
+
508
+ private static void endBlockElement(Editable text) {
509
+ Newline n = getLast(text, Newline.class);
510
+ if (n != null) {
511
+ appendNewlines(text, n.mNumNewlines);
512
+ text.removeSpan(n);
513
+ }
514
+ Alignment a = getLast(text, Alignment.class);
515
+ if (a != null) {
516
+ setSpanFromMark(text, a, new AlignmentSpan.Standard(a.mAlignment));
517
+ }
518
+ }
519
+
520
+ private static void handleBr(Editable text) {
521
+ text.append('\n');
522
+ }
523
+
524
+ private void startLi(Editable text) {
525
+ startBlockElement(text);
526
+
527
+ if (isInOrderedList) {
528
+ currentOrderedListItemIndex++;
529
+ start(text, new List("ol", currentOrderedListItemIndex));
530
+ } else {
531
+ start(text, new List("ul", 0));
532
+ }
533
+ }
534
+
535
+ private static void endLi(Editable text, HtmlStyle style) {
536
+ endBlockElement(text);
537
+
538
+ List l = getLast(text, List.class);
539
+ if (l != null) {
540
+ if (l.mType.equals("ol")) {
541
+ setParagraphSpanFromMark(text, l, new EnrichedOrderedListSpan(l.mIndex, style));
542
+ } else {
543
+ setParagraphSpanFromMark(text, l, new EnrichedUnorderedListSpan(style));
544
+ }
545
+ }
546
+
547
+ endBlockElement(text);
548
+ }
549
+
550
+ private void startBlockquote(Editable text) {
551
+ startBlockElement(text);
552
+ start(text, new Blockquote());
553
+ }
554
+
555
+ private static void endBlockquote(Editable text, HtmlStyle style) {
556
+ endBlockElement(text);
557
+ Blockquote last = getLast(text, Blockquote.class);
558
+ setParagraphSpanFromMark(text, last, new EnrichedBlockQuoteSpan(style));
559
+ }
560
+
561
+ private void startCodeBlock(Editable text) {
562
+ startBlockElement(text);
563
+ start(text, new CodeBlock());
564
+ }
565
+
566
+ private static void endCodeBlock(Editable text, HtmlStyle style) {
567
+ endBlockElement(text);
568
+ CodeBlock last = getLast(text, CodeBlock.class);
569
+ setParagraphSpanFromMark(text, last, new EnrichedCodeBlockSpan(style));
570
+ }
571
+
572
+ private void startHeading(Editable text, int level) {
573
+ startBlockElement(text);
574
+
575
+ switch (level) {
576
+ case 1:
577
+ start(text, new H1());
578
+ break;
579
+ case 2:
580
+ start(text, new H2());
581
+ break;
582
+ case 3:
583
+ start(text, new H3());
584
+ break;
585
+ default:
586
+ throw new IllegalArgumentException("Unsupported heading level: " + level);
587
+ }
588
+ }
589
+
590
+ private static void endHeading(Editable text, HtmlStyle style, int level) {
591
+ endBlockElement(text);
592
+
593
+ switch (level) {
594
+ case 1:
595
+ H1 lastH1 = getLast(text, H1.class);
596
+ setParagraphSpanFromMark(text, lastH1, new EnrichedH1Span(style));
597
+ break;
598
+ case 2:
599
+ H2 lastH2 = getLast(text, H2.class);
600
+ setParagraphSpanFromMark(text, lastH2, new EnrichedH2Span(style));
601
+ break;
602
+ case 3:
603
+ H3 lastH3 = getLast(text, H3.class);
604
+ setParagraphSpanFromMark(text, lastH3, new EnrichedH3Span(style));
605
+ break;
606
+ default:
607
+ throw new IllegalArgumentException("Unsupported heading level: " + level);
608
+ }
609
+ }
610
+
611
+ private static <T> T getLast(Spanned text, Class<T> kind) {
612
+ /*
613
+ * This knows that the last returned object from getSpans()
614
+ * will be the most recently added.
615
+ */
616
+ T[] objs = text.getSpans(0, text.length(), kind);
617
+ if (objs.length == 0) {
618
+ return null;
619
+ } else {
620
+ return objs[objs.length - 1];
621
+ }
622
+ }
623
+
624
+ private static void setSpanFromMark(Spannable text, Object mark, Object... spans) {
625
+ int where = text.getSpanStart(mark);
626
+ text.removeSpan(mark);
627
+ int len = text.length();
628
+ if (where != len) {
629
+ for (Object span : spans) {
630
+ text.setSpan(span, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
631
+ }
632
+ }
633
+ }
634
+
635
+ private static void setParagraphSpanFromMark(Spannable text, Object mark, Object... spans) {
636
+ int where = text.getSpanStart(mark);
637
+ text.removeSpan(mark);
638
+ int len = text.length();
639
+
640
+ // Adjust the end position to exclude the newline character, if present
641
+ if (len > 0 && text.charAt(len - 1) == '\n') {
642
+ len--;
643
+ }
644
+
645
+ if (where != len) {
646
+ for (Object span : spans) {
647
+ text.setSpan(span, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
648
+ }
649
+ }
650
+ }
651
+
652
+ private static void start(Editable text, Object mark) {
653
+ int len = text.length();
654
+ text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
655
+ }
656
+
657
+ private static void end(Editable text, Class kind, Object repl) {
658
+ Object obj = getLast(text, kind);
659
+ if (obj != null) {
660
+ setSpanFromMark(text, obj, repl);
661
+ }
662
+ }
663
+
664
+ private static void startImg(Editable text, Attributes attributes, EnrichedParser.ImageGetter img, HtmlStyle style) {
665
+ String src = attributes.getValue("", "src");
666
+ Drawable d = null;
667
+ if (img != null) {
668
+ d = img.getDrawable(src);
669
+ }
670
+
671
+ if (d == null) {
672
+ return;
673
+ }
674
+
675
+ int len = text.length();
676
+ text.append("");
677
+ text.setSpan(new EnrichedImageSpan(d, src, style), len, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
678
+ }
679
+
680
+ private static void startA(Editable text, Attributes attributes) {
681
+ String href = attributes.getValue("", "href");
682
+ start(text, new Href(href));
683
+ }
684
+
685
+ private void endA(Editable text, HtmlStyle style) {
686
+ Href h = getLast(text, Href.class);
687
+ if (h != null) {
688
+ if (h.mHref != null) {
689
+ setSpanFromMark(text, h, new EnrichedLinkSpan(h.mHref, style));
690
+ }
691
+ }
692
+ }
693
+
694
+ private static void startMention(Editable mention, Attributes attributes) {
695
+ String text = attributes.getValue("", "text");
696
+ String indicator = attributes.getValue("", "indicator");
697
+
698
+ Map<String, String> attributesMap = new HashMap<>();
699
+ for (int i = 0; i < attributes.getLength(); i++) {
700
+ String localName = attributes.getLocalName(i);
701
+
702
+ if (!"text".equals(localName) && !"indicator".equals(localName)) {
703
+ attributesMap.put(localName, attributes.getValue(i));
704
+ }
705
+ }
706
+
707
+ start(mention, new Mention(indicator, text, attributesMap));
708
+ }
709
+
710
+ private void endMention(Editable text, HtmlStyle style) {
711
+ Mention m = getLast(text, Mention.class);
712
+
713
+ if (m == null) return;
714
+ if (m.mText == null) return;
715
+
716
+ setSpanFromMark(text, m, new EnrichedMentionSpan(m.mText, m.mIndicator, m.mAttributes, style));
717
+ }
718
+
719
+ public void setDocumentLocator(Locator locator) {
720
+ }
721
+
722
+ public void startDocument() {
723
+ }
724
+
725
+ public void endDocument() {
726
+ }
727
+
728
+ public void startPrefixMapping(String prefix, String uri) {
729
+ }
730
+
731
+ public void endPrefixMapping(String prefix) {
732
+ }
733
+
734
+ public void startElement(String uri, String localName, String qName, Attributes attributes) {
735
+ handleStartTag(localName, attributes);
736
+ }
737
+
738
+ public void endElement(String uri, String localName, String qName) {
739
+ handleEndTag(localName);
740
+ }
741
+
742
+ public void characters(char[] ch, int start, int length) {
743
+ StringBuilder sb = new StringBuilder();
744
+ /*
745
+ * Ignore whitespace that immediately follows other whitespace;
746
+ * newlines count as spaces.
747
+ */
748
+ for (int i = 0; i < length; i++) {
749
+ char c = ch[i + start];
750
+ if (c == ' ' || c == '\n') {
751
+ char pred;
752
+ int len = sb.length();
753
+ if (len == 0) {
754
+ len = mSpannableStringBuilder.length();
755
+ if (len == 0) {
756
+ pred = '\n';
757
+ } else {
758
+ pred = mSpannableStringBuilder.charAt(len - 1);
759
+ }
760
+ } else {
761
+ pred = sb.charAt(len - 1);
762
+ }
763
+ if (pred != ' ' && pred != '\n') {
764
+ sb.append(' ');
765
+ }
766
+ } else {
767
+ sb.append(c);
768
+ }
769
+ }
770
+ mSpannableStringBuilder.append(sb);
771
+ }
772
+
773
+ public void ignorableWhitespace(char[] ch, int start, int length) {
774
+ }
775
+
776
+ public void processingInstruction(String target, String data) {
777
+ }
778
+
779
+ public void skippedEntity(String name) {
780
+ }
781
+
782
+ private static class H1 {
783
+ }
784
+
785
+ private static class H2 {
786
+ }
787
+
788
+ private static class H3 {
789
+ }
790
+
791
+ private static class Bold {
792
+ }
793
+
794
+ private static class Italic {
795
+ }
796
+
797
+ private static class Underline {
798
+ }
799
+
800
+ private static class Code {
801
+ }
802
+
803
+ private static class CodeBlock {
804
+ }
805
+
806
+ private static class Strikethrough {
807
+ }
808
+
809
+ private static class Blockquote {
810
+ }
811
+
812
+ private static class List {
813
+ public int mIndex;
814
+ public String mType;
815
+
816
+ public List(String type, int index) {
817
+ mType = type;
818
+ mIndex = index;
819
+ }
820
+ }
821
+
822
+ private static class Mention {
823
+ public Map<String, String> mAttributes;
824
+ public String mIndicator;
825
+ public String mText;
826
+
827
+ public Mention(String indicator, String text, Map<String, String> attributes) {
828
+ mIndicator = indicator;
829
+ mAttributes = attributes;
830
+ mText = text;
831
+ }
832
+ }
833
+
834
+ private static class Href {
835
+ public String mHref;
836
+
837
+ public Href(String href) {
838
+ mHref = href;
839
+ }
840
+ }
841
+
842
+ private static class Newline {
843
+ private final int mNumNewlines;
844
+
845
+ public Newline(int numNewlines) {
846
+ mNumNewlines = numNewlines;
847
+ }
848
+ }
849
+
850
+ private static class Alignment {
851
+ private final Layout.Alignment mAlignment;
852
+
853
+ public Alignment(Layout.Alignment alignment) {
854
+ mAlignment = alignment;
855
+ }
856
+ }
857
+ }