All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.res.android.ResTableTheme Maven / Gradle / Ivy

There is a newer version: 4.14.1
Show newest version
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