Skip to content

Commit fedea73

Browse files
authored
Merge pull request #1041 from quizlet/feature/background-color-span-support-v3-upstream
Added backgroundColorSpan support
2 parents ed7799b + 714318b commit fedea73

19 files changed

+296
-11
lines changed

app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import androidx.appcompat.app.AlertDialog
3434
import androidx.appcompat.app.AppCompatActivity
3535
import androidx.appcompat.content.res.AppCompatResources
3636
import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback
37+
import androidx.core.content.ContextCompat
3738
import androidx.core.content.FileProvider
3839
import org.wordpress.android.util.AppLog
3940
import org.wordpress.android.util.ImageUtils
@@ -48,6 +49,8 @@ import org.wordpress.aztec.IHistoryListener
4849
import org.wordpress.aztec.ITextFormat
4950
import org.wordpress.aztec.glideloader.GlideImageLoader
5051
import org.wordpress.aztec.glideloader.GlideVideoThumbnailLoader
52+
import org.wordpress.aztec.plugins.BackgroundColorButton
53+
import org.wordpress.aztec.plugins.CssBackgroundColorPlugin
5154
import org.wordpress.aztec.plugins.CssUnderlinePlugin
5255
import org.wordpress.aztec.plugins.IMediaToolbarButton
5356
import org.wordpress.aztec.plugins.shortcodes.AudioShortcodePlugin
@@ -92,6 +95,7 @@ open class MainActivity : AppCompatActivity(),
9295
private val BOLD = "<b>Bold</b><br>"
9396
private val ITALIC = "<i style=\"color:darkred\">Italic</i><br>"
9497
private val UNDERLINE = "<u style=\"color:lime\">Underline</u><br>"
98+
private val BACKGROUND = "<span style=\"background-color:#005082\">BACK<b>GROUND</b></span><br>"
9599
private val STRIKETHROUGH = "<s style=\"color:#ff666666\" class=\"test\">Strikethrough</s><br>" // <s> or <strike> or <del>
96100
private val ORDERED = "<ol style=\"color:green\"><li>Ordered</li><li>should have color</li></ol>"
97101
private val TASK_LIST = "<ul type=\"task-list\">\n" +
@@ -193,6 +197,7 @@ open class MainActivity : AppCompatActivity(),
193197
BOLD +
194198
ITALIC +
195199
UNDERLINE +
200+
BACKGROUND +
196201
STRIKETHROUGH +
197202
TASK_LIST +
198203
ORDERED +
@@ -484,9 +489,13 @@ open class MainActivity : AppCompatActivity(),
484489
aztec.visualEditor.setCalypsoMode(false)
485490
aztec.sourceEditor?.setCalypsoMode(false)
486491

492+
aztec.visualEditor.setBackgroundSpanColor(ContextCompat.getColor(this, R.color.blue_dark))
493+
487494
aztec.sourceEditor?.displayStyledAndFormattedHtml(EXAMPLE)
488495

489496
aztec.addPlugin(CssUnderlinePlugin())
497+
aztec.addPlugin(CssBackgroundColorPlugin())
498+
aztec.addPlugin(BackgroundColorButton(visualEditor))
490499
}
491500

