Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support semantic/named colors for color properties #11699

Merged
merged 35 commits into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
42a1faa
feat(ios): support using named colors for color properties directly
hansemannn May 10, 2020
7e3b4fa
feat(android): support using named colors for color properties directly
hansemannn May 10, 2020
915ce8f
docs: add notes about ARGB hex format
sgtcoolguy May 27, 2020
41b44ff
fix(common): assume hex is ARGB
sgtcoolguy May 27, 2020
3bdefe0
test: add test for direct named color property, fix alpha hex ordering
sgtcoolguy May 27, 2020
68012ff
fix(ios): have Ti.Color hex be AARRGGBB format (not RRGGBBAA)
sgtcoolguy May 28, 2020
ee0ad88
feat(android): add Ti.UI.Android.getColorResource(), Ti.UI.Color
sgtcoolguy May 28, 2020
69b78e5
test: color resource names for Ti.UI.fetchSemanticColor, color props
sgtcoolguy May 28, 2020
7634e0a
test(android): verify Ti.UI.Android.getColorResource()
sgtcoolguy May 28, 2020
95e1fc6
docs(android): add Ti.UI.Android.getColorResource()
sgtcoolguy May 28, 2020
a555584
docs: update Ti.UI.Color for android, fix toHex() string details
sgtcoolguy May 28, 2020
0accfd1
docs(ui): note that Ti.UI.fetchSemanticColor can return a String
sgtcoolguy May 28, 2020
884150a
test: add support for collecting generated images in tests
sgtcoolguy May 29, 2020
8095daf
test: add image comparison test for view with named system color as bg
sgtcoolguy May 29, 2020
15c40b3
test: check exact hex values of iOS system colors
sgtcoolguy May 29, 2020
512df9e
test: support diffs for images
sgtcoolguy May 29, 2020
5222ee1
test: generate diff for mismatching images
sgtcoolguy May 29, 2020
acaaa38
chore: ignore test image diff directory
sgtcoolguy May 29, 2020
885a5a1
ci(jenkins): archive diff images if any
sgtcoolguy May 29, 2020
ab7af02
build(tests): use npm ci to install test dependencies
sgtcoolguy Jun 1, 2020
80f9eb9
test: use image from ci server
sgtcoolguy Jun 1, 2020
0348010
test: fix comparison of buffers
sgtcoolguy Jun 1, 2020
c22b1e3
fix(common): copy sliced buffer doesn't extend beyond view now
sgtcoolguy Jun 1, 2020
ee4479f
test: add tests to verify fixes for Buffer
sgtcoolguy Jun 1, 2020
70c398a
feat(common): add basic stream shim
sgtcoolguy Jun 1, 2020
6992449
fix(buffer): add .buffer and #set to Buffer
sgtcoolguy Jun 2, 2020
ff1dc34
test(buffer): add tests for Buffer .buffer and #set
sgtcoolguy Jun 2, 2020
8a9c4e1
test: add support for grabbing diff image
sgtcoolguy Jun 2, 2020
8a3841e
test: use pngjs, pixelmatch and browserify-zlib to compare images
sgtcoolguy Jun 2, 2020
760a1cb
style(js): don't run eslint on test fake_node_modules
sgtcoolguy Jun 2, 2020
ac54ee9
docs(android): fix typo
sgtcoolguy Jun 23, 2020
73c350b
refactor(android): just wrap ColorInt in proxy
sgtcoolguy Jun 23, 2020
e31f0a1
test: port over latest changes to ti.ui.test
sgtcoolguy Jun 23, 2020
cbd77c1
test(android): compare hex strings from getColorResource ignoring case
sgtcoolguy Jun 24, 2020
85011fe
test: use 12 x 12 view/image, otherwise ios blob pt conversion goes w…
sgtcoolguy Jul 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ android/runtime/v8/tools/bootstrap.js
android/dev
android/modules/ui/assets/Resources/ti.internal/webview/*.js
android/titanium/build
android/**/generated/

iphone/Resources/app.js

# TODO: We probably do want to lint these eventually!
templates/**


titanium-mobile-mocha-suite/
tests/Resources/fake_node_modules/

dist/
android/**/generated/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ junit_report.xml

.nyc_output/
coverage/

tests/diffs/
2 changes: 2 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def androidUnitTests(nodeVersion, npmVersion, testSuiteBranch, testOnDevices) {
stash includes: 'junit.*.xml', name: 'test-report-android'
junit 'junit.*.xml'
} // dir('scripts')
archiveArtifacts allowEmptyArchive: true, artifacts: 'diffs/'
} // nodejs
} // dir('titanium-mobile-mocha-suite')
} finally {
Expand Down Expand Up @@ -172,6 +173,7 @@ def iosUnitTests(deviceFamily, nodeVersion, npmVersion, testSuiteBranch) {
stash includes: 'junit.ios.*.xml', name: "test-report-ios-${deviceFamily}"
junit 'junit.ios.*.xml'
} // dir('scripts')
archiveArtifacts allowEmptyArchive: true, artifacts: 'diffs/'
} // nodejs
} // dir('titanium-mobile-mocha-suite')
} finally {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Appcelerator Titanium Mobile
* Copyright (c) 2009-2016 by Appcelerator, Inc. All Rights Reserved.
* Copyright (c) 2009-2020 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Apache Public License
* Please see the LICENSE included with this distribution for details.
*/
Expand All @@ -9,21 +9,28 @@
import org.appcelerator.kroll.KrollModule;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.kroll.common.Log;

import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.proxy.ColorProxy;
import org.appcelerator.titanium.util.TiColorHelper;
import org.appcelerator.titanium.util.TiUIHelper;
import org.appcelerator.titanium.view.TiUIView;

import ti.modules.titanium.ui.UIModule;
import ti.modules.titanium.ui.widget.TiUIProgressIndicator;
import ti.modules.titanium.ui.widget.webview.TiUIWebView;

import android.app.Activity;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.view.Gravity;
import androidx.core.view.GravityCompat;
import android.view.WindowManager;
import android.webkit.WebSettings;

import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;

@SuppressWarnings("deprecation")
@Kroll.module(parentModule = UIModule.class)
@Kroll.dynamicApis(properties = { "currentActivity" })
Expand Down Expand Up @@ -257,6 +264,28 @@ public void run()
});
}

@Kroll.method
public ColorProxy getColorResource(Object idOrName)
{
try {
// Color by resource id
if (idOrName instanceof Number) {
int colorResId = ((Number) idOrName).intValue();
@ColorInt int packedColorInt = ContextCompat.getColor(TiApplication.getInstance(), colorResId);
return new ColorProxy(packedColorInt);
}
// Color by name
String colorName = idOrName.toString();
if (TiColorHelper.hasColorResource(colorName)) {
@ColorInt int packedColorInt = TiColorHelper.getColorResource(colorName);
return new ColorProxy(packedColorInt);
}
} catch (Exception e) {
// ignore
}
return null;
}

@Override
public String getApiName()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Appcelerator Titanium Mobile
* Copyright (c) 2020-Present by Axway, Inc. All Rights Reserved.
* Licensed under the terms of the Apache Public License
* Please see the LICENSE included with this distribution for details.
*/
package org.appcelerator.titanium.proxy;

import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.annotations.Kroll;

import androidx.annotation.ColorInt;

