Skip to content

Commit

Permalink
Move formatting logic to separate class on Android (#563)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomekzaw authored Dec 5, 2024
1 parent ee83f2c commit 9ada37e
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.expensify.livemarkdown;

import android.content.res.AssetManager;
import android.text.SpannableStringBuilder;
import android.text.Spanned;

import androidx.annotation.NonNull;

import com.expensify.livemarkdown.spans.*;
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;

import java.util.List;
import java.util.Objects;

public class MarkdownFormatter {
private final @NonNull AssetManager mAssetManager;

public MarkdownFormatter(@NonNull AssetManager assetManager) {
mAssetManager = assetManager;
}

public void format(SpannableStringBuilder ssb, List<MarkdownRange> markdownRanges, @NonNull MarkdownStyle markdownStyle) {
Objects.requireNonNull(markdownStyle, "mMarkdownStyle is null");
removeSpans(ssb);
applyRanges(ssb, markdownRanges, markdownStyle);
}

private void removeSpans(SpannableStringBuilder ssb) {
// We shouldn't use `removeSpans()` because it also removes SpellcheckSpan, SuggestionSpan etc.
MarkdownSpan[] spans = ssb.getSpans(0, ssb.length(), MarkdownSpan.class);
for (MarkdownSpan span : spans) {
ssb.removeSpan(span);
}
}

private void applyRanges(SpannableStringBuilder ssb, List<MarkdownRange> markdownRanges, @NonNull MarkdownStyle markdownStyle) {
for (MarkdownRange markdownRange : markdownRanges) {
applyRange(ssb, markdownRange, markdownStyle);
}
}

private void applyRange(SpannableStringBuilder ssb, MarkdownRange markdownRange, MarkdownStyle markdownStyle) {
String type = markdownRange.getType();
int start = markdownRange.getStart();
int end = start + markdownRange.getLength();
switch (type) {
case "bold":
setSpan(ssb, new MarkdownBoldSpan(), start, end);
break;
case "italic":
setSpan(ssb, new MarkdownItalicSpan(), start, end);
break;
case "strikethrough":
setSpan(ssb, new MarkdownStrikethroughSpan(), start, end);
break;
case "emoji":
setSpan(ssb, new MarkdownEmojiSpan(markdownStyle.getEmojiFontSize()), start, end);
break;
case "mention-here":
setSpan(ssb, new MarkdownForegroundColorSpan(markdownStyle.getMentionHereColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(markdownStyle.getMentionHereBackgroundColor()), start, end);
break;
case "mention-user":
// TODO: change mention color when it mentions current user
setSpan(ssb, new MarkdownForegroundColorSpan(markdownStyle.getMentionUserColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(markdownStyle.getMentionUserBackgroundColor()), start, end);
break;
case "mention-report":
setSpan(ssb, new MarkdownForegroundColorSpan(markdownStyle.getMentionReportColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(markdownStyle.getMentionReportBackgroundColor()), start, end);
break;
case "syntax":
setSpan(ssb, new MarkdownForegroundColorSpan(markdownStyle.getSyntaxColor()), start, end);
break;
case "link":
setSpan(ssb, new MarkdownUnderlineSpan(), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(markdownStyle.getLinkColor()), start, end);
break;
case "code":
setSpan(ssb, new MarkdownFontFamilySpan(markdownStyle.getCodeFontFamily(), mAssetManager), start, end);
setSpan(ssb, new MarkdownFontSizeSpan(markdownStyle.getCodeFontSize()), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(markdownStyle.getCodeColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(markdownStyle.getCodeBackgroundColor()), start, end);
break;
case "pre":
setSpan(ssb, new MarkdownFontFamilySpan(markdownStyle.getPreFontFamily(), mAssetManager), start, end);
setSpan(ssb, new MarkdownFontSizeSpan(markdownStyle.getPreFontSize()), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(markdownStyle.getPreColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(markdownStyle.getPreBackgroundColor()), start, end);
break;
case "h1":
setSpan(ssb, new MarkdownBoldSpan(), start, end);
CustomLineHeightSpan[] spans = ssb.getSpans(0, ssb.length(), CustomLineHeightSpan.class);
if (spans.length >= 1) {
int lineHeight = spans[0].getLineHeight();
setSpan(ssb, new MarkdownLineHeightSpan(lineHeight * 1.5f), start, end);
}
// NOTE: size span must be set after line height span to avoid height jumps
setSpan(ssb, new MarkdownFontSizeSpan(markdownStyle.getH1FontSize()), start, end);
break;
case "blockquote":
MarkdownBlockquoteSpan span = new MarkdownBlockquoteSpan(
markdownStyle.getBlockquoteBorderColor(),
markdownStyle.getBlockquoteBorderWidth(),
markdownStyle.getBlockquoteMarginLeft(),
markdownStyle.getBlockquotePaddingLeft(),
markdownRange.getDepth());
setSpan(ssb, span, start, end);
break;
}
}

private void setSpan(SpannableStringBuilder ssb, MarkdownSpan span, int start, int end) {
ssb.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
101 changes: 3 additions & 98 deletions android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
package com.expensify.livemarkdown;

import android.content.res.AssetManager;
import android.text.SpannableStringBuilder;
import android.text.Spanned;

import androidx.annotation.NonNull;

import com.expensify.livemarkdown.spans.*;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;

import java.util.List;
import java.util.Objects;

public class MarkdownUtils {
public MarkdownUtils(@NonNull ReactContext reactContext) {
mAssetManager = reactContext.getAssets();
mMarkdownParser = new MarkdownParser(reactContext);
mMarkdownFormatter = new MarkdownFormatter(reactContext.getAssets());
}

private final @NonNull AssetManager mAssetManager;
private final @NonNull MarkdownParser mMarkdownParser;
private final @NonNull MarkdownFormatter mMarkdownFormatter;

private MarkdownStyle mMarkdownStyle;
private int mParserId;
Expand All @@ -34,98 +29,8 @@ public void setParserId(int parserId) {
}

public void applyMarkdownFormatting(SpannableStringBuilder ssb) {
Objects.requireNonNull(mMarkdownStyle, "mMarkdownStyle is null");

removeSpans(ssb);

String text = ssb.toString();
List<MarkdownRange> markdownRanges = mMarkdownParser.parse(text, mParserId);

for (MarkdownRange markdownRange : markdownRanges) {
applyRange(ssb, markdownRange);
}
}

private void applyRange(SpannableStringBuilder ssb, MarkdownRange markdownRange) {
String type = markdownRange.getType();
int start = markdownRange.getStart();
int end = start + markdownRange.getLength();
switch (type) {
case "bold":
setSpan(ssb, new MarkdownBoldSpan(), start, end);
break;
case "italic":
setSpan(ssb, new MarkdownItalicSpan(), start, end);
break;
case "strikethrough":
setSpan(ssb, new MarkdownStrikethroughSpan(), start, end);
break;
case "emoji":
setSpan(ssb, new MarkdownEmojiSpan(mMarkdownStyle.getEmojiFontSize()), start, end);
break;
case "mention-here":
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getMentionHereColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getMentionHereBackgroundColor()), start, end);
break;
case "mention-user":
// TODO: change mention color when it mentions current user
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getMentionUserColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getMentionUserBackgroundColor()), start, end);
break;
case "mention-report":
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getMentionReportColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getMentionReportBackgroundColor()), start, end);
break;
case "syntax":
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getSyntaxColor()), start, end);
break;
case "link":
setSpan(ssb, new MarkdownUnderlineSpan(), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getLinkColor()), start, end);
break;
case "code":
setSpan(ssb, new MarkdownFontFamilySpan(mMarkdownStyle.getCodeFontFamily(), mAssetManager), start, end);
setSpan(ssb, new MarkdownFontSizeSpan(mMarkdownStyle.getCodeFontSize()), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getCodeColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getCodeBackgroundColor()), start, end);
break;
case "pre":
setSpan(ssb, new MarkdownFontFamilySpan(mMarkdownStyle.getPreFontFamily(), mAssetManager), start, end);
setSpan(ssb, new MarkdownFontSizeSpan(mMarkdownStyle.getPreFontSize()), start, end);
setSpan(ssb, new MarkdownForegroundColorSpan(mMarkdownStyle.getPreColor()), start, end);
setSpan(ssb, new MarkdownBackgroundColorSpan(mMarkdownStyle.getPreBackgroundColor()), start, end);
break;
case "h1":
setSpan(ssb, new MarkdownBoldSpan(), start, end);
CustomLineHeightSpan[] spans = ssb.getSpans(0, ssb.length(), CustomLineHeightSpan.class);
if (spans.length >= 1) {
int lineHeight = spans[0].getLineHeight();
setSpan(ssb, new MarkdownLineHeightSpan(lineHeight * 1.5f), start, end);
}
// NOTE: size span must be set after line height span to avoid height jumps
setSpan(ssb, new MarkdownFontSizeSpan(mMarkdownStyle.getH1FontSize()), start, end);
break;
case "blockquote":
MarkdownBlockquoteSpan span = new MarkdownBlockquoteSpan(
mMarkdownStyle.getBlockquoteBorderColor(),
mMarkdownStyle.getBlockquoteBorderWidth(),
mMarkdownStyle.getBlockquoteMarginLeft(),
mMarkdownStyle.getBlockquotePaddingLeft(),
markdownRange.getDepth());
setSpan(ssb, span, start, end);
break;
}
}

private void setSpan(SpannableStringBuilder ssb, MarkdownSpan span, int start, int end) {
ssb.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

private void removeSpans(SpannableStringBuilder ssb) {
// We shouldn't use `removeSpans()` because it also removes SpellcheckSpan, SuggestionSpan etc.
MarkdownSpan[] spans = ssb.getSpans(0, ssb.length(), MarkdownSpan.class);
for (MarkdownSpan span : spans) {
ssb.removeSpan(span);
}
mMarkdownFormatter.format(ssb, markdownRanges, mMarkdownStyle);
}
}

0 comments on commit 9ada37e

Please sign in to comment.