1package com.xtremelabs.robolectric.shadows;
2
3import android.util.DisplayMetrics;
4import android.util.TypedValue;
5import com.xtremelabs.robolectric.internal.Implementation;
6import com.xtremelabs.robolectric.internal.Implements;
7import com.xtremelabs.robolectric.internal.RealObject;
8
9import 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)
21public class ShadowTypedValue {
22
23    @RealObject TypedValue typedValue;
24
25    @Implementation
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
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
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
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
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
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
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
116    public float getDimension(DisplayMetrics metrics)
117    {
118        return complexToDimension(typedValue.data, metrics);
119    }
120
121    @Implementation
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
134    public float getFraction(float base, float pbase)
135    {
136        return complexToFraction(typedValue.data, base, pbase);
137    }
138
139    @Implementation
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
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
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
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