492501
if (savedInstanceState == null) {

aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import android.text.Spanned
2828
import androidx.appcompat.content.res.AppCompatResources
2929
import org.wordpress.aztec.plugins.IAztecPlugin
3030
import org.wordpress.aztec.plugins.html2visual.IHtmlTagHandler
31+
import org.wordpress.aztec.source.CssStyleFormatter
3132
import org.wordpress.aztec.spans.AztecAudioSpan
33+
import org.wordpress.aztec.spans.AztecBackgroundColorSpan
3234
import org.wordpress.aztec.spans.AztecHorizontalRuleSpan
3335
import org.wordpress.aztec.spans.AztecImageSpan
3436
import org.wordpress.aztec.spans.AztecListItemSpan
@@ -41,6 +43,7 @@ import org.wordpress.aztec.spans.AztecVideoSpan
4143
import org.wordpress.aztec.spans.HiddenHtmlSpan
4244
import org.wordpress.aztec.spans.IAztecAttributedSpan
4345
import org.wordpress.aztec.spans.IAztecNestable
46+
import org.wordpress.aztec.spans.IAztecSpan
4447
import org.wordpress.aztec.spans.createAztecQuoteSpan
4548
import org.wordpress.aztec.spans.createHeadingSpan
4649
import org.wordpress.aztec.spans.createHiddenHtmlBlockSpan
@@ -51,6 +54,7 @@ import org.wordpress.aztec.spans.createParagraphSpan
5154
import org.wordpress.aztec.spans.createPreformatSpan
5255
import org.wordpress.aztec.spans.createTaskListSpan
5356
import org.wordpress.aztec.spans.createUnorderedListSpan
57+
import org.wordpress.aztec.util.ColorConverter
5458
import org.wordpress.aztec.util.getLast
5559
import org.xml.sax.Attributes
5660
import java.util.Locale
@@ -87,7 +91,7 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar
8791
return true
8892
}
8993
SPAN -> {
90-
val span = createHiddenHtmlSpan(tag, AztecAttributes(attributes), nestingLevel, alignmentRendering)
94+
val span = handleBackgroundColorSpanTag(attributes, tag, nestingLevel)
9195
handleElement(output, opening, span)
9296
return true
9397
}
@@ -170,6 +174,21 @@ class AztecTagHandler(val context: Context, val plugins: List<IAztecPlugin> = Ar
170174
return false
171175
}
172176

