diff options
author | Xavier Ducrohet <xav@android.com> | 2011-01-16 19:38:33 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-01-16 19:38:33 -0800 |
commit | 87ab45c0fe4db814a7364fce3b88be3a49507e90 (patch) | |
tree | 04460db0ef24ecdbdb7d45d171a3100dea0aa6fd /tools/layoutlib/bridge | |
parent | bdac9e0fbe874cf7414228624bd0d3af4f715ccc (diff) | |
parent | d1d6fafc7fc63543b10552dadf202dd6fa40fe6b (diff) | |
download | frameworks_base-87ab45c0fe4db814a7364fce3b88be3a49507e90.zip frameworks_base-87ab45c0fe4db814a7364fce3b88be3a49507e90.tar.gz frameworks_base-87ab45c0fe4db814a7364fce3b88be3a49507e90.tar.bz2 |
Merge "LayoutLib: extract resource resolution into its own class." into honeycomb
Diffstat (limited to 'tools/layoutlib/bridge')
9 files changed, 652 insertions, 561 deletions
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk index 9b7bc5f..a0a7307 100644 --- a/tools/layoutlib/bridge/Android.mk +++ b/tools/layoutlib/bridge/Android.mk @@ -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 diff --git a/tools/layoutlib/bridge/src/com/android/ide/common/resources/ResourceResolver.java b/tools/layoutlib/bridge/src/com/android/ide/common/resources/ResourceResolver.java new file mode 100644 index 0000000..4c500e7 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/ide/common/resources/ResourceResolver.java @@ -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; + } + + +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java index 194687e..112af1e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java @@ -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"; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 82e217a..f633201 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -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(); @@ -500,295 +497,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. * <p/> diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java index e95d295..61f47ba 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java @@ -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]); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 23d81a2..3af6a1b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -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]); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index 84bb4d1..b166da5 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -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; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java index ba45217..45d8e26 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java @@ -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; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 2439791..a227d0c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -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; - } /** |