Skip to content

Commit 2b941d0

Browse files
authored
Merge pull request #202 from wordpress-mobile/issue/201-image-blocks
Issue/201 image blocks
2 parents 007a890 + 28fa5ed commit 2b941d0

File tree

12 files changed

+145
-120
lines changed

12 files changed

+145
-120
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque
127127
}
128128
}
129129

130-
val source = "<img src=\"$mediaPath\">" // Temporary source value. Replace with URL after uploaded.
130+
val source = mediaPath // Temporary source value. Replace with URL after uploaded.
131131
aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), source)
132132
}
133133

aztec/src/main/java/org/wordpress/aztec/Html.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import org.wordpress.aztec.spans.AztecContentSpan;
4343
import org.wordpress.aztec.spans.AztecCursorSpan;
4444
import org.wordpress.aztec.spans.AztecHeadingSpan;
45+
import org.wordpress.aztec.spans.AztecListSpan;
46+
import org.wordpress.aztec.spans.AztecMediaSpan;
4547
import org.wordpress.aztec.spans.AztecRelativeSizeSpan;
4648
import org.wordpress.aztec.spans.AztecStyleSpan;
4749
import org.wordpress.aztec.spans.AztecSubscriptSpan;
@@ -103,7 +105,7 @@ public interface TagHandler {
103105
* This method will be called whenn the HTML parser encounters
104106
* a tag that it does not know how to interpret.
105107
*/
106-
boolean handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader, Attributes attributes);
108+
boolean handleTag(boolean opening, String tag, Editable output, Context context, Attributes attributes);
107109
}
108110

109111
private Html() {
@@ -473,7 +475,7 @@ public Spanned convert() {
473475
// Fix flags and range for paragraph-type markup.
474476
Object[] obj = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ParagraphStyle.class);
475477
for (int i = 0; i < obj.length; i++) {
476-
if (obj[i] instanceof UnknownHtmlSpan || obj[i] instanceof AztecBlockSpan || obj[i] instanceof AztecHeadingSpan) {
478+
if (obj[i] instanceof UnknownHtmlSpan || obj[i] instanceof AztecBlockSpan || obj[i] instanceof AztecMediaSpan || obj[i] instanceof AztecHeadingSpan) {
477479
continue;
478480
}
479481
int start = spannableStringBuilder.getSpanStart(obj[i]);
@@ -544,11 +546,9 @@ private void handleStartTag(String tag, Attributes attributes) {
544546
start(spannableStringBuilder, new Sub(attributes));
545547
} else if (tag.equalsIgnoreCase("code")) {
546548
start(spannableStringBuilder, new Code(attributes));
547-
} else if (tag.equalsIgnoreCase("img")) {
548-
startImg(spannableStringBuilder, attributes, context);
549549
} else {
550550
if (tagHandler != null) {
551-
boolean tagHandled = tagHandler.handleTag(true, tag, spannableStringBuilder, mReader, attributes);
551+
boolean tagHandled = tagHandler.handleTag(true, tag, spannableStringBuilder, context, attributes);
552552
if (tagHandled) {
553553
return;
554554
}
@@ -617,7 +617,7 @@ private void handleEndTag(String tag) {
617617
} else if (tag.equalsIgnoreCase("code")) {
618618
end(spannableStringBuilder, TextFormat.FORMAT_CODE);
619619
} else if (tagHandler != null) {
620-
tagHandler.handleTag(false, tag, spannableStringBuilder, mReader, null);
620+
tagHandler.handleTag(false, tag, spannableStringBuilder, context, null);
621621
}
622622
}
623623

@@ -747,21 +747,6 @@ private static void end(SpannableStringBuilder text, TextFormat textFormat) {
747747
}
748748
}
749749

750-
private static void startImg(final SpannableStringBuilder text,
751-
Attributes attributes, final Context context) {
752-
final String src = attributes.getValue("", "src");
753-
final int start = text.length();
754-
755-
// TODO: we should some placeholder drawable while loading imges
756-
Drawable loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading);
757-
758-
loadingDrawable.setBounds(0, 0, loadingDrawable.getIntrinsicWidth(), loadingDrawable.getIntrinsicHeight());
759-
final ImageSpan imageSpan = new ImageSpan(loadingDrawable, src);
760-
761-
text.append("\uFFFC");
762-
text.setSpan(imageSpan, start, text.length(),
763-
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
764-
}
765750

766751
private static void endFont(SpannableStringBuilder text) {
767752
int len = text.length();

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

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,6 @@ class AztecParser {
251251
withinUnknown(out, text, i, next, styles[0] as UnknownHtmlSpan)
252252
} else if (styles[0] is ParagraphSpan) {
253253
withinParagraph(out, text, i, next)
254-
} else if (styles[0] is AztecMediaSpan) {
255-
withinMedia(out, text, i, next, styles[0] as AztecMediaSpan)
256254
} else {
257255
withinContent(out, text, i, next)
258256
}
@@ -384,13 +382,7 @@ class AztecParser {
384382
i = next
385383
}
386384
}
387-
388-
private fun withinMedia(out: StringBuilder, text: Spanned, start: Int, end: Int, mediaSpan: AztecMediaSpan) {
389-
consumeCursorIfInInput(out, text, start)
390-
out.append(mediaSpan.getHtml())
391-
consumeCursorIfInInput(out, text, end)
392-
}
393-
385+
394386
private fun withinContent(out: StringBuilder, text: Spanned, start: Int, end: Int, ignoreHeading: Boolean = false) {
395387
var next: Int
396388

@@ -456,19 +448,15 @@ class AztecParser {
456448
out.append("<${span.getStartTag()}>")
457449
}
458450

459-
if (span is ImageSpan && span !is AztecCommentSpan && span !is AztecMediaSpan && span !is UnknownHtmlSpan) {
460-
out.append("<img src=\"")
461-
out.append(span.source)
462-
out.append("\">")
463-
464-
// Don't output the dummy character underlying the image.
465-
i = next
466-
}
467-
468451
if (span is AztecCommentSpan || span is CommentSpan) {
469452
out.append("<!--")
470453
}
471454

455+
if (span is AztecMediaSpan) {
456+
out.append(span.getHtml())
457+
i = next
458+
}
459+
472460
if (span is HiddenHtmlSpan) {
473461
parseHiddenSpans(i, out, span, text)
474462
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121

2222
package org.wordpress.aztec
2323

24+
import android.content.Context
25+
import android.support.v4.content.ContextCompat
2426
import android.text.Editable
2527
import android.text.Spannable
28+
import android.text.SpannableStringBuilder
2629
import android.text.Spanned
2730
import org.wordpress.aztec.spans.*
2831
import org.xml.sax.Attributes
@@ -32,7 +35,7 @@ class AztecTagHandler : Html.TagHandler {
3235

3336
private var order = 0
3437

35-
override fun handleTag(opening: Boolean, tag: String, output: Editable, xmlReader: XMLReader, attributes: Attributes?): Boolean {
38+
override fun handleTag(opening: Boolean, tag: String, output: Editable, context: Context, attributes: Attributes?): Boolean {
3639
val attributeString = Html.stringifyAttributes(attributes).toString()
3740

3841
when (tag.toLowerCase()) {
@@ -72,6 +75,15 @@ class AztecTagHandler : Html.TagHandler {
7275
handleBlockElement(output, opening, AztecQuoteSpan(attributeString))
7376
return true
7477
}
78+
IMAGE -> {
79+
if (opening) {
80+
start(output, createImageSpan(attributes, context))
81+
output.append("\uFFFC")
82+
} else {
83+
end(output, AztecMediaSpan::class.java)
84+
}
85+
return true
86+
}
7587
PARAGRAPH -> {
7688
handleBlockElement(output, opening, ParagraphSpan(attributeString))
7789
return true
@@ -87,6 +99,12 @@ class AztecTagHandler : Html.TagHandler {
8799
return false
88100
}
89101

102+
private fun createImageSpan(attributes: Attributes?, context: Context) : AztecMediaSpan {
103+
val src = attributes?.getValue("", "src") ?: ""
104+
val loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading)
105+
return AztecMediaSpan(context, loadingDrawable, src, Html.stringifyAttributes(attributes).toString())
106+
}
107+
90108
private fun handleBlockElement(output: Editable, opening: Boolean, span: Any) {
91109
if (output.isNotBlank()) {
92110
val nestedInBlockElement = isNestedInBlockElement(output, opening)
@@ -170,6 +188,7 @@ class AztecTagHandler : Html.TagHandler {
170188
private val SPAN = "span"
171189
private val BLOCKQUOTE = "blockquote"
172190
private val PARAGRAPH = "p"
191+
private val IMAGE = "img"
173192

174193
private fun getLast(text: Editable, kind: Class<*>): Any? {
175194
val spans = text.getSpans(0, text.length, kind)

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

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -572,44 +572,41 @@ class AztecText : EditText, TextWatcher {
572572
}
573573

574574
private fun loadImages() {
575-
val spans = this.text.getSpans(0, text.length, ImageSpan::class.java)
575+
val spans = this.text.getSpans(0, text.length, AztecMediaSpan::class.java)
576576
spans.forEach {
577-
if (it !is AztecMediaSpan && it !is UnknownHtmlSpan && it !is AztecCommentSpan) {
578-
val callbacks = object : Html.ImageGetter.Callbacks {
577+
val callbacks = object : Html.ImageGetter.Callbacks {
579578

580-
override fun onImageLoaded(drawable: Drawable?) {
581-
replaceImage(drawable)
582-
}
579+
override fun onImageLoaded(drawable: Drawable?) {
580+
replaceImage(drawable)
581+
}
583582

584-
override fun onImageLoadingFailed() {
585-
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_image_failed)
586-
replaceImage(drawable)
587-
}
583+
override fun onImageLoadingFailed() {
584+
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_image_failed)
585+
replaceImage(drawable)
586+
}
588587

589-
private fun replaceImage(drawable: Drawable?) {
588+
private fun replaceImage(drawable: Drawable?) {
589+
if (drawable != null) {
590590
val start = text.getSpanStart(it)
591591
val end = text.getSpanEnd(it)
592592

593593
if (start == -1 || end == -1) return
594594

595-
text.removeSpan(it)
596-
597-
val newImageSpan = ImageSpan(drawable, it.source)
598-
drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
599-
text.setSpan(newImageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
595+
it.drawable = drawable
596+
refreshText()
600597
}
601598
}
602-
603-
/*
604-
* Following Android guidelines for keylines and spacing, screen edge margins should
605-
* be 16dp. Therefore, the width of images should be the width of the screen minus
606-
* 16dp on both sides (i.e. 16 * 2 = 32).
607-
*
608-
* https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-baseline-grids
609-
*/
610-
val width = context.resources.displayMetrics.widthPixels - DisplayUtils.dpToPx(context, 32)
611-
imageGetter?.loadImage(it.source, callbacks, width)
612599
}
600+
601+
/*
602+
* Following Android guidelines for keylines and spacing, screen edge margins should
603+
* be 16dp. Therefore, the width of images should be the width of the screen minus
604+
* 16dp on both sides (i.e. 16 * 2 = 32).
605+
*
606+
* https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-baseline-grids
607+
*/
608+
val width = DisplayUtils.getDisplayPixelWidth(context) - DisplayUtils.dpToPx(context, 32)
609+
imageGetter?.loadImage(it.source, callbacks, width)
613610
}
614611
}
615612

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ data class TextChangedEvent(val textBefore: CharSequence = "", val deletedFromBl
5353
return false
5454
}
5555

56-
fun isNewLine() : Boolean {
56+
fun isNewLine(): Boolean {
5757
if (isAddingCharacters) {
5858
val currentCharacter = text[inputStart]
5959
if (currentCharacter == '\n' ||
@@ -65,7 +65,7 @@ data class TextChangedEvent(val textBefore: CharSequence = "", val deletedFromBl
6565
return false
6666
}
6767

68-
fun isNewLineButNotAtTheBeginning() : Boolean {
68+
fun isNewLineButNotAtTheBeginning(): Boolean {
6969
if (!isAddingCharacters) return false
7070

7171
if (inputStart >= 1 && count == 1) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,10 @@ class BlockFormatter(editor: AztecText, listStyle: ListStyle, quoteStyle: QuoteS
291291

292292

293293
//TODO: Come up with a better way to init spans and get their classes (all the "make" methods)
294-
fun makeBlockSpan(textFormat: TextFormat, lastItem: AztecListItemSpan = AztecListItemSpan(), attrs: String = ""): AztecBlockSpan {
294+
fun makeBlockSpan(textFormat: TextFormat, attrs: String = ""): AztecBlockSpan {
295295
when (textFormat) {
296-
TextFormat.FORMAT_ORDERED_LIST -> return AztecOrderedListSpan(listStyle, attrs, lastItem)
297-
TextFormat.FORMAT_UNORDERED_LIST -> return AztecUnorderedListSpan(listStyle, attrs, lastItem)
296+
TextFormat.FORMAT_ORDERED_LIST -> return AztecOrderedListSpan(listStyle, attrs, AztecListItemSpan())
297+
TextFormat.FORMAT_UNORDERED_LIST -> return AztecUnorderedListSpan(listStyle, attrs, AztecListItemSpan())
298298
TextFormat.FORMAT_QUOTE -> return AztecQuoteSpan(quoteStyle, attrs)
299299
else -> return ParagraphSpan(attrs)
300300
}

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

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -270,24 +270,15 @@ class LineBlockFormatter(editor: AztecText, val headerStyle: LineBlockFormatter.
270270
}
271271

272272
fun insertMedia(drawable: Drawable, source: String) {
273-
//check if we add media into a block element, at the end of the line, but not at the end of last line
274-
var applyingOnTheEndOfBlockLine = false
275-
editableText.getSpans(selectionStart, selectionEnd, AztecBlockSpan::class.java).forEach {
276-
if (editableText.getSpanEnd(it) > selectionEnd && editableText[selectionEnd] == '\n') {
277-
applyingOnTheEndOfBlockLine = true
278-
return@forEach
279-
}
280-
}
281-
282-
val mediaStartIndex = selectionStart + 1
283-
val mediaEndIndex = selectionStart + source.length + 1
273+
val mediaStartIndex = selectionStart
274+
val mediaEndIndex = selectionStart + source.length
284275

285276
editor.disableTextChangedListener()
286-
editableText.replace(selectionStart, selectionEnd, "\n" + source + if (applyingOnTheEndOfBlockLine) "" else "\n")
277+
editableText.replace(selectionStart, selectionEnd, source)
287278

288-
editor.removeBlockStylesFromRange(mediaStartIndex, mediaEndIndex + 1, true)
289-
editor.removeHeadingStylesFromRange(mediaStartIndex, mediaEndIndex + 1)
290-
editor.removeInlineStylesFromRange(mediaStartIndex, mediaEndIndex + 1)
279+
editor.removeBlockStylesFromRange(mediaStartIndex, mediaEndIndex, true)
280+
editor.removeHeadingStylesFromRange(mediaStartIndex, mediaEndIndex)
281+
editor.removeInlineStylesFromRange(mediaStartIndex, mediaEndIndex)
291282

292283
val span = AztecMediaSpan(editor.context, drawable, source)
293284

@@ -305,7 +296,7 @@ class LineBlockFormatter(editor: AztecText, val headerStyle: LineBlockFormatter.
305296
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
306297
)
307298

308-
editor.setSelection(mediaEndIndex + 1)
299+
editor.setSelection(mediaEndIndex)
309300
editor.isMediaAdded = true
310301
}
311302
}

0 commit comments

Comments
 (0)