From 85c1c280e6b5bb63ed516d0caeffbb08d920c472 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Sun, 14 Feb 2016 19:01:07 +0100 Subject: [PATCH] Fixed issues with the mIRC color serializing/deserializing --- .../ui/chat/chatview/ChatMessageRenderer.java | 2 +- .../ui/chat/drawer/BufferItem.java | 9 +- .../ui/editor/AdvancedEditor.java | 9 +- .../quasseldroid_ng/ui/theme/AppContext.java | 15 + .../editor => util/irc/format}/BoldSpan.java | 2 +- .../irc/format/IrcFormatDeserializer.java | 304 ++++++++++++++++++ .../irc/{ => format}/IrcFormatHelper.java | 7 +- .../irc/format/IrcFormatSerializer.java} | 6 +- .../irc/format}/ItalicSpan.java | 2 +- .../java/de/kuschku/util/ui/MessageUtil.java | 191 ----------- 10 files changed, 335 insertions(+), 212 deletions(-) rename app/src/main/java/de/kuschku/{quasseldroid_ng/ui/editor => util/irc/format}/BoldSpan.java (95%) create mode 100644 app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java rename app/src/main/java/de/kuschku/util/irc/{ => format}/IrcFormatHelper.java (95%) rename app/src/main/java/de/kuschku/{quasseldroid_ng/ui/editor/FormattingHelper.java => util/irc/format/IrcFormatSerializer.java} (97%) rename app/src/main/java/de/kuschku/{quasseldroid_ng/ui/editor => util/irc/format}/ItalicSpan.java (95%) delete mode 100644 app/src/main/java/de/kuschku/util/ui/MessageUtil.java diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java index fdbf8fe17..b9d21cfbf 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/chatview/ChatMessageRenderer.java @@ -29,8 +29,8 @@ import android.support.annotation.UiThread; import de.kuschku.libquassel.localtypes.buffers.Buffer; import de.kuschku.libquassel.message.Message; import de.kuschku.quasseldroid_ng.ui.theme.AppContext; -import de.kuschku.util.irc.IrcFormatHelper; import de.kuschku.util.irc.IrcUserUtils; +import de.kuschku.util.irc.format.IrcFormatHelper; import static de.kuschku.util.AndroidAssert.assertNotNull; diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java index c466e13cf..60514b98f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/drawer/BufferItem.java @@ -40,7 +40,6 @@ import de.kuschku.libquassel.message.Message; import de.kuschku.libquassel.primitives.types.BufferInfo; import de.kuschku.quasseldroid_ng.R; import de.kuschku.quasseldroid_ng.ui.theme.AppContext; -import de.kuschku.util.ui.MessageUtil; public class BufferItem extends SecondaryDrawerItem { @NonNull @@ -144,14 +143,10 @@ public class BufferItem extends SecondaryDrawerItem { } @Override - public void onPostBindView(IDrawerItem drawerItem, @NonNull View view) { + public void onPostBindView(IDrawerItem drawerItem, View view) { super.onPostBindView(drawerItem, view); if (getDescription() != null && getDescription().getText() != null) - ((TextView) view.findViewById(R.id.material_drawer_description)).setText(MessageUtil.parseStyleCodes( - context.themeUtil(), - getDescription().getText(), - context.settings().mircColors.or(true) - )); + ((TextView) view.findViewById(R.id.material_drawer_description)).setText(context.deserializer().formatString(getDescription().getText())); } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java index 427a29df9..4293222e4 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/AdvancedEditor.java @@ -31,14 +31,15 @@ import android.text.style.UnderlineSpan; import android.widget.EditText; import de.kuschku.quasseldroid_ng.ui.theme.AppContext; +import de.kuschku.util.irc.format.BoldSpan; +import de.kuschku.util.irc.format.ItalicSpan; public class AdvancedEditor { + private final AppContext context; private final EditText editText; - @NonNull - private final FormattingHelper helper; public AdvancedEditor(AppContext context, EditText editText) { - this.helper = new FormattingHelper(context); + this.context = context; this.editText = editText; } @@ -140,6 +141,6 @@ public class AdvancedEditor { @NonNull public String toFormatString() { - return helper.toEscapeCodes(editText.getText()); + return context.serializer().toEscapeCodes(editText.getText()); } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppContext.java b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppContext.java index 3ccec30be..9cd1af445 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppContext.java +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/theme/AppContext.java @@ -26,12 +26,16 @@ import android.support.annotation.NonNull; import de.kuschku.libquassel.BusProvider; import de.kuschku.libquassel.client.Client; import de.kuschku.quasseldroid_ng.ui.chat.Settings; +import de.kuschku.util.irc.format.IrcFormatDeserializer; +import de.kuschku.util.irc.format.IrcFormatSerializer; public class AppContext { private ThemeUtil themeUtil; private Settings settings; private Client client; private BusProvider provider; + private IrcFormatDeserializer deserializer; + private IrcFormatSerializer serializer; public ThemeUtil themeUtil() { return themeUtil; @@ -39,6 +43,9 @@ public class AppContext { public void setThemeUtil(ThemeUtil themeUtil) { this.themeUtil = themeUtil; + + this.serializer = new IrcFormatSerializer(this); + this.deserializer = new IrcFormatDeserializer(this); } @NonNull @@ -88,4 +95,12 @@ public class AppContext { setProvider(provider); return this; } + + public IrcFormatDeserializer deserializer() { + return deserializer; + } + + public IrcFormatSerializer serializer() { + return serializer; + } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/BoldSpan.java b/app/src/main/java/de/kuschku/util/irc/format/BoldSpan.java similarity index 95% rename from app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/BoldSpan.java rename to app/src/main/java/de/kuschku/util/irc/format/BoldSpan.java index c5014797c..3e1342368 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/BoldSpan.java +++ b/app/src/main/java/de/kuschku/util/irc/format/BoldSpan.java @@ -19,7 +19,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid_ng.ui.editor; +package de.kuschku.util.irc.format; import android.graphics.Typeface; import android.text.style.StyleSpan; diff --git a/app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java new file mode 100644 index 000000000..1540be165 --- /dev/null +++ b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatDeserializer.java @@ -0,0 +1,304 @@ +/* + * QuasselDroid - Quassel client for Android + * Copyright (C) 2016 Janne Koschinski + * Copyright (C) 2016 Ken Børge Viktil + * Copyright (C) 2016 Magnus Fjell + * Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.util.irc.format; + + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.BackgroundColorSpan; +import android.text.style.ForegroundColorSpan; +import android.text.style.UnderlineSpan; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +import de.kuschku.quasseldroid_ng.ui.theme.AppContext; + +import static de.kuschku.util.AndroidAssert.assertNotNull; + +/** + * A helper class to turn mIRC formatted Strings into Android’s SpannableStrings with the same + * color and format codes + */ +public class IrcFormatDeserializer { + public static final int CODE_BOLD = 0x02; + public static final int CODE_COLOR = 0x03; + public static final int CODE_ITALIC = 0x1D; + public static final int CODE_UNDERLINE = 0x1F; + public static final int CODE_SWAP = 0x16; + public static final int CODE_RESET = 0x0F; + + private final AppContext context; + + public IrcFormatDeserializer(AppContext context) { + this.context = context; + } + + /** + * Try to read a number from a String in specified bounds + * + * @param str String to be read from + * @param start Start index (inclusive) + * @param end End index (exclusive) + * @return The byte represented by the digits read from the string + */ + public static byte readNumber(@NonNull String str, int start, int end) { + String result = str.substring(start, end); + if (result.isEmpty()) + return -1; + else + return (byte) Integer.parseInt(result, 10); + } + + /** + * @param str String to be searched in + * @param start Start position (inclusive) + * @return Index of first character that is not a digit + */ + private static int findEndOfNumber(@NonNull String str, int start) { + Set<Character> validCharCodes = new HashSet<>(Arrays.asList('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')); + String searchFrame = str.substring(start); + int i; + for (i = 0; i < 2 && i < searchFrame.length(); i++) { + if (!validCharCodes.contains(searchFrame.charAt(i))) { + break; + } + } + return start + i; + } + + @Nullable + private static IrcFormat fromId(char id) { + switch (id) { + case CODE_BOLD: + return new BoldIrcFormat(); + case CODE_ITALIC: + return new ItalicIrcFormat(); + case CODE_UNDERLINE: + return new UnderlineIrcFormat(); + default: + return null; + } + } + + /** + * Function to handle mIRC formatted strings + * + * @param str mIRC formatted String + * @return a CharSequence with Android’s span format representing the input string + */ + @NonNull + public CharSequence formatString(@NonNull String str) { + SpannableStringBuilder plainText = new SpannableStringBuilder(); + Stack<FormatDescription> stack = new Stack<>(); + boolean colorize = context.settings().mircColors.get(); + + // Iterating over every character + for (int i = 0; i < str.length(); i++) { + char character = str.charAt(i); + switch (character) { + case CODE_BOLD: + case CODE_ITALIC: + case CODE_UNDERLINE: { + if (!colorize) continue; + + // If there is an element on stack with the same code, close it + if (!stack.empty() && stack.peek().format.id() == character) { + stack.pop().apply(plainText, plainText.length()); + + // Otherwise create a new one + } else { + IrcFormat format = fromId(character); + assertNotNull(format); + stack.push(new FormatDescription(plainText.length(), format)); + } + } + break; + case CODE_COLOR: { + if (!colorize) continue; + + int foregroundStart = i + 1; + int foregroundEnd = findEndOfNumber(str, foregroundStart); + // If we have a foreground element + if (foregroundEnd > foregroundStart) { + byte foreground = readNumber(str, foregroundStart, foregroundEnd); + + byte background = -1; + int backgroundEnd = -1; + // If we have a background code, read it + if (str.length() > foregroundEnd && str.charAt(foregroundEnd) == ',') { + backgroundEnd = findEndOfNumber(str, foregroundEnd + 1); + background = readNumber(str, foregroundEnd + 1, backgroundEnd); + } + // If previous element was also a color element, try to reuse background + if (!stack.empty() && stack.peek().format.id() == CODE_COLOR) { + // Apply old format + FormatDescription oldFormat = stack.pop(); + oldFormat.apply(plainText, plainText.length()); + // Reuse old background, if possible + if (background == -1) + background = ((ColorIrcFormat) oldFormat.format).background; + } + // Add new format + stack.push(new FormatDescription(plainText.length(), new ColorIrcFormat(foreground, background))); + + // i points in front of the next character + i = ((backgroundEnd == -1) ? foregroundEnd : backgroundEnd) - 1; + + // Otherwise assume this is a closing tag + } else if (!stack.empty() && stack.peek().format.id() == CODE_COLOR) { + stack.pop().apply(plainText, plainText.length()); + } + } + break; + case CODE_SWAP: { + if (!colorize) continue; + + // If we have a color tag before, apply it, and create a new one with swapped colors + if (!stack.empty() && stack.peek().format.id() == CODE_COLOR) { + FormatDescription format = stack.pop(); + format.apply(plainText, plainText.length()); + stack.push(new FormatDescription(plainText.length(), ((ColorIrcFormat) format.format).copySwapped())); + } + } + break; + case CODE_RESET: { + if (!colorize) continue; + + // End all formatting tags + while (!stack.empty()) { + stack.pop().apply(plainText, plainText.length()); + } + } + break; + default: { + // Just append it, if it’s not special + plainText.append(character); + } + } + } + + // End all formatting tags + while (!stack.empty()) { + stack.pop().apply(plainText, plainText.length()); + } + return plainText; + } + + private interface IrcFormat { + void applyTo(@NonNull SpannableStringBuilder editable, int from, int to); + + byte id(); + } + + public interface ColorSupplier { + @ColorInt + int ircColor(byte color); + } + + private static class FormatDescription { + public final int start; + @NonNull + public final IrcFormat format; + + public FormatDescription(int start, @NonNull IrcFormat format) { + this.start = start; + this.format = format; + } + + public void apply(@NonNull SpannableStringBuilder editable, int end) { + format.applyTo(editable, start, end); + } + } + + private static class ItalicIrcFormat implements IrcFormat { + @Override + public void applyTo(@NonNull SpannableStringBuilder editable, int from, int to) { + editable.setSpan(new ItalicSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + + @Override + public byte id() { + return CODE_ITALIC; + } + } + + private static class UnderlineIrcFormat implements IrcFormat { + @Override + public void applyTo(@NonNull SpannableStringBuilder editable, int from, int to) { + editable.setSpan(new UnderlineSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + + @Override + public byte id() { + return CODE_UNDERLINE; + } + } + + private static class BoldIrcFormat implements IrcFormat { + @Override + public void applyTo(@NonNull SpannableStringBuilder editable, int from, int to) { + editable.setSpan(new BoldSpan(), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + + @Override + public byte id() { + return CODE_BOLD; + } + } + + private class ColorIrcFormat implements IrcFormat { + private final byte foreground; + private final byte background; + + public ColorIrcFormat(byte foreground, byte background) { + this.foreground = foreground; + this.background = background; + } + + @Override + public void applyTo(@NonNull SpannableStringBuilder editable, int from, int to) { + int[] mircColors = context.themeUtil().res.mircColors; + if (foreground != -1) { + editable.setSpan(new ForegroundColorSpan(mircColors[foreground % 16]), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + if (background != -1) { + editable.setSpan(new BackgroundColorSpan(mircColors[background % 16]), from, to, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + + @NonNull + public ColorIrcFormat copySwapped() { + return new ColorIrcFormat(background, foreground); + } + + @Override + public byte id() { + return CODE_COLOR; + } + } +} diff --git a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatHelper.java similarity index 95% rename from app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java rename to app/src/main/java/de/kuschku/util/irc/format/IrcFormatHelper.java index b538a376f..21fadd263 100644 --- a/app/src/main/java/de/kuschku/util/irc/IrcFormatHelper.java +++ b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatHelper.java @@ -19,10 +19,9 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.util.irc; +package de.kuschku.util.irc.format; -import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -47,7 +46,7 @@ import java.util.regex.Pattern; import de.kuschku.quasseldroid_ng.R; import de.kuschku.quasseldroid_ng.ui.theme.AppContext; -import de.kuschku.util.ui.MessageUtil; +import de.kuschku.util.irc.IrcUserUtils; public class IrcFormatHelper { @NonNull @@ -85,7 +84,7 @@ public class IrcFormatHelper { public CharSequence formatIrcMessage(@NonNull String message) { List<FutureClickableSpan> spans = new LinkedList<>(); - SpannableString str = new SpannableString(MessageUtil.parseStyleCodes(context.themeUtil(), message, context.settings().mircColors.get())); + SpannableString str = new SpannableString(context.deserializer().formatString(message)); Matcher urlMatcher = urlPattern.matcher(str); while (urlMatcher.find()) { spans.add(new FutureClickableSpan(new CustomURLSpan(urlMatcher.group()), urlMatcher.start(), urlMatcher.end())); diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatSerializer.java similarity index 97% rename from app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java rename to app/src/main/java/de/kuschku/util/irc/format/IrcFormatSerializer.java index e40bde252..df9bcc4db 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/FormattingHelper.java +++ b/app/src/main/java/de/kuschku/util/irc/format/IrcFormatSerializer.java @@ -19,7 +19,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid_ng.ui.editor; +package de.kuschku.util.irc.format; import android.support.annotation.NonNull; import android.text.Spanned; @@ -32,10 +32,10 @@ import java.util.Locale; import de.kuschku.quasseldroid_ng.ui.theme.AppContext; -public class FormattingHelper { +public class IrcFormatSerializer { private final AppContext context; - public FormattingHelper(AppContext context) { + public IrcFormatSerializer(AppContext context) { this.context = context; } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/ItalicSpan.java b/app/src/main/java/de/kuschku/util/irc/format/ItalicSpan.java similarity index 95% rename from app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/ItalicSpan.java rename to app/src/main/java/de/kuschku/util/irc/format/ItalicSpan.java index 7a8a638cc..96c93a85b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/editor/ItalicSpan.java +++ b/app/src/main/java/de/kuschku/util/irc/format/ItalicSpan.java @@ -19,7 +19,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -package de.kuschku.quasseldroid_ng.ui.editor; +package de.kuschku.util.irc.format; import android.graphics.Typeface; import android.text.style.StyleSpan; diff --git a/app/src/main/java/de/kuschku/util/ui/MessageUtil.java b/app/src/main/java/de/kuschku/util/ui/MessageUtil.java deleted file mode 100644 index 856ecac77..000000000 --- a/app/src/main/java/de/kuschku/util/ui/MessageUtil.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * QuasselDroid - Quassel client for Android - * Copyright (C) 2016 Janne Koschinski - * Copyright (C) 2016 Ken Børge Viktil - * Copyright (C) 2016 Magnus Fjell - * Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package de.kuschku.util.ui; - -import android.graphics.Typeface; -import android.support.annotation.NonNull; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.style.BackgroundColorSpan; -import android.text.style.ForegroundColorSpan; -import android.text.style.StyleSpan; -import android.text.style.UnderlineSpan; - -import de.kuschku.quasseldroid_ng.ui.theme.ThemeUtil; - -public class MessageUtil { - // Transparent in ARGB - private static final int COLOR_TRANSPARENT = 0x00000000; - - /** - * Parse mIRC style codes in IrcMessage - */ - @NonNull - public static SpannableString parseStyleCodes(@NonNull ThemeUtil themeUtil, @NonNull String content, boolean parse) { - if (!parse) { - return new SpannableString(content - .replaceAll("\\x02", "") - .replaceAll("\\x0F", "") - .replaceAll("\\x1D", "") - .replaceAll("\\x1F", "") - .replaceAll("\\x03[0-9]{1,2}(,[0-9]{1,2})?", "") - .replaceAll("\\x03", "")); - } - - final char boldIndicator = 2; - final char normalIndicator = 15; - final char italicIndicator = 29; - final char underlineIndicator = 31; - final char colorIndicator = 3; - - if (content.indexOf(boldIndicator) == -1 - && content.indexOf(italicIndicator) == -1 - && content.indexOf(underlineIndicator) == -1 - && content.indexOf(colorIndicator) == -1) - return new SpannableString(content); - - SpannableStringBuilder newString = new SpannableStringBuilder(content); - - int start, end, endSearchOffset, startIndicatorLength, style, fg, bg; - while (true) { - content = newString.toString(); - end = -1; - startIndicatorLength = 1; - style = 0; - fg = -1; - bg = -1; - - // Colors? - start = content.indexOf(colorIndicator); - - if (start != -1) { - // Note that specifying colour codes here is optional, as the same indicator will cancel existing colours - endSearchOffset = start + 1; - if (endSearchOffset < content.length()) { - if (Character.isDigit(content.charAt(endSearchOffset))) { - if (endSearchOffset + 1 < content.length() && Character.isDigit(content.charAt(endSearchOffset + 1))) { - fg = Integer.parseInt(content.substring(endSearchOffset, endSearchOffset + 2)); - endSearchOffset += 2; - } else { - fg = Integer.parseInt(content.substring(endSearchOffset, endSearchOffset + 1)); - endSearchOffset += 1; - } - - if (endSearchOffset < content.length() && content.charAt(endSearchOffset) == ',') { - if (endSearchOffset + 1 < content.length() && Character.isDigit(content.charAt(endSearchOffset + 1))) { - endSearchOffset++; - if (endSearchOffset + 1 < content.length() && Character.isDigit(content.charAt(endSearchOffset + 1))) { - bg = Integer.parseInt(content.substring(endSearchOffset, endSearchOffset + 2)); - endSearchOffset += 2; - } else { - bg = Integer.parseInt(content.substring(endSearchOffset, endSearchOffset + 1)); - endSearchOffset += 1; - } - } - } - } - } - startIndicatorLength = endSearchOffset - start; - - end = content.indexOf(colorIndicator, endSearchOffset); - } - - if (start == -1) { - start = content.indexOf(boldIndicator); - if (start != -1) { - end = content.indexOf(boldIndicator, start + 1); - style = Typeface.BOLD; - } - } - - if (start == -1) { - start = content.indexOf(italicIndicator); - if (start != -1) { - end = content.indexOf(italicIndicator, start + 1); - style = Typeface.ITALIC; - } - } - - if (start == -1) { - start = content.indexOf(underlineIndicator); - if (start != -1) { - end = content.indexOf(underlineIndicator, start + 1); - style = -1; - } - } - - if (start == -1) - break; - - int norm = content.indexOf(normalIndicator, start + 1); - if (norm != -1 && (end == -1 || norm < end)) - end = norm; - - if (end == -1) - end = content.length(); - - if (end - (start + startIndicatorLength) > 0) { - // Only set spans if there's any text between start & end - if (style == -1) { - newString.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } else { - newString.setSpan(new StyleSpan(style), start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - - if (fg != -1 && themeUtil.res.mircColors[fg] != COLOR_TRANSPARENT) { - newString.setSpan(new ForegroundColorSpan(themeUtil.res.mircColors[fg]), start, end, - Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - if (bg != -1 && themeUtil.res.mircColors[fg] != COLOR_TRANSPARENT) { - newString.setSpan(new BackgroundColorSpan(themeUtil.res.mircColors[bg]), start, end, - Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - - // Intentionally don't remove "normal" indicators or color here, as they are multi-purpose - if (end < content.length() && (content.charAt(end) == boldIndicator - || content.charAt(end) == italicIndicator - || content.charAt(end) == underlineIndicator)) - newString.delete(end, end + 1); - - newString.delete(start, start + startIndicatorLength); - } - - // NOW we remove the "normal" and color indicator - while (true) { - content = newString.toString(); - int normPos = content.indexOf(normalIndicator); - if (normPos != -1) - newString.delete(normPos, normPos + 1); - - int colorPos = content.indexOf(colorIndicator); - if (colorPos != -1) - newString.delete(colorPos, colorPos + 1); - - if (normPos == -1 && colorPos == -1) - break; - } - - return new SpannableString(newString); - } -} -- GitLab