Skip to content

Commit

Permalink
feat(android): implement ATTRIBUTE_UNDERLINE_COLOR functionality (#12471
Browse files Browse the repository at this point in the history
)

Fixes TIMOB-24735
  • Loading branch information
garymathews committed Feb 19, 2021
1 parent a8df4f5 commit 645c91e
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package ti.modules.titanium.ui;

import java.lang.reflect.Method;
import java.util.HashMap;

import org.appcelerator.kroll.KrollDict;
Expand All @@ -19,9 +20,11 @@
import android.app.Activity;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
Expand All @@ -40,6 +43,65 @@ public class AttributedStringProxy extends KrollProxy
{
private static final String TAG = "AttributedString";

private static final class UnderlineColorSpan extends UnderlineSpan
{
private final int color;
private final float thickness;

public UnderlineColorSpan(final int color, final float thickness)
{
this.color = color;
this.thickness = thickness;
}

public UnderlineColorSpan(final int color)
{
this(color, 2.0f);
}

@Override
public void updateDrawState(final TextPaint ds)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ds.underlineColor = this.color;
ds.underlineThickness = this.thickness;
} else {
try {
final Method setUnderlineText =
TextPaint.class.getMethod("setUnderlineText", Integer.TYPE, Float.TYPE);
setUnderlineText.invoke(ds, this.color, this.thickness);
} catch (final Exception e) {

// Fallback to default underline behavior.
ds.setUnderlineText(true);
}
}
}
}

private static final class URLColorSpan extends URLSpan
{
private boolean underline = true;

public URLColorSpan(String url)
{
super(url);
}

public void setUnderline(boolean underline)
{
this.underline = underline;
}

@Override
public void updateDrawState(final TextPaint ds)
{
super.updateDrawState(ds);

ds.setUnderlineText(underline);
}
}

public AttributedStringProxy()
{
}
Expand Down Expand Up @@ -199,6 +261,21 @@ public static Bundle toSpannableInBundle(AttributedStringProxy attrString, Activ
spannableText.setSpan(new StrikethroughSpan(), range[0], range[0] + range[1],
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
case UIModule.ATTRIBUTE_UNDERLINE_COLOR:
final UnderlineColorSpan underlineColorSpan = new UnderlineColorSpan(
TiConvert.toColor(TiConvert.toString(attrValue)));

spannableText.setSpan(underlineColorSpan, range[0], range[0] + range[1],
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

final URLColorSpan[] urlSpans = spannableText.getSpans(
range[0], range[0] + range[1], URLColorSpan.class);
for (final URLColorSpan urlSpan : urlSpans) {

// Disable link underline, override with our underline color.
urlSpan.setUnderline(false);
}
break;
case UIModule.ATTRIBUTE_UNDERLINES_STYLE:
spannableText.setSpan(new UnderlineSpan(), range[0], range[0] + range[1],
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Expand All @@ -213,9 +290,19 @@ public static Bundle toSpannableInBundle(AttributedStringProxy attrString, Activ
break;
case UIModule.ATTRIBUTE_LINK:
if (attrValue != null) {
spannableText.setSpan(new URLSpan(TiConvert.toString(attrValue)), range[0],
range[0] + range[1],
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
final URLColorSpan urlColorSpan =
new URLColorSpan(TiConvert.toString(attrValue));

spannableText.setSpan(urlColorSpan, range[0], range[0] + range[1],
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

final UnderlineColorSpan[] underlineSpans = spannableText.getSpans(
range[0], range[0] + range[1], UnderlineColorSpan.class);
if (underlineSpans.length > 0) {

// Disable link underline, allow override with our underline color.
urlColorSpan.setUnderline(false);
}
}
results.putBoolean(TiC.PROPERTY_HAS_LINK, true);
break;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions tests/Resources/ti.ui.attributedstring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
* Licensed under the terms of the Apache Public License
* Please see the LICENSE included with this distribution for details.
*/
/* global OS_ANDROID, OS_IOS, OS_VERSION_MAJOR */
/* eslint-env mocha */
/* eslint no-unused-expressions: "off" */
'use strict';
const should = require('./utilities/assertions');
const utilities = require('./utilities/utilities');

const isCI = Ti.App.Properties.getBool('isCI', false);

describe('Titanium.UI', () => {
it('#createAttributedString()', () => {
Expand Down Expand Up @@ -65,4 +69,42 @@ describe('Titanium.UI.AttributedString', function () {
});
should(attributedString.attributes.length).be.eql(2);
});

it('colored link', () => {
// FIXME: Does not honour scale correctly on macOS: https://jira.appcelerator.org/browse/TIMOB-28261
if (isCI && utilities.isMacOS() && OS_VERSION_MAJOR < 11) {
return;
}

const view = Ti.UI.createView({
width: '960px',
height: '220px'
});
const label = Ti.UI.createLabel({
attributedString: Ti.UI.createAttributedString({
text: 'Check out the Appcelerator Developer Portal',
attributes: [
{
type: Ti.UI.ATTRIBUTE_LINK,
value: 'https://developer.appcelerator.com',
range: [ 14, 29 ]
},
{
type: Ti.UI.ATTRIBUTE_FOREGROUND_COLOR,
value: 'purple',
range: [ 14, 29 ]
},
{
type: Ti.UI.ATTRIBUTE_UNDERLINE_COLOR,
value: 'orange',
range: [ 14, 29 ]
}
]
})
});

view.add(label);

should(view).matchImage('snapshots/attributedString_coloredLink.png', { maxPixelMismatch: OS_IOS ? 2 : 0 }); // 2 pixels differ on actual iPhone
});
});

0 comments on commit 645c91e

Please sign in to comment.