@nativescript-community/text 1.4.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,692 @@
1
+ package com.nativescript.text;
2
+
3
+ import org.xml.sax.Attributes;
4
+ import org.xml.sax.Locator;
5
+ import org.xml.sax.SAXException;
6
+ import org.xml.sax.helpers.DefaultHandler;
7
+
8
+ import android.content.Context;
9
+ import android.content.res.ColorStateList;
10
+ import android.content.res.Resources;
11
+ import android.graphics.Color;
12
+ import android.graphics.Typeface;
13
+ import android.graphics.drawable.Drawable;
14
+ import android.text.Html;
15
+ import android.text.Spannable;
16
+ import android.text.SpannableStringBuilder;
17
+ import android.text.Spanned;
18
+ import android.text.TextUtils;
19
+ import android.text.style.AbsoluteSizeSpan;
20
+ import android.text.style.ForegroundColorSpan;
21
+ import android.text.style.ImageSpan;
22
+ import android.text.style.ParagraphStyle;
23
+ import android.text.style.QuoteSpan;
24
+ import android.text.style.RelativeSizeSpan;
25
+ import android.text.style.StrikethroughSpan;
26
+ import android.text.style.StyleSpan;
27
+ import android.text.style.SubscriptSpan;
28
+ import android.text.style.SuperscriptSpan;
29
+ import android.text.style.TextAppearanceSpan;
30
+ import android.text.style.TypefaceSpan;
31
+ import android.text.style.UnderlineSpan;
32
+
33
+ import java.util.ArrayList;
34
+ import java.util.Collection;
35
+ import java.util.LinkedList;
36
+ import java.util.List;
37
+
38
+ import android.util.Log;
39
+
40
+ class HtmlToSpannedConverter extends DefaultHandler {
41
+ private final String TAG = "HtmlToSpannedConverter";
42
+
43
+ // private Attributes _currentAtts = null;
44
+ public boolean disableLinkStyle = false;
45
+ private SpannableStringBuilder mSpannableStringBuilder;
46
+
47
+ private Html.ImageGetter mImageGetter;
48
+ private float density;
49
+ private String fontFolder;
50
+ private String parentFontFamily;
51
+ private Context context;
52
+
53
+ public void reset() {
54
+ mSpannableStringBuilder = new SpannableStringBuilder();
55
+ }
56
+
57
+ public HtmlToSpannedConverter(Context context, String fontFolder, String parentFontFamily, Html.ImageGetter imageGetter,
58
+ Html.TagHandler tagHandler, final boolean disableLinkStyle) {
59
+ mSpannableStringBuilder = new SpannableStringBuilder();
60
+ mImageGetter = imageGetter;
61
+ this.fontFolder = fontFolder;
62
+ this.parentFontFamily = parentFontFamily;
63
+ this.context = context;
64
+ this.disableLinkStyle = disableLinkStyle;
65
+ density = context.getResources().getDisplayMetrics().density;
66
+ // if (mImageGetter == null) {
67
+ // mImageGetter = new Html.ImageGetter() {
68
+ //
69
+ // @Override
70
+ // public Drawable getDrawable(String source) {
71
+ // return null;
72
+ // }
73
+ // };
74
+ // }
75
+ }
76
+
77
+ private final float[] HEADER_SIZES = { 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f, };
78
+
79
+ private void handleP(SpannableStringBuilder text) {
80
+ int len = text.length();
81
+
82
+ if (len >= 1 && text.charAt(len - 1) == '\n') {
83
+ if (len >= 2 && text.charAt(len - 2) == '\n') {
84
+ return;
85
+ }
86
+
87
+ text.append("\n");
88
+ return;
89
+ }
90
+
91
+ if (len != 0) {
92
+ text.append("\n\n");
93
+ }
94
+ }
95
+
96
+ private void handleBr(SpannableStringBuilder text) {
97
+ text.append("\n");
98
+ }
99
+
100
+ private Object getLast(Spanned text, Class kind) {
101
+ /*
102
+ * This knows that the last returned object from getSpans() will be the most
103
+ * recently added.
104
+ */
105
+ Object[] objs = text.getSpans(0, text.length(), kind);
106
+
107
+ if (objs.length == 0) {
108
+ return null;
109
+ } else {
110
+ return objs[objs.length - 1];
111
+ }
112
+ }
113
+
114
+ private void start(SpannableStringBuilder text, Object mark) {
115
+ int len = text.length();
116
+ text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
117
+ }
118
+
119
+ private void end(SpannableStringBuilder text, Class kind, Object repl) {
120
+
121
+ int len = text.length();
122
+ Object obj = getLast(text, kind);
123
+ int where = text.getSpanStart(obj);
124
+ text.removeSpan(obj);
125
+ if (repl == null) {
126
+ return;
127
+ }
128
+ if (where != len) {
129
+ if (Collection.class.isAssignableFrom(repl.getClass())) {
130
+ Collection<Object> col = (Collection<Object>) repl;
131
+ for (Object iterable_element : col) {
132
+ text.setSpan(iterable_element, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
133
+ }
134
+ } else {
135
+ text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
136
+ }
137
+ }
138
+ }
139
+
140
+ private void startImg(SpannableStringBuilder text, Attributes attributes, Html.ImageGetter img) {
141
+ String src = attributes.getValue("src");
142
+ Drawable d = null;
143
+
144
+ if (img != null) {
145
+ d = img.getDrawable(src);
146
+ if (d != null) {
147
+ int len = text.length();
148
+ text.append("\uFFFC");
149
+
150
+ text.setSpan(new ImageSpan(d, src), len, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
151
+ }
152
+
153
+ }
154
+
155
+ // if (d == null) {
156
+ // d = Resources.getSystem().
157
+ // getDrawable(com.android.internal.R.drawable.unknown_image);
158
+ // d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
159
+ // }
160
+
161
+ }
162
+
163
+ private void startFont(SpannableStringBuilder text, Attributes attributes) {
164
+ String color = attributes.getValue("color");
165
+ String face = attributes.getValue("face");
166
+
167
+ int len = text.length();
168
+ text.setSpan(new FontSpan(color, face), len, len, Spannable.SPAN_MARK_MARK);
169
+ }
170
+
171
+ private void endFont(SpannableStringBuilder text) {
172
+ int len = text.length();
173
+ Object obj = getLast(text, FontSpan.class);
174
+ int where = text.getSpanStart(obj);
175
+
176
+ text.removeSpan(obj);
177
+
178
+ if (where != len) {
179
+ FontSpan f = (FontSpan) obj;
180
+
181
+ if (!TextUtils.isEmpty(f.mColor)) {
182
+ if (f.mColor.startsWith("@")) {
183
+ Resources res = Resources.getSystem();
184
+ String name = f.mColor.substring(1);
185
+ int colorRes = res.getIdentifier(name, "color", "android");
186
+ if (colorRes != 0) {
187
+ ColorStateList colors = res.getColorStateList(colorRes);
188
+ text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null), where, len,
189
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
190
+ }
191
+ } else {
192
+ int c = Color.parseColor(f.mColor);
193
+ if (c != -1) {
194
+ text.setSpan(new ForegroundColorSpan(c), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
195
+ }
196
+ }
197
+ }
198
+
199
+ if (f.mFace != null) {
200
+ text.setSpan(new TypefaceSpan(f.mFace), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
201
+ }
202
+ }
203
+ }
204
+
205
+ private void startA(SpannableStringBuilder text, Attributes attributes) {
206
+ String href = attributes.getValue("href");
207
+
208
+ int len = text.length();
209
+ text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK);
210
+ }
211
+
212
+ private void endA(SpannableStringBuilder text) {
213
+ int len = text.length();
214
+ Object obj = getLast(text, Href.class);
215
+ int where = text.getSpanStart(obj);
216
+
217
+ text.removeSpan(obj);
218
+
219
+ if (where != len) {
220
+ Href h = (Href) obj;
221
+
222
+ if (h.mHref != null) {
223
+ text.setSpan(new URLSpanNoUnderline(h.mHref, !disableLinkStyle), where, len,
224
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
225
+ }
226
+ }
227
+ }
228
+
229
+ private void endHeader(SpannableStringBuilder text) {
230
+ int len = text.length();
231
+ Object obj = getLast(text, Header.class);
232
+
233
+ int where = text.getSpanStart(obj);
234
+
235
+ text.removeSpan(obj);
236
+
237
+ // Back off not to change only the text, not the blank line.
238
+ while (len > where && text.charAt(len - 1) == '\n') {
239
+ len--;
240
+ }
241
+
242
+ if (where != len) {
243
+ Header h = (Header) obj;
244
+
245
+ text.setSpan(new RelativeSizeSpan(HEADER_SIZES[h.mLevel]), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
246
+ text.setSpan(new StyleSpan(Typeface.BOLD), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
247
+ }
248
+ }
249
+
250
+ private class Bold {
251
+ }
252
+
253
+ private class Italic {
254
+ }
255
+
256
+ private class Underline {
257
+ }
258
+
259
+ private class Big {
260
+ }
261
+
262
+ private class Small {
263
+ }
264
+
265
+ private class Monospace {
266
+ }
267
+
268
+ private class Blockquote {
269
+ }
270
+
271
+ private class Super {
272
+ }
273
+
274
+ private class Sub {
275
+ }
276
+
277
+ private class Span {
278
+ }
279
+
280
+ private class Div {
281
+ }
282
+
283
+ private class Strike {
284
+ }
285
+
286
+ private class FontSpan {
287
+ public String mColor;
288
+ public String mFace;
289
+
290
+ public FontSpan(String color, String face) {
291
+ mColor = color;
292
+ mFace = face;
293
+ }
294
+ }
295
+
296
+ private class Href {
297
+ public String mHref;
298
+
299
+ public Href(String href) {
300
+ mHref = href;
301
+ }
302
+ }
303
+
304
+ private class Header {
305
+ private int mLevel;
306
+
307
+ public Header(int level) {
308
+ mLevel = level;
309
+ }
310
+ }
311
+
312
+ // private void addText(String characters) {
313
+ // StringBuilder sb = new StringBuilder();
314
+ // /*
315
+ // * Ignore whitespace that immediately follows other whitespace;
316
+ // * newlines count as spaces.
317
+ // */
318
+ //
319
+ // for (int i = 0; i < characters.length(); i++) {
320
+ // char c = characters.charAt(i);
321
+ //
322
+ // if (c == ' ' || c == '\n') {
323
+ // char pred;
324
+ // int len = sb.length();
325
+ //
326
+ // if (len == 0) {
327
+ // len = mSpannableStringBuilder.length();
328
+ //
329
+ // if (len == 0) {
330
+ // pred = '\n';
331
+ // } else {
332
+ // pred = mSpannableStringBuilder.charAt(len - 1);
333
+ // }
334
+ // } else {
335
+ // pred = sb.charAt(len - 1);
336
+ // }
337
+ //
338
+ // if (pred != ' ' && pred != '\n') {
339
+ // sb.append(' ');
340
+ // }
341
+ // } else {
342
+ // sb.append(c);
343
+ // }
344
+ // }
345
+
346
+ // mSpannableStringBuilder.append(characters);
347
+ // }
348
+
349
+ // hit when the node is first seen
350
+ private LinkedList<Object> _currentStyles = new LinkedList<Object>();
351
+
352
+ public void handleStartTag(String tag, Attributes attributes) {
353
+ // _currentAtts = attributes;
354
+ final String lowTag = tag.toLowerCase();
355
+ switch (lowTag) {
356
+ case "br":
357
+ // We don't need to handle this. TagSoup will ensure that there's a
358
+ // </br> for each <br>
359
+ // so we can safely emite the linebreaks when we handle the close
360
+ // tag.
361
+
362
+ break;
363
+ case "p":
364
+ handleP(mSpannableStringBuilder);
365
+ break;
366
+ case "div":
367
+ _currentStyles.add(getStyleSpan(attributes));
368
+ handleP(mSpannableStringBuilder);
369
+ start(mSpannableStringBuilder, new Div());
370
+ break;
371
+ case "strong":
372
+ case "b":
373
+ start(mSpannableStringBuilder, new Bold());
374
+ break;
375
+ case "cite":
376
+ case "em":
377
+ case "dfn":
378
+ case "i":
379
+ start(mSpannableStringBuilder, new Italic());
380
+ break;
381
+ case "big":
382
+ start(mSpannableStringBuilder, new Big());
383
+ break;
384
+ case "small":
385
+ start(mSpannableStringBuilder, new Small());
386
+ break;
387
+ case "font":
388
+ startFont(mSpannableStringBuilder, attributes);
389
+ break;
390
+ case "blockquote":
391
+ handleP(mSpannableStringBuilder);
392
+ start(mSpannableStringBuilder, new Blockquote());
393
+ break;
394
+ case "tt":
395
+ start(mSpannableStringBuilder, new Monospace());
396
+ break;
397
+ case "a":
398
+ startA(mSpannableStringBuilder, attributes);
399
+ break;
400
+ case "u":
401
+ start(mSpannableStringBuilder, new Underline());
402
+ break;
403
+ case "sup":
404
+ start(mSpannableStringBuilder, new Super());
405
+ break;
406
+ case "sub":
407
+ start(mSpannableStringBuilder, new Sub());
408
+ break;
409
+ case "strike":
410
+ start(mSpannableStringBuilder, new Strike());
411
+ break;
412
+ case "span":
413
+ _currentStyles.add(getStyleSpan(attributes));
414
+ start(mSpannableStringBuilder, new Span());
415
+ break;
416
+ case "h1":
417
+ case "h2":
418
+ case "h3":
419
+ case "h4":
420
+ case "h5":
421
+ case "h6":
422
+ handleP(mSpannableStringBuilder);
423
+ start(mSpannableStringBuilder, new Header(tag.charAt(1) - '1'));
424
+ break;
425
+ case "img":
426
+ startImg(mSpannableStringBuilder, attributes, mImageGetter);
427
+ break;
428
+ }
429
+ }
430
+
431
+ private Object getStyleSpan(Attributes attr) {
432
+ String style = (attr != null) ? attr.getValue("style") : null;
433
+ if (style != null) {
434
+ String[] items = style.toLowerCase().trim().split(";");
435
+ int fillColor = Color.TRANSPARENT;
436
+ int strokeColor = Color.TRANSPARENT;
437
+ String color = null;
438
+ int strokeWidth = 1;
439
+ int radius = 0;
440
+ String fontWeight = null;
441
+ String fontFamily = this.parentFontFamily;
442
+ String fontStyle = null;
443
+ float fontSize = 0;
444
+ boolean needsBgdSpan = false;
445
+ boolean needsFontSpan = false;
446
+ for (String item : items) {
447
+ String[] values = item.trim().split(":");
448
+ String key = values[0];
449
+ String value = values[1];
450
+ switch (key) {
451
+
452
+ case "background-color":
453
+ fillColor = Color.parseColor(value);
454
+ needsBgdSpan = true;
455
+ break;
456
+ case "border-color":
457
+ strokeColor = Color.parseColor(value);
458
+ needsBgdSpan = true;
459
+ break;
460
+ case "border-width":
461
+ strokeWidth = Math.round(Float.parseFloat(value) * density);
462
+ needsBgdSpan = true;
463
+ break;
464
+ case "border-radius":
465
+ radius = Math.round(Float.parseFloat(value) * density);
466
+ needsBgdSpan = true;
467
+ break;
468
+ case "font-family":
469
+ fontFamily = value;
470
+ needsFontSpan = true;
471
+ break;
472
+ case "font-size":
473
+ fontSize = Float.parseFloat(value.replace("px", "").replace("pt", "").trim()) * density;
474
+ needsFontSpan = true;
475
+ break;
476
+ case "font-weight":
477
+ fontWeight = value;
478
+ needsFontSpan = true;
479
+ break;
480
+ case "font-style":
481
+ fontStyle = value;
482
+ needsFontSpan = true;
483
+ break;
484
+ case "color":
485
+ color = value;
486
+ needsFontSpan = true;
487
+ break;
488
+ }
489
+ }
490
+ if (needsBgdSpan || needsFontSpan) {
491
+ List<Object> result = new ArrayList<Object>();
492
+ if (needsBgdSpan) {
493
+ result.add(new CustomBackgroundSpan(radius, fillColor, strokeColor, strokeWidth));
494
+
495
+ }
496
+ if (needsFontSpan) {
497
+ boolean isBold = fontWeight != null && (fontWeight.equals("bold") || fontWeight.equals("700"));
498
+ if (fontFamily != null) {
499
+ result.add(new CustomTypefaceSpan(fontFamily, Font.createTypeface(this.context, this.fontFolder,
500
+ fontFamily, fontWeight, isBold, false)));
501
+ } else {
502
+ if (isBold) {
503
+ result.add(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD));
504
+ }
505
+ }
506
+ if (fontSize != 0) {
507
+ result.add(new AbsoluteSizeSpan(Math.round(fontSize)));
508
+ }
509
+ if (color != null) {
510
+ result.add(new ForegroundColorSpan(Color.parseColor(color)));
511
+ }
512
+ if (fontStyle != null) {
513
+ if (fontStyle.equals("italic")) {
514
+ result.add(new android.text.style.StyleSpan(android.graphics.Typeface.ITALIC));
515
+ }
516
+ }
517
+ }
518
+ return result;
519
+ }
520
+ }
521
+ return null;
522
+ }
523
+
524
+ // hit when all of the node's children (if any) have been visited
525
+ public void handleEndTag(String tag) {
526
+ final String lowTag = tag.toLowerCase();
527
+ switch (lowTag) {
528
+ case "br":
529
+ handleBr(mSpannableStringBuilder);
530
+ break;
531
+ case "p":
532
+ handleP(mSpannableStringBuilder);
533
+ break;
534
+ case "div":
535
+ handleP(mSpannableStringBuilder);
536
+ end(mSpannableStringBuilder, Div.class, _currentStyles.pollLast());
537
+ break;
538
+ case "strong":
539
+ case "b":
540
+ end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
541
+ break;
542
+ case "em":
543
+ case "cite":
544
+ case "dfn":
545
+ case "i":
546
+ end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
547
+ break;
548
+ case "big":
549
+ end(mSpannableStringBuilder, Big.class, new RelativeSizeSpan(1.25f));
550
+ break;
551
+ case "small":
552
+ end(mSpannableStringBuilder, Small.class, new RelativeSizeSpan(0.8f));
553
+ break;
554
+ case "font":
555
+ endFont(mSpannableStringBuilder);
556
+ break;
557
+ case "blockquote":
558
+ handleP(mSpannableStringBuilder);
559
+ end(mSpannableStringBuilder, Blockquote.class, new QuoteSpan());
560
+ break;
561
+ case "tt":
562
+ end(mSpannableStringBuilder, Monospace.class, new TypefaceSpan("monospace"));
563
+ break;
564
+ case "a":
565
+ endA(mSpannableStringBuilder);
566
+ break;
567
+ case "u":
568
+ end(mSpannableStringBuilder, Underline.class, new UnderlineSpan());
569
+ break;
570
+ case "sup":
571
+ end(mSpannableStringBuilder, Super.class, new SuperscriptSpan());
572
+ break;
573
+ case "sub":
574
+ end(mSpannableStringBuilder, Sub.class, new SubscriptSpan());
575
+ break;
576
+ case "strike":
577
+ end(mSpannableStringBuilder, Strike.class, new StrikethroughSpan());
578
+ break;
579
+ case "span":
580
+ end(mSpannableStringBuilder, Span.class, _currentStyles.pollLast());
581
+ break;
582
+ case "h1":
583
+ case "h2":
584
+ case "h3":
585
+ case "h4":
586
+ case "h5":
587
+ case "h6":
588
+ handleP(mSpannableStringBuilder);
589
+ endHeader(mSpannableStringBuilder);
590
+ break;
591
+ }
592
+ }
593
+
594
+ public Spanned spannable() {
595
+ // Fix flags and range for paragraph-type markup.
596
+ Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class);
597
+ for (int i = 0; i < obj.length; i++) {
598
+ int start = mSpannableStringBuilder.getSpanStart(obj[i]);
599
+ int end = mSpannableStringBuilder.getSpanEnd(obj[i]);
600
+
601
+ // If the last line of the range is blank, back off by one.
602
+ if (end - 2 >= 0) {
603
+ if (mSpannableStringBuilder.charAt(end - 1) == '\n'
604
+ && mSpannableStringBuilder.charAt(end - 2) == '\n') {
605
+ end--;
606
+ }
607
+ }
608
+
609
+ if (end == start) {
610
+ mSpannableStringBuilder.removeSpan(obj[i]);
611
+ } else {
612
+ mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_PARAGRAPH);
613
+ }
614
+ }
615
+
616
+ return mSpannableStringBuilder;
617
+ }
618
+
619
+ @Override
620
+ public void setDocumentLocator(Locator locator) {
621
+ }
622
+
623
+ @Override
624
+ public void startDocument() throws SAXException {
625
+ }
626
+
627
+ @Override
628
+ public void endDocument() throws SAXException {
629
+ }
630
+
631
+ @Override
632
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
633
+ }
634
+
635
+ @Override
636
+ public void endPrefixMapping(String prefix) throws SAXException {
637
+ }
638
+
639
+ @Override
640
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
641
+ handleStartTag(localName, attributes);
642
+ }
643
+
644
+ @Override
645
+ public void endElement(String uri, String localName, String qName) throws SAXException {
646
+ handleEndTag(localName);
647
+ }
648
+
649
+ @Override
650
+ public void characters(char ch[], int start, int length) throws SAXException {
651
+ StringBuilder sb = new StringBuilder();
652
+ /*
653
+ * Ignore whitespace that immediately follows other whitespace; newlines count
654
+ * as spaces.
655
+ */
656
+ for (int i = 0; i < length; i++) {
657
+ char c = ch[i + start];
658
+ if (c == ' ' || c == '\n') {
659
+ char pred;
660
+ int len = sb.length();
661
+ if (len == 0) {
662
+ len = mSpannableStringBuilder.length();
663
+ if (len == 0) {
664
+ pred = '\n';
665
+ } else {
666
+ pred = mSpannableStringBuilder.charAt(len - 1);
667
+ }
668
+ } else {
669
+ pred = sb.charAt(len - 1);
670
+ }
671
+ if (pred != ' ' && pred != '\n') {
672
+ sb.append(' ');
673
+ }
674
+ } else {
675
+ sb.append(c);
676
+ }
677
+ }
678
+ mSpannableStringBuilder.append(sb);
679
+ }
680
+
681
+ @Override
682
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
683
+ }
684
+
685
+ @Override
686
+ public void processingInstruction(String target, String data) throws SAXException {
687
+ }
688
+
689
+ @Override
690
+ public void skippedEntity(String name) throws SAXException {
691
+ }
692
+ }
@@ -0,0 +1,24 @@
1
+ package com.nativescript.text;
2
+
3
+ import android.text.TextPaint;
4
+ import android.text.style.URLSpan;
5
+
6
+ public class URLSpanNoUnderline extends URLSpan {
7
+ private boolean showUnderline;
8
+ public URLSpanNoUnderline(String url, boolean showUnderline) {
9
+ super(url);
10
+ this.showUnderline = showUnderline;
11
+ }
12
+ @Override
13
+ public void updateDrawState(TextPaint ds) {
14
+ if (this.showUnderline) {
15
+ super.updateDrawState(ds);
16
+ }
17
+ // super.updateDrawState(ds);
18
+ // ds.setUnderlineText(false);
19
+ }
20
+ @Override
21
+ public void onClick(android.view.View widget) {
22
+ // custom click handle
23
+ }
24
+ }