From 921c3e99bae5fd681b3620cb26e37b90454e6bca Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 31 Jan 2017 14:57:13 +0200 Subject: [PATCH 01/36] Delegate the media menu opening to the client --- .../wordpress/aztec/toolbar/AztecToolbar.kt | 28 ++++++++----------- .../toolbar/AztecToolbarClickListener.kt | 5 ++++ 2 files changed, 16 insertions(+), 17 deletions(-) create mode 100644 aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt index 222d636e5..31e5cdfd4 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt @@ -20,12 +20,12 @@ import org.wordpress.aztec.source.SourceViewEditText import java.util.* class AztecToolbar : FrameLayout, OnMenuItemClickListener { + private var aztecToolbarListener: AztecToolbarClickListener? = null private var addPhotoMediaDialog: AlertDialog? = null private var addVideoMediaDialog: AlertDialog? = null private var mediaUploadDialog: AlertDialog? = null private var editor: AztecText? = null private var headingMenu: PopupMenu? = null - private var mediaMenu: PopupMenu? = null private var mediaOptionSelectedListener: OnMediaOptionSelectedListener? = null private var sourceEditor: SourceViewEditText? = null @@ -51,6 +51,10 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { fun onVideosMediaOptionSelected() } + fun setToolbarListener(listener: AztecToolbarClickListener) { + aztecToolbarListener = listener + } + override fun onRestoreInstanceState(state: Parcelable?) { var superState = state @@ -168,10 +172,6 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { val button = findViewById(toolbarAction.buttonId) button?.setOnClickListener { onToolbarAction(toolbarAction) } - if (toolbarAction == ToolbarAction.ADD_MEDIA) { - setMediaMenu(findViewById(toolbarAction.buttonId)) - } - if (toolbarAction == ToolbarAction.HEADING) { setHeaderMenu(findViewById(toolbarAction.buttonId)) } @@ -241,7 +241,7 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { //other toolbar action when (action) { - ToolbarAction.ADD_MEDIA -> mediaMenu?.show() + ToolbarAction.ADD_MEDIA -> aztecToolbarListener?.onToolbarAddMediaClicked() ToolbarAction.HEADING -> headingMenu?.show() ToolbarAction.LINK -> editor!!.showLinkDialog() ToolbarAction.HTML -> toggleEditorMode() @@ -253,16 +253,16 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { fun toggleEditorMode() { if (editor!!.visibility == View.VISIBLE) { - if (!editor!!.isMediaAdded) { +// if (!editor!!.isMediaAdded) { sourceEditor!!.displayStyledAndFormattedHtml(editor!!.toHtml(true)) editor!!.visibility = View.GONE sourceEditor!!.visibility = View.VISIBLE toggleHtmlMode(true) - } else { - toggleButton(findViewById(ToolbarAction.HTML.buttonId), false) - showMediaUploadDialog() - } +// } else { +// toggleButton(findViewById(ToolbarAction.HTML.buttonId), false) +// showMediaUploadDialog() +// } } else { editor!!.fromHtml(sourceEditor!!.getPureHtml(true)) editor!!.visibility = View.VISIBLE @@ -295,12 +295,6 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { headingMenu?.inflate(R.menu.heading) } - private fun setMediaMenu(view: View) { - mediaMenu = PopupMenu(context, view) - mediaMenu?.setOnMenuItemClickListener(this) - mediaMenu?.inflate(R.menu.media) - } - fun getSelectedHeading(): TextFormat? { if (headingMenu?.menu?.getItem(1)?.isChecked!!) return TextFormat.FORMAT_HEADING_1 else if (headingMenu?.menu?.getItem(2)?.isChecked!!) return TextFormat.FORMAT_HEADING_2 diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt new file mode 100644 index 000000000..534f1b0bb --- /dev/null +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt @@ -0,0 +1,5 @@ +package org.wordpress.aztec.toolbar + +interface AztecToolbarClickListener { + fun onToolbarAddMediaClicked() +} From e927ff49680ac5160fbc7b7b563a680181ae166b Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 31 Jan 2017 14:58:31 +0200 Subject: [PATCH 02/36] MediaSpan with attributes instead of ImageSpan We need the attributes since the client will keep some state there. For example, keep a unique id to identify the image when in need to replace it with another (like when replacing a local image with a remote one). --- .../main/java/org/wordpress/aztec/Html.java | 3 ++- .../kotlin/org/wordpress/aztec/AztecParser.kt | 15 ++----------- .../aztec/formatting/LineBlockFormatter.kt | 12 +++++----- .../wordpress/aztec/spans/AztecMediaSpan.kt | 22 +++++++++++++++---- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index 8b658d3de..94410b01b 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -42,6 +42,7 @@ import org.wordpress.aztec.spans.AztecContentSpan; import org.wordpress.aztec.spans.AztecCursorSpan; import org.wordpress.aztec.spans.AztecListSpan; +import org.wordpress.aztec.spans.AztecMediaSpan; import org.wordpress.aztec.spans.AztecRelativeSizeSpan; import org.wordpress.aztec.spans.AztecStyleSpan; import org.wordpress.aztec.spans.AztecSubscriptSpan; @@ -756,7 +757,7 @@ private static void startImg(final SpannableStringBuilder text, Drawable loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading); loadingDrawable.setBounds(0, 0, loadingDrawable.getIntrinsicWidth(), loadingDrawable.getIntrinsicHeight()); - final ImageSpan imageSpan = new ImageSpan(loadingDrawable, src); + final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, attributes); text.append("\uFFFC"); text.setSpan(imageSpan, start, text.length(), diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt index d274f2d39..5e5d53bca 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt @@ -24,7 +24,6 @@ import android.text.SpannableStringBuilder import android.text.Spanned import android.text.TextUtils import android.text.style.CharacterStyle -import android.text.style.ImageSpan import android.text.style.ParagraphStyle import org.wordpress.aztec.spans.* import java.util.* @@ -252,8 +251,6 @@ class AztecParser { withinUnknown(out, text, i, next, styles[0] as UnknownHtmlSpan) } else if (styles[0] is ParagraphSpan) { withinParagraph(out, text, i, next) - } else if (styles[0] is AztecMediaSpan) { - withinMedia(out, text, i, next, styles[0] as AztecMediaSpan) } else { withinContent(out, text, i, next) } @@ -386,12 +383,6 @@ class AztecParser { } } - private fun withinMedia(out: StringBuilder, text: Spanned, start: Int, end: Int, mediaSpan: AztecMediaSpan) { - consumeCursorIfInInput(out, text, start) - out.append(mediaSpan.getHtml()) - consumeCursorIfInInput(out, text, end) - } - private fun withinContent(out: StringBuilder, text: Spanned, start: Int, end: Int, ignoreHeading: Boolean = false) { var next: Int @@ -457,10 +448,8 @@ class AztecParser { out.append("<${span.getStartTag()}>") } - if (span is ImageSpan && span !is AztecCommentSpan && span !is AztecMediaSpan && span !is UnknownHtmlSpan) { - out.append("") + if (span is AztecMediaSpan && span !is AztecCommentSpan && span !is UnknownHtmlSpan) { + out.append(span.getHtml()) // Don't output the dummy character underlying the image. i = next diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt index b6820d772..e4598ad7f 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt @@ -9,6 +9,7 @@ import org.wordpress.aztec.R import org.wordpress.aztec.TextChangedEvent import org.wordpress.aztec.TextFormat import org.wordpress.aztec.spans.* +import org.xml.sax.Attributes import java.util.* @@ -267,7 +268,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { editor.setSelection(commentEndIndex + 1) } - fun insertMedia(drawable: Drawable, source: String) { + fun insertMedia(drawable: Drawable?, attributes: Attributes) { //check if we add media into a block element, at the end of the line, but not at the end of last line var applyingOnTheEndOfBlockLine = false editableText.getSpans(selectionStart, selectionEnd, AztecBlockSpan::class.java).forEach { @@ -277,18 +278,19 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { } } + val span = AztecMediaSpan(editor.context, drawable, attributes) + val html = span.getHtml(); + val mediaStartIndex = selectionStart + 1 - val mediaEndIndex = selectionStart + source.length + 1 + val mediaEndIndex = selectionStart + html.length + 1 editor.disableTextChangedListener() - editableText.replace(selectionStart, selectionEnd, "\n" + source + if (applyingOnTheEndOfBlockLine) "" else "\n") + editableText.replace(selectionStart, selectionEnd, "\n" + html + if (applyingOnTheEndOfBlockLine) "" else "\n") editor.removeBlockStylesFromRange(mediaStartIndex, mediaEndIndex + 1, true) editor.removeHeadingStylesFromRange(mediaStartIndex, mediaEndIndex + 1) editor.removeInlineStylesFromRange(mediaStartIndex, mediaEndIndex + 1) - val span = AztecMediaSpan(editor.context, drawable, source) - editableText.setSpan( span, mediaStartIndex, diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index 40e6f6de6..eddc74b88 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -10,9 +10,10 @@ import android.view.View import android.widget.Toast import org.wordpress.android.util.DisplayUtils +import org.xml.sax.Attributes -class AztecMediaSpan(val context: Context, drawable: Drawable, source: String) : ImageSpan(drawable), ParagraphStyle { - private val html = source +class AztecMediaSpan(val context: Context, drawable: Drawable?, var attributes: Attributes) : + ImageSpan(drawable) { companion object { private val rect: Rect = Rect() @@ -54,10 +55,23 @@ class AztecMediaSpan(val context: Context, drawable: Drawable, source: String) : } fun getHtml(): String { - return html + var sb = StringBuilder() + sb.append("") + return sb.toString() } fun onClick(view: View) { - Toast.makeText(view.context, html, Toast.LENGTH_SHORT).show() + Toast.makeText(view.context, getHtml(), Toast.LENGTH_SHORT).show() } + + } From 867886b500065015ad73decadcba7d78f7bdd786 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 31 Jan 2017 15:35:21 +0200 Subject: [PATCH 03/36] Fix demo app: use Attributes for MediaSpan --- .../main/kotlin/org/wordpress/aztec/demo/MainActivity.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index f94f3927d..fcbe633b1 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -30,6 +30,7 @@ import org.wordpress.aztec.picassoloader.PicassoImageLoader import org.wordpress.aztec.source.SourceViewEditText import org.wordpress.aztec.toolbar.AztecToolbar import org.wordpress.aztec.toolbar.AztecToolbar.OnMediaOptionSelectedListener +import org.xml.sax.helpers.AttributesImpl import java.io.File class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnRequestPermissionsResultCallback, @@ -127,8 +128,9 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque } } - val source = "" // Temporary source value. Replace with URL after uploaded. - aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), source) + val attrs = AttributesImpl() + attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. + aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), attrs) } super.onActivityResult(requestCode, resultCode, data) From f8c67e609a410360aa619591fa25294de2d89f33 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 31 Jan 2017 15:42:48 +0200 Subject: [PATCH 04/36] Fix: load images when a MediaSpan --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index db136420c..45ca0d9b7 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -568,7 +568,7 @@ class AztecText : EditText, TextWatcher { private fun loadImages() { val spans = this.text.getSpans(0, text.length, ImageSpan::class.java) spans.forEach { - if (it !is AztecMediaSpan && it !is UnknownHtmlSpan && it !is AztecCommentSpan) { + if (it !is UnknownHtmlSpan && it !is AztecCommentSpan) { val callbacks = object : Html.ImageGetter.Callbacks { override fun onImageLoaded(drawable: Drawable?) { From a547a1738dc7c60d4ad7fd443c2c747e082729f5 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 31 Jan 2017 15:43:13 +0200 Subject: [PATCH 05/36] Fix: image src lives in attributes now --- .../main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index eddc74b88..1121cdf4e 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -73,5 +73,7 @@ class AztecMediaSpan(val context: Context, drawable: Drawable?, var attributes: Toast.makeText(view.context, getHtml(), Toast.LENGTH_SHORT).show() } - + override fun getSource(): String { + return attributes.getValue("src") + } } From 276b5f0d3ea4aae7873fec41210021a202b9f7a6 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 31 Jan 2017 16:42:25 +0200 Subject: [PATCH 06/36] Fix: MediaSpan image loading --- .../kotlin/org/wordpress/aztec/AztecText.kt | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 45ca0d9b7..f81932c91 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -566,44 +566,42 @@ class AztecText : EditText, TextWatcher { } private fun loadImages() { - val spans = this.text.getSpans(0, text.length, ImageSpan::class.java) + val spans = this.text.getSpans(0, text.length, AztecMediaSpan::class.java) spans.forEach { - if (it !is UnknownHtmlSpan && it !is AztecCommentSpan) { - val callbacks = object : Html.ImageGetter.Callbacks { + val callbacks = object : Html.ImageGetter.Callbacks { - override fun onImageLoaded(drawable: Drawable?) { - replaceImage(drawable) - } + override fun onImageLoaded(drawable: Drawable?) { + replaceImage(drawable) + } - override fun onImageLoadingFailed() { - val drawable = ContextCompat.getDrawable(context, R.drawable.ic_image_failed) - replaceImage(drawable) - } + override fun onImageLoadingFailed() { + val drawable = ContextCompat.getDrawable(context, R.drawable.ic_image_failed) + replaceImage(drawable) + } - private fun replaceImage(drawable: Drawable?) { - val start = text.getSpanStart(it) - val end = text.getSpanEnd(it) + private fun replaceImage(drawable: Drawable?) { + val start = text.getSpanStart(it) + val end = text.getSpanEnd(it) - if (start == -1 || end == -1) return + if (start == -1 || end == -1) return - text.removeSpan(it) + text.removeSpan(it) - val newImageSpan = ImageSpan(drawable, it.source) - drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) - text.setSpan(newImageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - } + val newImageSpan = AztecMediaSpan(context, drawable, it.attributes) + drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) + text.setSpan(newImageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) } - - /* - * Following Android guidelines for keylines and spacing, screen edge margins should - * be 16dp. Therefore, the width of images should be the width of the screen minus - * 16dp on both sides (i.e. 16 * 2 = 32). - * - * https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-baseline-grids - */ - val width = context.resources.displayMetrics.widthPixels - DisplayUtils.dpToPx(context, 32) - imageGetter?.loadImage(it.source, callbacks, width) } + + /* + * Following Android guidelines for keylines and spacing, screen edge margins should + * be 16dp. Therefore, the width of images should be the width of the screen minus + * 16dp on both sides (i.e. 16 * 2 = 32). + * + * https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-baseline-grids + */ + val width = context.resources.displayMetrics.widthPixels - DisplayUtils.dpToPx(context, 32) + imageGetter?.loadImage(it.source, callbacks, width) } } From 29ab5889e78ce116e8111d222228116a61b032f2 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 Feb 2017 10:13:15 +0200 Subject: [PATCH 07/36] Wrap lineBlockFormatter's insertmedia --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index f81932c91..4fc2f3484 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -48,6 +48,7 @@ import org.wordpress.aztec.formatting.LinkFormatter import org.wordpress.aztec.source.Format import org.wordpress.aztec.spans.* import org.wordpress.aztec.util.TypefaceCache +import org.xml.sax.Attributes import java.util.* @@ -874,4 +875,7 @@ class AztecText : EditText, TextWatcher { } } + fun insertMedia(drawable: Drawable?, attributes: Attributes) { + lineBlockFormatter.insertMedia(drawable, attributes) + } } From 0065c39d9c5206856408e2041f34786f3cfbb94a Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 Feb 2017 16:33:01 +0200 Subject: [PATCH 08/36] Cleanup some imports --- aztec/src/main/java/org/wordpress/aztec/Html.java | 2 -- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 1 - .../src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt | 1 - 3 files changed, 4 deletions(-) diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index 94410b01b..8849d1b5a 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -29,7 +29,6 @@ import android.text.Spanned; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; -import android.text.style.ImageSpan; import android.text.style.ParagraphStyle; import android.text.style.TextAppearanceSpan; import android.text.style.TypefaceSpan; @@ -41,7 +40,6 @@ import org.wordpress.aztec.spans.AztecCommentSpan; import org.wordpress.aztec.spans.AztecContentSpan; import org.wordpress.aztec.spans.AztecCursorSpan; -import org.wordpress.aztec.spans.AztecListSpan; import org.wordpress.aztec.spans.AztecMediaSpan; import org.wordpress.aztec.spans.AztecRelativeSizeSpan; import org.wordpress.aztec.spans.AztecStyleSpan; diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 4fc2f3484..3315bb2e8 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -27,7 +27,6 @@ import android.os.Parcelable import android.support.v4.content.ContextCompat import android.support.v7.app.AlertDialog import android.text.* -import android.text.style.ImageSpan import android.text.style.LeadingMarginSpan import android.text.style.ParagraphStyle import android.text.style.SuggestionSpan diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index 1121cdf4e..cfc4514e1 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -5,7 +5,6 @@ import android.graphics.Paint import android.graphics.Rect import android.graphics.drawable.Drawable import android.text.style.ImageSpan -import android.text.style.ParagraphStyle import android.view.View import android.widget.Toast From 5a9a647c1385187bd93aa883369e5926d910bc56 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 Feb 2017 16:34:58 +0200 Subject: [PATCH 09/36] Overlay progress bar on image --- .../kotlin/org/wordpress/aztec/AztecText.kt | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 3315bb2e8..1a56c4cda 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -21,6 +21,7 @@ import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.os.Parcel import android.os.Parcelable @@ -108,6 +109,10 @@ class AztecText : EditText, TextWatcher { private fun init(attrs: AttributeSet?) { TypefaceCache.setCustomTypeface(context, this, TypefaceCache.TYPEFACE_MERRIWEATHER_REGULAR) + // It seems that hardware accel makes the progressbar in MediaSpan to not show that it updates. + // Instead, software rendering works. See: https://github.com/koral--/android-gif-drawable/issues/234#issuecomment-165938445 + setLayerType(View.LAYER_TYPE_SOFTWARE, null); + val array = context.obtainStyledAttributes(attrs, R.styleable.AztecText, 0, R.style.AztecTextStyle) setLineSpacing( array.getDimension( @@ -875,6 +880,36 @@ class AztecText : EditText, TextWatcher { } fun insertMedia(drawable: Drawable?, attributes: Attributes) { - lineBlockFormatter.insertMedia(drawable, attributes) + lineBlockFormatter.insertMedia(overlayProgress(drawable), attributes) + } + + fun setProgressOnMedia(attributePredicate: AttributePredicate, progress: Float) { + text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + if (attributePredicate.matches(it.attributes)) { + if (setLevelOverlayProgress(it.drawable, progress)) invalidate() + } + } + } + + interface AttributePredicate { + /** + * Return true if the attributes list includes the desired key + */ + fun matches(attrs: Attributes): Boolean + } + + fun overlayProgress(drawable: Drawable?): Drawable { + val progressDrawable = ContextCompat.getDrawable(context, android.R.drawable.progress_horizontal) + val layerDrawable = LayerDrawable(arrayOf(drawable, progressDrawable)) + + val l = DisplayUtils.dpToPx(context, drawable!!.intrinsicWidth / 4) + val t = DisplayUtils.dpToPx(context, drawable!!.intrinsicHeight / 4) + layerDrawable.setLayerInset(1, l, t, l, t) + + return layerDrawable + } + + fun setLevelOverlayProgress(drawable: Drawable, progress: Float): Boolean { + return (drawable as LayerDrawable).getDrawable(1).setLevel((progress * 10000).toInt()) } } From 950d9d564c2dad50bc4f9d60fb6e47f55aa5a7bb Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 Feb 2017 17:37:25 +0200 Subject: [PATCH 10/36] Remove progress overlay function --- .../kotlin/org/wordpress/aztec/AztecText.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 1a56c4cda..23930f8bc 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -912,4 +912,26 @@ class AztecText : EditText, TextWatcher { fun setLevelOverlayProgress(drawable: Drawable, progress: Float): Boolean { return (drawable as LayerDrawable).getDrawable(1).setLevel((progress * 10000).toInt()) } + + fun removeOverlayProgress(attributePredicate: AttributePredicate, attributes: Attributes) { + text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + if (attributePredicate.matches(it.attributes)) { + val start = text.getSpanStart(it) + val end = text.getSpanEnd(it) + + val drawable = (it.drawable as? LayerDrawable)?.getDrawable(0) + + text.removeSpan(it) + + text.setSpan( + AztecMediaSpan(context, drawable, attributes), + start, + end, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + + invalidate() + } + } + } } From 749d4fc030934996aa1d71586e035e5f24be01a9 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 Feb 2017 17:56:24 +0200 Subject: [PATCH 11/36] More accurate comment --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 23930f8bc..b8c5c5bfc 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -893,7 +893,7 @@ class AztecText : EditText, TextWatcher { interface AttributePredicate { /** - * Return true if the attributes list includes the desired key + * Return true if the attributes list fulfills some condition */ fun matches(attrs: Attributes): Boolean } From 5c6726eacbb526be9cd74748b51b16ebb3088995 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 Feb 2017 18:28:36 +0200 Subject: [PATCH 12/36] Refactor AztecMediaSpan: allow changing the drawable --- .../kotlin/org/wordpress/aztec/AztecText.kt | 18 ++----- .../wordpress/aztec/spans/AztecMediaSpan.kt | 47 ++++++++++++++----- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index b8c5c5bfc..9a5520c28 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -606,7 +606,7 @@ class AztecText : EditText, TextWatcher { * https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-baseline-grids */ val width = context.resources.displayMetrics.widthPixels - DisplayUtils.dpToPx(context, 32) - imageGetter?.loadImage(it.source, callbacks, width) + imageGetter?.loadImage(it.getSource(), callbacks, width) } } @@ -909,26 +909,14 @@ class AztecText : EditText, TextWatcher { return layerDrawable } - fun setLevelOverlayProgress(drawable: Drawable, progress: Float): Boolean { + fun setLevelOverlayProgress(drawable: Drawable?, progress: Float): Boolean { return (drawable as LayerDrawable).getDrawable(1).setLevel((progress * 10000).toInt()) } fun removeOverlayProgress(attributePredicate: AttributePredicate, attributes: Attributes) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { if (attributePredicate.matches(it.attributes)) { - val start = text.getSpanStart(it) - val end = text.getSpanEnd(it) - - val drawable = (it.drawable as? LayerDrawable)?.getDrawable(0) - - text.removeSpan(it) - - text.setSpan( - AztecMediaSpan(context, drawable, attributes), - start, - end, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE - ) + it.drawable = (it.drawable as? LayerDrawable)?.getDrawable(0) invalidate() } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index cfc4514e1..a64e24ef5 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -1,36 +1,59 @@ package org.wordpress.aztec.spans import android.content.Context +import android.graphics.Canvas import android.graphics.Paint import android.graphics.Rect import android.graphics.drawable.Drawable -import android.text.style.ImageSpan +import android.text.style.DynamicDrawableSpan import android.view.View import android.widget.Toast import org.wordpress.android.util.DisplayUtils import org.xml.sax.Attributes -class AztecMediaSpan(val context: Context, drawable: Drawable?, var attributes: Attributes) : - ImageSpan(drawable) { +class AztecMediaSpan(val context: Context, internal var drawable: Drawable?, var attributes: Attributes) : + DynamicDrawableSpan() { companion object { private val rect: Rect = Rect() } override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, metrics: Paint.FontMetricsInt?): Int { - val drawable = drawable - val bounds = getBounds(drawable) + drawable?.let { + val bounds = getBounds(it) - if (metrics != null) { - metrics.ascent = -bounds.bottom - metrics.descent = 0 + if (metrics != null) { + metrics.ascent = -bounds.bottom + metrics.descent = 0 - metrics.top = metrics.ascent - metrics.bottom = 0 + metrics.top = metrics.ascent + metrics.bottom = 0 + } + + return bounds.right } - return bounds.right + return 0 + } + + override fun getDrawable(): Drawable? { + return drawable + } + + override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { + canvas.save() + + if (drawable != null) { + var transY = bottom - drawable!!.bounds.bottom + if (mVerticalAlignment == ALIGN_BASELINE) { + transY -= paint.fontMetricsInt.descent + } + + canvas.translate(x, transY.toFloat()) + drawable!!.draw(canvas) + canvas.restore() + } } private fun getBounds(drawable: Drawable): Rect { @@ -72,7 +95,7 @@ class AztecMediaSpan(val context: Context, drawable: Drawable?, var attributes: Toast.makeText(view.context, getHtml(), Toast.LENGTH_SHORT).show() } - override fun getSource(): String { + fun getSource(): String { return attributes.getValue("src") } } From b0516a5890d54ce5ab77bcc7d967fde6606b93bd Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 Feb 2017 19:09:43 +0200 Subject: [PATCH 13/36] Fix: update media attributes on overlay removal --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 9a5520c28..da945aa3e 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -917,7 +917,7 @@ class AztecText : EditText, TextWatcher { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { if (attributePredicate.matches(it.attributes)) { it.drawable = (it.drawable as? LayerDrawable)?.getDrawable(0) - + it.attributes = attributes invalidate() } } From ccda437a4f3590708966cdfcfe54ace0d65ddc61 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Thu, 2 Feb 2017 12:32:19 +0200 Subject: [PATCH 14/36] Don't overwrite default image while loading --- aztec/src/main/java/org/wordpress/aztec/Html.java | 1 + aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index 8849d1b5a..5cc29d110 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -88,6 +88,7 @@ public interface ImageGetter { void loadImage(String source, Html.ImageGetter.Callbacks callbacks, int maxWidth); interface Callbacks { + void onUseDefaultImage(); void onImageLoaded(Drawable drawable); void onImageLoadingFailed(); } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index da945aa3e..fd8567012 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -575,6 +575,10 @@ class AztecText : EditText, TextWatcher { spans.forEach { val callbacks = object : Html.ImageGetter.Callbacks { + override fun onUseDefaultImage() { + // we already have a default image loaded so, noop + } + override fun onImageLoaded(drawable: Drawable?) { replaceImage(drawable) } From e3553f291f292927a67db10f66483bd9b198b296 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Thu, 2 Feb 2017 12:32:58 +0200 Subject: [PATCH 15/36] Fix: properly set fetched image --- .../src/main/kotlin/org/wordpress/aztec/AztecText.kt | 6 ++---- .../org/wordpress/aztec/spans/AztecMediaSpan.kt | 11 ++++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index fd8567012..fc9228946 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -594,11 +594,9 @@ class AztecText : EditText, TextWatcher { if (start == -1 || end == -1) return - text.removeSpan(it) + it.drawable = drawable - val newImageSpan = AztecMediaSpan(context, drawable, it.attributes) - drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) - text.setSpan(newImageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + refreshText() } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index a64e24ef5..2f63b5a6a 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -12,7 +12,7 @@ import android.widget.Toast import org.wordpress.android.util.DisplayUtils import org.xml.sax.Attributes -class AztecMediaSpan(val context: Context, internal var drawable: Drawable?, var attributes: Attributes) : +class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var attributes: Attributes) : DynamicDrawableSpan() { companion object { @@ -21,7 +21,7 @@ class AztecMediaSpan(val context: Context, internal var drawable: Drawable?, var override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, metrics: Paint.FontMetricsInt?): Int { drawable?.let { - val bounds = getBounds(it) + val bounds = adjustBounds(it) if (metrics != null) { metrics.ascent = -bounds.bottom @@ -41,6 +41,11 @@ class AztecMediaSpan(val context: Context, internal var drawable: Drawable?, var return drawable } + fun setDrawable(newDrawable: Drawable?) { + drawable = newDrawable + drawable?.let { adjustBounds(it) } + } + override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { canvas.save() @@ -56,7 +61,7 @@ class AztecMediaSpan(val context: Context, internal var drawable: Drawable?, var } } - private fun getBounds(drawable: Drawable): Rect { + private fun adjustBounds(drawable: Drawable): Rect { if (drawable.intrinsicWidth === 0) { rect.set(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) return rect From 1342ca0888231db23a667aebf46808e1b6e1d399 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Thu, 2 Feb 2017 15:47:27 +0200 Subject: [PATCH 16/36] Fix progress overlay size By not having AztecMediaSpan always adjust the bounds of the input drawable. When the input drawable is a LayerDrawable, setting its bounds messes with the individual layer bounds and in this case the progress indication drawable gets broken. So instead, have the called decide if the bounds should be adjusted by the AztecMediaSpan. --- .../main/java/org/wordpress/aztec/Html.java | 3 +- .../kotlin/org/wordpress/aztec/AztecText.kt | 10 ++--- .../aztec/formatting/LineBlockFormatter.kt | 2 +- .../wordpress/aztec/spans/AztecMediaSpan.kt | 44 ++++++++----------- 4 files changed, 25 insertions(+), 34 deletions(-) diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index 5cc29d110..cca87f7d4 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -755,8 +755,7 @@ private static void startImg(final SpannableStringBuilder text, // TODO: we should some placeholder drawable while loading imges Drawable loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading); - loadingDrawable.setBounds(0, 0, loadingDrawable.getIntrinsicWidth(), loadingDrawable.getIntrinsicHeight()); - final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, attributes); + final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, true, attributes); text.append("\uFFFC"); text.setSpan(imageSpan, start, text.length(), diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index fc9228946..3aab3c048 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -594,7 +594,7 @@ class AztecText : EditText, TextWatcher { if (start == -1 || end == -1) return - it.drawable = drawable + it.setDrawableAndAdjustBounds(drawable) refreshText() } @@ -903,10 +903,10 @@ class AztecText : EditText, TextWatcher { fun overlayProgress(drawable: Drawable?): Drawable { val progressDrawable = ContextCompat.getDrawable(context, android.R.drawable.progress_horizontal) val layerDrawable = LayerDrawable(arrayOf(drawable, progressDrawable)) + AztecMediaSpan.setBoundsToDp(context, layerDrawable) - val l = DisplayUtils.dpToPx(context, drawable!!.intrinsicWidth / 4) - val t = DisplayUtils.dpToPx(context, drawable!!.intrinsicHeight / 4) - layerDrawable.setLayerInset(1, l, t, l, t) + // Make the progessbar as wide as the image and only 2dp tall + progressDrawable.setBounds(0, 0, layerDrawable.bounds.right, DisplayUtils.dpToPx(context, (2))) return layerDrawable } @@ -918,7 +918,7 @@ class AztecText : EditText, TextWatcher { fun removeOverlayProgress(attributePredicate: AttributePredicate, attributes: Attributes) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { if (attributePredicate.matches(it.attributes)) { - it.drawable = (it.drawable as? LayerDrawable)?.getDrawable(0) + it.setDrawableAndAdjustBounds((it.drawable as? LayerDrawable)?.getDrawable(0)) it.attributes = attributes invalidate() } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt index e4598ad7f..53304ec53 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt @@ -278,7 +278,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { } } - val span = AztecMediaSpan(editor.context, drawable, attributes) + val span = AztecMediaSpan(editor.context, drawable, false, attributes) val html = span.getHtml(); val mediaStartIndex = selectionStart + 1 diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index 2f63b5a6a..a719e7afb 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -3,7 +3,6 @@ package org.wordpress.aztec.spans import android.content.Context import android.graphics.Canvas import android.graphics.Paint -import android.graphics.Rect import android.graphics.drawable.Drawable import android.text.style.DynamicDrawableSpan import android.view.View @@ -12,16 +11,25 @@ import android.widget.Toast import org.wordpress.android.util.DisplayUtils import org.xml.sax.Attributes -class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var attributes: Attributes) : +class AztecMediaSpan(val context: Context, private var drawable: Drawable?, initAdjustBounds: Boolean, var attributes: Attributes) : DynamicDrawableSpan() { + init { + if (initAdjustBounds) { + setBoundsToDp(context, drawable) + } + } + companion object { - private val rect: Rect = Rect() + fun setBoundsToDp(context: Context, drawable: Drawable?) { + drawable?.setBounds(0, 0, DisplayUtils.dpToPx(context, (drawable.intrinsicWidth)), + DisplayUtils.dpToPx(context, (drawable.intrinsicHeight))) + } } override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, metrics: Paint.FontMetricsInt?): Int { drawable?.let { - val bounds = adjustBounds(it) + val bounds = drawable!!.bounds if (metrics != null) { metrics.ascent = -bounds.bottom @@ -41,9 +49,13 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var return drawable } - fun setDrawable(newDrawable: Drawable?) { + fun setDrawableAndAdjustBounds(newDrawable: Drawable?) { + drawable = newDrawable + setBoundsToDp(context, drawable) + } + + fun setDrawableWithoutAdjustingBounds(newDrawable: Drawable?) { drawable = newDrawable - drawable?.let { adjustBounds(it) } } override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { @@ -61,26 +73,6 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var } } - private fun adjustBounds(drawable: Drawable): Rect { - if (drawable.intrinsicWidth === 0) { - rect.set(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) - return rect - } - - /* - * Following Android guidelines for keylines and spacing, screen edge margins should - * be 16dp. Therefore, the width of images should be the width of the screen minus - * 16dp on both sides (i.e. 16 * 2 = 32). - * - * https://material.io/guidelines/layout/metrics-keylines.html#metrics-keylines-baseline-grids - */ - val width = context.resources.displayMetrics.widthPixels - DisplayUtils.dpToPx(context, 32) - val height = drawable.intrinsicHeight * width / drawable.intrinsicWidth - drawable.setBounds(0, 0, width, height) - - return drawable.bounds - } - fun getHtml(): String { var sb = StringBuilder() sb.append(" Date: Fri, 3 Feb 2017 12:24:14 +0200 Subject: [PATCH 17/36] Overlay with gravity for AztecMediaSpan This is useful for easily overlay some informational drawable on top of the main drawable. --- .../main/java/org/wordpress/aztec/Html.java | 3 +- .../kotlin/org/wordpress/aztec/AztecText.kt | 45 +++++------ .../aztec/formatting/LineBlockFormatter.kt | 4 +- .../wordpress/aztec/spans/AztecMediaSpan.kt | 75 +++++++++++++------ 4 files changed, 73 insertions(+), 54 deletions(-) diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index cca87f7d4..d87353115 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -754,8 +754,7 @@ private static void startImg(final SpannableStringBuilder text, // TODO: we should some placeholder drawable while loading imges Drawable loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading); - - final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, true, attributes); + final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, null, 0, attributes); text.append("\uFFFC"); text.setSpan(imageSpan, start, text.length(), diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 3aab3c048..fe87d2fdd 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -21,7 +21,6 @@ import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.graphics.drawable.Drawable -import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.os.Parcel import android.os.Parcelable @@ -594,7 +593,7 @@ class AztecText : EditText, TextWatcher { if (start == -1 || end == -1) return - it.setDrawableAndAdjustBounds(drawable) + it.drawable = drawable refreshText() } @@ -881,16 +880,8 @@ class AztecText : EditText, TextWatcher { } } - fun insertMedia(drawable: Drawable?, attributes: Attributes) { - lineBlockFormatter.insertMedia(overlayProgress(drawable), attributes) - } - - fun setProgressOnMedia(attributePredicate: AttributePredicate, progress: Float) { - text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { - if (attributePredicate.matches(it.attributes)) { - if (setLevelOverlayProgress(it.drawable, progress)) invalidate() - } - } + fun insertMedia(drawable: Drawable?, overlay: Drawable?, overlayGravity: Int, attributes: Attributes) { + lineBlockFormatter.insertMedia(drawable, overlay, overlayGravity, attributes) } interface AttributePredicate { @@ -900,26 +891,24 @@ class AztecText : EditText, TextWatcher { fun matches(attrs: Attributes): Boolean } - fun overlayProgress(drawable: Drawable?): Drawable { - val progressDrawable = ContextCompat.getDrawable(context, android.R.drawable.progress_horizontal) - val layerDrawable = LayerDrawable(arrayOf(drawable, progressDrawable)) - AztecMediaSpan.setBoundsToDp(context, layerDrawable) - - // Make the progessbar as wide as the image and only 2dp tall - progressDrawable.setBounds(0, 0, layerDrawable.bounds.right, DisplayUtils.dpToPx(context, (2))) - - return layerDrawable - } - - fun setLevelOverlayProgress(drawable: Drawable?, progress: Float): Boolean { - return (drawable as LayerDrawable).getDrawable(1).setLevel((progress * 10000).toInt()) + fun setOverlayLevel(attributePredicate: AttributePredicate, level: Int) { + text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach lit@ { + if (attributePredicate.matches(it.attributes)) { + it.setOverayLevel(level) + } + } } - fun removeOverlayProgress(attributePredicate: AttributePredicate, attributes: Attributes) { + fun setOverlay(attributePredicate: AttributePredicate, overlay: Drawable?, gravity: Int, attributes: Attributes?) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { if (attributePredicate.matches(it.attributes)) { - it.setDrawableAndAdjustBounds((it.drawable as? LayerDrawable)?.getDrawable(0)) - it.attributes = attributes + // set the new overlay drawable + it.setOverlay(overlay, gravity) + + if (attributes != null) { + it.attributes = attributes + } + invalidate() } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt index 53304ec53..e481f8855 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt @@ -268,7 +268,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { editor.setSelection(commentEndIndex + 1) } - fun insertMedia(drawable: Drawable?, attributes: Attributes) { + fun insertMedia(drawable: Drawable?, overlay: Drawable?, overlayGravity: Int, attributes: Attributes) { //check if we add media into a block element, at the end of the line, but not at the end of last line var applyingOnTheEndOfBlockLine = false editableText.getSpans(selectionStart, selectionEnd, AztecBlockSpan::class.java).forEach { @@ -278,7 +278,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { } } - val span = AztecMediaSpan(editor.context, drawable, false, attributes) + val span = AztecMediaSpan(editor.context, drawable, overlay, overlayGravity, attributes) val html = span.getHtml(); val mediaStartIndex = selectionStart + 1 diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index a719e7afb..ac7aed06a 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -3,59 +3,87 @@ package org.wordpress.aztec.spans import android.content.Context import android.graphics.Canvas import android.graphics.Paint +import android.graphics.Rect import android.graphics.drawable.Drawable import android.text.style.DynamicDrawableSpan +import android.view.Gravity import android.view.View import android.widget.Toast import org.wordpress.android.util.DisplayUtils import org.xml.sax.Attributes -class AztecMediaSpan(val context: Context, private var drawable: Drawable?, initAdjustBounds: Boolean, var attributes: Attributes) : - DynamicDrawableSpan() { +class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var overlay: Drawable?, overlayGravity: Int, + var attributes: Attributes) : DynamicDrawableSpan() { - init { - if (initAdjustBounds) { - setBoundsToDp(context, drawable) + companion object { + @JvmStatic private fun setBoundsToPx(context: Context, drawable: Drawable?) { + drawable?.let { + if (it.intrinsicWidth < 0 && it.intrinsicHeight < 0) { + // client may have set the bounds manually so, use those to adjust to px + it.setBounds(0, 0, DisplayUtils.dpToPx(context, (it.bounds.width())), + DisplayUtils.dpToPx(context, (it.bounds.height()))) + } else { + it.setBounds(0, 0, DisplayUtils.dpToPx(context, (it.intrinsicWidth)), + DisplayUtils.dpToPx(context, (it.intrinsicHeight))) + } + } } } - companion object { - fun setBoundsToDp(context: Context, drawable: Drawable?) { - drawable?.setBounds(0, 0, DisplayUtils.dpToPx(context, (drawable.intrinsicWidth)), - DisplayUtils.dpToPx(context, (drawable.intrinsicHeight))) - } + init { + setBoundsToPx(context, drawable) + setBoundsToPx(context, overlay) + applyOverlayGravity(overlayGravity) } override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, metrics: Paint.FontMetricsInt?): Int { - drawable?.let { - val bounds = drawable!!.bounds + val width1 = drawable?.bounds?.width() ?: 0 + val width2 = overlay?.bounds?.width() ?: 0 + val width: Int = if (width1 > width2) width1 else width2 + drawable?.let { if (metrics != null) { - metrics.ascent = -bounds.bottom + metrics.ascent = -drawable!!.bounds!!.height() metrics.descent = 0 metrics.top = metrics.ascent metrics.bottom = 0 } - - return bounds.right } - return 0 + return width } override fun getDrawable(): Drawable? { return drawable } - fun setDrawableAndAdjustBounds(newDrawable: Drawable?) { + fun setDrawable(newDrawable: Drawable?) { + setBoundsToPx(context, newDrawable) drawable = newDrawable - setBoundsToDp(context, drawable) } - fun setDrawableWithoutAdjustingBounds(newDrawable: Drawable?) { - drawable = newDrawable + fun setOverlay(newDrawable: Drawable?, gravity: Int) { + setBoundsToPx(context, newDrawable) + overlay = newDrawable + + applyOverlayGravity(gravity) + } + + fun setOverayLevel(level: Int): Boolean { + return overlay?.setLevel(level) ?: false + } + + private fun applyOverlayGravity(gravity: Int) { + if (drawable != null && overlay != null) { + val rect = Rect(0, 0, drawable!!.bounds.width(), drawable!!.bounds.height()) + val outRect = Rect() + + Gravity.apply(gravity, overlay!!.bounds.width(), overlay!!.bounds.height(), rect, outRect) + + overlay!!.setBounds(outRect.left, outRect.top, outRect.right, outRect.bottom) + } } override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { @@ -69,12 +97,15 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, init canvas.translate(x, transY.toFloat()) drawable!!.draw(canvas) - canvas.restore() } + + overlay?.draw(canvas) + + canvas.restore() } fun getHtml(): String { - var sb = StringBuilder() + val sb = StringBuilder() sb.append(" Date: Fri, 3 Feb 2017 13:55:07 +0200 Subject: [PATCH 18/36] Fix build: No overlay in MainActivity.kt --- app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index fcbe633b1..b4b65439b 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -130,7 +130,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque val attrs = AttributesImpl() attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. - aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), attrs) + aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), null, 0, attrs) } super.onActivityResult(requestCode, resultCode, data) From b630e0a99c1349906afd53389e2a97dbd1cbedaa Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Fri, 3 Feb 2017 14:28:00 +0200 Subject: [PATCH 19/36] Cap drawable to screen width --- .../main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index ac7aed06a..40e6d8d65 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -27,6 +27,11 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var it.setBounds(0, 0, DisplayUtils.dpToPx(context, (it.intrinsicWidth)), DisplayUtils.dpToPx(context, (it.intrinsicHeight))) } + + val maxWidth = DisplayUtils.getDisplayPixelWidth(context) - DisplayUtils.dpToPx(context, 32) + if (drawable.bounds.width() > maxWidth) { + drawable.setBounds(0, 0, maxWidth, maxWidth * drawable.bounds.height() / drawable.bounds.width()) + } } } } From c14a2e37250169861aa45082868796c0f6c85b0c Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Fri, 3 Feb 2017 14:30:29 +0200 Subject: [PATCH 20/36] Let the client set the layer type It seems that the demo app doesn't work with software rendering while the wpandroid app doesn't work with hardware! --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index fe87d2fdd..b907c7f21 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -108,10 +108,6 @@ class AztecText : EditText, TextWatcher { private fun init(attrs: AttributeSet?) { TypefaceCache.setCustomTypeface(context, this, TypefaceCache.TYPEFACE_MERRIWEATHER_REGULAR) - // It seems that hardware accel makes the progressbar in MediaSpan to not show that it updates. - // Instead, software rendering works. See: https://github.com/koral--/android-gif-drawable/issues/234#issuecomment-165938445 - setLayerType(View.LAYER_TYPE_SOFTWARE, null); - val array = context.obtainStyledAttributes(attrs, R.styleable.AztecText, 0, R.style.AztecTextStyle) setLineSpacing( array.getDimension( From 5140ef9cdab228194bf9ebd31d52c5abda9c897d Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Fri, 3 Feb 2017 17:43:58 +0200 Subject: [PATCH 21/36] Delegate media tap to the client --- .../org/wordpress/aztec/demo/MainActivity.kt | 11 ++++- .../main/java/org/wordpress/aztec/Html.java | 29 +++++++++----- .../kotlin/org/wordpress/aztec/AztecParser.kt | 5 ++- .../kotlin/org/wordpress/aztec/AztecText.kt | 40 ++++++++++++++++--- .../aztec/formatting/LineBlockFormatter.kt | 6 ++- .../wordpress/aztec/spans/AztecMediaSpan.kt | 6 +-- 6 files changed, 74 insertions(+), 23 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index b4b65439b..30da544e2 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -30,11 +30,12 @@ import org.wordpress.aztec.picassoloader.PicassoImageLoader import org.wordpress.aztec.source.SourceViewEditText import org.wordpress.aztec.toolbar.AztecToolbar import org.wordpress.aztec.toolbar.AztecToolbar.OnMediaOptionSelectedListener +import org.xml.sax.Attributes import org.xml.sax.helpers.AttributesImpl import java.io.File class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnRequestPermissionsResultCallback, - View.OnTouchListener, AztecText.OnImeBackListener { + View.OnTouchListener, AztecText.OnMediaTappedListener, AztecText.OnImeBackListener { companion object { private val HEADING = "

Heading 1

" + @@ -130,7 +131,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque val attrs = AttributesImpl() attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. - aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), null, 0, attrs) + aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), null, 0, attrs, this) } super.onActivityResult(requestCode, resultCode, data) @@ -167,6 +168,8 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque aztec.setOnTouchListener(this) source.setOnImeBackListener(this) source.setOnTouchListener(this) + + aztec.setOnMediaTappedListener(this) } override fun onPause() { @@ -433,4 +436,8 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque super.onRequestPermissionsResult(requestCode, permissions, grantResults) } + + override fun mediaTapped(attrs: Attributes) { + ToastUtils.showToast(this, "Media tapped!") + } } diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index d87353115..0817795e4 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -35,11 +35,13 @@ import org.ccil.cowan.tagsoup.HTMLSchema; import org.ccil.cowan.tagsoup.Parser; +import org.wordpress.aztec.AztecText.OnMediaTappedListener; import org.wordpress.aztec.spans.AztecBlockSpan; import org.wordpress.aztec.spans.AztecCodeSpan; import org.wordpress.aztec.spans.AztecCommentSpan; import org.wordpress.aztec.spans.AztecContentSpan; import org.wordpress.aztec.spans.AztecCursorSpan; +import org.wordpress.aztec.spans.AztecMediaClickableSpan; import org.wordpress.aztec.spans.AztecMediaSpan; import org.wordpress.aztec.spans.AztecRelativeSizeSpan; import org.wordpress.aztec.spans.AztecStyleSpan; @@ -117,8 +119,8 @@ private Html() { *

*

This uses TagSoup to handle real HTML, including all of the brokenness found in the wild. */ - public static Spanned fromHtml(String source, Context context) { - return fromHtml(source, null, context); + public static Spanned fromHtml(String source, OnMediaTappedListener onMediaTappedListener, Context context) { + return fromHtml(source, null, onMediaTappedListener, context); } /** @@ -139,7 +141,8 @@ private static class HtmlParser { *

*

This uses TagSoup to handle real HTML, including all of the brokenness found in the wild. */ - public static Spanned fromHtml(String source, TagHandler tagHandler, Context context) { + public static Spanned fromHtml(String source, TagHandler tagHandler, OnMediaTappedListener onMediaTappedListener, + Context context) { Parser parser = new Parser(); try { parser.setProperty(Parser.schemaProperty, HtmlParser.schema); @@ -154,7 +157,7 @@ public static Spanned fromHtml(String source, TagHandler tagHandler, Context con HtmlToSpannedConverter converter = new HtmlToSpannedConverter(source, tagHandler, - parser, context); + parser, onMediaTappedListener, context); return converter.convert(); } @@ -446,15 +449,17 @@ class HtmlToSpannedConverter implements ContentHandler, LexicalHandler { private SpannableStringBuilder spannableStringBuilder; private Html.TagHandler tagHandler; private Context context; + private OnMediaTappedListener onMediaTappedListener; public HtmlToSpannedConverter( String source, Html.TagHandler tagHandler, - Parser parser, Context context) { + Parser parser, OnMediaTappedListener onMediaTappedListener, Context context) { mSource = source; spannableStringBuilder = new SpannableStringBuilder(); this.tagHandler = tagHandler; mReader = parser; this.context = context; + this.onMediaTappedListener = onMediaTappedListener; } public Spanned convert() { @@ -545,7 +550,7 @@ private void handleStartTag(String tag, Attributes attributes) { } else if (tag.equalsIgnoreCase("code")) { start(spannableStringBuilder, new Code(attributes)); } else if (tag.equalsIgnoreCase("img")) { - startImg(spannableStringBuilder, attributes, context); + startImg(spannableStringBuilder, attributes, onMediaTappedListener, context); } else { if (tagHandler != null) { boolean tagHandled = tagHandler.handleTag(true, tag, spannableStringBuilder, mReader, attributes); @@ -748,17 +753,23 @@ private static void end(SpannableStringBuilder text, TextFormat textFormat) { } private static void startImg(final SpannableStringBuilder text, - Attributes attributes, final Context context) { - final String src = attributes.getValue("", "src"); + Attributes attributes, final OnMediaTappedListener onMediaTappedListener, + final Context context) { final int start = text.length(); // TODO: we should some placeholder drawable while loading imges Drawable loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading); - final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, null, 0, attributes); + final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, null, 0, attributes, + onMediaTappedListener); text.append("\uFFFC"); text.setSpan(imageSpan, start, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan( + new AztecMediaClickableSpan(imageSpan), + start, + text.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } private static void endFont(SpannableStringBuilder text) { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt index 5e5d53bca..1e1a08983 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecParser.kt @@ -25,6 +25,7 @@ import android.text.Spanned import android.text.TextUtils import android.text.style.CharacterStyle import android.text.style.ParagraphStyle +import org.wordpress.aztec.AztecText.OnMediaTappedListener import org.wordpress.aztec.spans.* import java.util.* @@ -36,10 +37,10 @@ class AztecParser { internal var hiddenSpans: IntArray = IntArray(0) internal var spanCursorPosition = -1 - fun fromHtml(source: String, context: Context): Spanned { + fun fromHtml(source: String, onMediaTappedListener: OnMediaTappedListener?, context: Context): Spanned { val tidySource = tidy(source) - val spanned = SpannableStringBuilder(Html.fromHtml(tidySource, AztecTagHandler(), context)) + val spanned = SpannableStringBuilder(Html.fromHtml(tidySource, AztecTagHandler(), onMediaTappedListener, context)) addZwjCharToBlockSpans(spanned) adjustNestedSpanOrder(spanned) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index b907c7f21..ce7b460af 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -63,6 +63,8 @@ class AztecText : EditText, TextWatcher { private var onImeBackListener: OnImeBackListener? = null + private var onMediaTappedListener: OnMediaTappedListener? = null + private var isViewInitialized = false private var previousCursorPosition = 0 @@ -89,6 +91,10 @@ class AztecText : EditText, TextWatcher { fun onImeBack() } + interface OnMediaTappedListener { + fun mediaTapped(attrs: Attributes) + } + init { lineBlockFormatter = LineBlockFormatter(this) } @@ -296,6 +302,10 @@ class AztecText : EditText, TextWatcher { this.onImeBackListener = listener } + fun setOnMediaTappedListener(listener: OnMediaTappedListener) { + this.onMediaTappedListener = listener + } + override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { onImeBackListener?.onImeBack() @@ -553,7 +563,7 @@ class AztecText : EditText, TextWatcher { val builder = SpannableStringBuilder() val parser = AztecParser() - builder.append(parser.fromHtml(Format.clearFormatting(source), context)) + builder.append(parser.fromHtml(Format.clearFormatting(source), onMediaTappedListener, context)) switchToAztecStyle(builder, 0, builder.length) disableTextChangedListener() val cursorPosition = consumeCursorPosition(builder) @@ -761,7 +771,8 @@ class AztecText : EditText, TextWatcher { val textToPaste = clip.getItemAt(i).coerceToText(context) val builder = SpannableStringBuilder() - builder.append(parser.fromHtml(Format.clearFormatting(textToPaste.toString()), context).trim()) + builder.append(parser.fromHtml(Format.clearFormatting(textToPaste.toString()),onMediaTappedListener, + context).trim()) Selection.setSelection(editable, max) disableTextChangedListener() @@ -877,7 +888,15 @@ class AztecText : EditText, TextWatcher { } fun insertMedia(drawable: Drawable?, overlay: Drawable?, overlayGravity: Int, attributes: Attributes) { - lineBlockFormatter.insertMedia(drawable, overlay, overlayGravity, attributes) + lineBlockFormatter.insertMedia(drawable, overlay, overlayGravity, attributes, onMediaTappedListener) + } + + fun removeMedia(attributePredicate: AttributePredicate) { + text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + if (attributePredicate.matches(it.attributes)) { + text.removeSpan(it) + } + } } interface AttributePredicate { @@ -887,10 +906,11 @@ class AztecText : EditText, TextWatcher { fun matches(attrs: Attributes): Boolean } - fun setOverlayLevel(attributePredicate: AttributePredicate, level: Int) { - text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach lit@ { + fun setOverlayLevel(attributePredicate: AttributePredicate, level: Int, attrs: Attributes) { + text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { if (attributePredicate.matches(it.attributes)) { it.setOverayLevel(level) + it.attributes = attrs } } } @@ -909,4 +929,14 @@ class AztecText : EditText, TextWatcher { } } } + + fun getMediaAttributes(attributePredicate: AttributePredicate): Attributes? { + text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + if (attributePredicate.matches(it.attributes)) { + return it.attributes + } + } + + return null; + } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt index e481f8855..378e07107 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt @@ -5,6 +5,7 @@ import android.support.v4.content.ContextCompat import android.text.Spanned import android.text.TextUtils import org.wordpress.aztec.AztecText +import org.wordpress.aztec.AztecText.OnMediaTappedListener import org.wordpress.aztec.R import org.wordpress.aztec.TextChangedEvent import org.wordpress.aztec.TextFormat @@ -268,7 +269,8 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { editor.setSelection(commentEndIndex + 1) } - fun insertMedia(drawable: Drawable?, overlay: Drawable?, overlayGravity: Int, attributes: Attributes) { + fun insertMedia(drawable: Drawable?, overlay: Drawable?, overlayGravity: Int, attributes: Attributes, + onMediaTappedListener: OnMediaTappedListener?) { //check if we add media into a block element, at the end of the line, but not at the end of last line var applyingOnTheEndOfBlockLine = false editableText.getSpans(selectionStart, selectionEnd, AztecBlockSpan::class.java).forEach { @@ -278,7 +280,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { } } - val span = AztecMediaSpan(editor.context, drawable, overlay, overlayGravity, attributes) + val span = AztecMediaSpan(editor.context, drawable, overlay, overlayGravity, attributes, onMediaTappedListener) val html = span.getHtml(); val mediaStartIndex = selectionStart + 1 diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index 40e6d8d65..697c4fbc7 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -8,13 +8,13 @@ import android.graphics.drawable.Drawable import android.text.style.DynamicDrawableSpan import android.view.Gravity import android.view.View -import android.widget.Toast import org.wordpress.android.util.DisplayUtils +import org.wordpress.aztec.AztecText.OnMediaTappedListener import org.xml.sax.Attributes class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var overlay: Drawable?, overlayGravity: Int, - var attributes: Attributes) : DynamicDrawableSpan() { + var attributes: Attributes, val onMediaTappedListener: OnMediaTappedListener?) : DynamicDrawableSpan() { companion object { @JvmStatic private fun setBoundsToPx(context: Context, drawable: Drawable?) { @@ -125,7 +125,7 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var } fun onClick(view: View) { - Toast.makeText(view.context, getHtml(), Toast.LENGTH_SHORT).show() + onMediaTappedListener?.mediaTapped(attributes) } fun getSource(): String { From 5e77933a92aaab089ed8315639b190138d7db507 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Fri, 3 Feb 2017 18:09:11 +0200 Subject: [PATCH 22/36] Fix the tests syntax errors --- .../org/wordpress/aztec/AztecParserTest.kt | 150 +++++++++--------- .../org/wordpress/aztec/HtmlFormattingTest.kt | 6 +- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/aztec/src/test/kotlin/org/wordpress/aztec/AztecParserTest.kt b/aztec/src/test/kotlin/org/wordpress/aztec/AztecParserTest.kt index 644d457b3..df1a41431 100644 --- a/aztec/src/test/kotlin/org/wordpress/aztec/AztecParserTest.kt +++ b/aztec/src/test/kotlin/org/wordpress/aztec/AztecParserTest.kt @@ -124,7 +124,7 @@ class AztecParserTest : AndroidTestCase() { HTML_NESTED_INTERLEAVING + HTML_EMOJI - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -139,7 +139,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlBold_isEqual() { val input = HTML_BOLD - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -154,7 +154,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListUnordered_isEqual() { val input = HTML_LIST_UNORDERED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -169,7 +169,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListUnorderedWithQuote_isEqual() { val input = HTML_LIST_UNORDERED_WITH_QUOTE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -184,7 +184,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListUnorderedWithQuoteSurroundedByText_isEqual() { val input = "One" + HTML_LIST_UNORDERED_WITH_QUOTE + "Two" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -199,7 +199,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListUnorderedWhiteSpace_isEqual() { val input = HTML_LIST_UNORDERED_WITH_WHITE_SPACE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(HTML_LIST_UNORDERED, output) } @@ -214,7 +214,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrdered_isEqual() { val input = HTML_LIST_ORDERED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -229,7 +229,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrderedWithQuote_isEqual() { val input = HTML_LIST_ORDERED_WITH_QUOTE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -244,7 +244,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrderedWithQuoteSurroundedByText_isEqual() { val input = "One" + HTML_LIST_ORDERED_WITH_QUOTE + "Two" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -259,7 +259,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrderedWhiteSpace_isEqual() { val input = HTML_LIST_ORDERED_WITH_WHITE_SPACE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(HTML_LIST_ORDERED, output) } @@ -274,7 +274,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrderedSurroundedByText_isEqual() { val input = "1" + HTML_LIST_ORDERED + "2" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -289,7 +289,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrderedSurroundedByNewlineAndText_isEqual() { val input = "1
$HTML_LIST_ORDERED
2" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -304,7 +304,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListsWithTextBetween_isEqual() { val input = HTML_LIST_ORDERED + "1" + HTML_LIST_ORDERED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -323,46 +323,46 @@ class AztecParserTest : AndroidTestCase() { var span: SpannableString input = "
$HTML_HEADING_ONE" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) Assert.assertEquals("\nHeading 1", span.toString()) output = mParser.toHtml(span) Assert.assertEquals(input, output) input = "Text
$HTML_HEADING_ONE" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) output = mParser.toHtml(span) Assert.assertEquals(input, output) input = "
$HTML_QUOTE" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) Assert.assertEquals("\nQuote", span.toString()) output = mParser.toHtml(span) Assert.assertEquals(input, output) input = "Text
$HTML_QUOTE" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) output = mParser.toHtml(span) Assert.assertEquals(input, output) input = "
$HTML_LIST_ORDERED" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) Assert.assertEquals("\nOrdered", span.toString()) output = mParser.toHtml(span) Assert.assertEquals(input, output) input = "Text
$HTML_LIST_ORDERED" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) output = mParser.toHtml(span) Assert.assertEquals(input, output) input = "
$HTML_LIST_UNORDERED" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) Assert.assertEquals("\nUnordered", span.toString()) output = mParser.toHtml(span) Assert.assertEquals(input, output) input = "Text
$HTML_LIST_UNORDERED" - span = SpannableString(mParser.fromHtml(input, context)) + span = SpannableString(mParser.fromHtml(input, null, context)) output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -377,7 +377,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlComment_isEqual() { val input = HTML_COMMENT - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -392,7 +392,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlHeading_isEqual() { val input = HTML_HEADING_ALL - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -407,7 +407,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlItalic_isEqual() { val input = HTML_ITALIC - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -422,7 +422,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlLink_isEqual() { val input = HTML_LINK - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -437,7 +437,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlMore_isEqual() { val input = HTML_MORE - val span = SpannableString(mParser.fromHtml(input, RuntimeEnvironment.application.applicationContext)) + val span = SpannableString(mParser.fromHtml(input, null, RuntimeEnvironment.application.applicationContext)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -452,7 +452,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlPage_isEqual() { val input = HTML_PAGE - val span = SpannableString(mParser.fromHtml(input, RuntimeEnvironment.application.applicationContext)) + val span = SpannableString(mParser.fromHtml(input, null, RuntimeEnvironment.application.applicationContext)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -467,7 +467,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlQuote_isEqual() { val input = HTML_QUOTE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -482,7 +482,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlQuoteWithWhiteSpace_isEqual() { val input = HTML_QUOTE_WITH_WHITE_SPACE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(HTML_QUOTE, output) } @@ -497,7 +497,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlQuoteWithListOrdered_isEqual() { val input = HTML_QUOTE_WITH_LIST_ORDERED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -512,7 +512,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlQuoteWithListUnordered_isEqual() { val input = HTML_QUOTE_WITH_LIST_UNORDERED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -527,7 +527,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlStrikethrough_isEqual() { val input = HTML_STRIKETHROUGH - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -542,7 +542,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlUnderline_isEqual() { val input = HTML_UNDERLINE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -557,7 +557,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlUnknown_isEqual() { val input = HTML_UNKNOWN - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -572,7 +572,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedMixed_isEqual() { val input = HTML_NESTED_MIXED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -588,7 +588,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedMixedTwice_isEqual() { val input = HTML_NESTED_MIXED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) Assert.assertEquals(input, mParser.toHtml(span)) Assert.assertEquals(input, mParser.toHtml(span)) } @@ -603,7 +603,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedEmpty_isEqual() { val input = HTML_NESTED_EMPTY - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -618,7 +618,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedEmptyEnd_isEqual() { val input = HTML_NESTED_EMPTY_END - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -633,7 +633,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedEmptyStart_isEqual() { val input = HTML_NESTED_EMPTY_START - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -648,7 +648,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedNonEmpty_isEqual() { val input = HTML_NESTED_WITH_TEXT - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -663,7 +663,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedInterleaving_isEqual() { val input = HTML_NESTED_INTERLEAVING - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -678,7 +678,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHiddenHtmlWithNoTextToSpanToHtmlNestedInterleaving_isEqual() { val input = HTML_HIDDEN_WITH_NO_TEXT - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -687,7 +687,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun preserveListListUnorderedWithEmptyListItem() { val input = HTML_LIST_UNORDERED_WITH_EMPTY_ITEM - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -696,7 +696,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun preserveListOrderedWithEmptyListItem() { val input = HTML_LIST_ORDERED_WITH_EMPTY_ITEM - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -723,7 +723,7 @@ class AztecParserTest : AndroidTestCase() { SPAN_COMMENT ) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -738,7 +738,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanBold_isEqual() { val input = SpannableString(SPAN_BOLD) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -753,7 +753,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanListOrdered_isEqual() { val input = SpannableString(SPAN_LIST_ORDERED) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -768,7 +768,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanListUnordered_isEqual() { val input = SpannableString(SPAN_LIST_UNORDERED) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -783,7 +783,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanComment_isEqual() { val input = SpannableString(SPAN_COMMENT) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -798,7 +798,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanHeading_isEqual() { val input = SpannableString(SPAN_HEADING) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -813,7 +813,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanItalic_isEqual() { val input = SpannableString(SPAN_ITALIC) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -828,7 +828,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanLink_isEqual() { val input = SpannableString(SPAN_LINK) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -843,7 +843,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanMore_isEqual() { val input = SpannableString(SPAN_MORE) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -858,7 +858,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanPage_isEqual() { val input = SpannableString(SPAN_PAGE) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -873,7 +873,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanQuote_isEqual() { val input = SpannableString(SPAN_QUOTE) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -888,7 +888,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanStrikethrough_isEqual() { val input = SpannableString(SPAN_STRIKETHROUGH) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -903,7 +903,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanUnderline_isEqual() { val input = SpannableString(SPAN_UNDERLINE) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -918,7 +918,7 @@ class AztecParserTest : AndroidTestCase() { fun parseSpanToHtmlToSpanUnknown_isEqual() { val input = SpannableString(SPAN_UNKNOWN) val html = mParser.toHtml(input) - val output = mParser.fromHtml(html, context) + val output = mParser.fromHtml(html, null, context) Assert.assertEquals(input, output) } @@ -932,7 +932,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlCommentInsideUnknown_isEqual() { val input = HTML_COMMENT_INSIDE_UNKNOWN - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -947,7 +947,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlSingleHeading_isEqual() { val input = HTML_HEADING_ONE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -962,7 +962,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlSingleHeadingSurroundedByText_isEqual() { val input = "1" + HTML_HEADING_ONE + "1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -977,7 +977,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlHeadingSurroundedByList_isEqual() { val input = HTML_LIST_ORDERED + HTML_HEADING_ONE + HTML_LIST_ORDERED - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -992,7 +992,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlHeadingSurroundedByQuote_isEqual() { val input = HTML_QUOTE + HTML_HEADING_ONE + HTML_QUOTE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1007,7 +1007,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlLineBreakBetweenHeadings_isEqual() { val input = HTML_HEADING_ONE + "
" + HTML_HEADING_ONE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1022,7 +1022,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlNestedInlineStyles_isEqual() { val input = HTML_NESTED_INLINE - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1031,7 +1031,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrderedWithTrailingEmptyItem_isEqual() { val input = "

  1. Ordered item
" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1040,7 +1040,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListUnorderedWithLinebreak_isEqual() { val input = "
  • a

1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1049,7 +1049,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListOrderedWithTrailingEmptyItemAndLinebreak_isEqual() { val input = "
  1. Ordered item

1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1058,7 +1058,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListUnorderedFollowedByLinebreak_isEqual() { val input = "
  • Ordered item
  • b

1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1067,7 +1067,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListUnorderedFollowedByUnknwonHtml_isEqual() { val input = HTML_LIST_UNORDERED + HTML_UNKNOWN - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1082,7 +1082,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlSingleCharHeaderSurroundedByHeaders_isEqual() { val input = "

Heading 1

2

Heading 3

" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1091,7 +1091,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlOrderedListWithTrailingEmptyItemAnd2Linebreaks_isEqual() { val input = "
  1. Ordered item


1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1100,7 +1100,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlUnorderedListFollowedBy2Linebreaks_isEqual() { val input = "
  • Ordered item
  • b


1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1109,7 +1109,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListWithEmptyItemFollowedByText_isEqual() { val input = "
  1. Ordered item
1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } @@ -1118,7 +1118,7 @@ class AztecParserTest : AndroidTestCase() { @Throws(Exception::class) fun parseHtmlToSpanToHtmlListWithNonEmptyItemsFollowedByText_isEqual() { val input = "
  1. Ordered item
  2. a
1" - val span = SpannableString(mParser.fromHtml(input, context)) + val span = SpannableString(mParser.fromHtml(input, null, context)) val output = mParser.toHtml(span) Assert.assertEquals(input, output) } diff --git a/aztec/src/test/kotlin/org/wordpress/aztec/HtmlFormattingTest.kt b/aztec/src/test/kotlin/org/wordpress/aztec/HtmlFormattingTest.kt index d4b08fcfd..aaa7d839e 100644 --- a/aztec/src/test/kotlin/org/wordpress/aztec/HtmlFormattingTest.kt +++ b/aztec/src/test/kotlin/org/wordpress/aztec/HtmlFormattingTest.kt @@ -86,7 +86,7 @@ class HtmlFormattingTest() : AndroidTestCase() { @Throws(Exception::class) fun formatNestedHtml() { val input = HTML_NESTED - val span = SpannableString(parser.fromHtml(input, context)) + val span = SpannableString(parser.fromHtml(input, null, context)) val output = Format.clearFormatting(Format.addFormatting(parser.toHtml(span))) Assert.assertEquals(input, output) } @@ -100,7 +100,7 @@ class HtmlFormattingTest() : AndroidTestCase() { @Throws(Exception::class) fun formatLineBreaks() { val input = HTML_LINE_BREAKS - val span = SpannableString(parser.fromHtml(input, context)) + val span = SpannableString(parser.fromHtml(input, null, context)) val output = Format.clearFormatting(Format.addFormatting(parser.toHtml(span))) Assert.assertEquals(input, output) } @@ -114,7 +114,7 @@ class HtmlFormattingTest() : AndroidTestCase() { @Throws(Exception::class) fun formatMixedHtml() { val input = HTML_MIXED - val span = SpannableString(parser.fromHtml(input, context)) + val span = SpannableString(parser.fromHtml(input, null, context)) val output = Format.clearFormatting(Format.addFormatting(parser.toHtml(span))) Assert.assertEquals(HTML_MIXED_NO_WS, output) } From 293db7981148e6abd42b0398d654929f4c993939 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 11:44:00 +0200 Subject: [PATCH 23/36] Pass the image size when tapped --- .../org/wordpress/aztec/demo/MainActivity.kt | 2 +- .../kotlin/org/wordpress/aztec/AztecText.kt | 2 +- .../wordpress/aztec/spans/AztecMediaSpan.kt | 28 ++++++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index 30da544e2..ee16ce691 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -437,7 +437,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque super.onRequestPermissionsResult(requestCode, permissions, grantResults) } - override fun mediaTapped(attrs: Attributes) { + override fun mediaTapped(attrs: Attributes, naturalWidth: Int, naturalHeight: Int) { ToastUtils.showToast(this, "Media tapped!") } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index ce7b460af..901b76b7f 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -92,7 +92,7 @@ class AztecText : EditText, TextWatcher { } interface OnMediaTappedListener { - fun mediaTapped(attrs: Attributes) + fun mediaTapped(attrs: Attributes, naturalWidth: Int, naturalHeight: Int) } init { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index 697c4fbc7..df4932bfb 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -34,6 +34,32 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var } } } + + @JvmStatic private fun getWidth(drawable: Drawable?): Int { + drawable?.let { + if (it.intrinsicWidth < 0) { + // client may have set the bounds manually so, use those to adjust to px + return it.bounds.width() + } else { + return it.intrinsicWidth + } + } + + return 0 + } + + @JvmStatic private fun getHeight(drawable: Drawable?): Int { + drawable?.let { + if (it.intrinsicHeight < 0) { + // client may have set the bounds manually so, use those to adjust to px + return it.bounds.height() + } else { + return it.intrinsicHeight + } + } + + return 0 + } } init { @@ -125,7 +151,7 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var } fun onClick(view: View) { - onMediaTappedListener?.mediaTapped(attributes) + onMediaTappedListener?.mediaTapped(attributes, getWidth(drawable), getHeight(drawable)) } fun getSource(): String { From 54b78365d4c732fd8f7aa0d071f0054ceded26d2 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 15:23:15 +0200 Subject: [PATCH 24/36] Allow for multiple overlays This way we can have a easy way to add a "shade" overlay between the main image and the real overlay drawable. --- .../org/wordpress/aztec/demo/MainActivity.kt | 2 +- .../main/java/org/wordpress/aztec/Html.java | 3 +- .../kotlin/org/wordpress/aztec/AztecText.kt | 27 ++++++++--- .../aztec/formatting/LineBlockFormatter.kt | 5 +- .../wordpress/aztec/spans/AztecMediaSpan.kt | 48 +++++++++++++------ 5 files changed, 58 insertions(+), 27 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index ee16ce691..c4eeae353 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -131,7 +131,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque val attrs = AttributesImpl() attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. - aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), null, 0, attrs, this) + aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), attrs, this) } super.onActivityResult(requestCode, resultCode, data) diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index 0817795e4..0aa72b9b8 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -759,8 +759,7 @@ private static void startImg(final SpannableStringBuilder text, // TODO: we should some placeholder drawable while loading imges Drawable loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading); - final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, null, 0, attributes, - onMediaTappedListener); + final AztecMediaSpan imageSpan = new AztecMediaSpan(context, loadingDrawable, attributes, onMediaTappedListener); text.append("\uFFFC"); text.setSpan(imageSpan, start, text.length(), diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 901b76b7f..da0f70944 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -887,8 +887,8 @@ class AztecText : EditText, TextWatcher { } } - fun insertMedia(drawable: Drawable?, overlay: Drawable?, overlayGravity: Int, attributes: Attributes) { - lineBlockFormatter.insertMedia(drawable, overlay, overlayGravity, attributes, onMediaTappedListener) + fun insertMedia(drawable: Drawable?, attributes: Attributes) { + lineBlockFormatter.insertMedia(drawable, attributes, onMediaTappedListener) } fun removeMedia(attributePredicate: AttributePredicate) { @@ -906,20 +906,35 @@ class AztecText : EditText, TextWatcher { fun matches(attrs: Attributes): Boolean } - fun setOverlayLevel(attributePredicate: AttributePredicate, level: Int, attrs: Attributes) { + fun setOverlayLevel(attributePredicate: AttributePredicate, index: Int, level: Int, attrs: Attributes) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { if (attributePredicate.matches(it.attributes)) { - it.setOverayLevel(level) + it.setOverayLevel(index, level) it.attributes = attrs } } } - fun setOverlay(attributePredicate: AttributePredicate, overlay: Drawable?, gravity: Int, attributes: Attributes?) { + fun setOverlay(attributePredicate: AttributePredicate, index: Int, overlay: Drawable?, gravity: Int, + attributes: Attributes?) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { if (attributePredicate.matches(it.attributes)) { // set the new overlay drawable - it.setOverlay(overlay, gravity) + it.setOverlay(index, overlay, gravity) + + if (attributes != null) { + it.attributes = attributes + } + + invalidate() + } + } + } + + fun clearOverlays(attributePredicate: AttributePredicate, attributes: Attributes?) { + text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + if (attributePredicate.matches(it.attributes)) { + it.clearOverlays() if (attributes != null) { it.attributes = attributes diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt index 378e07107..84f9b1631 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/formatting/LineBlockFormatter.kt @@ -269,8 +269,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { editor.setSelection(commentEndIndex + 1) } - fun insertMedia(drawable: Drawable?, overlay: Drawable?, overlayGravity: Int, attributes: Attributes, - onMediaTappedListener: OnMediaTappedListener?) { + fun insertMedia(drawable: Drawable?, attributes: Attributes, onMediaTappedListener: OnMediaTappedListener?) { //check if we add media into a block element, at the end of the line, but not at the end of last line var applyingOnTheEndOfBlockLine = false editableText.getSpans(selectionStart, selectionEnd, AztecBlockSpan::class.java).forEach { @@ -280,7 +279,7 @@ class LineBlockFormatter(editor: AztecText) : AztecFormatter(editor) { } } - val span = AztecMediaSpan(editor.context, drawable, overlay, overlayGravity, attributes, onMediaTappedListener) + val span = AztecMediaSpan(editor.context, drawable, attributes, onMediaTappedListener) val html = span.getHtml(); val mediaStartIndex = selectionStart + 1 diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index df4932bfb..6e68071b2 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -12,8 +12,9 @@ import android.view.View import org.wordpress.android.util.DisplayUtils import org.wordpress.aztec.AztecText.OnMediaTappedListener import org.xml.sax.Attributes +import java.util.* -class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var overlay: Drawable?, overlayGravity: Int, +class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var attributes: Attributes, val onMediaTappedListener: OnMediaTappedListener?) : DynamicDrawableSpan() { companion object { @@ -62,16 +63,20 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var } } + private val overlays: ArrayList> = ArrayList() + init { setBoundsToPx(context, drawable) - setBoundsToPx(context, overlay) - applyOverlayGravity(overlayGravity) } override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, metrics: Paint.FontMetricsInt?): Int { val width1 = drawable?.bounds?.width() ?: 0 - val width2 = overlay?.bounds?.width() ?: 0 - val width: Int = if (width1 > width2) width1 else width2 + var width: Int = 0 + + overlays.forEach { + val width2 = it.first?.bounds?.width() ?: 0 + width = if (width1 > width2) width1 else width2 + } drawable?.let { if (metrics != null) { @@ -95,25 +100,36 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var drawable = newDrawable } - fun setOverlay(newDrawable: Drawable?, gravity: Int) { - setBoundsToPx(context, newDrawable) - overlay = newDrawable + fun setOverlay(index: Int, newDrawable: Drawable?, gravity: Int) { + if (overlays.lastIndex >= index) { + overlays.removeAt(index) + } - applyOverlayGravity(gravity) + if (newDrawable != null) { + setBoundsToPx(context, newDrawable) + applyOverlayGravity(newDrawable, gravity) + + overlays.ensureCapacity(index + 1) + overlays.add(index, Pair(newDrawable, gravity)) + } } - fun setOverayLevel(level: Int): Boolean { - return overlay?.setLevel(level) ?: false + fun clearOverlays() { + overlays.clear() } - private fun applyOverlayGravity(gravity: Int) { + fun setOverayLevel(index: Int, level: Int): Boolean { + return overlays[index].first?.setLevel(level) ?: false + } + + private fun applyOverlayGravity(overlay: Drawable?, gravity: Int) { if (drawable != null && overlay != null) { val rect = Rect(0, 0, drawable!!.bounds.width(), drawable!!.bounds.height()) val outRect = Rect() - Gravity.apply(gravity, overlay!!.bounds.width(), overlay!!.bounds.height(), rect, outRect) + Gravity.apply(gravity, overlay.bounds.width(), overlay.bounds.height(), rect, outRect) - overlay!!.setBounds(outRect.left, outRect.top, outRect.right, outRect.bottom) + overlay.setBounds(outRect.left, outRect.top, outRect.right, outRect.bottom) } } @@ -130,7 +146,9 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, var drawable!!.draw(canvas) } - overlay?.draw(canvas) + overlays.forEach { + it.first?.draw(canvas) + } canvas.restore() } From e97293d2702a7c1f22c5b1dd559aa975ff136d85 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 16:42:34 +0200 Subject: [PATCH 25/36] Fix problems from merge with develop --- .../org/wordpress/aztec/demo/MainActivity.kt | 2 +- .../main/java/org/wordpress/aztec/Html.java | 16 +- .../org/wordpress/aztec/AztecTagHandler.kt | 13 +- .../kotlin/org/wordpress/aztec/AztecText.kt | 52 +- .../kotlin/org/wordpress/aztec/SpanLogger.kt | 84 +++ .../wordpress/aztec/spans/AztecMediaSpan.kt | 20 +- .../wordpress/aztec/toolbar/AztecToolbar.kt | 4 +- .../kotlin/org/wordpress/aztec/CodeTest.kt | 510 ++++++++++++++++++ 8 files changed, 651 insertions(+), 50 deletions(-) create mode 100644 aztec/src/main/kotlin/org/wordpress/aztec/SpanLogger.kt create mode 100644 aztec/src/test/kotlin/org/wordpress/aztec/CodeTest.kt diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index c4eeae353..c772e7313 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -437,7 +437,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque super.onRequestPermissionsResult(requestCode, permissions, grantResults) } - override fun mediaTapped(attrs: Attributes, naturalWidth: Int, naturalHeight: Int) { + override fun mediaTapped(attrs: Attributes?, naturalWidth: Int, naturalHeight: Int) { ToastUtils.showToast(this, "Media tapped!") } } diff --git a/aztec/src/main/java/org/wordpress/aztec/Html.java b/aztec/src/main/java/org/wordpress/aztec/Html.java index de200bb41..12ab190a9 100644 --- a/aztec/src/main/java/org/wordpress/aztec/Html.java +++ b/aztec/src/main/java/org/wordpress/aztec/Html.java @@ -22,7 +22,6 @@ import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.Drawable; -import android.support.v4.content.ContextCompat; import android.text.Editable; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -42,8 +41,6 @@ import org.wordpress.aztec.spans.AztecContentSpan; import org.wordpress.aztec.spans.AztecCursorSpan; import org.wordpress.aztec.spans.AztecHeadingSpan; -import org.wordpress.aztec.spans.AztecListSpan; -import org.wordpress.aztec.spans.AztecMediaClickableSpan; import org.wordpress.aztec.spans.AztecMediaSpan; import org.wordpress.aztec.spans.AztecRelativeSizeSpan; import org.wordpress.aztec.spans.AztecStyleSpan; @@ -107,7 +104,8 @@ public interface TagHandler { * This method will be called whenn the HTML parser encounters * a tag that it does not know how to interpret. */ - boolean handleTag(boolean opening, String tag, Editable output, Context context, Attributes attributes); + boolean handleTag(boolean opening, String tag, Editable output, OnMediaTappedListener onMediaTappedListener, + Context context, Attributes attributes); } private Html() { @@ -551,14 +549,10 @@ private void handleStartTag(String tag, Attributes attributes) { start(spannableStringBuilder, new Sub(attributes)); } else if (tag.equalsIgnoreCase("code")) { start(spannableStringBuilder, new Code(attributes)); -//<<<<<<< HEAD -// } else if (tag.equalsIgnoreCase("img")) { -// startImg(spannableStringBuilder, attributes, onMediaTappedListener, context); -//======= -//>>>>>>> develop } else { if (tagHandler != null) { - boolean tagHandled = tagHandler.handleTag(true, tag, spannableStringBuilder, context, attributes); + boolean tagHandled = tagHandler.handleTag(true, tag, spannableStringBuilder, onMediaTappedListener, + context, attributes); if (tagHandled) { return; } @@ -627,7 +621,7 @@ private void handleEndTag(String tag) { } else if (tag.equalsIgnoreCase("code")) { end(spannableStringBuilder, TextFormat.FORMAT_CODE); } else if (tagHandler != null) { - tagHandler.handleTag(false, tag, spannableStringBuilder, context, null); + tagHandler.handleTag(false, tag, spannableStringBuilder, onMediaTappedListener, context, null); } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt index 7b70adb8f..ba1c17c49 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt @@ -25,17 +25,16 @@ import android.content.Context import android.support.v4.content.ContextCompat import android.text.Editable import android.text.Spannable -import android.text.SpannableStringBuilder import android.text.Spanned import org.wordpress.aztec.spans.* import org.xml.sax.Attributes -import org.xml.sax.XMLReader class AztecTagHandler : Html.TagHandler { private var order = 0 - override fun handleTag(opening: Boolean, tag: String, output: Editable, context: Context, attributes: Attributes?): Boolean { + override fun handleTag(opening: Boolean, tag: String, output: Editable, + onMediaTappedListener: AztecText.OnMediaTappedListener?, context: Context, attributes: Attributes?): Boolean { val attributeString = Html.stringifyAttributes(attributes).toString() when (tag.toLowerCase()) { @@ -77,7 +76,7 @@ class AztecTagHandler : Html.TagHandler { } IMAGE -> { if (opening) { - start(output, createImageSpan(attributes, context)) + start(output, createImageSpan(attributes, onMediaTappedListener, context)) output.append("\uFFFC") } else { end(output, AztecMediaSpan::class.java) @@ -99,10 +98,10 @@ class AztecTagHandler : Html.TagHandler { return false } - private fun createImageSpan(attributes: Attributes?, context: Context) : AztecMediaSpan { - val src = attributes?.getValue("", "src") ?: "" + private fun createImageSpan(attributes: Attributes?, onMediaTappedListener: AztecText.OnMediaTappedListener?, + context: Context) : AztecMediaSpan { val loadingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_image_loading) - return AztecMediaSpan(context, loadingDrawable, src, Html.stringifyAttributes(attributes).toString()) + return AztecMediaSpan(context, loadingDrawable, attributes, onMediaTappedListener) } private fun handleBlockElement(output: Editable, opening: Boolean, span: Any) { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index da16a166f..75a27dac3 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -92,7 +92,7 @@ class AztecText : EditText, TextWatcher { } interface OnMediaTappedListener { - fun mediaTapped(attrs: Attributes, naturalWidth: Int, naturalHeight: Int) + fun mediaTapped(attrs: Attributes?, naturalWidth: Int, naturalHeight: Int) } constructor(context: Context) : super(context) { @@ -901,8 +901,10 @@ class AztecText : EditText, TextWatcher { fun removeMedia(attributePredicate: AttributePredicate) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { - if (attributePredicate.matches(it.attributes)) { - text.removeSpan(it) + if (it.attributes != null) { + if (attributePredicate.matches(it.attributes as Attributes)) { + text.removeSpan(it) + } } } } @@ -916,9 +918,11 @@ class AztecText : EditText, TextWatcher { fun setOverlayLevel(attributePredicate: AttributePredicate, index: Int, level: Int, attrs: Attributes) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { - if (attributePredicate.matches(it.attributes)) { - it.setOverayLevel(index, level) - it.attributes = attrs + if (it.attributes != null) { + if (attributePredicate.matches(it.attributes as Attributes)) { + it.setOverayLevel(index, level) + it.attributes = attrs + } } } } @@ -926,37 +930,43 @@ class AztecText : EditText, TextWatcher { fun setOverlay(attributePredicate: AttributePredicate, index: Int, overlay: Drawable?, gravity: Int, attributes: Attributes?) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { - if (attributePredicate.matches(it.attributes)) { - // set the new overlay drawable - it.setOverlay(index, overlay, gravity) + if (it.attributes != null) { + if (attributePredicate.matches(it.attributes as Attributes)) { + // set the new overlay drawable + it.setOverlay(index, overlay, gravity) - if (attributes != null) { - it.attributes = attributes - } + if (attributes != null) { + it.attributes = attributes + } - invalidate() + invalidate() + } } } } fun clearOverlays(attributePredicate: AttributePredicate, attributes: Attributes?) { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { - if (attributePredicate.matches(it.attributes)) { - it.clearOverlays() + if (it.attributes != null) { + if (attributePredicate.matches(it.attributes as Attributes)) { + it.clearOverlays() - if (attributes != null) { - it.attributes = attributes - } + if (attributes != null) { + it.attributes = attributes + } - invalidate() + invalidate() + } } } } fun getMediaAttributes(attributePredicate: AttributePredicate): Attributes? { text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { - if (attributePredicate.matches(it.attributes)) { - return it.attributes + if (it.attributes != null) { + if (attributePredicate.matches(it.attributes as Attributes)) { + return it.attributes + } } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/SpanLogger.kt b/aztec/src/main/kotlin/org/wordpress/aztec/SpanLogger.kt new file mode 100644 index 000000000..25a1190bb --- /dev/null +++ b/aztec/src/main/kotlin/org/wordpress/aztec/SpanLogger.kt @@ -0,0 +1,84 @@ +package org.wordpress.aztec + +import android.text.Spanned +import android.text.TextUtils +import java.util.* + +/** + * Created by hypest on 11/01/17. + */ +object SpanLogger { + private fun spaces(count: Int): String { + return TextUtils.join("", Collections.nCopies(count, " ")) + } + + @JvmStatic fun logSpans(text: Spanned): String { + val spans = text.getSpans(0, 9999999, Any::class.java) + val spansList = Arrays.asList(*spans) + +// // sort the spans list by start position and size +// Collections.sort(spansList) { o1, o2 -> +// var diff = text.getSpanStart(o1) - text.getSpanStart(o2) +// if (diff == 0) { +// diff = text.getSpanEnd(o1) - text.getSpanEnd(o2) +// } +// +// diff / Math.abs(if (diff == 0) 1 else diff) +// } + + val sb = StringBuilder() + sb.append('\n').append(text.toString().replace('\n', ' ')) + + for (span in spansList) { + val start = text.getSpanStart(span) + val end = text.getSpanEnd(span) + + var gap = text.length + 5 + + sb.append('\n') + + if (start > 0) { + sb.append(spaces(start)) + gap -= start + } + + sb.append('|') + gap-- + + if (end - start - 1 > 0) { + sb.append(spaces(end - start - 1)) + gap -= end - start - 1 + } + + if (end - start > 0) { + sb.append('|') + gap-- + } + + sb.append(spaces(gap)) + + sb.append(" ") + .append(String.format("%03d", start)) + .append(" -> ") + .append(String.format("%03d", end)) + .append(" : ") + .append(span.javaClass.simpleName) + } + + return sb.toString() + } + +// override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { +// Log.v("edit", "beforeTextChanged size=" + s.length + ", s=" + s + ", start=" + start + ", count=" + +// count + ", after=" + after + "\n\nspans:" + logSpans(s) + "\n\n ") +// } +// +// override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { +// Log.v("edit", "onTextChanged size=" + s.length + ", s=" + s + ", start=" + start + ", before=" + before +// + ", count=" + count + "\n\nspans: " + logSpans(s) + "\n\n ") +// } +// +// override fun afterTextChanged(s: Editable) { +// Log.v("edit", "afterTextChanged size=" + s.length + ", s=" + s + "\n\nspans:" + logSpans(s) + "\n\n ") +// } +} \ No newline at end of file diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index f9bdfdef3..66961e392 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -15,7 +15,7 @@ import org.xml.sax.Attributes import java.util.* class AztecMediaSpan(val context: Context, private var drawable: Drawable?, - var attributes: Attributes, val onMediaTappedListener: OnMediaTappedListener?) : DynamicDrawableSpan() { + var attributes: Attributes?, val onMediaTappedListener: OnMediaTappedListener?) : DynamicDrawableSpan() { private val TAG: String = "img" @@ -157,15 +157,19 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, fun getHtml(): String { val sb = StringBuilder() + sb.append("<") sb.append(TAG) - for (i in 0..attributes.length-1) { - sb.append(' ') - sb.append(attributes.getLocalName(i)) - sb.append("=\"") - sb.append(attributes.getValue(i)) - sb.append("\"") + attributes?.let { + for (i in 0..attributes!!.length-1) { + sb.append(' ') + sb.append(attributes!!.getLocalName(i)) + sb.append("=\"") + sb.append(attributes!!.getValue(i)) + sb.append("\"") + } } + sb.append("/>") return sb.toString() } @@ -175,6 +179,6 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, } fun getSource(): String { - return attributes.getValue("src") + return attributes?.getValue("src") ?: "" } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt index 24b96aa68..98a6246b3 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt @@ -254,7 +254,7 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { fun toggleEditorMode() { if (editor!!.visibility == View.VISIBLE) { - if (/*!editor!!.isMediaAdded ||*/ allImagesUploaded()) { +// if (!editor!!.isMediaAdded || allImagesUploaded()) { sourceEditor!!.displayStyledAndFormattedHtml(editor!!.toHtml(true)) editor!!.visibility = View.GONE sourceEditor!!.visibility = View.VISIBLE @@ -263,7 +263,7 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { // } else { // toggleButton(findViewById(ToolbarAction.HTML.buttonId), false) // showMediaUploadDialog() - } +// } } else { editor!!.fromHtml(sourceEditor!!.getPureHtml(true)) editor!!.visibility = View.VISIBLE diff --git a/aztec/src/test/kotlin/org/wordpress/aztec/CodeTest.kt b/aztec/src/test/kotlin/org/wordpress/aztec/CodeTest.kt new file mode 100644 index 000000000..6e777dd44 --- /dev/null +++ b/aztec/src/test/kotlin/org/wordpress/aztec/CodeTest.kt @@ -0,0 +1,510 @@ +//package org.wordpress.aztec +// +//import android.app.Activity +//import android.text.TextUtils +//import android.widget.EditText +//import org.junit.Assert +//import org.junit.Before +//import org.junit.Test +//import org.junit.runner.RunWith +//import org.robolectric.Robolectric +//import org.robolectric.RobolectricGradleTestRunner +//import org.robolectric.annotation.Config +//import java.util.* +// +// +///** +// * Testing quote behaviour. +// */ +//@RunWith(RobolectricGradleTestRunner::class) +//@Config(constants = BuildConfig::class) +//class CodeTest() { +// +// val formattingType = TextFormat.FORMAT_CODE +// val codeTag = "code" +// lateinit var editText: AztecText +// +// /** +// * Initialize variables. +// */ +// @Before +// fun init() { +// val activity = Robolectric.buildActivity(Activity::class.java).create().visible().get() +// editText = AztecText(activity) +// activity.setContentView(editText) +// } +// +// fun setStyles(editText: AztecText) { +// val styles = ArrayList() +// styles.add(formattingType) +// editText.setSelectedStyles(styles) +// } +// +// @Test +// @Throws(Exception::class) +// fun styleSingleItem() { +// editText.append("println(\"hello world\");") +// setStyles(editText) +// Assert.assertEquals("<$codeTag>println(\"hello world\");", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun styleMultipleSelectedItems() { +// junit.framework.Assert.assertTrue(TextUtils.isEmpty(editText.text)) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// editText.append("\n") +// editText.append("third item") +// editText.setSelection(0, editText.length()) +// +// setStyles(editText) +// Assert.assertEquals("<$codeTag>first item
second item
third item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun stylePartiallySelectedMultipleItems() { +// junit.framework.Assert.assertTrue(TextUtils.isEmpty(editText.text)) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// editText.append("\n") +// editText.append("third item") +// editText.setSelection(4, 15) //we partially selected first and second item +// +// setStyles(editText) +// Assert.assertEquals("<$codeTag>first item
second itemthird item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun styleSurroundedItem() { +// junit.framework.Assert.assertTrue(TextUtils.isEmpty(editText.text)) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// editText.append("\n") +// editText.append("third item") +// editText.setSelection(14) +// +// setStyles(editText) +// Assert.assertEquals("first item<$codeTag>second itemthird item", editText.toHtml()) +// } +// +// +// //enable styling on empty line and enter text +// +// @Test +// @Throws(Exception::class) +// fun emptyQuote() { +// editText.toggleFormatting(formattingType) +//// setStyles(editText) +// Assert.assertEquals("<$codeTag>", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun styleSingleEnteredItem() { +// setStyles(editText) +// editText.append("first item") +// Assert.assertEquals("<$codeTag>first item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun styleMultipleEnteredItems() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// Assert.assertEquals("<$codeTag>first item
second item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun closingPopulatedQuote1() { +// val styles = ArrayList() +// styles.add(TextFormat.FORMAT_STRIKETHROUGH) +// editText.setSelectedStyles(styles) +// editText.append("first item") +// Assert.assertEquals("first item", editText.toHtml().toString()) +// } +// +// @Test +// @Throws(Exception::class) +// fun closingPopulatedCode() { +// val styles = ArrayList() +// styles.add(formattingType) +// editText.setSelectedStyles(styles) +// editText.append("first item") +// +// editText.append("\n") +// editText.append("\n") +// editText.append("not in the quote") +// Assert.assertEquals("<$codeTag>first itemnot in the quote", editText.toHtml().toString()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun closingEmptyQuote() { +// setStyles(editText) +// editText.append("\n") +// Assert.assertEquals("", editText.toHtml().toString()) +// } +// +// @Test +// @Throws(Exception::class) +// fun extendingQuoteBySplittingItems() { +// setStyles(editText) +// editText.append("firstitem") +// editText.text.insert(5, "\n") +// Assert.assertEquals("<$codeTag>first
item", editText.toHtml().toString()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun quoteSplitWithToolbar() { +// editText.fromHtml("<$codeTag>first item
second item
third item") +// editText.setSelection(14) +// setStyles(editText) +// +// Assert.assertEquals("<$codeTag>first itemsecond item<$codeTag>third item", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun removeQuoteStyling() { +// editText.fromHtml("<$codeTag>first item") +// editText.setSelection(1) +// setStyles(editText) +// +// Assert.assertEquals("first item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun removeQuoteStylingForPartialSelection() { +// editText.fromHtml("<$codeTag>first item") +// editText.setSelection(2, 4) +// setStyles(editText) +// +// Assert.assertEquals("first item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun removeQuoteStylingForMultilinePartialSelection() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// val firstMark = editText.length() - 4 +// editText.append("\n") +// editText.append("third item") +// editText.append("\n") +// val secondMark = editText.length() - 4 +// editText.append("fourth item") +// editText.append("\n") +// editText.append("\n") +// editText.append("not in quote") +// +// editText.setSelection(firstMark, secondMark) +// editText.setSelectedStyles(ArrayList()); +// +// Assert.assertEquals("<$codeTag>first itemsecond item
third item<$codeTag>fourth itemnot in quote", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun emptyQuoteSurroundedBytItems() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// val firstMark = editText.length() +// editText.append("second item") +// editText.append("\n") +// val secondMart = editText.length() +// editText.append("third item") +// +// editText.text.delete(firstMark - 1, secondMart - 2) +// +// Assert.assertEquals("<$codeTag>first item

third item", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun trailingEmptyLine() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// editText.append("\n") +// editText.append("third item") +// val mark = editText.length() +// editText.append("\n") +// +// Assert.assertEquals("<$codeTag>first item
second item
third item", editText.toHtml()) +// editText.append("\n") +// +// Assert.assertEquals("<$codeTag>first item
second item
third item", editText.toHtml()) +// +// editText.append("not in quote") +// editText.setSelection(mark) +// editText.text.insert(mark, "\n") +// Assert.assertEquals("<$codeTag>first item
second item
third item
not in quote", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun openQuoteByAddingNewline() { +// editText.fromHtml("<$codeTag>first item
second itemnot in quote") +// +// +// val mark = editText.text.indexOf("second item") + "second item".length +// +// editText.text.insert(mark, "\n") +// editText.text.insert(mark + 1, "third item") +// +// +// Assert.assertEquals("<$codeTag>first item
second item
third itemnot in quote", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun openQuoteByAppendingTextToTheEnd() { +// editText.fromHtml("<$codeTag>first item
second itemnot in quote") +// editText.setSelection(editText.length()) +// +// editText.text.insert(editText.text.indexOf("\nnot in quote"), " (appended)") +// +// Assert.assertEquals("<$codeTag>first item
second item (appended)not in quote", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun openQuoteByMovingOutsideTextInsideIt() { +// editText.fromHtml("<$codeTag>first item
second item") +// editText.append("not in quote") +// +// editText.text.delete(editText.text.indexOf("not in quote"), editText.text.indexOf("not in quote")) +// Assert.assertEquals("<$codeTag>first item
second itemnot in quote", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun quoteRemainsClosedWhenLastCharacterIsDeleted() { +// editText.fromHtml("<$codeTag>first item
second itemnot in quote") +// editText.setSelection(editText.length()) +// +// val mark = editText.text.indexOf("second item") + "second item".length; +// +// //delete last character from "second item" +// editText.text.delete(mark - 1, mark) +// Assert.assertEquals("<$codeTag>first item
second itenot in quote", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun openingAndReopeningOfQuote() { +// editText.fromHtml("<$codeTag>first item
second item") +// editText.setSelection(editText.length()) +// +// editText.append("\n") +// editText.append("third item") +// Assert.assertEquals("<$codeTag>first item
second item
third item", editText.toHtml()) +// editText.append("\n") +// editText.append("\n") +// val mark = editText.length() - 1 +// editText.append("not in the quote") +// Assert.assertEquals("<$codeTag>first item
second item
third itemnot in the quote", editText.toHtml()) +// editText.append("\n") +// editText.append("foo") +// Assert.assertEquals("<$codeTag>first item
second item
third itemnot in the quote
foo", editText.toHtml()) +// +// //reopen quote +// editText.text.delete(mark, mark + 1) +// Assert.assertEquals("<$codeTag>first item
second item
third itemnot in the quotefoo", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun closeCode() { +// editText.fromHtml("<$codeTag>first item") +// editText.setSelection(editText.length()) +// +// Assert.assertEquals("first item", editText.text.toString()) +// editText.append("\n") +// Assert.assertEquals("first item\n\u200B", editText.text.toString()) +// +// editText.text.delete(editText.length() - 1, editText.length()) +// Assert.assertEquals("first item\n", editText.text.toString()) +// +// editText.append("not in the quote") +// Assert.assertEquals("<$codeTag>first itemnot in the quote", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun handlequoteReopeningAfterLastElementDeletion() { +// editText.fromHtml("<$codeTag>first item
second item
third item") +// editText.setSelection(editText.length()) +// +// editText.text.delete(editText.text.indexOf("third item", 0), editText.length()) +// +// editText.append("not in the quote") +// Assert.assertEquals("<$codeTag>first item
second itemnot in the quote", editText.toHtml()) +// +// editText.text.insert(editText.text.indexOf("not in the quote") - 1, " addition") +// Assert.assertEquals("<$codeTag>first item
second item additionnot in the quote", editText.toHtml()) +// +// editText.text.insert(editText.text.indexOf("not in the quote") - 1, "\n") +// editText.text.insert(editText.text.indexOf("not in the quote") - 1, "third item") +// Assert.assertEquals("first item\nsecond item addition\nthird item\nnot in the quote", editText.text.toString()) +// Assert.assertEquals("<$codeTag>first item
second item addition
third itemnot in the quote", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun additionToClosedQuote() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// +// val mark = editText.length() +// +// editText.append("\n") +// editText.append("\n") +// editText.append("not in the quote") +// Assert.assertEquals("<$codeTag>first item
second itemnot in the quote", editText.toHtml().toString()) +// +// editText.text.insert(mark, " (addition)") +// +// Assert.assertEquals("<$codeTag>first item
second item (addition)not in the quote", editText.toHtml().toString()) +// } +// +// @Test +// @Throws(Exception::class) +// fun addItemToQuoteFromBottom() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// editText.append("\n") +// editText.append("\n") +// editText.append("third item") +// editText.setSelection(editText.length()) +// +// setStyles(editText) +// +// Assert.assertEquals("<$codeTag>first item
second item
third item", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun addItemToQuoteFromTop() { +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// editText.setSelection(editText.length()) +// editText.toggleFormatting(formattingType) +// editText.append("\n") +// editText.append("third item") +// +// editText.setSelection(0) +// +// editText.toggleFormatting(formattingType) +// +// Assert.assertEquals("<$codeTag>first item
second item
third item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun addItemToQuoteFromInside() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// editText.append("\n") +// editText.append("second item") +// editText.append("\n") +// editText.append("third item") +// editText.setSelection(editText.length()) +// editText.toggleFormatting(formattingType) +// +// Assert.assertEquals("<$codeTag>first itemsecond item<$codeTag>third item", editText.toHtml()) +// editText.setSelection(15) +// editText.toggleFormatting(formattingType) +// +// Assert.assertEquals("<$codeTag>first item
second item
third item", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun appendToQuoteFromTopAtFirstLine() { +// setStyles(editText) +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// editText.setSelection(0) +// editText.text.insert(0, "addition ") +// +// Assert.assertEquals("<$codeTag>addition first item
second item", editText.toHtml()) +// } +// +// @Test +// @Throws(Exception::class) +// fun appendToQuoteFromTop() { +// editText.append("not in quote") +// editText.append("\n") +// setStyles(editText) +// val mark = editText.length() - 1 +// editText.append("first item") +// editText.append("\n") +// editText.append("second item") +// +// editText.setSelection(mark) +// editText.text.insert(mark, "addition ") +// +// Assert.assertEquals("not in quote<$codeTag>addition first item
second item", editText.toHtml()) +// } +// +// +// @Test +// @Throws(Exception::class) +// fun deleteFirstItemWithKeyboard() { +// setStyles(editText) +// editText.append("first item") +// val firstMark = editText.length() +// editText.append("\n") +// editText.append("second item") +// val secondMark = editText.length() +// editText.append("\n") +// editText.append("third item") +// editText.setSelection(0) +// +// Assert.assertEquals("first item\nsecond item\nthird item", editText.text.toString()) +// +// editText.text.delete(firstMark + 1, secondMark) +// +// Assert.assertEquals("first item\n\nthird item", editText.text.toString()) +// +// Assert.assertEquals("<$codeTag>first item

third item", editText.toHtml()) +// +// editText.text.delete(0, firstMark) +// +// Assert.assertEquals("<$codeTag>

third item", editText.toHtml()) +// } +// +//} From 35a3ec07d4fd0e65e18772c97a94c1edce5dda95 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 17:23:01 +0200 Subject: [PATCH 26/36] Let the demo app/client handle the media menu --- .../org/wordpress/aztec/demo/MainActivity.kt | 141 +++++++++++++++++- .../wordpress/aztec/toolbar/AztecToolbar.kt | 134 ----------------- 2 files changed, 135 insertions(+), 140 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index c772e7313..687959af9 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -16,11 +16,10 @@ import android.os.Environment import android.provider.MediaStore import android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback import android.support.v4.content.FileProvider +import android.support.v7.app.AlertDialog import android.support.v7.app.AppCompatActivity -import android.view.Menu -import android.view.MenuItem -import android.view.MotionEvent -import android.view.View +import android.view.* +import android.widget.PopupMenu import android.widget.Toast import org.wordpress.android.util.AppLog import org.wordpress.android.util.PermissionUtils @@ -30,12 +29,14 @@ import org.wordpress.aztec.picassoloader.PicassoImageLoader import org.wordpress.aztec.source.SourceViewEditText import org.wordpress.aztec.toolbar.AztecToolbar import org.wordpress.aztec.toolbar.AztecToolbar.OnMediaOptionSelectedListener +import org.wordpress.aztec.toolbar.AztecToolbarClickListener import org.xml.sax.Attributes import org.xml.sax.helpers.AttributesImpl import java.io.File class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnRequestPermissionsResultCallback, - View.OnTouchListener, AztecText.OnMediaTappedListener, AztecText.OnImeBackListener { + View.OnTouchListener, PopupMenu.OnMenuItemClickListener, AztecToolbarClickListener, + AztecText.OnMediaTappedListener, AztecText.OnImeBackListener { companion object { private val HEADING = "

Heading 1

" + @@ -107,6 +108,11 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque private lateinit var source: SourceViewEditText private lateinit var formattingToolbar: AztecToolbar + private var addPhotoMediaDialog: AlertDialog? = null + private var addVideoMediaDialog: AlertDialog? = null + private var mediaUploadDialog: AlertDialog? = null + private var mediaMenu: PopupMenu? = null + private var mIsKeyboardOpen = false private var mHideActionBarOnSoftKeyboardUp = false @@ -156,7 +162,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque formattingToolbar = findViewById(R.id.formatting_toolbar) as AztecToolbar formattingToolbar.setEditor(aztec, source) - formattingToolbar.setMediaOptionSelectedListener(this) + formattingToolbar.setToolbarListener(this) // initialize the text & HTML source.displayStyledAndFormattedHtml(EXAMPLE) @@ -197,6 +203,40 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque } } + override fun onRestoreInstanceState(savedInstanceState: Bundle?) { + super.onRestoreInstanceState(savedInstanceState) + + savedInstanceState?.let { + if (savedInstanceState.getBoolean("isPhotoMediaDialogVisible")) { + showPhotoMediaDialog() + } + + if (savedInstanceState.getBoolean("isVideoMediaDialogVisible")) { + showVideoMediaDialog() + } + + if (savedInstanceState.getBoolean("isMediaUploadDialogVisible")) { + showMediaUploadDialog() + } + } + } + + override fun onSaveInstanceState(outState: Bundle?) { + super.onSaveInstanceState(outState) + + if (addPhotoMediaDialog != null && addPhotoMediaDialog!!.isShowing) { + outState?.putBoolean("isPhotoMediaDialogVisible", true) + } + + if (addVideoMediaDialog != null && addVideoMediaDialog!!.isShowing) { + outState?.putBoolean("isVideoMediaDialogVisible", true) + } + + if (mediaUploadDialog != null && mediaUploadDialog!!.isShowing) { + outState?.putBoolean("isMediaUploadDialogVisible", true) + } + } + /** * Returns true if a hardware keyboard is detected, otherwise false. */ @@ -437,6 +477,95 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque super.onRequestPermissionsResult(requestCode, permissions, grantResults) } + override fun onToolbarAddMediaClicked() { + mediaMenu = PopupMenu(this, formattingToolbar) + mediaMenu?.setOnMenuItemClickListener(this) + mediaMenu?.inflate(R.menu.media) + mediaMenu?.show() + } + + override fun onMenuItemClick(item: MenuItem?): Boolean { + item?.isChecked = (item?.isChecked == false) + + when (item?.itemId) { + org.wordpress.aztec.R.id.gallery -> { + onGalleryMediaOptionSelected() + return true + } + org.wordpress.aztec.R.id.photo -> { + showPhotoMediaDialog() + return true + } + org.wordpress.aztec.R.id.video -> { + showVideoMediaDialog() + return true + } + else -> return false + } + } + + private fun showMediaUploadDialog() { + val builder = AlertDialog.Builder(this) + builder.setMessage(getString(org.wordpress.aztec.R.string.media_upload_dialog_message)) + builder.setPositiveButton(getString(org.wordpress.aztec.R.string.media_upload_dialog_positive), null) + mediaUploadDialog = builder.create() + mediaUploadDialog!!.show() + } + + private fun showPhotoMediaDialog() { + val dialog = layoutInflater.inflate(R.layout.dialog_photo_media, null) + + val camera = dialog.findViewById(org.wordpress.aztec.R.id.media_camera) + camera.setOnClickListener({ + onCameraPhotoMediaOptionSelected() + addPhotoMediaDialog?.dismiss() + }) + + val photos = dialog.findViewById(org.wordpress.aztec.R.id.media_photos) + photos.setOnClickListener({ + onPhotosMediaOptionSelected() + addPhotoMediaDialog?.dismiss() + }) + + val library = dialog.findViewById(org.wordpress.aztec.R.id.media_library) + library.setOnClickListener({ + onPhotoLibraryMediaOptionSelected() + addPhotoMediaDialog?.dismiss() + }) + + val builder = AlertDialog.Builder(this) + builder.setView(dialog) + addPhotoMediaDialog = builder.create() + addPhotoMediaDialog!!.show() + } + + private fun showVideoMediaDialog() { + val dialog = layoutInflater.inflate(org.wordpress.aztec.R.layout.dialog_video_media, null) + + val camera = dialog.findViewById(org.wordpress.aztec.R.id.media_camera) + camera.setOnClickListener({ + onCameraVideoMediaOptionSelected() + addVideoMediaDialog?.dismiss() + }) + + val videos = dialog.findViewById(org.wordpress.aztec.R.id.media_videos) + videos.setOnClickListener({ + onVideosMediaOptionSelected() + addVideoMediaDialog?.dismiss() + }) + + val library = dialog.findViewById(org.wordpress.aztec.R.id.media_library) + library.setOnClickListener({ + onVideoLibraryMediaOptionSelected() + addVideoMediaDialog?.dismiss() + }) + + val builder = AlertDialog.Builder(this) + builder.setView(dialog) + addVideoMediaDialog = builder.create() + addVideoMediaDialog!!.show() + } + override fun mediaTapped(attrs: Attributes?, naturalWidth: Int, naturalHeight: Int) { ToastUtils.showToast(this, "Media tapped!") } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt index 98a6246b3..c1279466d 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt @@ -1,11 +1,7 @@ package org.wordpress.aztec.toolbar import android.content.Context -import android.os.Bundle -import android.os.Parcelable -import android.support.v7.app.AlertDialog import android.util.AttributeSet -import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.widget.FrameLayout @@ -22,12 +18,8 @@ import java.util.* class AztecToolbar : FrameLayout, OnMenuItemClickListener { private var aztecToolbarListener: AztecToolbarClickListener? = null - private var addPhotoMediaDialog: AlertDialog? = null - private var addVideoMediaDialog: AlertDialog? = null - private var mediaUploadDialog: AlertDialog? = null private var editor: AztecText? = null private var headingMenu: PopupMenu? = null - private var mediaOptionSelectedListener: OnMediaOptionSelectedListener? = null private var sourceEditor: SourceViewEditText? = null constructor(context: Context) : super(context) { @@ -56,65 +48,10 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { aztecToolbarListener = listener } - override fun onRestoreInstanceState(state: Parcelable?) { - var superState = state - - if (state is Bundle) { - superState = state.getParcelable("superState") - - if (state.getBoolean("isPhotoMediaDialogVisible")) { - showPhotoMediaDialog() - } - - if (state.getBoolean("isVideoMediaDialogVisible")) { - showVideoMediaDialog() - } - - if (state.getBoolean("isMediaUploadDialogVisible")) { - showMediaUploadDialog() - } - } - - super.onRestoreInstanceState(superState) - } - - override fun onSaveInstanceState(): Parcelable { - val bundle = Bundle() - bundle.putParcelable("superState", super.onSaveInstanceState()) - - if (addPhotoMediaDialog != null && addPhotoMediaDialog!!.isShowing) { - bundle.putBoolean("isPhotoMediaDialogVisible", true) - } - - if (addVideoMediaDialog != null && addVideoMediaDialog!!.isShowing) { - bundle.putBoolean("isVideoMediaDialogVisible", true) - } - - if (mediaUploadDialog != null && mediaUploadDialog!!.isShowing) { - bundle.putBoolean("isMediaUploadDialogVisible", true) - } - - return bundle - } - override fun onMenuItemClick(item: MenuItem?): Boolean { item?.isChecked = (item?.isChecked == false) when (item?.itemId) { - // Media popup menu options - R.id.gallery -> { - mediaOptionSelectedListener?.onGalleryMediaOptionSelected() - return true - } - R.id.photo -> { - showPhotoMediaDialog() - return true - } - R.id.video -> { - showVideoMediaDialog() - return true - } - // Heading popup menu options R.id.paragraph -> { editor?.toggleFormatting(TextFormat.FORMAT_PARAGRAPH) return true @@ -162,10 +99,6 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { }) } - fun setMediaOptionSelectedListener(listener: OnMediaOptionSelectedListener) { - mediaOptionSelectedListener = listener - } - private fun initView() { View.inflate(context, R.layout.aztec_format_bar, this) @@ -326,71 +259,4 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { } } - private fun showMediaUploadDialog() { - if (!isEditorAttached()) return - - val builder = AlertDialog.Builder(context) - builder.setMessage(context.getString(R.string.media_upload_dialog_message)) - builder.setPositiveButton(context.getString(R.string.media_upload_dialog_positive), null) - mediaUploadDialog = builder.create() - mediaUploadDialog!!.show() - } - - private fun showPhotoMediaDialog() { - if (!isEditorAttached()) return - - val dialog = LayoutInflater.from(context).inflate(R.layout.dialog_photo_media, null) - - val camera = dialog.findViewById(R.id.media_camera) - camera.setOnClickListener({ - mediaOptionSelectedListener?.onCameraPhotoMediaOptionSelected() - addPhotoMediaDialog?.dismiss() - }) - - val photos = dialog.findViewById(R.id.media_photos) - photos.setOnClickListener({ - mediaOptionSelectedListener?.onPhotosMediaOptionSelected() - addPhotoMediaDialog?.dismiss() - }) - - val library = dialog.findViewById(R.id.media_library) - library.setOnClickListener({ - mediaOptionSelectedListener?.onPhotoLibraryMediaOptionSelected() - addPhotoMediaDialog?.dismiss() - }) - - val builder = AlertDialog.Builder(context) - builder.setView(dialog) - addPhotoMediaDialog = builder.create() - addPhotoMediaDialog!!.show() - } - - private fun showVideoMediaDialog() { - if (!isEditorAttached()) return - - val dialog = LayoutInflater.from(context).inflate(R.layout.dialog_video_media, null) - - val camera = dialog.findViewById(R.id.media_camera) - camera.setOnClickListener({ - mediaOptionSelectedListener?.onCameraVideoMediaOptionSelected() - addVideoMediaDialog?.dismiss() - }) - - val videos = dialog.findViewById(R.id.media_videos) - videos.setOnClickListener({ - mediaOptionSelectedListener?.onVideosMediaOptionSelected() - addVideoMediaDialog?.dismiss() - }) - - val library = dialog.findViewById(R.id.media_library) - library.setOnClickListener({ - mediaOptionSelectedListener?.onVideoLibraryMediaOptionSelected() - addVideoMediaDialog?.dismiss() - }) - - val builder = AlertDialog.Builder(context) - builder.setView(dialog) - addVideoMediaDialog = builder.create() - addVideoMediaDialog!!.show() - } } From 4e22fea5ce39579394b0512f64568a46ebc00860 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 17:41:25 +0200 Subject: [PATCH 27/36] Fix from merge: add ClickableSpan to media span --- aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt index ba1c17c49..478aac327 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecTagHandler.kt @@ -76,10 +76,13 @@ class AztecTagHandler : Html.TagHandler { } IMAGE -> { if (opening) { - start(output, createImageSpan(attributes, onMediaTappedListener, context)) + val mediaSpan = createImageSpan(attributes, onMediaTappedListener, context) + start(output, mediaSpan) + start(output, AztecMediaClickableSpan(mediaSpan)) output.append("\uFFFC") } else { end(output, AztecMediaSpan::class.java) + end(output, AztecMediaClickableSpan::class.java) } return true } From 9a37598c0998b463bc5133e904d19d9f80d3816f Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 17:46:37 +0200 Subject: [PATCH 28/36] Fix from merge: use AztecText's insertMedia --- app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index 687959af9..f81fbdaf9 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -137,7 +137,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque val attrs = AttributesImpl() attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. - aztec.lineBlockFormatter.insertMedia(BitmapDrawable(resources, bitmap), attrs, this) + aztec.insertMedia(BitmapDrawable(resources, bitmap), attrs) } super.onActivityResult(requestCode, resultCode, data) From be76f79e98fe13a0f8692cbc41b3c13390c5d86a Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 18:26:35 +0200 Subject: [PATCH 29/36] Demo of media "upload animation" --- .../org/wordpress/aztec/demo/MainActivity.kt | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index f81fbdaf9..6bbbfa036 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -9,10 +9,12 @@ import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Environment +import android.os.Handler import android.provider.MediaStore import android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback import android.support.v4.content.FileProvider @@ -135,14 +137,55 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque } } - val attrs = AttributesImpl() - attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. - aztec.insertMedia(BitmapDrawable(resources, bitmap), attrs) + insertMediaAndSimulateUpload(bitmap, mediaPath) } super.onActivityResult(requestCode, resultCode, data) } + fun insertMediaAndSimulateUpload(bitmap: Bitmap?, mediaPath: String) { + val id = (Math.random() * Int.MAX_VALUE).toString() + + val attrs = AttributesImpl() + attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. + attrs.addAttribute("", "id", "id", "string", id) + + aztec.insertMedia(BitmapDrawable(resources, bitmap), attrs) + + val predicate = object : AztecText.AttributePredicate { + override fun matches(attrs: Attributes): Boolean { + return attrs.getValue("id") == id + } + } + + aztec.setOverlay(predicate, 0, ColorDrawable(0x80000000.toInt()), Gravity.FILL, attrs) + val progressDrawable = resources.getDrawable(android.R.drawable.progress_horizontal) + // set the height of the progress bar to 2 (it's in dp since the drawable will be adjusted by the span) + progressDrawable.setBounds(0, 0, 0, 4) + aztec.setOverlay(predicate, 1, progressDrawable, Gravity.FILL_HORIZONTAL or Gravity.TOP, attrs) + + var progress = 0 + + // simulate an upload delay + val runnable: Runnable = Runnable { + aztec.setOverlayLevel(predicate, 1, progress, attrs) + aztec.refreshText() + progress += 2000 + + if (progress >= 10000) { + aztec.clearOverlays(predicate, attrs) + } + } + + Handler().post(runnable); + Handler().postDelayed(runnable, 2000); + Handler().postDelayed(runnable, 4000); + Handler().postDelayed(runnable, 6000); + Handler().postDelayed(runnable, 8000); + + aztec.refreshText() + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) From 7ee88ff69eb127fc41a88b3061a6ee480ea8075c Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Mon, 6 Feb 2017 18:49:21 +0200 Subject: [PATCH 30/36] Don't allow html mode if media upload pending --- .../org/wordpress/aztec/toolbar/AztecToolbar.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt index c1279466d..7e74aa20f 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt @@ -9,6 +9,7 @@ import android.widget.PopupMenu import android.widget.PopupMenu.OnMenuItemClickListener import android.widget.Toast import android.widget.ToggleButton +import org.wordpress.android.util.ToastUtils import org.wordpress.aztec.AztecText import org.wordpress.aztec.R import org.wordpress.aztec.TextFormat @@ -187,16 +188,16 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { fun toggleEditorMode() { if (editor!!.visibility == View.VISIBLE) { -// if (!editor!!.isMediaAdded || allImagesUploaded()) { + if (!editor!!.isMediaAdded || allImagesUploaded()) { sourceEditor!!.displayStyledAndFormattedHtml(editor!!.toHtml(true)) editor!!.visibility = View.GONE sourceEditor!!.visibility = View.VISIBLE toggleHtmlMode(true) -// } else { -// toggleButton(findViewById(ToolbarAction.HTML.buttonId), false) -// showMediaUploadDialog() -// } + } else { + toggleButton(findViewById(ToolbarAction.HTML.buttonId), false) + ToastUtils.showToast(context, R.string.media_upload_dialog_message) + } } else { editor!!.fromHtml(sourceEditor!!.getPureHtml(true)) editor!!.visibility = View.VISIBLE From 26c5ac6de72766a4a8e5abe8cc24b459f0322632 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 7 Feb 2017 02:40:52 +0200 Subject: [PATCH 31/36] Fix: corrent getSize() when no overlays --- .../src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt index 66961e392..ebca75bd3 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/spans/AztecMediaSpan.kt @@ -73,7 +73,7 @@ class AztecMediaSpan(val context: Context, private var drawable: Drawable?, override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, metrics: Paint.FontMetricsInt?): Int { val width1 = drawable?.bounds?.width() ?: 0 - var width: Int = 0 + var width: Int = width1 overlays.forEach { val width2 = it.first?.bounds?.width() ?: 0 From 05051428c1b2154fd8869bb19a4ac2517d48abbf Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 7 Feb 2017 02:52:24 +0200 Subject: [PATCH 32/36] Minor code style fix --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 75a27dac3..8cb605478 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -779,7 +779,7 @@ class AztecText : EditText, TextWatcher { val textToPaste = clip.getItemAt(i).coerceToText(context) val builder = SpannableStringBuilder() - builder.append(parser.fromHtml(Format.clearFormatting(textToPaste.toString()),onMediaTappedListener, + builder.append(parser.fromHtml(Format.clearFormatting(textToPaste.toString()), onMediaTappedListener, context).trim()) Selection.setSelection(editable, max) From d057ca80fa92fcbec0ff39dd792b24a5a7b187a6 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 7 Feb 2017 02:58:01 +0200 Subject: [PATCH 33/36] Favour text.length instead of hardcoded length --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 8cb605478..8bf14b45f 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -900,7 +900,7 @@ class AztecText : EditText, TextWatcher { } fun removeMedia(attributePredicate: AttributePredicate) { - text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + text.getSpans(0, text.length, AztecMediaSpan::class.java).forEach { if (it.attributes != null) { if (attributePredicate.matches(it.attributes as Attributes)) { text.removeSpan(it) @@ -917,7 +917,7 @@ class AztecText : EditText, TextWatcher { } fun setOverlayLevel(attributePredicate: AttributePredicate, index: Int, level: Int, attrs: Attributes) { - text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + text.getSpans(0, text.length, AztecMediaSpan::class.java).forEach { if (it.attributes != null) { if (attributePredicate.matches(it.attributes as Attributes)) { it.setOverayLevel(index, level) @@ -929,7 +929,7 @@ class AztecText : EditText, TextWatcher { fun setOverlay(attributePredicate: AttributePredicate, index: Int, overlay: Drawable?, gravity: Int, attributes: Attributes?) { - text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + text.getSpans(0, text.length, AztecMediaSpan::class.java).forEach { if (it.attributes != null) { if (attributePredicate.matches(it.attributes as Attributes)) { // set the new overlay drawable @@ -946,7 +946,7 @@ class AztecText : EditText, TextWatcher { } fun clearOverlays(attributePredicate: AttributePredicate, attributes: Attributes?) { - text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + text.getSpans(0, text.length, AztecMediaSpan::class.java).forEach { if (it.attributes != null) { if (attributePredicate.matches(it.attributes as Attributes)) { it.clearOverlays() @@ -962,7 +962,7 @@ class AztecText : EditText, TextWatcher { } fun getMediaAttributes(attributePredicate: AttributePredicate): Attributes? { - text.getSpans(0, 999999999, AztecMediaSpan::class.java).forEach { + text.getSpans(0, text.length, AztecMediaSpan::class.java).forEach { if (it.attributes != null) { if (attributePredicate.matches(it.attributes as Attributes)) { return it.attributes From 2cc111c131a23820739a9595089b8d85c4cfa26e Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 7 Feb 2017 10:48:20 +0200 Subject: [PATCH 34/36] No need for the media selection interface anymore --- .../org/wordpress/aztec/demo/MainActivity.kt | 17 ++++++++--------- .../org/wordpress/aztec/toolbar/AztecToolbar.kt | 10 ---------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index 6bbbfa036..e3a24476c 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -30,13 +30,12 @@ import org.wordpress.aztec.AztecText import org.wordpress.aztec.picassoloader.PicassoImageLoader import org.wordpress.aztec.source.SourceViewEditText import org.wordpress.aztec.toolbar.AztecToolbar -import org.wordpress.aztec.toolbar.AztecToolbar.OnMediaOptionSelectedListener import org.wordpress.aztec.toolbar.AztecToolbarClickListener import org.xml.sax.Attributes import org.xml.sax.helpers.AttributesImpl import java.io.File -class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnRequestPermissionsResultCallback, +class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback, View.OnTouchListener, PopupMenu.OnMenuItemClickListener, AztecToolbarClickListener, AztecText.OnMediaTappedListener, AztecText.OnImeBackListener { companion object { @@ -366,7 +365,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque return true } - override fun onCameraPhotoMediaOptionSelected() { + fun onCameraPhotoMediaOptionSelected() { if (PermissionUtils.checkAndRequestCameraAndStoragePermissions(this, MEDIA_CAMERA_PHOTO_PERMISSION_REQUEST_CODE)) { val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) @@ -382,7 +381,7 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque } } - override fun onCameraVideoMediaOptionSelected() { + fun onCameraVideoMediaOptionSelected() { if (PermissionUtils.checkAndRequestCameraAndStoragePermissions(this, MEDIA_CAMERA_PHOTO_PERMISSION_REQUEST_CODE)) { val intent = Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA) @@ -392,15 +391,15 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque } } - override fun onGalleryMediaOptionSelected() { + fun onGalleryMediaOptionSelected() { Toast.makeText(this, "Launch gallery", Toast.LENGTH_SHORT).show() } - override fun onPhotoLibraryMediaOptionSelected() { + fun onPhotoLibraryMediaOptionSelected() { Toast.makeText(this, "Open library", Toast.LENGTH_SHORT).show() } - override fun onPhotosMediaOptionSelected() { + fun onPhotosMediaOptionSelected() { if (PermissionUtils.checkAndRequestStoragePermission(this, MEDIA_PHOTOS_PERMISSION_REQUEST_CODE)) { val intent: Intent @@ -422,11 +421,11 @@ class MainActivity : AppCompatActivity(), OnMediaOptionSelectedListener, OnReque } } - override fun onVideoLibraryMediaOptionSelected() { + fun onVideoLibraryMediaOptionSelected() { Toast.makeText(this, "Open library", Toast.LENGTH_SHORT).show() } - override fun onVideosMediaOptionSelected() { + fun onVideosMediaOptionSelected() { if (PermissionUtils.checkAndRequestStoragePermission(this, MEDIA_PHOTOS_PERMISSION_REQUEST_CODE)) { val intent: Intent diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt index 7e74aa20f..2dbe1a995 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt @@ -35,16 +35,6 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { initView() } - interface OnMediaOptionSelectedListener { - fun onCameraPhotoMediaOptionSelected() - fun onCameraVideoMediaOptionSelected() - fun onGalleryMediaOptionSelected() - fun onPhotoLibraryMediaOptionSelected() - fun onPhotosMediaOptionSelected() - fun onVideoLibraryMediaOptionSelected() - fun onVideosMediaOptionSelected() - } - fun setToolbarListener(listener: AztecToolbarClickListener) { aztecToolbarListener = listener } From c703c6c064e876704194ec9276c46903be73ade6 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 7 Feb 2017 11:06:57 +0200 Subject: [PATCH 35/36] Notify, let the client decide on html mode change For example, the client can know whether the images have finished uploading or not and might decide to inhibit the toggle until finished. --- .../org/wordpress/aztec/demo/MainActivity.kt | 24 ++++++++++++++-- .../kotlin/org/wordpress/aztec/AztecText.kt | 22 +++++++++------ .../wordpress/aztec/toolbar/AztecToolbar.kt | 28 ++++--------------- .../toolbar/AztecToolbarClickListener.kt | 1 + 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt index e3a24476c..9e0677ed7 100644 --- a/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt +++ b/app/src/main/kotlin/org/wordpress/aztec/demo/MainActivity.kt @@ -35,9 +35,9 @@ import org.xml.sax.Attributes import org.xml.sax.helpers.AttributesImpl import java.io.File -class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback, - View.OnTouchListener, PopupMenu.OnMenuItemClickListener, AztecToolbarClickListener, - AztecText.OnMediaTappedListener, AztecText.OnImeBackListener { +class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback, View.OnTouchListener, + PopupMenu.OnMenuItemClickListener, AztecToolbarClickListener, AztecText.OnMediaTappedListener, + AztecText.OnImeBackListener { companion object { private val HEADING = "

Heading 1

" + @@ -148,6 +148,7 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback, val attrs = AttributesImpl() attrs.addAttribute("", "src", "src", "string", mediaPath) // Temporary source value. Replace with URL after uploaded. attrs.addAttribute("", "id", "id", "string", id) + attrs.addAttribute("", "uploading", "uploading", "string", "true") aztec.insertMedia(BitmapDrawable(resources, bitmap), attrs) @@ -172,6 +173,7 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback, progress += 2000 if (progress >= 10000) { + attrs.removeAttribute(attrs.getIndex("uploading")) aztec.clearOverlays(predicate, attrs) } } @@ -519,6 +521,22 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback, super.onRequestPermissionsResult(requestCode, permissions, grantResults) } + override fun onToolbarHtmlModeClicked() { + val uploadingPredicate = object : AztecText.AttributePredicate { + override fun matches(attrs: Attributes): Boolean { + return attrs.getIndex("uploading") > -1 + } + } + + val mediaPending = aztec.getAllMediaAttributes(uploadingPredicate).size > 0 + + if (mediaPending) { + ToastUtils.showToast(this, R.string.media_upload_dialog_message) + } else { + formattingToolbar.toggleEditorMode() + } + } + override fun onToolbarAddMediaClicked() { mediaMenu = PopupMenu(this, formattingToolbar) mediaMenu?.setOnMenuItemClickListener(this) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index 8bf14b45f..d6ee64059 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -962,14 +962,18 @@ class AztecText : EditText, TextWatcher { } fun getMediaAttributes(attributePredicate: AttributePredicate): Attributes? { - text.getSpans(0, text.length, AztecMediaSpan::class.java).forEach { - if (it.attributes != null) { - if (attributePredicate.matches(it.attributes as Attributes)) { - return it.attributes - } - } - } - - return null; + return getAllMediaAttributes(attributePredicate).firstOrNull() + } + + fun getAllMediaAttributes(attributePredicate: AttributePredicate): List { + return text + .getSpans(0, text.length, AztecMediaSpan::class.java) + .filter { + if (it.attributes != null) { + return@filter attributePredicate.matches(it.attributes as Attributes) + } else { + return@filter false + }} + .map { it.attributes } } } diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt index 2dbe1a995..886dd952f 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbar.kt @@ -9,12 +9,10 @@ import android.widget.PopupMenu import android.widget.PopupMenu.OnMenuItemClickListener import android.widget.Toast import android.widget.ToggleButton -import org.wordpress.android.util.ToastUtils import org.wordpress.aztec.AztecText import org.wordpress.aztec.R import org.wordpress.aztec.TextFormat import org.wordpress.aztec.source.SourceViewEditText -import org.wordpress.aztec.spans.AztecMediaSpan import java.util.* class AztecToolbar : FrameLayout, OnMenuItemClickListener { @@ -169,25 +167,20 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { ToolbarAction.ADD_MEDIA -> aztecToolbarListener?.onToolbarAddMediaClicked() ToolbarAction.HEADING -> headingMenu?.show() ToolbarAction.LINK -> editor!!.showLinkDialog() - ToolbarAction.HTML -> toggleEditorMode() + ToolbarAction.HTML -> aztecToolbarListener?.onToolbarHtmlModeClicked() else -> { Toast.makeText(context, "Unsupported action", Toast.LENGTH_SHORT).show() } } } - fun toggleEditorMode() { + public fun toggleEditorMode() { if (editor!!.visibility == View.VISIBLE) { - if (!editor!!.isMediaAdded || allImagesUploaded()) { - sourceEditor!!.displayStyledAndFormattedHtml(editor!!.toHtml(true)) - editor!!.visibility = View.GONE - sourceEditor!!.visibility = View.VISIBLE + sourceEditor!!.displayStyledAndFormattedHtml(editor!!.toHtml(true)) + editor!!.visibility = View.GONE + sourceEditor!!.visibility = View.VISIBLE - toggleHtmlMode(true) - } else { - toggleButton(findViewById(ToolbarAction.HTML.buttonId), false) - ToastUtils.showToast(context, R.string.media_upload_dialog_message) - } + toggleHtmlMode(true) } else { editor!!.fromHtml(sourceEditor!!.getPureHtml(true)) editor!!.visibility = View.VISIBLE @@ -197,15 +190,6 @@ class AztecToolbar : FrameLayout, OnMenuItemClickListener { } } - private fun allImagesUploaded(): Boolean { - editor!!.text?.getSpans(0, editor!!.length(), AztecMediaSpan::class.java)?.forEach { - if (it.getSource().isNullOrBlank() || !it.getSource().startsWith("http")) { - return false - } - } - return true - } - private fun selectHeaderMenu(textFormats: ArrayList) { headingMenu?.menu?.getItem(0)?.isChecked = true textFormats.forEach { diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt index 534f1b0bb..72cdafd84 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/toolbar/AztecToolbarClickListener.kt @@ -2,4 +2,5 @@ package org.wordpress.aztec.toolbar interface AztecToolbarClickListener { fun onToolbarAddMediaClicked() + fun onToolbarHtmlModeClicked() } From 5f4e56bb86c302d7a1340583bd587c1fadbfe362 Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Tue, 7 Feb 2017 18:09:53 +0200 Subject: [PATCH 36/36] Remove markup and clickable span as well --- aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt index d6ee64059..8c1e5f59b 100644 --- a/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt +++ b/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt @@ -903,7 +903,15 @@ class AztecText : EditText, TextWatcher { text.getSpans(0, text.length, AztecMediaSpan::class.java).forEach { if (it.attributes != null) { if (attributePredicate.matches(it.attributes as Attributes)) { + val start = text.getSpanStart(it) + val end = text.getSpanEnd(it) + + val clickableSpan = text.getSpans(start, end, AztecMediaClickableSpan::class.java).firstOrNull() + + text.removeSpan(clickableSpan) text.removeSpan(it) + + text.delete(start, end) } } }