convert ResourceConfiguration.isBetterThan() to Java
diff --git a/resources/src/main/java/org/robolectric/res/android/ResourceConfiguration.java b/resources/src/main/java/org/robolectric/res/android/ResourceConfiguration.java
index 95c82b4..6e6128d 100644
--- a/resources/src/main/java/org/robolectric/res/android/ResourceConfiguration.java
+++ b/resources/src/main/java/org/robolectric/res/android/ResourceConfiguration.java
@@ -484,5 +484,370 @@
V value = map.get(key);
return value != null ? value : defaultValue;
}
+
+
+ // constants for isBetterThan...
+ private static final int MASK_LAYOUTDIR = SCREENLAYOUT_LAYOUTDIR_MASK;
+ private static final int MASK_SCREENSIZE = SCREENLAYOUT_SIZE_MASK;
+ private static final int ACONFIGURATION_SCREENSIZE_NORMAL = SCREENLAYOUT_SIZE_NORMAL;
+ private static final int SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL;
+ private static final int MASK_SCREENLONG = SCREENLAYOUT_LONG_MASK;
+ private static final int MASK_SCREENROUND = SCREENLAYOUT_ROUND_MASK;
+ private static final int MASK_UI_MODE_TYPE = UI_MODE_TYPE_MASK;
+ private static final int MASK_UI_MODE_NIGHT = UI_MODE_NIGHT_MASK;
+ private static final int ACONFIGURATION_DENSITY_MEDIUM = DENSITY_DPI_MDPI;
+ private static final int DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM;
+ private static final int ACONFIGURATION_DENSITY_ANY = DENSITY_DPI_ANY;
+ private static final int DENSITY_ANY = ACONFIGURATION_DENSITY_ANY;
+ private static final int MASK_KEYSHIDDEN = 0x0003;
+ public static final int MASK_NAVHIDDEN = 0x000c;
+
+
+ boolean isBetterThan(ResourceConfiguration o, ResourceConfiguration requested) {
+ if (requested != null) {
+ if (imsi() != 0 || o.imsi() != 0) {
+ if ((mcc != o.mcc) && requested.mcc != 0) {
+ return (mcc != 0);
+ }
+
+ if ((mnc != o.mnc) && requested.mnc != 0) {
+ return (mnc != 0);
+ }
+ }
+
+ if (isLocaleBetterThan(o, requested)) {
+ return true;
+ }
+
+ if (screenLayout != 0 || o.screenLayout != 0) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
+ && (requested.screenLayout & MASK_LAYOUTDIR) != 0) {
+ int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
+ int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
+ return (myLayoutDir > oLayoutDir);
+ }
+ }
+
+ if (smallestScreenWidthDp != 0 || o.smallestScreenWidthDp != 0) {
+ // The configuration closest to the actual size is best.
+ // We assume that larger configs have already been filtered
+ // out at this point. That means we just want the largest one.
+ if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
+ return smallestScreenWidthDp > o.smallestScreenWidthDp;
+ }
+ }
+
+ if (screenSizeDp() != 0 || o.screenSizeDp() != 0) {
+ // "Better" is based on the sum of the difference between both
+ // width and height from the requested dimensions. We are
+ // assuming the invalid configs (with smaller dimens) have
+ // already been filtered. Note that if a particular dimension
+ // is unspecified, we will end up with a large value (the
+ // difference between 0 and the requested dimension), which is
+ // good since we will prefer a config that has specified a
+ // dimension value.
+ int myDelta = 0, otherDelta = 0;
+ if (requested.screenWidthDp != 0) {
+ myDelta += requested.screenWidthDp - screenWidthDp;
+ otherDelta += requested.screenWidthDp - o.screenWidthDp;
+ }
+ if (requested.screenHeightDp != 0) {
+ myDelta += requested.screenHeightDp - screenHeightDp;
+ otherDelta += requested.screenHeightDp - o.screenHeightDp;
+ }
+
+ if (myDelta != otherDelta) {
+ return myDelta < otherDelta;
+ }
+ }
+
+ if (screenLayout != 0 || o.screenLayout != 0) {
+ if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0
+ && (requested.screenLayout & MASK_SCREENSIZE) != 0) {
+ // A little backwards compatibility here: undefined is
+ // considered equivalent to normal. But only if the
+ // requested size is at least normal; otherwise, small
+ // is better than the default.
+ int mySL = (screenLayout & MASK_SCREENSIZE);
+ int oSL = (o.screenLayout & MASK_SCREENSIZE);
+ int fixedMySL = mySL;
+ int fixedOSL = oSL;
+ if ((requested.screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) {
+ if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL;
+ if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL;
+ }
+ // For screen size, the best match is the one that is
+ // closest to the requested screen size, but not over
+ // (the not over part is dealt with in match() below).
+ if (fixedMySL == fixedOSL) {
+ // If the two are the same, but 'this' is actually
+ // undefined, then the other is really a better match.
+ if (mySL == 0) return false;
+ return true;
+ }
+ if (fixedMySL != fixedOSL) {
+ return fixedMySL > fixedOSL;
+ }
+ }
+ if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0
+ && (requested.screenLayout & MASK_SCREENLONG) != 0) {
+ return (screenLayout & MASK_SCREENLONG) != 0;
+ }
+ }
+
+ if (screenLayout2 != 0 || o.screenLayout2 != 0) {
+ if (((screenLayout2^o.screenLayout2) & MASK_SCREENROUND) != 0 &&
+ (requested.screenLayout2 & MASK_SCREENROUND) != 0) {
+ return (screenLayout2 & MASK_SCREENROUND) != 0;
+ }
+ }
+
+ if ((orientation != o.orientation) && requested.orientation != 0) {
+ return (orientation) != 0;
+ }
+
+ if (uiMode != 0 || o.uiMode != 0) {
+ if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0
+ && (requested.uiMode & MASK_UI_MODE_TYPE) != 0) {
+ return (uiMode & MASK_UI_MODE_TYPE) != 0;
+ }
+ if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0
+ && (requested.uiMode & MASK_UI_MODE_NIGHT) != 0) {
+ return (uiMode & MASK_UI_MODE_NIGHT) != 0;
+ }
+ }
+
+ if (screenType() != 0 || o.screenType() != 0) {
+ if (density != o.density) {
+ // Use the system default density (DENSITY_MEDIUM, 160dpi) if none specified.
+ final int thisDensity = density != 0 ? density : DENSITY_MEDIUM;
+ final int otherDensity = o.density != 0 ? o.density : DENSITY_MEDIUM;
+
+ // We always prefer DENSITY_ANY over scaling a density bucket.
+ if (thisDensity == DENSITY_ANY) {
+ return true;
+ } else if (otherDensity == DENSITY_ANY) {
+ return false;
+ }
+
+ int requestedDensity = requested.density;
+ if (requested.density == 0 ||
+ requested.density == DENSITY_ANY) {
+ requestedDensity = DENSITY_MEDIUM;
+ }
+
+ // DENSITY_ANY is now dealt with. We should look to
+ // pick a density bucket and potentially scale it.
+ // Any density is potentially useful
+ // because the system will scale it. Scaling down
+ // is generally better than scaling up.
+ int h = thisDensity;
+ int l = otherDensity;
+ boolean bImBigger = true;
+ if (l > h) {
+ int t = h;
+ h = l;
+ l = t;
+ bImBigger = false;
+ }
+
+ if (requestedDensity >= h) {
+ // requested value higher than both l and h, give h
+ return bImBigger;
+ }
+ if (l >= requestedDensity) {
+ // requested value lower than both l and h, give l
+ return !bImBigger;
+ }
+ // saying that scaling down is 2x better than up
+ if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
+ return !bImBigger;
+ } else {
+ return bImBigger;
+ }
+ }
+
+ if ((touchscreen != o.touchscreen) && requested.touchscreen != 0) {
+ return (touchscreen) != 0;
+ }
+ }
+
+ if (input() != 0 || o.input() != 0) {
+ final int keysHidden = inputFlags & MASK_KEYSHIDDEN;
+ final int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN;
+ if (keysHidden != oKeysHidden) {
+ final int reqKeysHidden =
+ requested.inputFlags & MASK_KEYSHIDDEN;
+ if (reqKeysHidden != 0) {
+
+ if (keysHidden == 0) return false;
+ if (oKeysHidden == 0) return true;
+ // For compatibility, we count KEYSHIDDEN_NO as being
+ // the same as KEYSHIDDEN_SOFT. Here we disambiguate
+ // these by making an exact match more specific.
+ if (reqKeysHidden == keysHidden) return true;
+ if (reqKeysHidden == oKeysHidden) return false;
+ }
+ }
+
+ final int navHidden = inputFlags & MASK_NAVHIDDEN;
+ final int oNavHidden = o.inputFlags & MASK_NAVHIDDEN;
+ if (navHidden != oNavHidden) {
+ final int reqNavHidden =
+ requested.inputFlags & MASK_NAVHIDDEN;
+ if (reqNavHidden != 0) {
+
+ if (navHidden == 0) return false;
+ if (oNavHidden == 0) return true;
+ }
+ }
+
+ if ((keyboard != o.keyboard) && requested.keyboard != 0) {
+ return (keyboard) != 0;
+ }
+
+ if ((navigation != o.navigation) && requested.navigation != 0) {
+ return (navigation) != 0;
+ }
+ }
+
+ if (screenSize() != 0 || o.screenSize() != 0) {
+ // "Better" is based on the sum of the difference between both
+ // width and height from the requested dimensions. We are
+ // assuming the invalid configs (with smaller sizes) have
+ // already been filtered. Note that if a particular dimension
+ // is unspecified, we will end up with a large value (the
+ // difference between 0 and the requested dimension), which is
+ // good since we will prefer a config that has specified a
+ // size value.
+ int myDelta = 0, otherDelta = 0;
+ if (requested.screenWidth != 0) {
+ myDelta += requested.screenWidth - screenWidth;
+ otherDelta += requested.screenWidth - o.screenWidth;
+ }
+ if (requested.screenHeight != 0) {
+ myDelta += requested.screenHeight - screenHeight;
+ otherDelta += requested.screenHeight - o.screenHeight;
+ }
+ if (myDelta != otherDelta) {
+ return myDelta < otherDelta;
+ }
+ }
+
+ if (version() != 0 || o.version() != 0) {
+ if ((sdkVersion != o.sdkVersion) && requested.sdkVersion != 0) {
+ return (sdkVersion > o.sdkVersion);
+ }
+
+ if ((minorVersion != o.minorVersion) &&
+ requested.minorVersion != 0) {
+ return (minorVersion) != 0;
+ }
+ }
+
+ return false;
+ }
+ return isMoreSpecificThan(o);
+ }
+
+ /**
+ * union {
+ struct {
+ // Mobile country code (from SIM). 0 means "any".
+ uint16_t mcc;
+ // Mobile network code (from SIM). 0 means "any".
+ uint16_t mnc;
+ };
+ uint32_t imsi;
+ };
+ */
+ private int imsi() {
+ return (mcc & 0xffff) << 16 | (mnc & 0xffff);
+ }
+
+ /**
+ * union {
+ struct {
+ uint16_t screenWidth;
+ uint16_t screenHeight;
+ };
+ uint32_t screenSize;
+ };
+ */
+ private int screenSize() {
+ return (screenWidth & 0xffff) << 16 | (screenHeight & 0xffff);
+ }
+
+
+ /**
+ * union {
+ struct {
+ uint16_t screenWidthDp;
+ uint16_t screenHeightDp;
+ };
+ uint32_t screenSizeDp;
+ };
+ */
+ private int screenSizeDp() {
+ // screenWidthDp and screenHeightDp are really shorts...
+ return (screenWidthDp & 0xffff) << 16 | (screenHeightDp & 0xffff);
+ }
+
+ /**
+ union {
+ struct {
+ uint8_t orientation;
+ uint8_t touchscreen;
+ uint16_t density;
+ };
+ uint32_t screenType;
+ };
+ */
+ private int screenType() {
+ return (orientation & 0xff << 24) | (touchscreen * 0xff << 16) | density & 0xffff;
+ }
+
+ /**
+ *
+ union {
+ struct {
+ uint8_t keyboard;
+ uint8_t navigation;
+ uint8_t inputFlags;
+ uint8_t inputPad0;
+ };
+ uint32_t input;
+ };
+ */
+ private int input() {
+ // TODO is Pad Zeros?
+ return (keyboard & 0xff << 24) | (navigation & 0xff << 16) | (inputFlags & 0xff << 8);
+ }
+
+ /**
+ * union {
+ struct {
+ uint16_t sdkVersion;
+ // For now minorVersion must always be 0!!! Its meaning
+ // is currently undefined.
+ uint16_t minorVersion;
+ };
+ uint32_t version;
+ };
+ */
+ private int version() {
+ return (sdkVersion & 0xffff) << 16 | (minorVersion & 0xffff);
+ }
+
+ // TODO Convert from C
+ private boolean isLocaleBetterThan(ResourceConfiguration o, ResourceConfiguration requested) {
+ return false;
+ }
+
+ // TODO Convert from C
+ private boolean isMoreSpecificThan(ResourceConfiguration o) {
+ return false;
+ }
+
}