177+
private fun handleBackgroundColorSpanTag(attributes: Attributes, tag: String, nestingLevel: Int): IAztecSpan {
178+
val attrs = AztecAttributes(attributes)
179+
return if (CssStyleFormatter.containsStyleAttribute(
180+
attrs,
181+
CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE
182+
) || (tagStack.isNotEmpty() && tagStack.last() is AztecBackgroundColorSpan)
183+
) {
184+
val att = CssStyleFormatter.getStyleAttribute(attrs, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE)
185+
val color = ColorConverter.getColorInt(att)
186+
AztecBackgroundColorSpan(color)
187+
} else {
188+
createHiddenHtmlSpan(tag, attrs, nestingLevel, alignmentRendering)
189+
}
190+
}
191+
173192
/**
174193
* This method takes the checkbox input inside a list item and applies a parameter to the parent list item.
175194
* We convert <li><input type=checkbox checked />Test</li>

aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,10 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
405405
isInCalypsoMode = isCompatibleWithCalypso
406406
}
407407

408+
fun setBackgroundSpanColor(color: Int) {
409+
inlineFormatter.backgroundSpanColor = color
410+
}
411+
408412
fun setGutenbergMode(isCompatibleWithGutenberg: Boolean) {
409413
isInGutenbergMode = isCompatibleWithGutenberg
410414
}
@@ -1304,6 +1308,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
13041308
AztecTextFormat.FORMAT_CITE,
13051309
AztecTextFormat.FORMAT_UNDERLINE,
13061310
AztecTextFormat.FORMAT_STRIKETHROUGH,
1311+
AztecTextFormat.FORMAT_BACKGROUND,
13071312
AztecTextFormat.FORMAT_HIGHLIGHT,
13081313
AztecTextFormat.FORMAT_CODE -> inlineFormatter.toggle(textFormat)
13091314
AztecTextFormat.FORMAT_BOLD,
@@ -1344,6 +1349,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
13441349
AztecTextFormat.FORMAT_CITE,
13451350
AztecTextFormat.FORMAT_UNDERLINE,
13461351
AztecTextFormat.FORMAT_STRIKETHROUGH,
1352+
AztecTextFormat.FORMAT_BACKGROUND,
13471353
AztecTextFormat.FORMAT_MARK,
13481354
AztecTextFormat.FORMAT_HIGHLIGHT,
13491355
AztecTextFormat.FORMAT_CODE -> return inlineFormatter.containsInlineStyle(format, selStart, selEnd)
@@ -1360,7 +1366,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
13601366
}
13611367
}
13621368

1363-
fun setToolbar(toolbar: IAztecToolbar) {
1369+
fun setToolbar(toolbar: IAztecToolbar?) {
13641370
formatToolbar = toolbar
13651371
}
13661372

@@ -1844,6 +1850,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
18441850
inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_STRIKETHROUGH, start, end)
18451851
inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_UNDERLINE, start, end)
18461852
inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_CODE, start, end)
1853+
inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_BACKGROUND, start, end)
18471854
inlineFormatter.removeInlineStyle(AztecTextFormat.FORMAT_MARK, start, end)
18481855
}
18491856

aztec/src/main/kotlin/org/wordpress/aztec/AztecTextFormat.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ enum class AztecTextFormat : ITextFormat {
3636
FORMAT_FONT,
3737
FORMAT_MONOSPACE,
3838
FORMAT_CODE,
39+
FORMAT_BACKGROUND,
3940
FORMAT_MARK,
4041
FORMAT_HIGHLIGHT
4142
}

aztec/src/main/kotlin/org/wordpress/aztec/formatting/InlineFormatter.kt

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.wordpress.aztec.formatting
22

33
import android.graphics.Typeface
44
import android.text.Spanned
5+
import android.text.style.BackgroundColorSpan
56
import android.text.style.ForegroundColorSpan
67
import android.text.style.StyleSpan
78
import androidx.annotation.ColorRes
@@ -12,14 +13,16 @@ import org.wordpress.aztec.AztecText
1213
import org.wordpress.aztec.AztecTextFormat
1314
import org.wordpress.aztec.Constants
1415
import org.wordpress.aztec.ITextFormat
16+
import org.wordpress.aztec.R
17+
import org.wordpress.aztec.spans.AztecBackgroundColorSpan
1518
import org.wordpress.aztec.spans.AztecCodeSpan
1619
import org.wordpress.aztec.spans.AztecStrikethroughSpan
1720
import org.wordpress.aztec.spans.AztecStyleBoldSpan
1821
import org.wordpress.aztec.spans.AztecStyleCiteSpan
19-
import org.wordpress.aztec.spans.AztecStyleItalicSpan
2022
import org.wordpress.aztec.spans.AztecStyleEmphasisSpan
21-
import org.wordpress.aztec.spans.AztecStyleStrongSpan
23+
import org.wordpress.aztec.spans.AztecStyleItalicSpan
2224
import org.wordpress.aztec.spans.AztecStyleSpan
25+
import org.wordpress.aztec.spans.AztecStyleStrongSpan
2326
import org.wordpress.aztec.spans.AztecUnderlineSpan
2427
import org.wordpress.aztec.spans.HighlightSpan
2528
import org.wordpress.aztec.spans.IAztecExclusiveInlineSpan
@@ -33,6 +36,8 @@ import org.wordpress.aztec.watchers.TextChangedEvent
3336
*/
3437
class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val highlightStyle: HighlightStyle) : AztecFormatter(editor) {
3538

39+
var backgroundSpanColor: Int? = null
40+
3641
data class CodeStyle(val codeBackground: Int, val codeBackgroundAlpha: Float, val codeColor: Int)
3742
data class HighlightStyle(@ColorRes val color: Int)
3843

@@ -93,6 +98,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
9398
AztecTextFormat.FORMAT_EMPHASIS,
9499
AztecTextFormat.FORMAT_CITE,
95100
AztecTextFormat.FORMAT_STRIKETHROUGH,
101+
AztecTextFormat.FORMAT_BACKGROUND,
96102
AztecTextFormat.FORMAT_UNDERLINE,
97103
AztecTextFormat.FORMAT_CODE -> {
98104
applyInlineStyle(item, textChangedEvent.inputStart, textChangedEvent.inputEnd)
@@ -146,6 +152,11 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
146152
return
147153
}
148154

155+
if (textFormat == AztecTextFormat.FORMAT_BACKGROUND) {
156+
//clear previous background before applying a new one to avoid problems when using multiple bg colors
157+
removeBackgroundInSelection(selectionStart, selectionEnd)
158+
}
159+
149160
var precedingSpan: IAztecInlineSpan? = null
150161
var followingSpan: IAztecInlineSpan? = null
151162

@@ -211,6 +222,34 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
211222
joinStyleSpans(start, end)
212223
}
213224

