Skip to content

Commit

Permalink
feat(android): add Ti.UI.Android.getColorResource(), Ti.UI.Color
Browse files Browse the repository at this point in the history
Also support passing color resource names to color properties
  • Loading branch information
sgtcoolguy committed Jul 6, 2020
1 parent 9c3321b commit d852331
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 65 deletions.
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,61 @@
/**
* 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 android.graphics.Color;

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 Color color;

public ColorProxy(@ColorInt int colorInt)
{
this(Color.valueOf(colorInt));
}

public ColorProxy(Color color)
{
this.color = color;
}

@Kroll.method
public String toHex()
{
if (this.color == null) {
return "#000000";
}
// Padding! If length is less than 8, pad with leading 0s
String hex = Integer.toHexString(this.color.toArgb());
while (hex.length() < 8) {
hex = "0" + hex;
}
return "#" + hex;
}

@Override
public void release()
{
super.release();
this.color = null;
}

@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,65 +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 if (TiRHelper.hasResource("color." + value)) {
try {
color = TiRHelper.getResource("color." + value);
} catch (TiRHelper.ResourceNotFoundException e) {
Log.e(TAG, "Cannot find named color: " + value);
}
} 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
20 changes: 15 additions & 5 deletions common/Resources/ti.internal/extensions/ti/ti.ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,34 @@ if (!isIOS13Plus) {
} catch (error) {
// We should probably throw an Error here (or return a fallback color!)
console.error('Failed to load colors file \'semantic.colors.json\'');
return Color.fallback().toRGBAString();
return Color.fallback().toHex();
}
}

try {
if (!colorset[colorName]) {
return Color.fallback().toRGBAString();
if (OS_ANDROID) {
// if it's not in the semantic colors and we're on Android, it may be a Ti.Android.R.color value
const systemColorId = Ti.Android.R.color[colorName];
if (systemColorId) {
const resourceColor = Ti.UI.Android.getColorResource(systemColorId);
if (resourceColor) {
return resourceColor.toHex();
}
}
}
return Color.fallback().toHex();
}

const entry = colorset[colorName][UI.semanticColorType];
const colorObj = Color.fromSemanticColorsEntry(entry);
// For now, return a string on iOS < 13, Android so we can pass the result directly to the UI property we want to set
// Otherwise we need to modify the Android APIs to accept this faked Ti.UI.Color instance and convert it to it's own internal
// Otherwise we need to modify the Android APIs to accept fake/real Ti.UI.Color instances and convert it to it's own internal
// Color representation
return colorObj.toRGBAString(); // rgba is standard across iOS/Android
return colorObj.toRGBAString(); // If there's an entry, use the more exact rgba function over 8-char ARGB hex. Hard to convert things like 75% alpha properly.
} catch (error) {
console.error(`Failed to lookup color for ${colorName}`);
}
return Color.fallback().toRGBAString();
return Color.fallback().toHex();
};
}

0 comments on commit d852331

Please sign in to comment.