@Kroll.proxy
/**
* This is a proxy representation of the Android Color type.
* Refer to <a href="https://developer.android.com/reference/android/graphics/Color">Android Color</a> for more details.
*/
public class ColorProxy extends KrollProxy
{
private final @ColorInt int color;

public ColorProxy(@ColorInt int colorInt)
{
this.color = colorInt;
}

@Kroll.method
public String toHex()
{
// Convert to ARGB hex string.
return String.format(
"#%02X%02X%02X%02X",
this.color >>> 24,
(this.color >>> 16) & 0xFF,
(this.color >>> 8) & 0xFF,
this.color & 0xFF);
}
jquick-axway marked this conversation as resolved.
Show resolved Hide resolved

jquick-axway marked this conversation as resolved.
Show resolved Hide resolved
@Kroll.method
@Override
public String toString()
{
return toHex();
}

@Override
public String getApiName()
{
return "Ti.UI.Color";
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Appcelerator Titanium Mobile
* Copyright (c) 2009-2012 by Appcelerator, Inc. All Rights Reserved.
* Copyright (c) 2009-2020 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Apache Public License
* Please see the LICENSE included with this distribution for details.
*/
Expand All @@ -13,10 +13,15 @@
import java.util.regex.Pattern;

import org.appcelerator.kroll.common.Log;
import org.appcelerator.titanium.TiApplication;

import android.content.res.Resources;
import android.graphics.Color;
import android.os.Build;

import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;

/**
* This class contain utility methods that converts a String color, like "red", into its corresponding RGB/RGBA representation.
*/
Expand Down Expand Up @@ -44,59 +49,87 @@ public class TiColorHelper
*/
public static int parseColor(String value)
{
int color = Color.TRANSPARENT;
if (value != null) {
String lowval = value.trim().toLowerCase();

Matcher m = null;
if ((m = shortHexPattern.matcher(lowval)).matches()) {
StringBuilder sb = new StringBuilder();
sb.append("#");
for (int i = 1; i <= m.groupCount(); i++) {
String s = m.group(i);
sb.append(s).append(s);
}
String newColor = sb.toString();
color = Color.parseColor(newColor);
} else if ((m = rgbPattern.matcher(lowval)).matches()) {
color =
Color.rgb(Integer.valueOf(m.group(1)), Integer.valueOf(m.group(2)), Integer.valueOf(m.group(3)));
} else if ((m = argbPattern.matcher(lowval)).matches()) {
color = Color.argb(Integer.valueOf(m.group(4)), Integer.valueOf(m.group(1)),
Integer.valueOf(m.group(2)), Integer.valueOf(m.group(3)));
} else if ((m = rgbaPattern.matcher(lowval)).matches()) {
color = Color.argb(Math.round(Float.valueOf(m.group(4)) * 255f), Integer.valueOf(m.group(1)),
Integer.valueOf(m.group(2)), Integer.valueOf(m.group(3)));
} else if ((m = floatsPattern.matcher(lowval)).matches()) {
color = Color.argb(
Math.round(Float.valueOf(m.group(4)) * 255f), Math.round(Float.valueOf(m.group(1)) * 255f),
Math.round(Float.valueOf(m.group(2)) * 255f), Math.round(Float.valueOf(m.group(3)) * 255f));
} else {
// Try the parser, will throw illegalArgument if it can't parse it.
try {
// In 4.3, Google introduced some new string color constants and they forgot to
// add the alpha bits to them! This is a temporary workaround
// until they fix it. I've created a Google ticket for this:
// https://code.google.com/p/android/issues/detail?id=58352&thanks=58352
if (Build.VERSION.SDK_INT > 17 && alphaMissingColors.contains(lowval)) {
color = Color.parseColor(lowval) | 0xFF000000;
} else {
color = Color.parseColor(lowval);
}
} catch (IllegalArgumentException e) {
if (colorTable == null) {
buildColorTable();
}

if (colorTable.containsKey(lowval)) {
color = colorTable.get(lowval);
} else {
Log.w(TAG, "Unknown color: " + value);
}
}
if (value == null) {
return Color.TRANSPARENT;
}

String lowval = value.trim().toLowerCase();

Matcher m = null;
if ((m = shortHexPattern.matcher(lowval)).matches()) {
StringBuilder sb = new StringBuilder();
sb.append("#");
for (int i = 1; i <= m.groupCount(); i++) {
String s = m.group(i);
sb.append(s).append(s);
}
String newColor = sb.toString();
return Color.parseColor(newColor);
}
// rgb(int, int, int)
if ((m = rgbPattern.matcher(lowval)).matches()) {
return Color.rgb(Integer.valueOf(m.group(1)), Integer.valueOf(m.group(2)), Integer.valueOf(m.group(3)));
}
// rgba(int, int, int, int)
if ((m = argbPattern.matcher(lowval)).matches()) {
return Color.argb(Integer.valueOf(m.group(4)), Integer.valueOf(m.group(1)),
Integer.valueOf(m.group(2)), Integer.valueOf(m.group(3)));
}
// rgba(int, int, int, float)
if ((m = rgbaPattern.matcher(lowval)).matches()) {
return Color.argb(Math.round(Float.valueOf(m.group(4)) * 255f), Integer.valueOf(m.group(1)),
Integer.valueOf(m.group(2)), Integer.valueOf(m.group(3)));
}
// rgba(float, float, float, float)
if ((m = floatsPattern.matcher(lowval)).matches()) {
return Color.argb(
Math.round(Float.valueOf(m.group(4)) * 255f), Math.round(Float.valueOf(m.group(1)) * 255f),
Math.round(Float.valueOf(m.group(2)) * 255f), Math.round(Float.valueOf(m.group(3)) * 255f));
}

// Ti.Android.R.color or Ti.App.Android.R.color resource (by name)
if (TiColorHelper.hasColorResource(value)) {
try {
return TiColorHelper.getColorResource(value);
} catch (Exception e) {
Log.e(TAG, "Cannot find named color: " + value, e);
}
}
return color;

// Try the parser, will throw illegalArgument if it can't parse it.
try {
// In 4.3, Google introduced some new string color constants and they forgot to
// add the alpha bits to them! This is a temporary workaround
// until they fix it. I've created a Google ticket for this:
// https://code.google.com/p/android/issues/detail?id=58352&thanks=58352
if (Build.VERSION.SDK_INT > 17 && alphaMissingColors.contains(lowval)) {
return Color.parseColor(lowval) | 0xFF000000;
}
return Color.parseColor(lowval);
} catch (IllegalArgumentException e) {
if (colorTable == null) {
buildColorTable();
}

if (colorTable.containsKey(lowval)) {
return colorTable.get(lowval);
}
Log.w(TAG, "Unknown color: " + value);
}
return Color.TRANSPARENT;
}

public static boolean hasColorResource(String colorName)
{
return TiRHelper.hasResource("color." + colorName);
}

public static @ColorInt int getColorResource(String colorName)
throws TiRHelper.ResourceNotFoundException, Resources.NotFoundException
{
int colorResId = TiRHelper.getResource("color." + colorName);
// Now we need to convert it!
return ContextCompat.getColor(TiApplication.getInstance(), colorResId);
}

private static void buildColorTable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,22 @@ public static int getResource(String path) throws ResourceNotFoundException
return getResource(path, true);
}

/**
* Checks if the given resource path exists. Refer to {@link #getResource(String)} for more details.
* @param path the resource's path
* @return whether or not the resource exists in the application's resource bundle.
* @module.api
*/
public static boolean hasResource(String path)
{
try {
int id = TiRHelper.getResource(path);
return id != 0;
} catch (ResourceNotFoundException e) {
return false;
}
}

/**
* @param path path of the resource.
* @return the application resource given its path.
Expand Down
18 changes: 18 additions & 0 deletions apidoc/Titanium/UI/Android/Android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ methods:
See the example for a demonstration, and the official Android Developer documentation for
the [preferences.xml](https://developer.android.com/guide/topics/ui/settings.html#DefiningPrefs)
format.

- name: getColorResource
summary: |
Returns a <Ti.Color> instance for a color defined by the system or user resources (colors.xml)
description: |
This method allows converting a known color resource id or short name to an actual color instance.

The resource id values can be accessed via <Titanium.Android.R.color> and <Titanium.App.Android.R.color> properties.

See the official Android Developer documentation for
the [R.color](https://developer.android.com/reference/android/R.color) constants.
parameters:
- name: resourceIdOrColorName
summary: integer resource id for a color, or the name of the color defined in the application resources (colors.xml).
type: [Number, String]
returns:
- type: Titanium.UI.Color
since: "9.1.0"

properties:
- name: FLAG_TRANSLUCENT_NAVIGATION
Expand Down
13 changes: 10 additions & 3 deletions apidoc/Titanium/UI/Color.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@ description: |
<Titanium.UI.fetchSemanticColor> method, which will return an instance to be passed to UI components.
This instance will automatically "adapt" the receiver to light/dark mode changes.

The only current instance you'd receive this type on Android is through the <Titanium.Android.UI.getColorResource> method.
Note that Android does not currently support passing in instances of this type for UI components/properties!
You must convert to a hex string representation to pass this in to any color properties on Android.

See [UI Element Colors](https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors?language=objc) and
[Standard Colors](https://developer.apple.com/documentation/uikit/uicolor/standard_colors?language=objc) for lists of
named colors to pass to <Titanium.UI.fetchSemanticColor> on iOS.

See [R.color](https://developer.android.com/reference/android/R.color) for a list of system color names to use to pass to
<Titanium.Android.UI.getColorResource> on Android.
extends: Titanium.Proxy
since: "9.1.0"
platforms: [iphone, ipad]
platforms: [iphone, ipad, android]
methods:
- name: toHex
summary: Returns a hex string representation of the color
summary: Returns a hex string representation of the color (in the RRGGBB or AARRGGBB formats)
jquick-axway marked this conversation as resolved.
Show resolved Hide resolved
description: |
The returned value is an uppercase hex representation of the color with a leading '#'. If fully opaque,
the alpha hex values will be omitted. For example, black would return '#000000'. Black with a 50% opacity
would return '#00000080'
would return '#80000000'
returns:
- type: String