org.robolectric.res.android.ResTableTheme Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resources Show documentation
Show all versions of resources Show documentation
An alternative Android testing framework.
package org.robolectric.res.android;
import static org.robolectric.res.android.Errors.BAD_INDEX;
import static org.robolectric.res.android.Errors.NO_ERROR;
import static org.robolectric.res.android.ResTable.Res_GETENTRY;
import static org.robolectric.res.android.ResTable.Res_GETPACKAGE;
import static org.robolectric.res.android.ResTable.Res_GETTYPE;
import static org.robolectric.res.android.ResTable.getOrDefault;
import static org.robolectric.res.android.ResourceTypes.Res_value.TYPE_ATTRIBUTE;
import static org.robolectric.res.android.ResourceTypes.Res_value.TYPE_NULL;
import static org.robolectric.res.android.Util.ALOGE;
import static org.robolectric.res.android.Util.ALOGI;
import static org.robolectric.res.android.Util.ALOGV;
import static org.robolectric.res.android.Util.ALOGW;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.robolectric.res.android.ResTable.PackageGroup;
import org.robolectric.res.android.ResTable.ResourceName;
import org.robolectric.res.android.ResTable.bag_entry;
import org.robolectric.res.android.ResourceTypes.Res_value;
// transliterated from
// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp and
// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/ResourceTypes.h
public class ResTableTheme {
private final List styles = new ArrayList<>();
private static boolean styleDebug = false;
private static final type_info EMPTY_TYPE_INFO = new type_info();
private static final theme_entry EMPTY_THEME_ENTRY = new theme_entry();
private class AppliedStyle {
private final int styleResId;
private final boolean forced;
public AppliedStyle(int styleResId, boolean forced) {
this.styleResId = styleResId;
this.forced = forced;
}
@Override
public String toString() {
ResourceName resourceName = new ResourceName();
boolean found = mTable.getResourceName(styleResId, true, resourceName);
return (found ? resourceName : "unknown") + (forced ? " (forced)" : "");
}
}
@Override
public String toString() {
if (styles.isEmpty()) {
return "theme with no applied styles";
} else {
return "theme with applied styles: " + styles + "";
}
}
private ResTable mTable;
private boolean kDebugTableTheme = false;
private boolean kDebugTableNoisy = false;
private package_info[] mPackages = new package_info[Res_MAXPACKAGE];
private Ref mTypeSpecFlags = new Ref<>(0);
public ResTableTheme(ResTable resources) {
this.mTable = resources;
}
public ResTable getResTable() {
return this.mTable;
}
public int GetAttribute(int resID, Ref valueRef,
final Ref outTypeSpecFlags) {
int cnt = 20;
if (outTypeSpecFlags != null) outTypeSpecFlags.set(0);
do {
final int p = mTable.getResourcePackageIndex(resID);
final int t = Res_GETTYPE(resID);
final int e = Res_GETENTRY(resID);
if (kDebugTableTheme) {
ALOGI("Looking up attr 0x%08x in theme %s", resID, this);
}
if (p >= 0) {
final package_info pi = mPackages[p];
if (kDebugTableTheme) {
ALOGI("Found package: %s", pi);
}
if (pi != null) {
if (kDebugTableTheme) {
ALOGI("Desired type index is %d in avail %d", t, Res_MAXTYPE + 1);
}
if (t <= Res_MAXTYPE) {
type_info ti = pi.types[t];
if (ti == null) {
ti = EMPTY_TYPE_INFO;
}
if (kDebugTableTheme) {
ALOGI("Desired entry index is %d in avail %d", e, ti.numEntries);
}
if (e < ti.numEntries) {
theme_entry te = ti.entries[e];
if (te == null) {
te = EMPTY_THEME_ENTRY;
}
if (outTypeSpecFlags != null) {
outTypeSpecFlags.set(outTypeSpecFlags.get() | te.typeSpecFlags);
}
if (kDebugTableTheme) {
ALOGI("Theme value: type=0x%x, data=0x%08x",
te.value.dataType, te.value.data);
}
final int type = te.value.dataType;
if (type == TYPE_ATTRIBUTE) {
if (cnt > 0) {
cnt--;
resID = te.value.data;
continue;
}
ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
return BAD_INDEX;
} else if (type != TYPE_NULL
|| te.value.data == Res_value.DATA_NULL_EMPTY) {
valueRef.set(te.value);
return te.stringBlock;
}
return BAD_INDEX;
}
}
}
}
break;
} while (true);
return BAD_INDEX;
}
public int resolveAttributeReference(Ref inOutValue,
int blockIndex, Ref outLastRef,
final Ref inoutTypeSpecFlags, Ref inoutConfig) {
//printf("Resolving type=0x%x\n", inOutValue->dataType);
if (inOutValue.get().dataType == TYPE_ATTRIBUTE) {
final Ref newTypeSpecFlags = new Ref<>(0);
blockIndex = GetAttribute(inOutValue.get().data, inOutValue, newTypeSpecFlags);
if (kDebugTableTheme) {
ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=0x%x\n",
(int)blockIndex, (int)inOutValue.get().dataType, inOutValue.get().data);
}
if (inoutTypeSpecFlags != null) inoutTypeSpecFlags.set(inoutTypeSpecFlags.get() | newTypeSpecFlags.get());
//printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType);
if (blockIndex < 0) {
return blockIndex;
}
}
return mTable.resolveReference(inOutValue, blockIndex, outLastRef,
inoutTypeSpecFlags, inoutConfig);
}
public int applyStyle(int resID, boolean force) {
AppliedStyle newAppliedStyle = new AppliedStyle(resID, force);
if (styleDebug) {
System.out.println("Apply " + newAppliedStyle + " to " + this);
}
styles.add(newAppliedStyle);
final Ref bag = new Ref<>(null);
final Ref bagTypeSpecFlags = new Ref<>(0);
mTable.lock();
final int N = mTable.getBagLocked(resID, bag, bagTypeSpecFlags);
if (kDebugTableNoisy) {
ALOGV("Applying style 0x%08x to theme %s, count=%d", resID, this, N);
}
if (N < 0) {
mTable.unlock();
return N;
}
mTypeSpecFlags.set(mTypeSpecFlags.get() | bagTypeSpecFlags.get());
int curPackage = 0xffffffff;
int curPackageIndex = 0;
package_info curPI = null;
int curType = 0xffffffff;
int numEntries = 0;
theme_entry[] curEntries = null;
final int end = N;
int bagIndex = 0;
while (bagIndex < end) {
bag_entry bagEntry = bag.get()[bagIndex];
final int attrRes = bagEntry.map.name.ident;
final int p = Res_GETPACKAGE(attrRes);
final int t = Res_GETTYPE(attrRes);
final int e = Res_GETENTRY(attrRes);
if (curPackage != p) {
final int pidx = mTable.getResourcePackageIndex(attrRes);
if (pidx < 0) {
ALOGE("Style contains key with bad package: 0x%08x\n", attrRes);
bagIndex++;
continue;
}
curPackage = p;
curPackageIndex = pidx;
curPI = mPackages[pidx];
if (curPI == null) {
curPI = new package_info();
mPackages[pidx] = curPI;
}
curType = 0xffffffff;
}
if (curType != t) {
if (t > Res_MAXTYPE) {
ALOGE("Style contains key with bad type: 0x%08x\n", attrRes);
bagIndex++;
continue;
}
curType = t;
curEntries = curPI.types[t] != null ? curPI.types[t].entries: null;
if (curEntries == null) {
final PackageGroup grp = mTable.mPackageGroups.get(curPackageIndex);
final List typeList = getOrDefault(grp.types, t, Collections.emptyList());
int cnt = typeList.isEmpty() ? 0 : typeList.get(0).entryCount;
curEntries = new theme_entry[cnt];
// memset(curEntries, Res_value::TYPE_NULL, buff_size);
curPI.types[t] = new type_info();
curPI.types[t].numEntries = cnt;
curPI.types[t].entries = curEntries;
}
numEntries = curPI.types[t].numEntries;
}
if (e >= numEntries) {
ALOGE("Style contains key with bad entry: 0x%08x\n", attrRes);
bagIndex++;
continue;
}
if (curEntries[e] == null) {
curEntries[e] = new theme_entry();
}
theme_entry curEntry = curEntries[e];
if (styleDebug) {
ResourceName outName = new ResourceName();
mTable.getResourceName(attrRes, true, outName);
System.out.println(" " + outName + "(" + attrRes + ")" + " := " + bagEntry.map.value);
}
if (kDebugTableNoisy) {
ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
attrRes, bag.get()[bagIndex].map.value.dataType, bag.get()[bagIndex].map.value.data,
curEntry.value.dataType);
}
if (force || (curEntry.value.dataType == TYPE_NULL
&& curEntry.value.data != Res_value.DATA_NULL_EMPTY)) {
curEntry.stringBlock = bagEntry.stringBlock;
curEntry.typeSpecFlags |= bagTypeSpecFlags.get();
curEntry.value = new Res_value(bagEntry.map.value);
}
bagIndex++;
}
mTable.unlock();
if (kDebugTableTheme) {
ALOGI("Applying style 0x%08x (force=%s) theme %s...\n", resID, force, this);
dumpToLog();
}
return NO_ERROR;
}
private void dumpToLog() {
}
public int setTo(ResTableTheme other) {
styles.clear();
styles.addAll(other.styles);
if (kDebugTableTheme) {
ALOGI("Setting theme %s from theme %s...\n", this, other);
dumpToLog();
other.dumpToLog();
}
if (mTable == other.mTable) {
for (int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy