Skip to content

Commit 5f866c4

Browse files
authored
Merge pull request #321 from wordpress-mobile/issue/314-editable-attrs
Issue/314 editable attrs
2 parents 7c6be7f + c4a4499 commit 5f866c4

36 files changed

+300
-241
lines changed

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

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import android.os.Environment
1717
import android.os.Handler
1818
import android.provider.MediaStore
1919
import android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback
20+
import android.support.v4.content.ContextCompat
2021
import android.support.v4.content.FileProvider
2122
import android.support.v7.app.AlertDialog
2223
import android.support.v7.app.AppCompatActivity
@@ -27,14 +28,14 @@ import android.widget.Toast
2728
import org.wordpress.android.util.AppLog
2829
import org.wordpress.android.util.PermissionUtils
2930
import org.wordpress.android.util.ToastUtils
31+
import org.wordpress.aztec.AztecAttributes
3032
import org.wordpress.aztec.AztecText
3133
import org.wordpress.aztec.HistoryListener
3234
import org.wordpress.aztec.picassoloader.PicassoImageLoader
3335
import org.wordpress.aztec.source.SourceViewEditText
3436
import org.wordpress.aztec.toolbar.AztecToolbar
3537
import org.wordpress.aztec.toolbar.AztecToolbarClickListener
3638
import org.xml.sax.Attributes
37-
import org.xml.sax.helpers.AttributesImpl
3839
import java.io.File
3940

4041
class MainActivity : AppCompatActivity(),
@@ -165,10 +166,10 @@ class MainActivity : AppCompatActivity(),
165166
fun insertMediaAndSimulateUpload(bitmap: Bitmap?, mediaPath: String) {
166167
val id = (Math.random() * Int.MAX_VALUE).toString()
167168

168-
val attrs = AttributesImpl()
169-
attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded.
170-
attrs.addAttribute("", "id", "id", "string", id)
171-
attrs.addAttribute("", "uploading", "uploading", "string", "true")
169+
val attrs = AztecAttributes()
170+
attrs.setValue("src", mediaPath) // Temporary source value. Replace with URL after uploaded.
171+
attrs.setValue("id", id)
172+
attrs.setValue("uploading", "true")
172173

173174
aztec.insertMedia(BitmapDrawable(resources, bitmap), attrs)
174175

@@ -178,23 +179,29 @@ class MainActivity : AppCompatActivity(),
178179
}
179180
}
180181

181-
aztec.setOverlay(predicate, 0, ColorDrawable(0x80000000.toInt()), Gravity.FILL, attrs)
182-
val progressDrawable = resources.getDrawable(android.R.drawable.progress_horizontal)
182+
aztec.setOverlay(predicate, 0, ColorDrawable(0x80000000.toInt()), Gravity.FILL)
183+
aztec.updateElementAttributes(predicate, attrs)
184+
185+
val progressDrawable = ContextCompat.getDrawable(this, android.R.drawable.progress_horizontal)
183186
// set the height of the progress bar to 2 (it's in dp since the drawable will be adjusted by the span)
184187
progressDrawable.setBounds(0, 0, 0, 4)
185-
aztec.setOverlay(predicate, 1, progressDrawable, Gravity.FILL_HORIZONTAL or Gravity.TOP, attrs)
188+
189+
aztec.setOverlay(predicate, 1, progressDrawable, Gravity.FILL_HORIZONTAL or Gravity.TOP)
190+
aztec.updateElementAttributes(predicate, attrs)
186191

187192
var progress = 0
188193

189194
// simulate an upload delay
190195
val runnable: Runnable = Runnable {
191-
aztec.setOverlayLevel(predicate, 1, progress, attrs)
196+
aztec.setOverlayLevel(predicate, 1, progress)
197+
aztec.updateElementAttributes(predicate, attrs)
192198
aztec.refreshText()
193199
progress += 2000
194200

195201
if (progress >= 10000) {
196202
attrs.removeAttribute(attrs.getIndex("uploading"))
197-
aztec.clearOverlays(predicate, attrs)
203+
aztec.clearOverlays(predicate)
204+
aztec.updateElementAttributes(predicate, attrs)
198205
}
199206
}
200207

@@ -212,7 +219,7 @@ class MainActivity : AppCompatActivity(),
212219
setContentView(R.layout.activity_main)
213220

214221
// Setup hiding the action bar when the soft keyboard is displayed for narrow viewports
215-
if (resources.configuration.orientation === Configuration.ORIENTATION_LANDSCAPE
222+
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
216223
&& !resources.getBoolean(R.bool.is_large_tablet_landscape)) {
217224
mHideActionBarOnSoftKeyboardUp = true
218225
}
@@ -330,7 +337,7 @@ class MainActivity : AppCompatActivity(),
330337
&& mHideActionBarOnSoftKeyboardUp
331338
&& mIsKeyboardOpen
332339
&& actionBar.isShowing) {
333-
actionBar!!.hide()
340+
actionBar.hide()
334341
}
335342
}
336343

@@ -573,7 +580,7 @@ class MainActivity : AppCompatActivity(),
573580
}
574581
}
575582

576-
val mediaPending = aztec.getAllMediaAttributes(uploadingPredicate).size > 0
583+
val mediaPending = aztec.getAllElementAttributes(uploadingPredicate).isNotEmpty()
577584

578585
if (mediaPending) {
579586
ToastUtils.showToast(this, R.string.media_upload_dialog_message)
@@ -671,7 +678,7 @@ class MainActivity : AppCompatActivity(),
671678
addVideoMediaDialog!!.show()
672679
}
673680

