TypedResource getAndResolve(@NotNull ResName resName, String qualifiers, boolean resolveRefs) { TypedResource value = resourceLoader.getValue(resName, qualifiers); if (resolveRefs) { value = resolve(value, qualifiers, resName); } // todo: make the drawable loader put stuff into the normal spot... if (value == null && DrawableResourceLoader.isStillHandledHere(resName)) { DrawableNode drawableNode = resourceLoader.getDrawableNode(resName, qualifiers); return new TypedResource<FsFile>(drawableNode.getFsFile(), ResType.FILE); } // todo: gross. this is so resources.getString(R.layout.foo) works for ABS. if (value == null && "layout".equals(resName.type)) { throw new UnsupportedOperationException("ugh, this doesn't work still?"); } return value; }
TypedResource resolve(TypedResource value, String qualifiers, ResName contextResName) { while (true) { if (value == null) return null; Object data = value.getData(); if (data instanceof String) { String s = (String) data; if (s.equals("@null")) { return null; } else if (s.startsWith("@")) { String refStr = s.substring(1).replace("+", ""); contextResName = ResName.qualifyResName(refStr, contextResName); value = resourceLoader.getValue(contextResName, qualifiers); // back through... } else { return value; } } else { return value; } } }
public boolean setThemeFromManifest() { ShadowApplication shadowApplication = shadowOf(realActivity.getApplication()); AndroidManifest appManifest = shadowApplication.getAppManifest(); if (appManifest == null) return false; String themeRef = appManifest.getThemeRef(realActivity.getClass()); if (themeRef != null) { ResName style = ResName.qualifyResName(themeRef.replace("@", ""), appManifest.getPackageName(), "style"); Integer themeRes = shadowApplication.getResourceLoader().getResourceIndex().getResourceId(style); if (themeRes == null) throw new Resources.NotFoundException("no such theme " + style.getFullyQualifiedName()); realActivity.setTheme(themeRes); return true; } return false; }
@Override public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { ResName resName = getAttrResName(namespace, attribute); Attribute attr = findByName(resName); if (attr == null) return defaultValue; String qualifiers = shadowOf(resources.getAssets()).getQualifiers(); TypedResource<AttrData> typedResource = resourceLoader.getValue(resName, qualifiers); if (typedResource == null) { System.out.println("WARN: no attr found for " + resName + ", assuming it's an integer..."); typedResource = new TypedResource<AttrData>(new AttrData(attribute, "integer", null), ResType.INTEGER); } TypedValue outValue = new TypedValue(); Converter.convertAndFill(attr, outValue, resourceLoader, qualifiers, typedResource.getData()); if (outValue.type == TypedValue.TYPE_NULL) { return defaultValue; } return outValue.data; }
@Override public ActivityInfo getActivityInfo(ComponentName className, int flags) throws NameNotFoundException { String packageName = className.getPackageName(); AndroidManifest androidManifest = androidManifests.get(packageName); String activityName = className.getClassName(); ActivityData activityData = androidManifest.getActivityData(activityName); ActivityInfo activityInfo = new ActivityInfo(); activityInfo.packageName = packageName; activityInfo.name = activityName; if (activityData != null) { ResourceIndex resourceIndex = Robolectric.getShadowApplication().getResourceLoader().getResourceIndex(); String themeRef; // Based on ShadowActivity if (activityData.getThemeRef() != null) { themeRef = activityData.getThemeRef(); } else { themeRef = androidManifest.getThemeRef(); } if (themeRef != null) { ResName style = ResName.qualifyResName(themeRef.replace("@", ""), packageName, "style"); activityInfo.theme = resourceIndex.getResourceId(style); } } activityInfo.applicationInfo = getApplicationInfo(packageName, flags); return activityInfo; }
@Override public synchronized ResName getResName(int resourceId) { ResName resName = super.getResName(resourceId); if (resName == null) { // todo: pull in android.internal.R, remove this, and remove the "synchronized" on methods since we should then be immutable... if ((resourceId & 0xfff00000) == 0x01000000) { new RuntimeException("WARN: couldn't find a name for resource id " + resourceId).printStackTrace(System.out); ResName internalResName = new ResName("android.internal", "unknown", resourceId + ""); resourceNameToId.put(internalResName, resourceId); resourceIdToResName.put(resourceId, internalResName); return internalResName; } } return resName; }
@HiddenApi @Implementation public CharSequence[] getResourceTextArray(final int id) { ResName resName = resourceLoader.getResourceIndex().getResName(id); if (resName == null) throw new Resources.NotFoundException("unknown resource " + id); TypedResource value = getAndResolve(resName, getQualifiers(), true); if (value == null) return null; TypedResource[] items = getConverter(value).getItems(value); CharSequence[] charSequences = new CharSequence[items.length]; for (int i = 0; i < items.length; i++) { TypedResource typedResource = resolve(items[i], getQualifiers(), resName); charSequences[i] = getConverter(typedResource).asCharSequence(typedResource); } return charSequences; }
@HiddenApi @Implementation public boolean getThemeValue(int theme, int ident, TypedValue outValue, boolean resolveRefs) { ResourceIndex resourceIndex = resourceLoader.getResourceIndex(); ResName resName = resourceIndex.getResName(ident); Resources.Theme theTheme = getThemeByInternalId(theme); // Load the style for the theme we represent. E.g. "@style/Theme.Robolectric" ResName themeStyleName = resourceIndex.getResName(shadowOf(theTheme).getStyleResourceId()); if (themeStyleName == null) return false; // is this right? Style themeStyle = resolveStyle(resourceLoader, themeStyleName, getQualifiers()); //// Load the theme attribute for the default style attributes. E.g., attr/buttonStyle //ResName defStyleName = resourceLoader.getResourceIndex().getResName(ident); // //// Load the style for the default style attribute. E.g. "@style/Widget.Robolectric.Button"; //String defStyleNameValue = themeStyle.getAttrValue(defStyleName); //ResName defStyleResName = new ResName(defStyleName.packageName, "style", defStyleName.name); //Style style = resolveStyle(resourceLoader, defStyleResName, getQualifiers()); if (themeStyle != null) { Attribute attrValue = themeStyle.getAttrValue(resName); if (attrValue == null) { System.out.println("Couldn't find " + resName + " in " + themeStyleName); } else { TypedResource attrDataValue = resourceLoader.getValue(resName, getQualifiers()); Converter.convertAndFill(attrValue, outValue, resourceLoader, getQualifiers()); return true; } } return false; }
@HiddenApi @Implementation public final InputStream openNonAsset(int cookie, String fileName, int accessMode) throws IOException { final ResName resName = qualifyFromNonAssetFileName(fileName); final DrawableNode drawableNode = resourceLoader.getDrawableNode(resName, getQualifiers()); if (drawableNode == null) { throw new IOException("Unable to find resource for " + fileName); } return new ByteArrayInputStream(drawableNode.getFsFile().getBytes()); }
private ResName qualifyFromNonAssetFileName(String fileName) { if (fileName.startsWith("jar:")) { // Must remove "jar:" prefix, or else qualifyFromFilePath fails on Windows return ResName.qualifyFromFilePath("android", fileName.replaceFirst("jar:", "")); } else { return ResName.qualifyFromFilePath(appManifest.getPackageName(), fileName); } }
@HiddenApi @Implementation public int[] getArrayIntResource(int arrayRes) { ResName resName = resourceLoader.getResourceIndex().getResName(arrayRes); if (resName == null) throw new Resources.NotFoundException("unknown resource " + arrayRes); TypedResource value = getAndResolve(resName, getQualifiers(), true); if (value == null) return null; TypedResource[] items = getConverter(value).getItems(value); int[] ints = new int[items.length]; for (int i = 0; i < items.length; i++) { TypedResource typedResource = resolve(items[i], getQualifiers(), resName); ints[i] = getConverter(typedResource).asInt(typedResource); } return ints; }
@Override public Attribute getAttrValue(ResName resName) { resName.mustBe("attr"); StyleData currentStyle = leafStyle; while (currentStyle != null) { Attribute value = currentStyle.getAttrValue(resName); if (value != null) return value; currentStyle = getParent(currentStyle); } return null; }
private StyleData getParent(StyleData currentStyle) { String parent = currentStyle.getParent(); if (parent == null || parent.isEmpty()) return null; if (parent.startsWith("@")) parent = parent.substring(1); ResName style = ResName.qualifyResName(parent, currentStyle.getPackageName(), "style"); TypedResource typedResource = resourceLoader.getValue(style, qualifiers); if (typedResource == null) { throw new RuntimeException("huh? can't find parent for " + currentStyle); } return (StyleData) typedResource.getData(); }
@Override public String getAttributeValue(String namespace, String attribute) { ResName resName = getAttrResName(namespace, attribute); Attribute attr = findByName(resName); if (attr != null && !attr.isNull()) { return attr.qualifiedValue(); } return null; }
@Override public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { ResName resName = getAttrResName(namespace, attribute); Attribute attr = findByName(resName); if (attr == null) return defaultValue; Integer resourceId = ResName.getResourceId(resourceLoader.getResourceIndex(), attr.value, attr.contextPackageName); return resourceId == null ? defaultValue : resourceId; }
@Override public int getAttributeResourceValue(int resourceId, int defaultValue) { String attrName = resourceLoader.getResourceIndex().getResourceName(resourceId); ResName resName = getAttrResName(null, attrName); Attribute attr = findByName(resName); if (attr == null) return defaultValue; Integer extracted = ResName.getResourceId(resourceLoader.getResourceIndex(), attr.value, attr.contextPackageName); return (extracted == null) ? defaultValue : extracted; }
@Override public int getStyleAttribute() { Attribute styleAttribute = Attribute.find(attributes, new ResName("", "attr", "style")); if (styleAttribute == null) { // Per Android specifications, return 0 if there is no style. return 0; } Integer i = ResName.getResourceId(resourceLoader.getResourceIndex(), styleAttribute.value, styleAttribute.contextPackageName); return i != null ? i : 0; }
public void validateStrictI18n() { for (ResName key : strictI18nAttrs) { Attribute attribute = findByName(key); if (attribute != null) { if (!attribute.value.startsWith("@string/")) { throw new I18nException("View class: " + (viewClass != null ? viewClass.getName() : "") + " has attribute: " + key + " with hardcoded value: \"" + attribute.value + "\" and is not i18n-safe."); } } } }
private Attribute findByName(ResName resName) { ResourceIndex resourceIndex = resourceLoader.getResourceIndex(); Integer resourceId = resourceIndex.getResourceId(resName); // canonicalize the attr name if we can, otherwise don't... // todo: this is awful; fix it. if (resourceId == null) { return Attribute.find(attributes, resName); } else { return Attribute.find(attributes, resourceId, resourceIndex); } }
public ImageView getHomeIcon() { ResourceLoader resourceLoader = Robolectric.getShadowApplication().getResourceLoader(); ResName internalResource = new ResName("android", "id", "home"); Integer resId = resourceLoader.getResourceIndex().getResourceId(internalResource); try { Class<?> actionBarViewClass = Class.forName("com.android.internal.widget.ActionBarView"); ViewGroup actionBarView = (ViewGroup) field("mActionBar").ofType(actionBarViewClass).in(realWindow).get(); return (ImageView) actionBarView.findViewById(resId); } catch (ClassNotFoundException e) { throw new RuntimeException("could not resolve ActionBarView"); } }
public void addManifest(AndroidManifest androidManifest, ResourceLoader loader) { androidManifests.put(androidManifest.getPackageName(), androidManifest); ResourceIndex resourceIndex = loader.getResourceIndex(); // first opportunity to access a resource index for this manifest, use it to init the references androidManifest.initMetaData(loader); PackageInfo packageInfo = new PackageInfo(); packageInfo.packageName = androidManifest.getPackageName(); packageInfo.versionName = androidManifest.getVersionName(); packageInfo.versionCode = androidManifest.getVersionCode(); ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.flags = androidManifest.getApplicationFlags(); applicationInfo.targetSdkVersion = androidManifest.getTargetSdkVersion(); applicationInfo.packageName = androidManifest.getPackageName(); applicationInfo.processName = androidManifest.getProcessName(); applicationInfo.name = androidManifest.getApplicationName(); applicationInfo.metaData = metaDataToBundle(androidManifest.getApplicationMetaData()); if (androidManifest.getLabelRef() != null && resourceIndex != null) { Integer id = ResName.getResourceId(resourceIndex, androidManifest.getLabelRef(), androidManifest.getPackageName()); applicationInfo.labelRes = id != null ? id : 0; } packageInfo.applicationInfo = applicationInfo; initApplicationInfo(applicationInfo); addPackage(packageInfo); }
private String getActivityTitle() { String title = null; /* Get the label for the activity from the manifest */ ShadowApplication shadowApplication = shadowOf_(component.getApplication()); AndroidManifest appManifest = shadowApplication.getAppManifest(); if (appManifest == null) return null; String labelRef = appManifest.getActivityLabel(component.getClass()); if (labelRef != null) { if(labelRef.startsWith("@")){ /* Label refers to a string value, get the resource identifier */ ResName style = ResName.qualifyResName(labelRef.replace("@", ""), appManifest.getPackageName(), "string"); Integer labelRes = shadowApplication.getResourceLoader().getResourceIndex().getResourceId(style); /* If we couldn't determine the resource ID, throw it up */ if (labelRes == null) { throw new Resources.NotFoundException("no such label " + style.getFullyQualifiedName()); } /* Get the resource ID, use the activity to look up the actual string */ title = component.getString(labelRes); } else { title = labelRef; /* Label isn't an identifier, use it directly as the title */ } } return title; }
public View inflate(Context context, String packageName, String key, ViewGroup parent, String qualifiers) { ResName resName = new ResName(packageName + ":layout/" + key); shadowOf(context.getAssets()).setQualifiers(qualifiers); ResourceLoader resourceLoader = shadowOf(context.getResources()).getResourceLoader(); Integer layoutResId = resourceLoader.getResourceIndex().getResourceId(resName); if (layoutResId == null) throw new AssertionError("no such resource " + resName); return LayoutInflater.from(context).inflate(layoutResId, parent); }
@Test public void shouldInheritThemeValuesFromImplicitParents() throws Exception { TestActivity activity = buildActivity(TestActivityWithAnotherTheme.class).create().get(); ResourceLoader resourceLoader = Robolectric.shadowOf(activity.getResources()).getResourceLoader(); Style style = ShadowAssetManager.resolveStyle(resourceLoader, new ResName(TestUtil.TEST_PACKAGE, "style", "Widget.AnotherTheme.Button.Blarf"), ""); assertThat(style.getAttrValue(new ResName("android", "attr", "background")).value) .isEqualTo("#ffff0000"); }
@Test public void whenAThemeHasExplicitlyEmptyParentAttr_shouldHaveNoParent() throws Exception { TestActivity activity = buildActivity(TestActivityWithAnotherTheme.class).create().get(); ResourceLoader resourceLoader = Robolectric.shadowOf(activity.getResources()).getResourceLoader(); Style style = ShadowAssetManager.resolveStyle(resourceLoader, new ResName(TestUtil.TEST_PACKAGE, "style", "Theme.MyTheme"), ""); assertThat(style.getAttrValue(new ResName("android", "attr", "background"))).isNull(); }
@Before public void setUp() { progressBar = new ProgressBar(Robolectric.application, new RoboAttributeSet(asList( new Attribute(new ResName(TestUtil.SYSTEM_PACKAGE, "attr", "max"), "100", TestUtil.TEST_PACKAGE), new Attribute(new ResName(TestUtil.SYSTEM_PACKAGE, "attr", "indeterminate"), "false", TestUtil.TEST_PACKAGE), new Attribute(new ResName(TestUtil.SYSTEM_PACKAGE, "attr", "indeterminateOnly"), "false", TestUtil.TEST_PACKAGE) ), Robolectric.application.getResources(), null)); }
@Override public DrawableNode getDrawableNode(ResName resName, String qualifiers) { DrawableNode node = super.getDrawableNode(resName, qualifiers); if (node != null) return node; return systemResourceLoader.getDrawableNode(DUMMY_RES_NAME, qualifiers); }
public void init(ResourceLoader resLoader, String packageName) { ResourceIndex resIndex = resLoader.getResourceIndex(); if (!initialised) { for (Map.Entry<String,MetaData.VALUE_TYPE> entry : typeMap.entrySet()) { String value = valueMap.get(entry.getKey()).toString(); if (value.startsWith("@")) { ResName resName = ResName.qualifyResName(value.substring(1), packageName, null); switch (entry.getValue()) { case RESOURCE: // Was provided by resource attribute, store resource ID valueMap.put(entry.getKey(), resIndex.getResourceId(resName)); break; case VALUE: // Was provided by value attribute, need to parse it TypedResource<?> typedRes = resLoader.getValue(resName, ""); // The typed resource's data is always a String, so need to parse the value. switch (typedRes.getResType()) { case BOOLEAN: case COLOR: case INTEGER: case FLOAT: valueMap.put(entry.getKey(),parseValue(typedRes.getData().toString())); break; default: valueMap.put(entry.getKey(),typedRes.getData()); } break; } } else if (entry.getValue() == MetaData.VALUE_TYPE.VALUE) { // Raw value, so parse it in to the appropriate type and store it valueMap.put(entry.getKey(), parseValue(value)); } } // Finished parsing, mark as initialised initialised = true; } }
public AttributeSetBuilder addAttribute(@NonNull String name, @NonNull String value) { attributes.add(new Attribute(new ResName(packageName, "attr", name), value, packageName)); return this; }
static Style resolveStyle(ResourceLoader resourceLoader, @NotNull ResName themeStyleName, String qualifiers) { TypedResource themeStyleResource = resourceLoader.getValue(themeStyleName, qualifiers); if (themeStyleResource == null) return null; StyleData themeStyleData = (StyleData) themeStyleResource.getData(); return new StyleResolver(resourceLoader, themeStyleData, themeStyleName, qualifiers); }
TypedResource getAndResolve(int resId, String qualifiers, boolean resolveRefs) { ResName resName = resourceLoader.getResourceIndex().getResName(resId); if (resName == null) throw new Resources.NotFoundException("unknown resource " + resId); return getAndResolve(resName, qualifiers, resolveRefs); }
public StyleResolver(ResourceLoader resourceLoader, StyleData styleData, ResName myResName, String qualifiers) { this.resourceLoader = resourceLoader; this.leafStyle = styleData; this.myResName = myResName; this.qualifiers = qualifiers; }
public void setCreatedFromResId(int resId, ResName resName) { this.createdFromResId = resId; appendDescription(" for resource:" + resName.getFullyQualifiedName()); }
@Override public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) { ResName resName = getAttrResName(namespace, attribute); Attribute attr = findByName(resName); return (attr != null) ? Boolean.valueOf(attr.value) : defaultValue; }
@Override public int getAttributeNameResource(int index) { ResName resName = attributes.get(index).resName; Integer resourceId = resourceLoader.getResourceIndex().getResourceId(resName); return resourceId == null ? 0 : resourceId; }
@Override public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { ResName resName = getAttrResName(namespace, attribute); Attribute attr = findByName(resName); return (attr != null) ? Float.valueOf(attr.value) : defaultValue; }
private ResName getAttrResName(String namespace, String attrName) { String packageName = Attribute.extractPackageName(namespace); return new ResName(packageName, "attr", attrName); }
public ResName getResName(int resourceId) { return getResourceLoader().getResourceIndex().getResName(resourceId); }
private static String getAttribute(PreferenceNode node, String name) { Attribute attr = Attribute.find(node.getAttributes(), new ResName("android", "attr", name)); return attr != null ? attr.value : null; }
@Override public synchronized Integer getResourceId(ResName resName) { return super.getResourceId(resName); }