225+
private fun removeBackgroundInSelection(selStart: Int, selEnd: Int) {
226+
val spans = editableText.getSpans(selStart, selEnd, AztecBackgroundColorSpan::class.java)
227+
spans.forEach { span ->
228+
if (span != null) {
229+
val currentSpanStart = editableText.getSpanStart(span)
230+
val currentSpanEnd = editableText.getSpanEnd(span)
231+
val color = span.backgroundColor
232+
editableText.removeSpan(span)
233+
if (selEnd < currentSpanEnd) {
234+
editableText.setSpan(
235+
AztecBackgroundColorSpan(color),
236+
selEnd,
237+
currentSpanEnd,
238+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
239+
)
240+
}
241+
if (selStart > currentSpanStart) {
242+
editableText.setSpan(
243+
AztecBackgroundColorSpan(color),
244+
currentSpanStart,
245+
selStart,
246+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
247+
)
248+
}
249+
}
250+
}
251+
}
252+
214253
private fun applyMarkInlineStyle(start: Int = selectionStart, end: Int = selectionEnd) {
215254
val previousSpans = editableText.getSpans(start, end, MarkSpan::class.java)
216255
previousSpans.forEach {
@@ -247,6 +286,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
247286
AztecStrikethroughSpan::class.java -> AztecTextFormat.FORMAT_STRIKETHROUGH
248287
AztecUnderlineSpan::class.java -> AztecTextFormat.FORMAT_UNDERLINE
249288
AztecCodeSpan::class.java -> AztecTextFormat.FORMAT_CODE
289+
AztecBackgroundColorSpan::class.java -> return AztecTextFormat.FORMAT_BACKGROUND
250290
MarkSpan::class.java -> AztecTextFormat.FORMAT_MARK
251291
HighlightSpan::class.java -> AztecTextFormat.FORMAT_HIGHLIGHT
252292
else -> null
@@ -265,6 +305,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
265305
editableText.removeSpan(it)
266306
}
267307
}
308+
268309
// remove the CSS style span
269310
removeInlineCssStyle()
270311

@@ -282,9 +323,11 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
282323
joinStyleSpans(start, end)
283324
}
284325

