Merge "LayoutLib: extract resource resolution into its own class." into honeycomb
This commit is contained in:
committed by
Android (Google) Code Review
commit
87ab45c0fe
@@ -20,10 +20,11 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := \
|
||||
kxml2-2.3.0 \
|
||||
layoutlib_api-prebuilt \
|
||||
ninepatch-prebuilt
|
||||
layoutlib_api-prebuilt
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := temp_layoutlib
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
temp_layoutlib \
|
||||
ninepatch-prebuilt
|
||||
|
||||
LOCAL_MODULE := layoutlib
|
||||
|
||||
|
||||
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.ide.common.resources;
|
||||
|
||||
import com.android.ide.common.rendering.api.LayoutLog;
|
||||
import com.android.ide.common.rendering.api.ResourceValue;
|
||||
import com.android.ide.common.rendering.api.StyleResourceValue;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ResourceResolver {
|
||||
|
||||
public final static String RES_ANIMATOR = "animator";
|
||||
public final static String RES_STYLE = "style";
|
||||
public final static String RES_ATTR = "attr";
|
||||
public final static String RES_DIMEN = "dimen";
|
||||
public final static String RES_DRAWABLE = "drawable";
|
||||
public final static String RES_COLOR = "color";
|
||||
public final static String RES_LAYOUT = "layout";
|
||||
public final static String RES_STRING = "string";
|
||||
public final static String RES_ID = "id";
|
||||
|
||||
public final static String REFERENCE_NULL = "@null";
|
||||
|
||||
private final static String REFERENCE_STYLE = RES_STYLE + "/";
|
||||
private final static String PREFIX_ANDROID_RESOURCE_REF = "@android:";
|
||||
private final static String PREFIX_RESOURCE_REF = "@";
|
||||
private final static String PREFIX_ANDROID_THEME_REF = "?android:";
|
||||
private final static String PREFIX_THEME_REF = "?";
|
||||
private final static String PREFIX_ANDROID = "android:";
|
||||
|
||||
|
||||
private final IFrameworkResourceIdProvider mFrameworkProvider;
|
||||
private final Map<String, Map<String, ResourceValue>> mProjectResources;
|
||||
private final Map<String, Map<String, ResourceValue>> mFrameworkResources;
|
||||
private final LayoutLog mLogger;
|
||||
|
||||
private final Map<StyleResourceValue, StyleResourceValue> mStyleInheritanceMap =
|
||||
new HashMap<StyleResourceValue, StyleResourceValue>();
|
||||
private StyleResourceValue mTheme;
|
||||
|
||||
public interface IFrameworkResourceIdProvider {
|
||||
Integer getId(String resType, String resName);
|
||||
}
|
||||
|
||||
private ResourceResolver(
|
||||
IFrameworkResourceIdProvider provider,
|
||||
Map<String, Map<String, ResourceValue>> projectResources,
|
||||
Map<String, Map<String, ResourceValue>> frameworkResources,
|
||||
LayoutLog logger) {
|
||||
mFrameworkProvider = provider;
|
||||
mProjectResources = projectResources;
|
||||
mFrameworkResources = frameworkResources;
|
||||
mLogger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ResourceResolver object.
|
||||
*
|
||||
* @param IFrameworkResourceIdProvider an optional framework resource ID provider
|
||||
* @param projectResources the project resources.
|
||||
* @param frameworkResources the framework resources.
|
||||
* @param themeName the name of the current theme.
|
||||
* @param isProjectTheme Is this a project theme?
|
||||
* @return
|
||||
*/
|
||||
public static ResourceResolver create(
|
||||
IFrameworkResourceIdProvider provider,
|
||||
Map<String, Map<String, ResourceValue>> projectResources,
|
||||
Map<String, Map<String, ResourceValue>> frameworkResources,
|
||||
String themeName, boolean isProjectTheme, LayoutLog logger) {
|
||||
|
||||
ResourceResolver resolver = new ResourceResolver(provider,
|
||||
projectResources, frameworkResources,
|
||||
logger);
|
||||
|
||||
resolver.computeStyleMaps(themeName, isProjectTheme);
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
public StyleResourceValue getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a framework resource by type and name. The returned resource is resolved.
|
||||
* @param resourceType the type of the resource
|
||||
* @param resourceName the name of the resource
|
||||
*/
|
||||
public ResourceValue getFrameworkResource(String resourceType, String resourceName) {
|
||||
return getResource(resourceType, resourceName, mFrameworkResources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a project resource by type and name. The returned resource is resolved.
|
||||
* @param resourceType the type of the resource
|
||||
* @param resourceName the name of the resource
|
||||
*/
|
||||
public ResourceValue getProjectResource(String resourceType, String resourceName) {
|
||||
return getResource(resourceType, resourceName, mProjectResources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourceValue} matching a given name in the current theme. If the
|
||||
* item is not directly available in the theme, the method looks in its parent theme.
|
||||
*
|
||||
* @param itemName the name of the item to search for.
|
||||
* @return the {@link ResourceValue} object or <code>null</code>
|
||||
*/
|
||||
public ResourceValue findItemInTheme(String itemName) {
|
||||
if (mTheme != null) {
|
||||
return findItemInStyle(mTheme, itemName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourceValue} matching a given name in a given style. If the
|
||||
* item is not directly available in the style, the method looks in its parent style.
|
||||
*
|
||||
* @param style the style to search in
|
||||
* @param itemName the name of the item to search for.
|
||||
* @return the {@link ResourceValue} object or <code>null</code>
|
||||
*/
|
||||
public ResourceValue findItemInStyle(StyleResourceValue style, String itemName) {
|
||||
ResourceValue item = style.findValue(itemName);
|
||||
|
||||
// if we didn't find it, we look in the parent style (if applicable)
|
||||
if (item == null && mStyleInheritanceMap != null) {
|
||||
StyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
|
||||
if (parentStyle != null) {
|
||||
return findItemInStyle(parentStyle, itemName);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for, and returns a {@link ResourceValue} by its reference.
|
||||
* <p/>
|
||||
* The reference format can be:
|
||||
* <pre>@resType/resName</pre>
|
||||
* <pre>@android:resType/resName</pre>
|
||||
* <pre>@resType/android:resName</pre>
|
||||
* <pre>?resType/resName</pre>
|
||||
* <pre>?android:resType/resName</pre>
|
||||
* <pre>?resType/android:resName</pre>
|
||||
* Any other string format will return <code>null</code>.
|
||||
* <p/>
|
||||
* The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method
|
||||
* only support the android namespace.
|
||||
*
|
||||
* @param reference the resource reference to search for.
|
||||
* @param forceFrameworkOnly if true all references are considered to be toward framework
|
||||
* resource even if the reference does not include the android: prefix.
|
||||
* @return a {@link ResourceValue} or <code>null</code>.
|
||||
*/
|
||||
public ResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
|
||||
if (reference == null) {
|
||||
return null;
|
||||
}
|
||||
if (reference.startsWith(PREFIX_THEME_REF)) {
|
||||
// no theme? no need to go further!
|
||||
if (mTheme == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean frameworkOnly = false;
|
||||
|
||||
// eliminate the prefix from the string
|
||||
if (reference.startsWith(PREFIX_ANDROID_THEME_REF)) {
|
||||
frameworkOnly = true;
|
||||
reference = reference.substring(PREFIX_ANDROID_THEME_REF.length());
|
||||
} else {
|
||||
reference = reference.substring(PREFIX_THEME_REF.length());
|
||||
}
|
||||
|
||||
// at this point, value can contain type/name (drawable/foo for instance).
|
||||
// split it to make sure.
|
||||
String[] segments = reference.split("\\/");
|
||||
|
||||
// we look for the referenced item name.
|
||||
String referenceName = null;
|
||||
|
||||
if (segments.length == 2) {
|
||||
// there was a resType in the reference. If it's attr, we ignore it
|
||||
// else, we assert for now.
|
||||
if (RES_ATTR.equals(segments[0])) {
|
||||
referenceName = segments[1];
|
||||
} else {
|
||||
// At this time, no support for ?type/name where type is not "attr"
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// it's just an item name.
|
||||
referenceName = segments[0];
|
||||
}
|
||||
|
||||
// now we look for android: in the referenceName in order to support format
|
||||
// such as: ?attr/android:name
|
||||
if (referenceName.startsWith(PREFIX_ANDROID)) {
|
||||
frameworkOnly = true;
|
||||
referenceName = referenceName.substring(PREFIX_ANDROID.length());
|
||||
}
|
||||
|
||||
// Now look for the item in the theme, starting with the current one.
|
||||
if (frameworkOnly) {
|
||||
// FIXME for now we do the same as if it didn't specify android:
|
||||
return findItemInStyle(mTheme, referenceName);
|
||||
}
|
||||
|
||||
return findItemInStyle(mTheme, referenceName);
|
||||
} else if (reference.startsWith(PREFIX_RESOURCE_REF)) {
|
||||
boolean frameworkOnly = false;
|
||||
|
||||
// check for the specific null reference value.
|
||||
if (REFERENCE_NULL.equals(reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Eliminate the prefix from the string.
|
||||
if (reference.startsWith(PREFIX_ANDROID_RESOURCE_REF)) {
|
||||
frameworkOnly = true;
|
||||
reference = reference.substring(
|
||||
PREFIX_ANDROID_RESOURCE_REF.length());
|
||||
} else {
|
||||
reference = reference.substring(PREFIX_RESOURCE_REF.length());
|
||||
}
|
||||
|
||||
// at this point, value contains type/[android:]name (drawable/foo for instance)
|
||||
String[] segments = reference.split("\\/");
|
||||
|
||||
// now we look for android: in the resource name in order to support format
|
||||
// such as: @drawable/android:name
|
||||
if (segments[1].startsWith(PREFIX_ANDROID)) {
|
||||
frameworkOnly = true;
|
||||
segments[1] = segments[1].substring(PREFIX_ANDROID.length());
|
||||
}
|
||||
|
||||
return findResValue(segments[0], segments[1],
|
||||
forceFrameworkOnly ? true :frameworkOnly);
|
||||
}
|
||||
|
||||
// Looks like the value didn't reference anything. Return null.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the value of a resource, if the value references a theme or resource value.
|
||||
* <p/>
|
||||
* This method ensures that it returns a {@link ResourceValue} object that does not
|
||||
* reference another resource.
|
||||
* If the resource cannot be resolved, it returns <code>null</code>.
|
||||
* <p/>
|
||||
* If a value that does not need to be resolved is given, the method will return a new
|
||||
* instance of {@link ResourceValue} that contains the input value.
|
||||
*
|
||||
* @param type the type of the resource
|
||||
* @param name the name of the attribute containing this value.
|
||||
* @param value the resource value, or reference to resolve
|
||||
* @param isFrameworkValue whether the value is a framework value.
|
||||
*
|
||||
* @return the resolved resource value or <code>null</code> if it failed to resolve it.
|
||||
*/
|
||||
public ResourceValue resolveValue(String type, String name, String value,
|
||||
boolean isFrameworkValue) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the ResourceValue referenced by this value
|
||||
ResourceValue resValue = findResValue(value, isFrameworkValue);
|
||||
|
||||
// if resValue is null, but value is not null, this means it was not a reference.
|
||||
// we return the name/value wrapper in a ResourceValue. the isFramework flag doesn't
|
||||
// matter.
|
||||
if (resValue == null) {
|
||||
return new ResourceValue(type, name, value, isFrameworkValue);
|
||||
}
|
||||
|
||||
// we resolved a first reference, but we need to make sure this isn't a reference also.
|
||||
return resolveResValue(resValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourceValue} referenced by the value of <var>value</var>.
|
||||
* <p/>
|
||||
* This method ensures that it returns a {@link ResourceValue} object that does not
|
||||
* reference another resource.
|
||||
* If the resource cannot be resolved, it returns <code>null</code>.
|
||||
* <p/>
|
||||
* If a value that does not need to be resolved is given, the method will return the input
|
||||
* value.
|
||||
*
|
||||
* @param value the value containing the reference to resolve.
|
||||
* @return a {@link ResourceValue} object or <code>null</code>
|
||||
*/
|
||||
public ResourceValue resolveResValue(ResourceValue value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if the resource value is a style, we simply return it.
|
||||
if (value instanceof StyleResourceValue) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// else attempt to find another ResourceValue referenced by this one.
|
||||
ResourceValue resolvedValue = findResValue(value.getValue(), value.isFramework());
|
||||
|
||||
// if the value did not reference anything, then we simply return the input value
|
||||
if (resolvedValue == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// otherwise, we attempt to resolve this new value as well
|
||||
return resolveResValue(resolvedValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Searches for, and returns a {@link ResourceValue} by its name, and type.
|
||||
* @param resType the type of the resource
|
||||
* @param resName the name of the resource
|
||||
* @param frameworkOnly if <code>true</code>, the method does not search in the
|
||||
* project resources
|
||||
*/
|
||||
private ResourceValue findResValue(String resType, String resName, boolean frameworkOnly) {
|
||||
// map of ResouceValue for the given type
|
||||
Map<String, ResourceValue> typeMap;
|
||||
|
||||
// if allowed, search in the project resources first.
|
||||
if (frameworkOnly == false) {
|
||||
typeMap = mProjectResources.get(resType);
|
||||
if (typeMap != null) {
|
||||
ResourceValue item = typeMap.get(resName);
|
||||
if (item != null) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now search in the framework resources.
|
||||
typeMap = mFrameworkResources.get(resType);
|
||||
if (typeMap != null) {
|
||||
ResourceValue item = typeMap.get(resName);
|
||||
if (item != null) {
|
||||
return item;
|
||||
}
|
||||
|
||||
// if it was not found and the type is an id, it is possible that the ID was
|
||||
// generated dynamically when compiling the framework resources.
|
||||
// Look for it in the R map.
|
||||
if (mFrameworkProvider != null && RES_ID.equals(resType)) {
|
||||
if (mFrameworkProvider.getId(resType, resName) != null) {
|
||||
return new ResourceValue(resType, resName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find the resource anywhere.
|
||||
// This is normal if the resource is an ID that is generated automatically.
|
||||
// For other resources, we output a warning
|
||||
if ("+id".equals(resType) == false && "+android:id".equals(resType) == false) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
mLogger.warning(LayoutLog.TAG_RESOURCES_RESOLVE,
|
||||
"Couldn't resolve resource @" +
|
||||
(frameworkOnly ? "android:" : "") + resType + "/" + resName,
|
||||
new ResourceValue(resType, resName, frameworkOnly));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ResourceValue getResource(String resourceType, String resourceName,
|
||||
Map<String, Map<String, ResourceValue>> resourceRepository) {
|
||||
Map<String, ResourceValue> typeMap = resourceRepository.get(resourceType);
|
||||
if (typeMap != null) {
|
||||
ResourceValue item = typeMap.get(resourceName);
|
||||
if (item != null) {
|
||||
item = resolveResValue(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find the resource anywhere.
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute style information from the given list of style for the project and framework.
|
||||
* @param themeName the name of the current theme.
|
||||
* @param isProjectTheme Is this a project theme?
|
||||
*/
|
||||
private void computeStyleMaps(String themeName, boolean isProjectTheme) {
|
||||
Map<String, ResourceValue> projectStyleMap = mProjectResources.get(RES_STYLE);
|
||||
Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get(RES_STYLE);
|
||||
|
||||
if (projectStyleMap != null && frameworkStyleMap != null) {
|
||||
// first, get the theme
|
||||
ResourceValue theme = null;
|
||||
|
||||
// project theme names have been prepended with a *
|
||||
if (isProjectTheme) {
|
||||
theme = projectStyleMap.get(themeName);
|
||||
} else {
|
||||
theme = frameworkStyleMap.get(themeName);
|
||||
}
|
||||
|
||||
if (theme instanceof StyleResourceValue) {
|
||||
// compute the inheritance map for both the project and framework styles
|
||||
computeStyleInheritance(projectStyleMap.values(), projectStyleMap,
|
||||
frameworkStyleMap);
|
||||
|
||||
// Compute the style inheritance for the framework styles/themes.
|
||||
// Since, for those, the style parent values do not contain 'android:'
|
||||
// we want to force looking in the framework style only to avoid using
|
||||
// similarly named styles from the project.
|
||||
// To do this, we pass null in lieu of the project style map.
|
||||
computeStyleInheritance(frameworkStyleMap.values(), null /*inProjectStyleMap */,
|
||||
frameworkStyleMap);
|
||||
|
||||
mTheme = (StyleResourceValue) theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compute the parent style for all the styles in a given list.
|
||||
* @param styles the styles for which we compute the parent.
|
||||
* @param inProjectStyleMap the map of project styles.
|
||||
* @param inFrameworkStyleMap the map of framework styles.
|
||||
* @param outInheritanceMap the map of style inheritance. This is filled by the method.
|
||||
*/
|
||||
private void computeStyleInheritance(Collection<ResourceValue> styles,
|
||||
Map<String, ResourceValue> inProjectStyleMap,
|
||||
Map<String, ResourceValue> inFrameworkStyleMap) {
|
||||
for (ResourceValue value : styles) {
|
||||
if (value instanceof StyleResourceValue) {
|
||||
StyleResourceValue style = (StyleResourceValue)value;
|
||||
StyleResourceValue parentStyle = null;
|
||||
|
||||
// first look for a specified parent.
|
||||
String parentName = style.getParentStyle();
|
||||
|
||||
// no specified parent? try to infer it from the name of the style.
|
||||
if (parentName == null) {
|
||||
parentName = getParentName(value.getName());
|
||||
}
|
||||
|
||||
if (parentName != null) {
|
||||
parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
|
||||
|
||||
if (parentStyle != null) {
|
||||
mStyleInheritanceMap.put(style, parentStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the name of the parent style, or <code>null</code> if the style is a root style.
|
||||
*/
|
||||
private String getParentName(String styleName) {
|
||||
int index = styleName.lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
return styleName.substring(0, index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for and returns the {@link StyleResourceValue} from a given name.
|
||||
* <p/>The format of the name can be:
|
||||
* <ul>
|
||||
* <li>[android:]<name></li>
|
||||
* <li>[android:]style/<name></li>
|
||||
* <li>@[android:]style/<name></li>
|
||||
* </ul>
|
||||
* @param parentName the name of the style.
|
||||
* @param inProjectStyleMap the project style map. Can be <code>null</code>
|
||||
* @param inFrameworkStyleMap the framework style map.
|
||||
* @return The matching {@link StyleResourceValue} object or <code>null</code> if not found.
|
||||
*/
|
||||
private StyleResourceValue getStyle(String parentName,
|
||||
Map<String, ResourceValue> inProjectStyleMap,
|
||||
Map<String, ResourceValue> inFrameworkStyleMap) {
|
||||
boolean frameworkOnly = false;
|
||||
|
||||
String name = parentName;
|
||||
|
||||
// remove the useless @ if it's there
|
||||
if (name.startsWith(PREFIX_RESOURCE_REF)) {
|
||||
name = name.substring(PREFIX_RESOURCE_REF.length());
|
||||
}
|
||||
|
||||
// check for framework identifier.
|
||||
if (name.startsWith(PREFIX_ANDROID)) {
|
||||
frameworkOnly = true;
|
||||
name = name.substring(PREFIX_ANDROID.length());
|
||||
}
|
||||
|
||||
// at this point we could have the format <type>/<name>. we want only the name as long as
|
||||
// the type is style.
|
||||
if (name.startsWith(REFERENCE_STYLE)) {
|
||||
name = name.substring(REFERENCE_STYLE.length());
|
||||
} else if (name.indexOf('/') != -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ResourceValue parent = null;
|
||||
|
||||
// if allowed, search in the project resources.
|
||||
if (frameworkOnly == false && inProjectStyleMap != null) {
|
||||
parent = inProjectStyleMap.get(name);
|
||||
}
|
||||
|
||||
// if not found, then look in the framework resources.
|
||||
if (parent == null) {
|
||||
parent = inFrameworkStyleMap.get(name);
|
||||
}
|
||||
|
||||
// make sure the result is the proper class type and return it.
|
||||
if (parent instanceof StyleResourceValue) {
|
||||
return (StyleResourceValue)parent;
|
||||
}
|
||||
|
||||
assert false;
|
||||
mLogger.error(LayoutLog.TAG_RESOURCES_RESOLVE,
|
||||
String.format("Unable to resolve parent style name: %s", parentName),
|
||||
null /*data*/);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -41,25 +41,6 @@ public class BridgeConstants {
|
||||
|
||||
public final static String R = "com.android.internal.R";
|
||||
|
||||
public final static String PREFIX_ANDROID_RESOURCE_REF = "@android:";
|
||||
public final static String PREFIX_RESOURCE_REF = "@";
|
||||
public final static String PREFIX_ANDROID_THEME_REF = "?android:";
|
||||
public final static String PREFIX_THEME_REF = "?";
|
||||
|
||||
public final static String PREFIX_ANDROID = "android:";
|
||||
|
||||
public final static String RES_ANIMATOR = "animator";
|
||||
public final static String RES_STYLE = "style";
|
||||
public final static String RES_ATTR = "attr";
|
||||
public final static String RES_DIMEN = "dimen";
|
||||
public final static String RES_DRAWABLE = "drawable";
|
||||
public final static String RES_COLOR = "color";
|
||||
public final static String RES_LAYOUT = "layout";
|
||||
public final static String RES_STRING = "string";
|
||||
public final static String RES_ID = "id";
|
||||
|
||||
public final static String REFERENCE_STYLE = RES_STYLE + "/";
|
||||
public final static String REFERENCE_NULL = "@null";
|
||||
|
||||
public final static String MATCH_PARENT = "match_parent";
|
||||
public final static String FILL_PARENT = "fill_parent";
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.android.ide.common.rendering.api.IProjectCallback;
|
||||
import com.android.ide.common.rendering.api.LayoutLog;
|
||||
import com.android.ide.common.rendering.api.ResourceValue;
|
||||
import com.android.ide.common.rendering.api.StyleResourceValue;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.layoutlib.bridge.BridgeConstants;
|
||||
import com.android.layoutlib.bridge.impl.Stack;
|
||||
@@ -76,12 +77,9 @@ public final class BridgeContext extends Activity {
|
||||
private Resources mResources;
|
||||
private Theme mTheme;
|
||||
private final HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>();
|
||||
private final StyleResourceValue mThemeValues;
|
||||
private final Object mProjectKey;
|
||||
private final DisplayMetrics mMetrics;
|
||||
private final Map<String, Map<String, ResourceValue>> mProjectResources;
|
||||
private final Map<String, Map<String, ResourceValue>> mFrameworkResources;
|
||||
private final Map<StyleResourceValue, StyleResourceValue> mStyleInheritanceMap;
|
||||
private final ResourceResolver mResourceResolver;
|
||||
|
||||
private final Map<Object, Map<String, String>> mDefaultPropMaps =
|
||||
new IdentityHashMap<Object, Map<String,String>>();
|
||||
@@ -116,19 +114,13 @@ public final class BridgeContext extends Activity {
|
||||
* @param projectCallback
|
||||
*/
|
||||
public BridgeContext(Object projectKey, DisplayMetrics metrics,
|
||||
StyleResourceValue currentTheme,
|
||||
Map<String, Map<String, ResourceValue>> projectResources,
|
||||
Map<String, Map<String, ResourceValue>> frameworkResources,
|
||||
Map<StyleResourceValue, StyleResourceValue> styleInheritanceMap,
|
||||
ResourceResolver resourceResolver,
|
||||
IProjectCallback projectCallback) {
|
||||
mProjectKey = projectKey;
|
||||
mMetrics = metrics;
|
||||
mProjectCallback = projectCallback;
|
||||
|
||||
mThemeValues = currentTheme;
|
||||
mProjectResources = projectResources;
|
||||
mFrameworkResources = frameworkResources;
|
||||
mStyleInheritanceMap = styleInheritanceMap;
|
||||
mResourceResolver = resourceResolver;
|
||||
|
||||
mFragments.mCurState = Fragment.CREATED;
|
||||
mFragments.mActivity = this;
|
||||
@@ -180,6 +172,10 @@ public final class BridgeContext extends Activity {
|
||||
return mProjectCallback;
|
||||
}
|
||||
|
||||
public ResourceResolver getResolver() {
|
||||
return mResourceResolver;
|
||||
}
|
||||
|
||||
public Map<String, String> getDefaultPropMap(Object key) {
|
||||
return mDefaultPropMaps.get(key);
|
||||
}
|
||||
@@ -265,7 +261,7 @@ public final class BridgeContext extends Activity {
|
||||
|
||||
@Override
|
||||
public final TypedArray obtainStyledAttributes(int[] attrs) {
|
||||
return createStyleBasedTypedArray(mThemeValues, attrs);
|
||||
return createStyleBasedTypedArray(mResourceResolver.getTheme(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -362,7 +358,8 @@ public final class BridgeContext extends Activity {
|
||||
customStyle = set.getAttributeValue(null /* namespace*/, "style");
|
||||
}
|
||||
if (customStyle != null) {
|
||||
ResourceValue item = findResValue(customStyle, false /*forceFrameworkOnly*/);
|
||||
ResourceValue item = mResourceResolver.findResValue(customStyle,
|
||||
false /*forceFrameworkOnly*/);
|
||||
|
||||
if (item instanceof StyleResourceValue) {
|
||||
defStyleValues = (StyleResourceValue)item;
|
||||
@@ -378,22 +375,21 @@ public final class BridgeContext extends Activity {
|
||||
}
|
||||
|
||||
// look for the style in the current theme, and its parent:
|
||||
if (mThemeValues != null) {
|
||||
ResourceValue item = findItemInStyle(mThemeValues, defStyleName);
|
||||
ResourceValue item = mResourceResolver.findItemInTheme(defStyleName);
|
||||
|
||||
if (item != null) {
|
||||
// item is a reference to a style entry. Search for it.
|
||||
item = findResValue(item.getValue(), false /*forceFrameworkOnly*/);
|
||||
if (item != null) {
|
||||
// item is a reference to a style entry. Search for it.
|
||||
item = mResourceResolver.findResValue(item.getValue(),
|
||||
false /*forceFrameworkOnly*/);
|
||||
|
||||
if (item instanceof StyleResourceValue) {
|
||||
defStyleValues = (StyleResourceValue)item;
|
||||
}
|
||||
} else {
|
||||
Bridge.getLog().error(null,
|
||||
String.format(
|
||||
"Failed to find style '%s' in current theme", defStyleName),
|
||||
null /*data*/);
|
||||
if (item instanceof StyleResourceValue) {
|
||||
defStyleValues = (StyleResourceValue)item;
|
||||
}
|
||||
} else {
|
||||
Bridge.getLog().error(null,
|
||||
String.format(
|
||||
"Failed to find style '%s' in current theme", defStyleName),
|
||||
null /*data*/);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,13 +421,13 @@ public final class BridgeContext extends Activity {
|
||||
|
||||
// look for the value in the defStyle first (and its parent if needed)
|
||||
if (defStyleValues != null) {
|
||||
resValue = findItemInStyle(defStyleValues, name);
|
||||
resValue = mResourceResolver.findItemInStyle(defStyleValues, name);
|
||||
}
|
||||
|
||||
// if the item is not present in the defStyle, we look in the main theme (and
|
||||
// its parent themes)
|
||||
if (resValue == null && mThemeValues != null) {
|
||||
resValue = findItemInStyle(mThemeValues, name);
|
||||
if (resValue == null) {
|
||||
resValue = mResourceResolver.findItemInTheme(name);
|
||||
}
|
||||
|
||||
// if we found a value, we make sure this doesn't reference another value.
|
||||
@@ -442,14 +438,15 @@ public final class BridgeContext extends Activity {
|
||||
defaultPropMap.put(name, resValue.getValue());
|
||||
}
|
||||
|
||||
resValue = resolveResValue(resValue);
|
||||
resValue = mResourceResolver.resolveResValue(resValue);
|
||||
}
|
||||
|
||||
ta.bridgeSetValue(index, name, resValue);
|
||||
} else {
|
||||
// there is a value in the XML, but we need to resolve it in case it's
|
||||
// referencing another resource or a theme value.
|
||||
ta.bridgeSetValue(index, name, resolveValue(null, name, value, isPlatformFile));
|
||||
ta.bridgeSetValue(index, name,
|
||||
mResourceResolver.resolveValue(null, name, value, isPlatformFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -487,10 +484,10 @@ public final class BridgeContext extends Activity {
|
||||
String name = styleAttribute.getValue();
|
||||
|
||||
// get the value from the style, or its parent styles.
|
||||
ResourceValue resValue = findItemInStyle(style, name);
|
||||
ResourceValue resValue = mResourceResolver.findItemInStyle(style, name);
|
||||
|
||||
// resolve it to make sure there are no references left.
|
||||
ta.bridgeSetValue(index, name, resolveResValue(resValue));
|
||||
ta.bridgeSetValue(index, name, mResourceResolver.resolveResValue(resValue));
|
||||
}
|
||||
|
||||
ta.sealArray();
|
||||
@@ -499,295 +496,6 @@ public final class BridgeContext extends Activity {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves the value of a resource, if the value references a theme or resource value.
|
||||
* <p/>
|
||||
* This method ensures that it returns a {@link ResourceValue} object that does not
|
||||
* reference another resource.
|
||||
* If the resource cannot be resolved, it returns <code>null</code>.
|
||||
* <p/>
|
||||
* If a value that does not need to be resolved is given, the method will return a new
|
||||
* instance of {@link ResourceValue} that contains the input value.
|
||||
*
|
||||
* @param type the type of the resource
|
||||
* @param name the name of the attribute containing this value.
|
||||
* @param value the resource value, or reference to resolve
|
||||
* @param isFrameworkValue whether the value is a framework value.
|
||||
*
|
||||
* @return the resolved resource value or <code>null</code> if it failed to resolve it.
|
||||
*/
|
||||
private ResourceValue resolveValue(String type, String name, String value,
|
||||
boolean isFrameworkValue) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get the ResourceValue referenced by this value
|
||||
ResourceValue resValue = findResValue(value, isFrameworkValue);
|
||||
|
||||
// if resValue is null, but value is not null, this means it was not a reference.
|
||||
// we return the name/value wrapper in a ResourceValue. the isFramework flag doesn't
|
||||
// matter.
|
||||
if (resValue == null) {
|
||||
return new ResourceValue(type, name, value, isFrameworkValue);
|
||||
}
|
||||
|
||||
// we resolved a first reference, but we need to make sure this isn't a reference also.
|
||||
return resolveResValue(resValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourceValue} referenced by the value of <var>value</var>.
|
||||
* <p/>
|
||||
* This method ensures that it returns a {@link ResourceValue} object that does not
|
||||
* reference another resource.
|
||||
* If the resource cannot be resolved, it returns <code>null</code>.
|
||||
* <p/>
|
||||
* If a value that does not need to be resolved is given, the method will return the input
|
||||
* value.
|
||||
*
|
||||
* @param value the value containing the reference to resolve.
|
||||
* @return a {@link ResourceValue} object or <code>null</code>
|
||||
*/
|
||||
public ResourceValue resolveResValue(ResourceValue value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if the resource value is a style, we simply return it.
|
||||
if (value instanceof StyleResourceValue) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// else attempt to find another ResourceValue referenced by this one.
|
||||
ResourceValue resolvedValue = findResValue(value.getValue(), value.isFramework());
|
||||
|
||||
// if the value did not reference anything, then we simply return the input value
|
||||
if (resolvedValue == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// otherwise, we attempt to resolve this new value as well
|
||||
return resolveResValue(resolvedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for, and returns a {@link ResourceValue} by its reference.
|
||||
* <p/>
|
||||
* The reference format can be:
|
||||
* <pre>@resType/resName</pre>
|
||||
* <pre>@android:resType/resName</pre>
|
||||
* <pre>@resType/android:resName</pre>
|
||||
* <pre>?resType/resName</pre>
|
||||
* <pre>?android:resType/resName</pre>
|
||||
* <pre>?resType/android:resName</pre>
|
||||
* Any other string format will return <code>null</code>.
|
||||
* <p/>
|
||||
* The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method
|
||||
* only support the android namespace.
|
||||
*
|
||||
* @param reference the resource reference to search for.
|
||||
* @param forceFrameworkOnly if true all references are considered to be toward framework
|
||||
* resource even if the reference does not include the android: prefix.
|
||||
* @return a {@link ResourceValue} or <code>null</code>.
|
||||
*/
|
||||
ResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
|
||||
if (reference == null) {
|
||||
return null;
|
||||
}
|
||||
if (reference.startsWith(BridgeConstants.PREFIX_THEME_REF)) {
|
||||
// no theme? no need to go further!
|
||||
if (mThemeValues == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean frameworkOnly = false;
|
||||
|
||||
// eliminate the prefix from the string
|
||||
if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_THEME_REF)) {
|
||||
frameworkOnly = true;
|
||||
reference = reference.substring(BridgeConstants.PREFIX_ANDROID_THEME_REF.length());
|
||||
} else {
|
||||
reference = reference.substring(BridgeConstants.PREFIX_THEME_REF.length());
|
||||
}
|
||||
|
||||
// at this point, value can contain type/name (drawable/foo for instance).
|
||||
// split it to make sure.
|
||||
String[] segments = reference.split("\\/");
|
||||
|
||||
// we look for the referenced item name.
|
||||
String referenceName = null;
|
||||
|
||||
if (segments.length == 2) {
|
||||
// there was a resType in the reference. If it's attr, we ignore it
|
||||
// else, we assert for now.
|
||||
if (BridgeConstants.RES_ATTR.equals(segments[0])) {
|
||||
referenceName = segments[1];
|
||||
} else {
|
||||
// At this time, no support for ?type/name where type is not "attr"
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// it's just an item name.
|
||||
referenceName = segments[0];
|
||||
}
|
||||
|
||||
// now we look for android: in the referenceName in order to support format
|
||||
// such as: ?attr/android:name
|
||||
if (referenceName.startsWith(BridgeConstants.PREFIX_ANDROID)) {
|
||||
frameworkOnly = true;
|
||||
referenceName = referenceName.substring(BridgeConstants.PREFIX_ANDROID.length());
|
||||
}
|
||||
|
||||
// Now look for the item in the theme, starting with the current one.
|
||||
if (frameworkOnly) {
|
||||
// FIXME for now we do the same as if it didn't specify android:
|
||||
return findItemInStyle(mThemeValues, referenceName);
|
||||
}
|
||||
|
||||
return findItemInStyle(mThemeValues, referenceName);
|
||||
} else if (reference.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
|
||||
boolean frameworkOnly = false;
|
||||
|
||||
// check for the specific null reference value.
|
||||
if (BridgeConstants.REFERENCE_NULL.equals(reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Eliminate the prefix from the string.
|
||||
if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_RESOURCE_REF)) {
|
||||
frameworkOnly = true;
|
||||
reference = reference.substring(
|
||||
BridgeConstants.PREFIX_ANDROID_RESOURCE_REF.length());
|
||||
} else {
|
||||
reference = reference.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
|
||||
}
|
||||
|
||||
// at this point, value contains type/[android:]name (drawable/foo for instance)
|
||||
String[] segments = reference.split("\\/");
|
||||
|
||||
// now we look for android: in the resource name in order to support format
|
||||
// such as: @drawable/android:name
|
||||
if (segments[1].startsWith(BridgeConstants.PREFIX_ANDROID)) {
|
||||
frameworkOnly = true;
|
||||
segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length());
|
||||
}
|
||||
|
||||
return findResValue(segments[0], segments[1],
|
||||
forceFrameworkOnly ? true :frameworkOnly);
|
||||
}
|
||||
|
||||
// Looks like the value didn't reference anything. Return null.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for, and returns a {@link ResourceValue} by its name, and type.
|
||||
* @param resType the type of the resource
|
||||
* @param resName the name of the resource
|
||||
* @param frameworkOnly if <code>true</code>, the method does not search in the
|
||||
* project resources
|
||||
*/
|
||||
private ResourceValue findResValue(String resType, String resName, boolean frameworkOnly) {
|
||||
// map of ResouceValue for the given type
|
||||
Map<String, ResourceValue> typeMap;
|
||||
|
||||
// if allowed, search in the project resources first.
|
||||
if (frameworkOnly == false) {
|
||||
typeMap = mProjectResources.get(resType);
|
||||
if (typeMap != null) {
|
||||
ResourceValue item = typeMap.get(resName);
|
||||
if (item != null) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now search in the framework resources.
|
||||
typeMap = mFrameworkResources.get(resType);
|
||||
if (typeMap != null) {
|
||||
ResourceValue item = typeMap.get(resName);
|
||||
if (item != null) {
|
||||
return item;
|
||||
}
|
||||
|
||||
// if it was not found and the type is an id, it is possible that the ID was
|
||||
// generated dynamically when compiling the framework resources.
|
||||
// Look for it in the R map.
|
||||
if (BridgeConstants.RES_ID.equals(resType)) {
|
||||
if (Bridge.getResourceValue(resType, resName) != null) {
|
||||
return new ResourceValue(resType, resName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find the resource anywhere.
|
||||
// This is normal if the resource is an ID that is generated automatically.
|
||||
// For other resources, we output a warning
|
||||
if ("+id".equals(resType) == false && "+android:id".equals(resType) == false) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE,
|
||||
"Couldn't resolve resource @" +
|
||||
(frameworkOnly ? "android:" : "") + resType + "/" + resName,
|
||||
new ResourceValue(resType, resName, frameworkOnly));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a framework resource by type and name. The returned resource is resolved.
|
||||
* @param resourceType the type of the resource
|
||||
* @param resourceName the name of the resource
|
||||
*/
|
||||
public ResourceValue getFrameworkResource(String resourceType, String resourceName) {
|
||||
return getResource(resourceType, resourceName, mFrameworkResources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a project resource by type and name. The returned resource is resolved.
|
||||
* @param resourceType the type of the resource
|
||||
* @param resourceName the name of the resource
|
||||
*/
|
||||
public ResourceValue getProjectResource(String resourceType, String resourceName) {
|
||||
return getResource(resourceType, resourceName, mProjectResources);
|
||||
}
|
||||
|
||||
ResourceValue getResource(String resourceType, String resourceName,
|
||||
Map<String, Map<String, ResourceValue>> resourceRepository) {
|
||||
Map<String, ResourceValue> typeMap = resourceRepository.get(resourceType);
|
||||
if (typeMap != null) {
|
||||
ResourceValue item = typeMap.get(resourceName);
|
||||
if (item != null) {
|
||||
item = resolveResValue(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find the resource anywhere.
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourceValue} matching a given name in a given style. If the
|
||||
* item is not directly available in the style, the method looks in its parent style.
|
||||
* @param style the style to search in
|
||||
* @param itemName the name of the item to search for.
|
||||
* @return the {@link ResourceValue} object or <code>null</code>
|
||||
*/
|
||||
public ResourceValue findItemInStyle(StyleResourceValue style, String itemName) {
|
||||
ResourceValue item = style.findValue(itemName);
|
||||
|
||||
// if we didn't find it, we look in the parent style (if applicable)
|
||||
if (item == null && mStyleInheritanceMap != null) {
|
||||
StyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
|
||||
if (parentStyle != null) {
|
||||
return findItemInStyle(parentStyle, itemName);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* The input int[] attrs is one of com.android.internal.R.styleable fields where the name
|
||||
* of the field is the style being referenced and the array contains one index per attribute.
|
||||
|
||||
@@ -19,8 +19,8 @@ package com.android.layoutlib.bridge.android;
|
||||
import com.android.ide.common.rendering.api.IProjectCallback;
|
||||
import com.android.ide.common.rendering.api.LayoutLog;
|
||||
import com.android.ide.common.rendering.api.ResourceValue;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.layoutlib.bridge.BridgeConstants;
|
||||
|
||||
import org.kxml2.io.KXmlParser;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
@@ -155,14 +155,14 @@ public final class BridgeInflater extends LayoutInflater {
|
||||
|
||||
String[] layoutInfo = Bridge.resolveResourceValue(resource);
|
||||
if (layoutInfo != null) {
|
||||
value = bridgeContext.getFrameworkResource(BridgeConstants.RES_LAYOUT,
|
||||
layoutInfo[0]);
|
||||
value = bridgeContext.getResolver().getFrameworkResource(
|
||||
ResourceResolver.RES_LAYOUT, layoutInfo[0]);
|
||||
} else {
|
||||
layoutInfo = mProjectCallback.resolveResourceValue(resource);
|
||||
|
||||
if (layoutInfo != null) {
|
||||
value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT,
|
||||
layoutInfo[0]);
|
||||
value = bridgeContext.getResolver().getProjectResource(
|
||||
ResourceResolver.RES_LAYOUT, layoutInfo[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ public final class BridgeResources extends Resources {
|
||||
|
||||
if (resourceInfo != null) {
|
||||
platformResFlag_out[0] = true;
|
||||
return mContext.getFrameworkResource(resourceInfo[1], resourceInfo[0]);
|
||||
return mContext.getResolver().getFrameworkResource(resourceInfo[1], resourceInfo[0]);
|
||||
}
|
||||
|
||||
// didn't find a match in the framework? look in the project.
|
||||
@@ -113,7 +113,7 @@ public final class BridgeResources extends Resources {
|
||||
|
||||
if (resourceInfo != null) {
|
||||
platformResFlag_out[0] = false;
|
||||
return mContext.getProjectResource(resourceInfo[1], resourceInfo[0]);
|
||||
return mContext.getResolver().getProjectResource(resourceInfo[1], resourceInfo[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.layoutlib.bridge.android;
|
||||
import com.android.ide.common.rendering.api.LayoutLog;
|
||||
import com.android.ide.common.rendering.api.ResourceValue;
|
||||
import com.android.ide.common.rendering.api.StyleResourceValue;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.layoutlib.bridge.BridgeConstants;
|
||||
@@ -633,11 +634,11 @@ public final class BridgeTypedArray extends TypedArray {
|
||||
// if this is a framework id
|
||||
if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
|
||||
// look for idName in the android R classes
|
||||
return mContext.getFrameworkResourceValue(BridgeConstants.RES_ID, idName, defValue);
|
||||
return mContext.getFrameworkResourceValue(ResourceResolver.RES_ID, idName, defValue);
|
||||
}
|
||||
|
||||
// look for idName in the project R class.
|
||||
return mContext.getProjectResourceValue(BridgeConstants.RES_ID, idName, defValue);
|
||||
return mContext.getProjectResourceValue(ResourceResolver.RES_ID, idName, defValue);
|
||||
}
|
||||
|
||||
// not a direct id valid reference? resolve it
|
||||
@@ -682,7 +683,7 @@ public final class BridgeTypedArray extends TypedArray {
|
||||
|
||||
ResourceValue value = mResourceData[index];
|
||||
String stringValue = value.getValue();
|
||||
if (stringValue == null || BridgeConstants.REFERENCE_NULL.equals(stringValue)) {
|
||||
if (stringValue == null || ResourceResolver.REFERENCE_NULL.equals(stringValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.layoutlib.bridge.android;
|
||||
|
||||
import com.android.ide.common.rendering.api.ResourceValue;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.layoutlib.bridge.BridgeConstants;
|
||||
|
||||
@@ -57,7 +58,7 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
|
||||
String ns = mParser.getAttributeNamespace(index);
|
||||
|
||||
if (BridgeConstants.NS_RESOURCES.equals(ns)) {
|
||||
Integer v = Bridge.getResourceValue(BridgeConstants.RES_ATTR, name);
|
||||
Integer v = Bridge.getResourceValue(ResourceResolver.RES_ATTR, name);
|
||||
if (v != null) {
|
||||
return v.intValue();
|
||||
}
|
||||
@@ -68,7 +69,7 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
|
||||
// this is not an attribute in the android namespace, we query the customviewloader, if
|
||||
// the namespaces match.
|
||||
if (mContext.getProjectCallback().getNamespace().equals(ns)) {
|
||||
Integer v = mContext.getProjectCallback().getResourceValue(BridgeConstants.RES_ATTR,
|
||||
Integer v = mContext.getProjectCallback().getResourceValue(ResourceResolver.RES_ATTR,
|
||||
name);
|
||||
if (v != null) {
|
||||
return v.intValue();
|
||||
@@ -102,8 +103,9 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
|
||||
|
||||
private int resolveResourceValue(String value, int defaultValue) {
|
||||
// now look for this particular value
|
||||
ResourceValue resource = mContext.resolveResValue(
|
||||
mContext.findResValue(value, mPlatformFile));
|
||||
ResourceResolver resolver = mContext.getResolver();
|
||||
ResourceValue resource = resolver.resolveResValue(
|
||||
resolver.findResValue(value, mPlatformFile));
|
||||
|
||||
if (resource != null) {
|
||||
Integer id = null;
|
||||
|
||||
@@ -38,9 +38,10 @@ import com.android.ide.common.rendering.api.StyleResourceValue;
|
||||
import com.android.ide.common.rendering.api.ViewInfo;
|
||||
import com.android.ide.common.rendering.api.Params.RenderingMode;
|
||||
import com.android.ide.common.rendering.api.Result.Status;
|
||||
import com.android.ide.common.resources.ResourceResolver;
|
||||
import com.android.ide.common.resources.ResourceResolver.IFrameworkResourceIdProvider;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.layoutlib.bridge.BridgeConstants;
|
||||
import com.android.layoutlib.bridge.android.BridgeContext;
|
||||
import com.android.layoutlib.bridge.android.BridgeInflater;
|
||||
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
|
||||
@@ -73,8 +74,6 @@ import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -106,7 +105,6 @@ public class RenderSessionImpl {
|
||||
private BridgeContext mContext;
|
||||
private BridgeXmlBlockParser mBlockParser;
|
||||
private BridgeInflater mInflater;
|
||||
private StyleResourceValue mCurrentTheme;
|
||||
private int mScreenOffset;
|
||||
private ResourceValue mWindowBackground;
|
||||
private FrameLayout mViewRoot;
|
||||
@@ -170,18 +168,23 @@ public class RenderSessionImpl {
|
||||
metrics.xdpi = mParams.getXdpi();
|
||||
metrics.ydpi = mParams.getYdpi();
|
||||
|
||||
// find the current theme and compute the style inheritance map
|
||||
Map<StyleResourceValue, StyleResourceValue> styleParentMap =
|
||||
new HashMap<StyleResourceValue, StyleResourceValue>();
|
||||
// create the resource resolver
|
||||
ResourceResolver resolver = ResourceResolver.create(
|
||||
new IFrameworkResourceIdProvider() {
|
||||
public Integer getId(String resType, String resName) {
|
||||
return Bridge.getResourceValue(resType, resName);
|
||||
}
|
||||
},
|
||||
mParams.getProjectResources(),
|
||||
mParams.getFrameworkResources(),
|
||||
mParams.getThemeName(),
|
||||
mParams.isProjectTheme(),
|
||||
mParams.getLog());
|
||||
|
||||
mCurrentTheme = computeStyleMaps(mParams.getThemeName(), mParams.isProjectTheme(),
|
||||
mParams.getProjectResources().get(BridgeConstants.RES_STYLE),
|
||||
mParams.getFrameworkResources().get(BridgeConstants.RES_STYLE), styleParentMap);
|
||||
|
||||
// build the context
|
||||
mContext = new BridgeContext(mParams.getProjectKey(), metrics, mCurrentTheme,
|
||||
mParams.getProjectResources(), mParams.getFrameworkResources(),
|
||||
styleParentMap, mParams.getProjectCallback());
|
||||
mContext = new BridgeContext(mParams.getProjectKey(), metrics, resolver,
|
||||
mParams.getProjectCallback());
|
||||
|
||||
// set the current rendering context
|
||||
sCurrentContext = mContext;
|
||||
@@ -193,12 +196,12 @@ public class RenderSessionImpl {
|
||||
// get the screen offset and window-background resource
|
||||
mWindowBackground = null;
|
||||
mScreenOffset = 0;
|
||||
if (mCurrentTheme != null && mParams.isBgColorOverridden() == false) {
|
||||
mWindowBackground = mContext.findItemInStyle(mCurrentTheme, "windowBackground");
|
||||
mWindowBackground = mContext.resolveResValue(mWindowBackground);
|
||||
StyleResourceValue theme = resolver.getTheme();
|
||||
if (theme != null && mParams.isBgColorOverridden() == false) {
|
||||
mWindowBackground = resolver.findItemInTheme("windowBackground");
|
||||
mWindowBackground = resolver.resolveResValue(mWindowBackground);
|
||||
|
||||
mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme,
|
||||
mContext);
|
||||
mScreenOffset = getScreenOffset(resolver, metrics);
|
||||
}
|
||||
|
||||
// build the inflater and parser.
|
||||
@@ -499,18 +502,18 @@ public class RenderSessionImpl {
|
||||
ResourceValue animationResource = null;
|
||||
int animationId = 0;
|
||||
if (isFrameworkAnimation) {
|
||||
animationResource = mContext.getFrameworkResource(BridgeConstants.RES_ANIMATOR,
|
||||
animationName);
|
||||
animationResource = mContext.getResolver().getFrameworkResource(
|
||||
ResourceResolver.RES_ANIMATOR, animationName);
|
||||
if (animationResource != null) {
|
||||
animationId = Bridge.getResourceValue(BridgeConstants.RES_ANIMATOR,
|
||||
animationId = Bridge.getResourceValue(ResourceResolver.RES_ANIMATOR,
|
||||
animationName);
|
||||
}
|
||||
} else {
|
||||
animationResource = mContext.getProjectResource(BridgeConstants.RES_ANIMATOR,
|
||||
animationName);
|
||||
animationResource = mContext.getResolver().getProjectResource(
|
||||
ResourceResolver.RES_ANIMATOR, animationName);
|
||||
if (animationResource != null) {
|
||||
animationId = mContext.getProjectCallback().getResourceValue(
|
||||
BridgeConstants.RES_ANIMATOR, animationName);
|
||||
ResourceResolver.RES_ANIMATOR, animationName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,182 +908,21 @@ public class RenderSessionImpl {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute style information from the given list of style for the project and framework.
|
||||
* @param themeName the name of the current theme. In order to differentiate project and
|
||||
* platform themes sharing the same name, all project themes must be prepended with
|
||||
* a '*' character.
|
||||
* @param isProjectTheme Is this a project theme
|
||||
* @param inProjectStyleMap the project style map
|
||||
* @param inFrameworkStyleMap the framework style map
|
||||
* @param outInheritanceMap the map of style inheritance. This is filled by the method
|
||||
* @return the {@link StyleResourceValue} matching <var>themeName</var>
|
||||
*/
|
||||
private StyleResourceValue computeStyleMaps(
|
||||
String themeName, boolean isProjectTheme, Map<String,
|
||||
ResourceValue> inProjectStyleMap, Map<String, ResourceValue> inFrameworkStyleMap,
|
||||
Map<StyleResourceValue, StyleResourceValue> outInheritanceMap) {
|
||||
|
||||
if (inProjectStyleMap != null && inFrameworkStyleMap != null) {
|
||||
// first, get the theme
|
||||
ResourceValue theme = null;
|
||||
|
||||
// project theme names have been prepended with a *
|
||||
if (isProjectTheme) {
|
||||
theme = inProjectStyleMap.get(themeName);
|
||||
} else {
|
||||
theme = inFrameworkStyleMap.get(themeName);
|
||||
}
|
||||
|
||||
if (theme instanceof StyleResourceValue) {
|
||||
// compute the inheritance map for both the project and framework styles
|
||||
computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap,
|
||||
inFrameworkStyleMap, outInheritanceMap);
|
||||
|
||||
// Compute the style inheritance for the framework styles/themes.
|
||||
// Since, for those, the style parent values do not contain 'android:'
|
||||
// we want to force looking in the framework style only to avoid using
|
||||
// similarly named styles from the project.
|
||||
// To do this, we pass null in lieu of the project style map.
|
||||
computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */,
|
||||
inFrameworkStyleMap, outInheritanceMap);
|
||||
|
||||
return (StyleResourceValue)theme;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the parent style for all the styles in a given list.
|
||||
* @param styles the styles for which we compute the parent.
|
||||
* @param inProjectStyleMap the map of project styles.
|
||||
* @param inFrameworkStyleMap the map of framework styles.
|
||||
* @param outInheritanceMap the map of style inheritance. This is filled by the method.
|
||||
*/
|
||||
private void computeStyleInheritance(Collection<ResourceValue> styles,
|
||||
Map<String, ResourceValue> inProjectStyleMap,
|
||||
Map<String, ResourceValue> inFrameworkStyleMap,
|
||||
Map<StyleResourceValue, StyleResourceValue> outInheritanceMap) {
|
||||
for (ResourceValue value : styles) {
|
||||
if (value instanceof StyleResourceValue) {
|
||||
StyleResourceValue style = (StyleResourceValue)value;
|
||||
StyleResourceValue parentStyle = null;
|
||||
|
||||
// first look for a specified parent.
|
||||
String parentName = style.getParentStyle();
|
||||
|
||||
// no specified parent? try to infer it from the name of the style.
|
||||
if (parentName == null) {
|
||||
parentName = getParentName(value.getName());
|
||||
}
|
||||
|
||||
if (parentName != null) {
|
||||
parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
|
||||
|
||||
if (parentStyle != null) {
|
||||
outInheritanceMap.put(style, parentStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for and returns the {@link StyleResourceValue} from a given name.
|
||||
* <p/>The format of the name can be:
|
||||
* <ul>
|
||||
* <li>[android:]<name></li>
|
||||
* <li>[android:]style/<name></li>
|
||||
* <li>@[android:]style/<name></li>
|
||||
* </ul>
|
||||
* @param parentName the name of the style.
|
||||
* @param inProjectStyleMap the project style map. Can be <code>null</code>
|
||||
* @param inFrameworkStyleMap the framework style map.
|
||||
* @return The matching {@link StyleResourceValue} object or <code>null</code> if not found.
|
||||
*/
|
||||
private StyleResourceValue getStyle(String parentName,
|
||||
Map<String, ResourceValue> inProjectStyleMap,
|
||||
Map<String, ResourceValue> inFrameworkStyleMap) {
|
||||
boolean frameworkOnly = false;
|
||||
|
||||
String name = parentName;
|
||||
|
||||
// remove the useless @ if it's there
|
||||
if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
|
||||
name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
|
||||
}
|
||||
|
||||
// check for framework identifier.
|
||||
if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) {
|
||||
frameworkOnly = true;
|
||||
name = name.substring(BridgeConstants.PREFIX_ANDROID.length());
|
||||
}
|
||||
|
||||
// at this point we could have the format <type>/<name>. we want only the name as long as
|
||||
// the type is style.
|
||||
if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
|
||||
name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
|
||||
} else if (name.indexOf('/') != -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ResourceValue parent = null;
|
||||
|
||||
// if allowed, search in the project resources.
|
||||
if (frameworkOnly == false && inProjectStyleMap != null) {
|
||||
parent = inProjectStyleMap.get(name);
|
||||
}
|
||||
|
||||
// if not found, then look in the framework resources.
|
||||
if (parent == null) {
|
||||
parent = inFrameworkStyleMap.get(name);
|
||||
}
|
||||
|
||||
// make sure the result is the proper class type and return it.
|
||||
if (parent instanceof StyleResourceValue) {
|
||||
return (StyleResourceValue)parent;
|
||||
}
|
||||
|
||||
assert false;
|
||||
mParams.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
|
||||
String.format("Unable to resolve parent style name: %s", parentName),
|
||||
null /*data*/);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the name of the parent style, or <code>null</code> if the style is a root style.
|
||||
*/
|
||||
private String getParentName(String styleName) {
|
||||
int index = styleName.lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
return styleName.substring(0, index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top screen offset. This depends on whether the current theme defines the user
|
||||
* of the title and status bars.
|
||||
* @param frameworkResources The framework resources
|
||||
* @param currentTheme The current theme
|
||||
* @param context The context
|
||||
* @param resolver The {@link ResourceResolver}
|
||||
* @param metrics The display metrics
|
||||
* @return the pixel height offset
|
||||
*/
|
||||
private int getScreenOffset(Map<String, Map<String, ResourceValue>> frameworkResources,
|
||||
StyleResourceValue currentTheme, BridgeContext context) {
|
||||
private int getScreenOffset(ResourceResolver resolver, DisplayMetrics metrics) {
|
||||
int offset = 0;
|
||||
|
||||
// get the title bar flag from the current theme.
|
||||
ResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle");
|
||||
ResourceValue value = resolver.findItemInTheme("windowNoTitle");
|
||||
|
||||
// because it may reference something else, we resolve it.
|
||||
value = context.resolveResValue(value);
|
||||
value = resolver.resolveResValue(value);
|
||||
|
||||
// if there's a value and it's true (default is false)
|
||||
if (value == null || value.getValue() == null ||
|
||||
@@ -1089,17 +931,17 @@ public class RenderSessionImpl {
|
||||
int defaultOffset = DEFAULT_TITLE_BAR_HEIGHT;
|
||||
|
||||
// get value from the theme.
|
||||
value = context.findItemInStyle(currentTheme, "windowTitleSize");
|
||||
value = resolver.findItemInTheme("windowTitleSize");
|
||||
|
||||
// resolve it
|
||||
value = context.resolveResValue(value);
|
||||
value = resolver.resolveResValue(value);
|
||||
|
||||
if (value != null) {
|
||||
// get the numerical value, if available
|
||||
TypedValue typedValue = ResourceHelper.getValue(value.getValue());
|
||||
if (typedValue != null) {
|
||||
// compute the pixel value based on the display metrics
|
||||
defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
|
||||
defaultOffset = (int)typedValue.getDimension(metrics);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1107,10 +949,10 @@ public class RenderSessionImpl {
|
||||
}
|
||||
|
||||
// get the fullscreen flag from the current theme.
|
||||
value = context.findItemInStyle(currentTheme, "windowFullscreen");
|
||||
value = resolver.findItemInTheme("windowFullscreen");
|
||||
|
||||
// because it may reference something else, we resolve it.
|
||||
value = context.resolveResValue(value);
|
||||
value = resolver.resolveResValue(value);
|
||||
|
||||
if (value == null || value.getValue() == null ||
|
||||
XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
|
||||
@@ -1118,16 +960,13 @@ public class RenderSessionImpl {
|
||||
// default value
|
||||
int defaultOffset = DEFAULT_STATUS_BAR_HEIGHT;
|
||||
|
||||
// get the real value, first the list of Dimensions from the framework map
|
||||
Map<String, ResourceValue> dimens = frameworkResources.get(BridgeConstants.RES_DIMEN);
|
||||
|
||||
// now get the value
|
||||
value = dimens.get("status_bar_height");
|
||||
// get the real value
|
||||
value = resolver.getFrameworkResource(ResourceResolver.RES_DIMEN, "status_bar_height");
|
||||
if (value != null) {
|
||||
TypedValue typedValue = ResourceHelper.getValue(value.getValue());
|
||||
if (typedValue != null) {
|
||||
// compute the pixel value based on the display metrics
|
||||
defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics);
|
||||
defaultOffset = (int)typedValue.getDimension(metrics);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1136,7 +975,6 @@ public class RenderSessionImpl {
|
||||
}
|
||||
|
||||
return offset;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user