674-
override fun mediaTapped(attrs: Attributes?, naturalWidth: Int, naturalHeight: Int) {
681+
override fun mediaTapped(attrs: AztecAttributes, naturalWidth: Int, naturalHeight: Int) {
675682
ToastUtils.showToast(this, "Media tapped!")
676683
}
677684
}

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

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -262,35 +262,35 @@ private void handleStartTag(String tag, Attributes attributes, int nestingLevel)
262262
} else if (tag.equalsIgnoreCase("aztec_cursor")) {
263263
handleCursor(spannableStringBuilder);
264264
} else if (tag.equalsIgnoreCase("strong")) {
265-
start(spannableStringBuilder, TextFormat.FORMAT_BOLD, attributes, nestingLevel);
265+
start(spannableStringBuilder, TextFormat.FORMAT_BOLD, attributes);
266266
} else if (tag.equalsIgnoreCase("b")) {
267-
start(spannableStringBuilder, TextFormat.FORMAT_BOLD, attributes, nestingLevel);
267+
start(spannableStringBuilder, TextFormat.FORMAT_BOLD, attributes);
268268
} else if (tag.equalsIgnoreCase("em")) {
269-
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes, nestingLevel);
269+
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes);
270270
} else if (tag.equalsIgnoreCase("cite")) {
271-
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes, nestingLevel);
271+
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes);
272272
} else if (tag.equalsIgnoreCase("dfn")) {
273-
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes, nestingLevel);
273+
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes);
274274
} else if (tag.equalsIgnoreCase("i")) {
275-
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes, nestingLevel);
275+
start(spannableStringBuilder, TextFormat.FORMAT_ITALIC, attributes);
276276
} else if (tag.equalsIgnoreCase("big")) {
277-
start(spannableStringBuilder, TextFormat.FORMAT_BIG, attributes, nestingLevel);
277+
start(spannableStringBuilder, TextFormat.FORMAT_BIG, attributes);
278278
} else if (tag.equalsIgnoreCase("small")) {
279-
start(spannableStringBuilder, TextFormat.FORMAT_SMALL, attributes, nestingLevel);
279+
start(spannableStringBuilder, TextFormat.FORMAT_SMALL, attributes);
280280
} else if (tag.equalsIgnoreCase("font")) {
281-
start(spannableStringBuilder, TextFormat.FORMAT_FONT, attributes, nestingLevel);
281+
start(spannableStringBuilder, TextFormat.FORMAT_FONT, attributes);
282282
} else if (tag.equalsIgnoreCase("tt")) {
283-
start(spannableStringBuilder, TextFormat.FORMAT_MONOSPACE, attributes, nestingLevel);
283+
start(spannableStringBuilder, TextFormat.FORMAT_MONOSPACE, attributes);
284284
} else if (tag.equalsIgnoreCase("a")) {
285-
start(spannableStringBuilder, TextFormat.FORMAT_LINK, attributes, nestingLevel);
285+
start(spannableStringBuilder, TextFormat.FORMAT_LINK, attributes);
286286
} else if (tag.equalsIgnoreCase("u")) {
287-
start(spannableStringBuilder, TextFormat.FORMAT_UNDERLINE, attributes, nestingLevel);
287+
start(spannableStringBuilder, TextFormat.FORMAT_UNDERLINE, attributes);
288288
} else if (tag.equalsIgnoreCase("sup")) {
289-
start(spannableStringBuilder, TextFormat.FORMAT_SUPERSCRIPT, attributes, nestingLevel);
289+
start(spannableStringBuilder, TextFormat.FORMAT_SUPERSCRIPT, attributes);
290290
} else if (tag.equalsIgnoreCase("sub")) {
291-
start(spannableStringBuilder, TextFormat.FORMAT_SUBSCRIPT, attributes, nestingLevel);
291+
start(spannableStringBuilder, TextFormat.FORMAT_SUBSCRIPT, attributes);
292292
} else if (tag.equalsIgnoreCase("code")) {
293-
start(spannableStringBuilder, TextFormat.FORMAT_CODE, attributes, nestingLevel);
293+
start(spannableStringBuilder, TextFormat.FORMAT_CODE, attributes);
294294
} else {
295295
if (tagHandler != null) {
296296
boolean tagHandled = tagHandler.handleTag(true, tag, spannableStringBuilder, onMediaTappedListener,
@@ -367,7 +367,7 @@ private void handleEndTag(String tag, int nestingLevel) {
367367
} else if (tag.equalsIgnoreCase("code")) {
368368
end(spannableStringBuilder, TextFormat.FORMAT_CODE);
369369
} else if (tagHandler != null) {
370-
tagHandler.handleTag(false, tag, spannableStringBuilder, onMediaTappedListener, context, null,
370+
tagHandler.handleTag(false, tag, spannableStringBuilder, onMediaTappedListener, context, new AztecAttributes(),
371371
nestingLevel);
372372
}
373373
}
@@ -402,45 +402,44 @@ private static Object getLast(Spanned text, Class kind) {
402402
}
403403
}
404404

405-
private static void start(SpannableStringBuilder text, TextFormat textFormat, Attributes attributes,
406-
int nestingLevel) {
407-
final String attrs = Html.stringifyAttributes(attributes).toString();
405+
private static void start(SpannableStringBuilder text, TextFormat textFormat, Attributes attrs) {
406+
final AztecAttributes attributes = new AztecAttributes(attrs);
408407
AztecInlineSpan newSpan;
409408

410409
switch (textFormat) {
411410
case FORMAT_BOLD:
412-
newSpan = new AztecStyleBoldSpan(attrs);
411+
newSpan = new AztecStyleBoldSpan(attributes);
413412
break;
414413
case FORMAT_ITALIC:
415-
newSpan = new AztecStyleItalicSpan(attrs);
414+
newSpan = new AztecStyleItalicSpan(attributes);
416415
break;
417416
case FORMAT_UNDERLINE:
418-
newSpan = new AztecUnderlineSpan(attrs);
417+
newSpan = new AztecUnderlineSpan(attributes);
419418
break;
420419
case FORMAT_LINK:
421-
String url = attributes.getValue("href") == null ? "" : attributes.getValue("href");
422-
newSpan = new AztecURLSpan(url, attrs);
420+
String url = attributes.hasAttribute("href") ? attributes.getValue("href") : "";
421+
newSpan = new AztecURLSpan(url, attributes);
423422
break;
424423
case FORMAT_BIG:
425-
newSpan = new AztecRelativeSizeBigSpan(attrs);
424+
newSpan = new AztecRelativeSizeBigSpan(attributes);
426425
break;
427426
case FORMAT_SMALL:
428-
newSpan = new AztecRelativeSizeSmallSpan(attrs);
427+
newSpan = new AztecRelativeSizeSmallSpan(attributes);
429428
break;
430429
case FORMAT_SUPERSCRIPT:
431-
newSpan = new AztecSuperscriptSpan(attrs);
430+
newSpan = new AztecSuperscriptSpan(attributes);
432431
break;
433432
case FORMAT_SUBSCRIPT:
434-
newSpan = new AztecSubscriptSpan(attrs);
433+
newSpan = new AztecSubscriptSpan(attributes);
435434
break;
436435
case FORMAT_MONOSPACE:
437-
newSpan = new AztecTypefaceMonospaceSpan(attrs);
436+
newSpan = new AztecTypefaceMonospaceSpan(attributes);
438437
break;
439438
case FORMAT_FONT:
440-
newSpan = new FontSpan(attrs, attributes);
439+
newSpan = new FontSpan(attributes);
441440
break;
442441
case FORMAT_CODE:
443-
newSpan = new AztecCodeSpan(attrs);
442+
newSpan = new AztecCodeSpan(attributes);
444443
break;
445444
default:
446445
throw new IllegalArgumentException("Style not supported");
@@ -504,7 +503,7 @@ private static void endFont(SpannableStringBuilder text) {
504503

505504
if (font != null && where != len) {
506505

507-
String color = font.getAttrs().getValue("color");
506+
String color = font.getAttributes().getValue("color");
508507

509508
if (!TextUtils.isEmpty(color)) {
510509
if (color.startsWith("@")) {
@@ -531,7 +530,7 @@ private static void endFont(SpannableStringBuilder text) {
531530
}
532531
}
533532

534-
String face = font.getAttrs().getValue("face");
533+
String face = font.getAttributes().getValue("face");
535534

536535
if (face != null) {
537536
text.setSpan(new TypefaceSpan(face), where, len,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.wordpress.aztec
2+
3+
import org.xml.sax.Attributes
4+
import org.xml.sax.helpers.AttributesImpl
5+
6+
class AztecAttributes(attributes: Attributes = AttributesImpl()) : AttributesImpl(attributes) {
7+
8+
fun setValue(key: String, value: String) {
9+
val index = getIndex(key)
10+
if (index == -1) {
11+
addAttribute("", key, key, "string", value)
12+
} else {
13+
setValue(index, value)
14+
}
15+
}
16+
17+
fun isEmpty() : Boolean {
18+
return length == 0
19+
}
20+
21+
fun removeAttribute(key: String) {
22+
if (hasAttribute(key)) {
23+
val index = getIndex(key)
24+
removeAttribute(index)
25+
}
26+
}
27+
28+
fun hasAttribute(key: String) : Boolean {
29+
return getValue(key) != null
30+
}
31+
32+
override fun toString(): String {
33+
val sb = StringBuilder()
34+
for (i in 0..this.length-1) {
35+
sb.append(this.getLocalName(i))
36+
sb.append("=\"")
37+
sb.append(this.getValue(i))
38+
sb.append("\" ")
39+
}
40+
return sb.trimEnd().toString()
41+
}
42+
}

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,48 +28,48 @@ import android.text.Spannable
2828
import android.text.Spanned
2929
import org.wordpress.aztec.spans.*
3030
import org.xml.sax.Attributes
31+
import org.wordpress.aztec.AztecAttributes
3132

3233
class AztecTagHandler : Html.TagHandler {
3334

3435
private var order = 0
3536

3637
override fun handleTag(opening: Boolean, tag: String, output: Editable,
37-
onMediaTappedListener: AztecText.OnMediaTappedListener?, context: Context, attributes: Attributes?,
38+
onMediaTappedListener: AztecText.OnMediaTappedListener?, context: Context, attributes: Attributes,
3839
nestingLevel: Int): Boolean {
39-
val attributeString = Html.stringifyAttributes(attributes).toString()
4040

4141
when (tag.toLowerCase()) {
4242
LIST_LI -> {
43-
handleElement(output, opening, AztecListItemSpan(nestingLevel, attributeString))
43+
handleElement(output, opening, AztecListItemSpan(nestingLevel, AztecAttributes(attributes)))
4444
return true
4545
}
4646
STRIKETHROUGH_S, STRIKETHROUGH_STRIKE, STRIKETHROUGH_DEL -> {
47-
handleElement(output, opening, AztecStrikethroughSpan(tag, attributeString))
47+
handleElement(output, opening, AztecStrikethroughSpan(tag, AztecAttributes(attributes)))
4848
return true
4949
}
5050
DIV, SPAN -> {
5151
if (opening) {
52-
start(output, HiddenHtmlSpan(tag, attributeString, order++))
52+
start(output, HiddenHtmlSpan(tag, AztecAttributes(attributes), order++))
5353
} else {
5454
endHidden(output, order++)
5555
}
5656
return true
5757
}
5858
LIST_UL -> {
59-
handleElement(output, opening, AztecUnorderedListSpan(nestingLevel, attributeString))
59+
handleElement(output, opening, AztecUnorderedListSpan(nestingLevel, AztecAttributes(attributes)))
6060
return true
6161
}
6262
LIST_OL -> {
63-
handleElement(output, opening, AztecOrderedListSpan(nestingLevel, attributeString))
63+
handleElement(output, opening, AztecOrderedListSpan(nestingLevel, AztecAttributes(attributes)))
6464
return true
6565
}
6666
BLOCKQUOTE -> {
67-
handleElement(output, opening, AztecQuoteSpan(nestingLevel, attributeString))
67+
handleElement(output, opening, AztecQuoteSpan(nestingLevel, AztecAttributes(attributes)))
6868
return true
6969
}
7070
IMAGE -> {
7171
if (opening) {
72-
val mediaSpan = createImageSpan(attributes, onMediaTappedListener, context)
72+
val mediaSpan = createImageSpan(AztecAttributes(attributes), onMediaTappedListener, context)
7373
start(output, mediaSpan)
7474
start(output, AztecMediaClickableSpan(mediaSpan))
7575
output.append(Constants.IMG_CHAR)
@@ -80,7 +80,7 @@ class AztecTagHandler : Html.TagHandler {
8080
return true
8181
}
8282
PARAGRAPH -> {
83-
handleElement(output, opening, ParagraphSpan(nestingLevel, attributeString))
83+
handleElement(output, opening, ParagraphSpan(nestingLevel, AztecAttributes(attributes)))
8484
return true
8585
}
8686
LINE -> {
@@ -96,15 +96,15 @@ class AztecTagHandler : Html.TagHandler {
9696
}
9797
else -> {
9898
if (tag.length == 2 && Character.toLowerCase(tag[0]) == 'h' && tag[1] >= '1' && tag[1] <= '6') {
99-
handleElement(output, opening, AztecHeadingSpan(nestingLevel, tag, attributeString))
99+
handleElement(output, opening, AztecHeadingSpan(nestingLevel, tag, AztecAttributes(attributes)))
100100
return true
101101
}
102102
}
103103
}
104104
return false
105105
}
106106

107-
private fun createImageSpan(attributes: Attributes?, onMediaTappedListener: AztecText.OnMediaTappedListener?,
107+
private fun createImageSpan(attributes: AztecAttributes, onMediaTappedListener: AztecText.OnMediaTappedListener?,
108108
context: Context) : AztecMediaSpan {
109109
val styles = context.obtainStyledAttributes(R.styleable.AztecText)
110110
val loadingDrawable = ContextCompat.getDrawable(context, styles.getResourceId(R.styleable.AztecText_drawableLoading, R.drawable.ic_image_loading))

0 commit comments

Comments
 (0)