285-
fun removeInlineCssStyle(start: Int = selectionStart, end: Int = selectionEnd) {
286-
val spans = editableText.getSpans(start, end, ForegroundColorSpan::class.java)
287-
spans.forEach {
326+
private fun removeInlineCssStyle(start: Int = selectionStart, end: Int = selectionEnd) {
327+
editableText.getSpans(start, end, ForegroundColorSpan::class.java).forEach {
328+
editableText.removeSpan(it)
329+
}
330+
editableText.getSpans(start, end, BackgroundColorSpan::class.java).forEach {
288331
editableText.removeSpan(it)
289332
}
290333
}
@@ -298,6 +341,10 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
298341
if (firstSpan is StyleSpan && secondSpan is StyleSpan) {
299342
return firstSpan.style == secondSpan.style
300343
}
344+
// special check for BackgroundSpan
345+
if (firstSpan is AztecBackgroundColorSpan && secondSpan is AztecBackgroundColorSpan) {
346+
return firstSpan.backgroundColor == secondSpan.backgroundColor
347+
}
301348

302349
return firstSpan.javaClass == secondSpan.javaClass
303350
}
@@ -393,6 +440,7 @@ class InlineFormatter(editor: AztecText, val codeStyle: CodeStyle, private val h
393440
AztecTextFormat.FORMAT_STRIKETHROUGH -> AztecStrikethroughSpan()
394441
AztecTextFormat.FORMAT_UNDERLINE -> AztecUnderlineSpan()
395442
AztecTextFormat.FORMAT_CODE -> AztecCodeSpan(codeStyle)
443+
AztecTextFormat.FORMAT_BACKGROUND -> AztecBackgroundColorSpan(backgroundSpanColor ?: R.color.background)
396444
AztecTextFormat.FORMAT_HIGHLIGHT -> {
397445
HighlightSpan(highlightStyle = highlightStyle, context = editor.context)
398446
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.wordpress.aztec.plugins
2+
3+
import android.view.LayoutInflater
4+
import android.view.View
5+
import android.view.ViewGroup
6+
import org.wordpress.aztec.AztecText
7+
import org.wordpress.aztec.R
8+
import org.wordpress.aztec.toolbar.AztecToolbar
9+
import org.wordpress.aztec.toolbar.IToolbarAction
10+
import org.wordpress.aztec.toolbar.ToolbarAction
11+
12+
class BackgroundColorButton(private val visualEditor: AztecText) : IToolbarButton {
13+
14+
override val action: IToolbarAction = ToolbarAction.BACKGROUND
15+
override val context = visualEditor.context!!
16+
17+
override fun toggle() {
18+
visualEditor.toggleFormatting(action.textFormats.first())
19+
}
20+
21+
override fun inflateButton(parent: ViewGroup) {
22+
LayoutInflater.from(context).inflate(R.layout.background_color_button, parent)
23+
}
24+
25+
override fun toolbarStateAboutToChange(toolbar: AztecToolbar, enable: Boolean) {
26+
toolbar.findViewById<View>(action.buttonId).isEnabled = enable
27+
}
28+
29+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.wordpress.aztec.plugins
2+
3+
import android.text.SpannableStringBuilder
4+
import org.wordpress.aztec.plugins.visual2html.ISpanPreprocessor
5+
import org.wordpress.aztec.source.CssStyleFormatter
6+
import org.wordpress.aztec.spans.AztecBackgroundColorSpan
7+
8+
class CssBackgroundColorPlugin : ISpanPreprocessor {
9+
10+
override fun beforeSpansProcessed(spannable: SpannableStringBuilder) {
11+
spannable.getSpans(0, spannable.length, AztecBackgroundColorSpan::class.java).forEach {
12+
if (!CssStyleFormatter.containsStyleAttribute(it.attributes, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE)) {
13+
CssStyleFormatter.addStyleAttribute(it.attributes, CssStyleFormatter.CSS_BACKGROUND_COLOR_ATTRIBUTE, it.getColorHex())
14+
}
15+
}
16+
}
17+
}

aztec/src/main/kotlin/org/wordpress/aztec/source/CssStyleFormatter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class CssStyleFormatter {
3131

3232
/**
3333
* Check the provided [attributedSpan] for the *style* attribute. If found, parse out the
34-
* supported CSS style properties and use the results to create a [ForegroundColorSpan],
34+
* supported CSS style properties and use the results to create a [ForegroundColorSpan] and/or [BackgroundColorSpan]
3535
* then add it to the provided [text].
3636
*
3737
* Must be called immediately after the base [IAztecAttributedSpan] has been processed.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.wordpress.aztec.spans
2+
3+
import android.graphics.Color
4+
import android.text.TextPaint
5+
import android.text.style.BackgroundColorSpan
6+
import org.wordpress.aztec.AztecAttributes
7+
8+
class AztecBackgroundColorSpan(
9+
val color: Int
10+
) : BackgroundColorSpan(color), IAztecInlineSpan {
11+
12+
var alpha: Int = 220
13+
var tag: String = "span"
14+
override var attributes: AztecAttributes = AztecAttributes()
15+
16+
fun getColorHex(): String {
17+
return java.lang.String.format("#%06X", 0xFFFFFF and color)
18+
}
19+
20+
override fun updateDrawState(textPaint: TextPaint) {
21+
textPaint.bgColor = Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color))
22+
}
23+
24+
override val TAG = tag
25+
}

0 commit comments

Comments
 (0)