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

fix(android): Fixed crash caused by reading View's "backgroundDisabledColor" if background/border properties are set #10744

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.TiDimension;
import org.appcelerator.titanium.util.TiAnimationBuilder;
import org.appcelerator.titanium.util.TiCast;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.util.TiUrl;
import org.appcelerator.titanium.util.TiUIHelper;
Expand Down Expand Up @@ -1030,50 +1031,49 @@ public String getBackgroundDisabledColor()
{
// Try to get the background drawable if one is available.
TiBackgroundDrawable backgroundDrawable = getOrCreateView().getBackground();
// Guard for views without color state backgrounds.
if (backgroundDrawable == null) {
return null;
} else {
try {
// Get the backgroundDrawable background as a StateListDrawable.
StateListDrawable stateListDrawable = ((StateListDrawable) backgroundDrawable.getBackground());
// Get the reflection methods.
Method getStateDrawableIndexMethod =
StateListDrawable.class.getMethod("getStateDrawableIndex", int[].class);
Method getStateDrawableMethod = StateListDrawable.class.getMethod("getStateDrawable", int.class);
// Get the disabled state's (as defined in TiUIHelper) index.
int index =
(int) getStateDrawableIndexMethod.invoke(stateListDrawable, TiUIHelper.BACKGROUND_DISABLED_STATE);
// Get the drawable at the index.
Drawable drawable = (Drawable) getStateDrawableMethod.invoke(stateListDrawable, index);
// Try to get the 0 index of the result.
if (drawable instanceof LayerDrawable) {
Drawable drawableFromLayer = ((LayerDrawable) drawable).getDrawable(0);
// Cast it as a ColorDrawable.
if (drawableFromLayer instanceof ColorDrawable) {
// Transcript the color int to HexString.
String strColor =
String.format("#%08X", 0xFFFFFFFF & ((ColorDrawable) drawableFromLayer).getColor());
return strColor;
} else {
Log.w(TAG, "Background drawable of unexpected type. Expected - ColorDrawable. Found - "
+ drawableFromLayer.getClass().toString());
return null;
}
}

// Get the backgroundDrawable background as a StateListDrawable.
StateListDrawable stateListDrawable = TiCast.as(backgroundDrawable.getBackground(), StateListDrawable.class);
if (stateListDrawable == null) {
return null;
}

try {
// Get the reflection methods.
Method getStateDrawableIndexMethod =
StateListDrawable.class.getMethod("getStateDrawableIndex", int[].class);
Method getStateDrawableMethod = StateListDrawable.class.getMethod("getStateDrawable", int.class);
// Get the disabled state's (as defined in TiUIHelper) index.
int index =
(int) getStateDrawableIndexMethod.invoke(stateListDrawable, TiUIHelper.BACKGROUND_DISABLED_STATE);
// Get the drawable at the index.
Drawable drawable = (Drawable) getStateDrawableMethod.invoke(stateListDrawable, index);
// Try to get the 0 index of the result.
if (drawable instanceof LayerDrawable) {
Drawable drawableFromLayer = ((LayerDrawable) drawable).getDrawable(0);
// Cast it as a ColorDrawable.
if (drawableFromLayer instanceof ColorDrawable) {
// Transcript the color int to HexString.
String strColor =
String.format("#%08X", 0xFFFFFFFF & ((ColorDrawable) drawableFromLayer).getColor());
return strColor;
} else {
Log.w(TAG, "Background drawable of unexpected type. Expected - LayerDrawable. Found - "
+ drawable.getClass().toString());
Log.w(TAG, "Background drawable of unexpected type. Expected - ColorDrawable. Found - "
+ drawableFromLayer.getClass().toString());
return null;
}
} catch (NoSuchMethodException e) {
Log.w(TAG, "Unable to get a method for reflection.");
} catch (IllegalAccessException e) {
Log.w(TAG, "Unable to access a method for reflection.");
} catch (InvocationTargetException e) {
Log.w(TAG, "Unable to invoke a method for reflection.");
} else {
Log.w(TAG, "Background drawable of unexpected type. Expected - LayerDrawable. Found - "
+ drawable.getClass().toString());
return null;
}
return null;
} catch (Exception e) {
Log.w(TAG, "Unable access disabled background drawable via reflection.");
}
return null;
}

public void setParent(TiViewProxy parent)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Appcelerator Titanium Mobile
* Copyright (c) 2019 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.util;

/**
* Provides a safe means of casting an object via an as() method, similar to how the "as" keyword is
* used in C#, Swift, Rust, etc. This means if the object cannot be cast to the given type, then
* the as() method will return null.
* <p>
* Example Usage:
* <pre>
* String result;
*
* result = TiCast.as("Test", String.class);
* if (result != null) {
* // Was safely cast to a String type.
* }
*
* result = TiCast.as(new Integer(1), String.class);
* if (result == null) {
* // Will be null since Integer cannot be cast to a String.
* }
* </pre>
*/
public class TiCast
{
/** Object value boxed by this class to be casted by the as() method. Can be null. */
private Object value;

/**
* Private constructor used to box a given object value to be casted later.
* @param value Object value to be casted by the as() method later. Can be null.
*/
private TiCast(Object value)
{
this.value = value;
}

/**
* Gets the object value given to the TiCast.from() static method.
* @return Returns the object value stored by this instance. Can be null.
*/
public Object getObject()
{
return this.value;
}

/**
* Safely casts the stored object to the given type.
* @param type The class type to cast the 1st argument type such as "String.class", "List.class", etc.
* @return
* Returns the object casted to the given type if successful.
* <p>
* Returns null if the given object cannot be cast to the given type or if given a null argument.
*/
public <T> T as(Class<T> type)
{
return TiCast.as(this.value, type);
}

/**
* Safely casts the given object to the given type.
* @param value The object to be cast. Can be null.
* @param type The class type to cast the 1st argument type such as "String.class", "List.class", etc.
* @return
* Returns the object casted to the given type if successful.
* <p>
* Returns null if the given object cannot be cast to the given type or if given a null argument.
*/
public static <T> T as(Object value, Class<T> type)
{
if ((value != null) && (type != null)) {
if (type.isInstance(value)) {
return (T) value;
}
}
return null;
}

/**
* Boxes the given object value and returns a "TiCast" instance type.
* Returned type can then be used to call the as() instance method.
* @param value The object value to be casted later by the TiCast instance's as() method. Can be null.
* @return Returns a new TiCast instance whose getObject() method will return the given "value" argument.
*/
public static TiCast from(Object value)
{
return new TiCast(value);
}
}