1 package com.xtremelabs.robolectric.shadows;
2 
3 import android.util.DisplayMetrics;
4 import android.util.TypedValue;
5 import com.xtremelabs.robolectric.internal.Implementation;
6 import com.xtremelabs.robolectric.internal.Implements;
7 import com.xtremelabs.robolectric.internal.RealObject;
8 
9 import static android.util.TypedValue.*;
10 
11 
12 /**
13  * Portions of this file were copied from the Android source code,
14  * licenced under the Apache License, Version 2.0
15  *
16  * http://www.google.com/codesearch/p?hl=en#uX1GffpyOZk/core/java/android/util/TypedValue.java
17  */
18 
19 @SuppressWarnings({"UnusedDeclaration"})
20 @Implements(TypedValue.class)
21 public class ShadowTypedValue {
22 
23     @RealObject TypedValue typedValue;
24 
25     @Implementation
getFloat()26     public final float getFloat() {
27         return Float.intBitsToFloat(typedValue.data);
28     }
29 
30     private static final float MANTISSA_MULT =
31         1.0f / (1<<TypedValue.COMPLEX_MANTISSA_SHIFT);
32     private static final float[] RADIX_MULTS = new float[] {
33         1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
34         1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
35     };
36 
37     @Implementation
complexToFloat(int complex)38     public static float complexToFloat(int complex)
39     {
40         return (complex&(TypedValue.COMPLEX_MANTISSA_MASK
41                    <<TypedValue.COMPLEX_MANTISSA_SHIFT))
42             * RADIX_MULTS[(complex>>TypedValue.COMPLEX_RADIX_SHIFT)
43                             & TypedValue.COMPLEX_RADIX_MASK];
44     }
45 
46     @Implementation
complexToDimension(int data, DisplayMetrics metrics)47     public static float complexToDimension(int data, DisplayMetrics metrics)
48     {
49         return applyDimension(
50             (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
51             complexToFloat(data),
52             metrics);
53     }
54 
55     @Implementation
complexToDimensionPixelOffset(int data, DisplayMetrics metrics)56     public static int complexToDimensionPixelOffset(int data,
57             DisplayMetrics metrics)
58     {
59         return (int)applyDimension(
60                 (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
61                 complexToFloat(data),
62                 metrics);
63     }
64 
65     @Implementation
complexToDimensionPixelSize(int data, DisplayMetrics metrics)66     public static int complexToDimensionPixelSize(int data,
67             DisplayMetrics metrics)
68     {
69         final float value = complexToFloat(data);
70         final float f = applyDimension(
71                 (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
72                 value,
73                 metrics);
74         final int res = (int)(f+0.5f);
75         if (res != 0) return res;
76         if (value == 0) return 0;
77         if (value > 0) return 1;
78         return -1;
79     }
80 
81     @Implementation
complexToDimensionNoisy(int data, DisplayMetrics metrics)82     public static float complexToDimensionNoisy(int data, DisplayMetrics metrics)
83     {
84         float res = complexToDimension(data, metrics);
85         System.out.println(
86             "Dimension (0x" + ((data>>TypedValue.COMPLEX_MANTISSA_SHIFT)
87                                & TypedValue.COMPLEX_MANTISSA_MASK)
88             + "*" + (RADIX_MULTS[(data>>TypedValue.COMPLEX_RADIX_SHIFT)
89                                 & TypedValue.COMPLEX_RADIX_MASK] / MANTISSA_MULT)
90             + ")" + DIMENSION_UNIT_STRS[(data>>COMPLEX_UNIT_SHIFT)
91                                 & COMPLEX_UNIT_MASK]
92             + " = " + res);
93         return res;
94     }
95 
96     @Implementation
applyDimension(int unit, float value, DisplayMetrics metrics)97     public static float applyDimension(int unit, float value, DisplayMetrics metrics) {
98         switch (unit) {
99             case COMPLEX_UNIT_PX:
100                 return value;
101             case COMPLEX_UNIT_DIP:
102                 return value * metrics.density;
103             case COMPLEX_UNIT_SP:
104                 return value * metrics.scaledDensity;
105             case COMPLEX_UNIT_PT:
106                 return value * metrics.xdpi * (1.0f / 72);
107             case COMPLEX_UNIT_IN:
108                 return value * metrics.xdpi;
109             case COMPLEX_UNIT_MM:
110                 return value * metrics.xdpi * (1.0f / 25.4f);
111         }
112         return 0;
113     }
114 
115     @Implementation
getDimension(DisplayMetrics metrics)116     public float getDimension(DisplayMetrics metrics)
117     {
118         return complexToDimension(typedValue.data, metrics);
119     }
120 
121     @Implementation
complexToFraction(int data, float base, float pbase)122     public static float complexToFraction(int data, float base, float pbase)
123     {
124         switch ((data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK) {
125         case COMPLEX_UNIT_FRACTION:
126             return complexToFloat(data) * base;
127         case COMPLEX_UNIT_FRACTION_PARENT:
128             return complexToFloat(data) * pbase;
129         }
130         return 0;
131     }
132 
133     @Implementation
getFraction(float base, float pbase)134     public float getFraction(float base, float pbase)
135     {
136         return complexToFraction(typedValue.data, base, pbase);
137     }
138 
139     @Implementation
coerceToString()140     public final CharSequence coerceToString()
141     {
142         int t = typedValue.type;
143         if (t == TYPE_STRING) {
144             return typedValue.string;
145         }
146         return coerceToString(t, typedValue.data);
147     }
148 
149     private static final String[] DIMENSION_UNIT_STRS = new String[] {
150         "px", "dip", "sp", "pt", "in", "mm"
151     };
152     private static final String[] FRACTION_UNIT_STRS = new String[] {
153         "%", "%p"
154     };
155 
156     @Implementation
coerceToString(int type, int data)157     public static final String coerceToString(int type, int data)
158     {
159         switch (type) {
160         case TYPE_NULL:
161             return null;
162         case TYPE_REFERENCE:
163             return "@" + data;
164         case TYPE_ATTRIBUTE:
165             return "?" + data;
166         case TYPE_FLOAT:
167             return Float.toString(Float.intBitsToFloat(data));
168         case TYPE_DIMENSION:
169             return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[
170                 (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
171         case TYPE_FRACTION:
172             return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[
173                 (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
174         case TYPE_INT_HEX:
175             return "0x" + Integer.toHexString(data);
176         case TYPE_INT_BOOLEAN:
177             return data != 0 ? "true" : "false";
178         }
179 
180         if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
181             return "#" + Integer.toHexString(data);
182         } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
183             return Integer.toString(data);
184         }
185 
186         return null;
187     }
188 
189     @Implementation
setTo(TypedValue other)190     public void setTo(TypedValue other)
191     {
192         typedValue.type = other.type;
193         typedValue.string = other.string;
194         typedValue.data = other.data;
195         typedValue.assetCookie = other.assetCookie;
196         typedValue.resourceId = other.resourceId;
197         typedValue.density = other.density;
198     }
199 
200     @Implementation
toString()201     public String toString()
202     {
203         StringBuilder sb = new StringBuilder();
204         sb.append("TypedValue{t=0x").append(Integer.toHexString(typedValue.type));
205         sb.append("/d=0x").append(Integer.toHexString(typedValue.data));
206         if (typedValue.type == TYPE_STRING) {
207             sb.append(" \"").append(typedValue.string != null ? typedValue.string : "<null>").append("\"");
208         }
209         if (typedValue.assetCookie != 0) {
210             sb.append(" a=").append(typedValue.assetCookie);
211         }
212         if (typedValue.resourceId != 0) {
213             sb.append(" r=0x").append(Integer.toHexString(typedValue.resourceId));
214         }
215         sb.append("}");
216         return sb.toString();
217     }
218 }
219