
org.robolectric.res.android.ResTableTheme Maven / Gradle / Ivy
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 < Res_MAXPACKAGE; i++) {
if (mPackages[i] != null) {
mPackages[i] = null;
}
if (other.mPackages[i] != null) {
mPackages[i] = copy_package(other.mPackages[i]);
} else {
mPackages[i] = null;
}
}
} else {
// @todo: need to really implement this, not just copy
// the system package (which is still wrong because it isn't
// fixing up resource references).
for (int i = 0; i < Res_MAXPACKAGE; i++) {
if (mPackages[i] != null) {
mPackages[i] = null;
}
// todo: C++ code presumably assumes index 0 is system, and only system
// if (i == 0 && other.mPackages[i] != null) {
if (other.mPackages[i] != null) {
mPackages[i] = copy_package(other.mPackages[i]);
} else {
mPackages[i] = null;
}
}
}
mTypeSpecFlags = other.mTypeSpecFlags;
if (kDebugTableTheme) {
ALOGI("Final theme:");
dumpToLog();
}
return NO_ERROR;
}
private static package_info copy_package(package_info pi) {
package_info newpi = new package_info();
for (int j = 0; j <= Res_MAXTYPE; j++) {
if (pi.types[j] == null) {
newpi.types[j] = null;
continue;
}
int cnt = pi.types[j].numEntries;
newpi.types[j] = new type_info();
newpi.types[j].numEntries = cnt;
theme_entry[] te = pi.types[j].entries;
if (te != null) {
theme_entry[] newte = new theme_entry[cnt];
newpi.types[j].entries = newte;
// memcpy(newte, te, cnt*sizeof(theme_entry));
for (int i = 0; i < newte.length; i++) {
newte[i] = te[i] == null ? null : new theme_entry(te[i]); // deep copy
}
} else {
newpi.types[j].entries = null;
}
}
return newpi;
}
static class theme_entry {
int stringBlock;
int typeSpecFlags;
Res_value value = new Res_value();
theme_entry() {}
/** copy constructor. Performs a deep copy */
public theme_entry(theme_entry src) {
if (src != null) {
stringBlock = src.stringBlock;
typeSpecFlags = src.typeSpecFlags;
value = new Res_value(src.value);
}
}
}
;
static class type_info {
int numEntries;
theme_entry[] entries;
type_info() {}
/** copy constructor. Performs a deep copy */
type_info(type_info src) {
numEntries = src.numEntries;
entries = new theme_entry[src.entries.length];
for (int i = 0; i < src.entries.length; i++) {
if (src.entries[i] == null) {
entries[i] = null;
} else {
entries[i] = new theme_entry(src.entries[i]);
}
}
}
}
;
static class package_info {
type_info[] types = new type_info[Res_MAXTYPE + 1];
package_info() {}
/** copy constructor. Performs a deep copy */
package_info(package_info src) {
for (int i = 0; i < src.types.length; i++) {
if (src.types[i] == null) {
types[i] = null;
} else {
types[i] = new type_info(src.types[i]);
}
}
}
}
;
static final int Res_MAXPACKAGE = 255;
static final int Res_MAXTYPE = 255;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy