5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | *
11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package cn.zaratustra.axmlparser.utils; 18 | 19 | import cn.zaratustra.axmlparser.model.StartTagChunk; 20 | 21 | import java.util.Scanner; 22 | 23 | /** 24 | * Container for a dynamically typed data value. Primarily used with 25 | * {@link android.content.res.Resources} for holding resource values. 26 | */ 27 | public class TypedValue { 28 | /** 29 | * The value contains no data. 30 | */ 31 | public static final int TYPE_NULL = 0x00; 32 | 33 | /** 34 | * The data field holds a resource identifier. 35 | */ 36 | public static final int TYPE_REFERENCE = 0x01; 37 | /** 38 | * The data field holds an attribute resource identifier 39 | * (referencing an attribute in the current theme style, not a resource 40 | * entry). 41 | */ 42 | public static final int TYPE_ATTRIBUTE = 0x02; 43 | /** 44 | * The string field holds string data. In addition, if 45 | * data is non-zero then it is the string block index of the 46 | * string and assetCookie is the set of assets the string came 47 | * from. 48 | */ 49 | public static final int TYPE_STRING = 0x03; 50 | /** 51 | * The data field holds an IEEE 754 floating point number. 52 | */ 53 | public static final int TYPE_FLOAT = 0x04; 54 | /** 55 | * The data field holds a complex number encoding a dimension 56 | * value. 57 | */ 58 | public static final int TYPE_DIMENSION = 0x05; 59 | /** 60 | * The data field holds a complex number encoding a fraction of a 61 | * container. 62 | */ 63 | public static final int TYPE_FRACTION = 0x06; 64 | /** 65 | * The data holds a dynamic res table reference, which needs to be 66 | * resolved before it can be used like TYPE_REFERENCE 67 | */ 68 | public static final int TYPE_DYNAMIC_REFERENCE = 0x07; 69 | /** 70 | * The data an attribute resource identifier, which needs to be resolved 71 | * before it can be used like a TYPE_ATTRIBUTE. 72 | */ 73 | public static final int TYPE_DYNAMIC_ATTRIBUTE = 0x08; 74 | /** 75 | * Identifies the start of plain integer values. Any type value from this to 76 | * {@link #TYPE_LAST_INT} means the data field holds a generic 77 | * integer value. 78 | */ 79 | public static final int TYPE_FIRST_INT = 0x10; 80 | 81 | /** 82 | * The data field holds a number that was originally specified in 83 | * decimal. 84 | */ 85 | public static final int TYPE_INT_DEC = 0x10; 86 | /** 87 | * The data field holds a number that was originally specified in 88 | * hexadecimal (0xn). 89 | */ 90 | public static final int TYPE_INT_HEX = 0x11; 91 | /** 92 | * The data field holds 0 or 1 that was originally specified as 93 | * "false" or "true". 94 | */ 95 | public static final int TYPE_INT_BOOLEAN = 0x12; 96 | 97 | /** 98 | * Identifies the start of integer values that were specified as color 99 | * constants (starting with '#'). 100 | */ 101 | public static final int TYPE_FIRST_COLOR_INT = 0x1c; 102 | 103 | /** 104 | * The data field holds a color that was originally specified as 105 | * #aarrggbb. 106 | */ 107 | public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; 108 | /** 109 | * The data field holds a color that was originally specified as 110 | * #rrggbb. 111 | */ 112 | public static final int TYPE_INT_COLOR_RGB8 = 0x1d; 113 | /** 114 | * The data field holds a color that was originally specified as 115 | * #argb. 116 | */ 117 | public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; 118 | /** 119 | * The data field holds a color that was originally specified as 120 | * #rgb. 121 | */ 122 | public static final int TYPE_INT_COLOR_RGB4 = 0x1f; 123 | 124 | /** 125 | * Identifies the end of integer values that were specified as color 126 | * constants. 127 | */ 128 | public static final int TYPE_LAST_COLOR_INT = 0x1f; 129 | 130 | /** 131 | * Identifies the end of plain integer values. 132 | */ 133 | public static final int TYPE_LAST_INT = 0x1f; 134 | 135 | /* ------------------------------------------------------------ */ 136 | 137 | /** 138 | * Complex data: bit location of unit information. 139 | */ 140 | public static final int COMPLEX_UNIT_SHIFT = 0; 141 | /** 142 | * Complex data: mask to extract unit information (after shifting by 143 | * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as defined 144 | * below. 145 | */ 146 | public static final int COMPLEX_UNIT_MASK = 0xf; 147 | 148 | /** 149 | * {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. 150 | */ 151 | public static final int COMPLEX_UNIT_PX = 0; 152 | /** 153 | * {@link #TYPE_DIMENSION} complex unit: Value is Device Independent Pixels. 154 | */ 155 | public static final int COMPLEX_UNIT_DIP = 1; 156 | /** 157 | * {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. 158 | */ 159 | public static final int COMPLEX_UNIT_SP = 2; 160 | /** 161 | * {@link #TYPE_DIMENSION} complex unit: Value is in points. 162 | */ 163 | public static final int COMPLEX_UNIT_PT = 3; 164 | /** 165 | * {@link #TYPE_DIMENSION} complex unit: Value is in inches. 166 | */ 167 | public static final int COMPLEX_UNIT_IN = 4; 168 | /** 169 | * {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. 170 | */ 171 | public static final int COMPLEX_UNIT_MM = 5; 172 | 173 | /** 174 | * {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall size. 175 | */ 176 | public static final int COMPLEX_UNIT_FRACTION = 0; 177 | /** 178 | * {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. 179 | */ 180 | public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; 181 | 182 | /** 183 | * Complex data: where the radix information is, telling where the decimal 184 | * place appears in the mantissa. 185 | */ 186 | public static final int COMPLEX_RADIX_SHIFT = 4; 187 | /** 188 | * Complex data: mask to extract radix information (after shifting by 189 | * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point 190 | * representations as defined below. 191 | */ 192 | public static final int COMPLEX_RADIX_MASK = 0x3; 193 | 194 | /** 195 | * Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 196 | */ 197 | public static final int COMPLEX_RADIX_23p0 = 0; 198 | /** 199 | * Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn 200 | */ 201 | public static final int COMPLEX_RADIX_16p7 = 1; 202 | /** 203 | * Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn 204 | */ 205 | public static final int COMPLEX_RADIX_8p15 = 2; 206 | /** 207 | * Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn 208 | */ 209 | public static final int COMPLEX_RADIX_0p23 = 3; 210 | 211 | /** 212 | * Complex data: bit location of mantissa information. 213 | */ 214 | public static final int COMPLEX_MANTISSA_SHIFT = 8; 215 | /** 216 | * Complex data: mask to extract mantissa information (after shifting by 217 | * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; the 218 | * top bit is the sign. 219 | */ 220 | public static final int COMPLEX_MANTISSA_MASK = 0xffffff; 221 | 222 | /* ------------------------------------------------------------ */ 223 | 224 | /** 225 | * {@link #TYPE_NULL} data indicating the value was not specified. 226 | */ 227 | public static final int DATA_NULL_UNDEFINED = 0; 228 | /** 229 | * {@link #TYPE_NULL} data indicating the value was explicitly set to null. 230 | */ 231 | public static final int DATA_NULL_EMPTY = 1; 232 | 233 | /* ------------------------------------------------------------ */ 234 | 235 | /** 236 | * If {@link #density} is equal to this value, then the density should be 237 | * treated as the system's default density value: 238 | * {@link DisplayMetrics#DENSITY_DEFAULT}. 239 | */ 240 | public static final int DENSITY_DEFAULT = 0; 241 | 242 | /** 243 | * If {@link #density} is equal to this value, then there is no density 244 | * associated with the resource and it should not be scaled. 245 | */ 246 | public static final int DENSITY_NONE = 0xffff; 247 | 248 | /* ------------------------------------------------------------ */ 249 | 250 | /** 251 | * The type held by this value, as defined by the constants here. This tells 252 | * you how to interpret the other fields in the object. 253 | */ 254 | public int type; 255 | 256 | private static final float MANTISSA_MULT = 1.0f / (1 << TypedValue.COMPLEX_MANTISSA_SHIFT); 257 | private static final float[] RADIX_MULTS = new float[]{ 258 | 1.0f * MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT, 259 | 1.0f / (1 << 15) * MANTISSA_MULT, 1.0f / (1 << 23) * MANTISSA_MULT}; 260 | 261 | /** 262 | * Retrieve the base value from a complex data integer. This uses the 263 | * {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of 264 | * the data to compute a floating point representation of the number they 265 | * describe. The units are ignored. 266 | * 267 | * @param complex A complex data value. 268 | * @return A floating point value corresponding to the complex data. 269 | */ 270 | public static float complexToFloat(int complex) { 271 | return (complex & (TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT)) 272 | * RADIX_MULTS[(complex >> TypedValue.COMPLEX_RADIX_SHIFT) 273 | & TypedValue.COMPLEX_RADIX_MASK]; 274 | } 275 | 276 | private static final String[] DIMENSION_UNIT_STRS = new String[]{"px", 277 | "dip", "sp", "pt", "in", "mm"}; 278 | private static final String[] FRACTION_UNIT_STRS = new String[]{"%", "%p"}; 279 | 280 | /** 281 | * Perform type conversion as per {@link #coerceToString()} on an explicitly 282 | * supplied type and data. 283 | * 284 | * @param type The data type identifier. 285 | * @param data The data value. 286 | * @return String The coerced string value. If the value is null or the type 287 | * is not known, null is returned. 288 | */ 289 | public static final String coerceToString(int type, int data) { 290 | switch (type) { 291 | case TYPE_NULL: 292 | return null; 293 | case TYPE_REFERENCE: 294 | return "@" + data; 295 | case TYPE_ATTRIBUTE: 296 | return "?" + data; 297 | case TYPE_FLOAT: 298 | return Float.toString(Float.intBitsToFloat(data)); 299 | case TYPE_DIMENSION: 300 | return Float.toString(complexToFloat(data)) 301 | + DIMENSION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT) 302 | & COMPLEX_UNIT_MASK]; 303 | case TYPE_FRACTION: 304 | return Float.toString(complexToFloat(data) * 100) 305 | + FRACTION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT) 306 | & COMPLEX_UNIT_MASK]; 307 | case TYPE_INT_HEX: 308 | return String.format("0x%08X", data); 309 | case TYPE_INT_BOOLEAN: 310 | return data != 0 ? "true" : "false"; 311 | } 312 | 313 | if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { 314 | String res = String.format("%08x", data); 315 | char[] vals = res.toCharArray(); 316 | switch (type) { 317 | default: 318 | case TYPE_INT_COLOR_ARGB8:// #AaRrGgBb 319 | break; 320 | case TYPE_INT_COLOR_RGB8:// #FFRrGgBb->#RrGgBb 321 | res = res.substring(2); 322 | break; 323 | case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB 324 | res = new StringBuffer().append(vals[0]).append(vals[2]) 325 | .append(vals[4]).append(vals[6]).toString(); 326 | break; 327 | case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB 328 | res = new StringBuffer().append(vals[2]).append(vals[4]) 329 | .append(vals[6]).toString(); 330 | break; 331 | } 332 | return "#" + res; 333 | } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { 334 | String res; 335 | switch (type) { 336 | default: 337 | case TYPE_INT_DEC: 338 | res = Integer.toString(data); 339 | break; 340 | } 341 | return res; 342 | } 343 | 344 | return null; 345 | } 346 | 347 | public static void initAttribute(StartTagChunk.Attribute attribute, String valueString) throws Exception { 348 | if (valueString.startsWith("@")) { 349 | attribute.setType(TYPE_REFERENCE); 350 | attribute.setData(Integer.valueOf(valueString.substring(1))); 351 | attribute.setValueString(-1); 352 | } else if (valueString.startsWith("?")) { 353 | attribute.setType(TYPE_ATTRIBUTE); 354 | attribute.setData(Integer.valueOf(valueString.substring(1))); 355 | attribute.setValueString(-1); 356 | } else if (isDimension(valueString)) { 357 | throw new Exception("Do not support dimension value in AndroidManifest"); 358 | } else if (isRadix(valueString)) { 359 | throw new Exception("Do not support radix value in AndroidManifest"); 360 | } else if (valueString.startsWith("#")) { 361 | throw new Exception("Do not support color value in AndroidManifest"); 362 | } else if (valueString.startsWith("0x")) { 363 | attribute.setType(TYPE_INT_HEX); 364 | attribute.setData(Integer.decode(valueString)); 365 | attribute.setValueString(-1); 366 | } else if (valueString.equals("true") || valueString.equals("false")) { 367 | attribute.setType(TYPE_INT_BOOLEAN); 368 | attribute.setData(valueString.equals("true") ? -1 : 0); 369 | attribute.setValueString(-1); 370 | } else if (isInteger(valueString)) { 371 | attribute.setType(16); 372 | attribute.setData(Integer.valueOf(valueString)); 373 | attribute.setValueString(-1); 374 | } else if (isFloat(valueString)) { 375 | attribute.setType(TYPE_FLOAT); 376 | attribute.setData(Float.floatToIntBits(Float.valueOf(valueString))); 377 | attribute.setValueString(-1); 378 | } else { 379 | attribute.setType(TYPE_NULL); 380 | throw new Exception("nothing get in attribute"); 381 | } 382 | } 383 | 384 | private static boolean isRadix(String valueString) { 385 | for (int i = 0; i < FRACTION_UNIT_STRS.length; i++) { 386 | if (valueString.endsWith(FRACTION_UNIT_STRS[i])) { 387 | return true; 388 | } 389 | } 390 | return false; 391 | } 392 | 393 | private static boolean isDimension(String valueString) { 394 | for (int i = 0; i < DIMENSION_UNIT_STRS.length; i++) { 395 | if (valueString.endsWith(DIMENSION_UNIT_STRS[i])) { 396 | try { 397 | Integer.parseInt(valueString.substring(0, valueString.length() - DIMENSION_UNIT_STRS[i].length())); 398 | return true; 399 | } catch (Exception e) { 400 | continue; 401 | } 402 | } 403 | } 404 | return false; 405 | } 406 | 407 | public static boolean isInteger(String s) { 408 | try { 409 | Integer.parseInt(s); 410 | } catch (NumberFormatException e) { 411 | return false; 412 | } catch (NullPointerException e) { 413 | return false; 414 | } 415 | return true; 416 | } 417 | 418 | public static boolean isFloat(String s) { 419 | try { 420 | Float.parseFloat(s); 421 | } catch (NumberFormatException e) { 422 | return false; 423 | } catch (NullPointerException e) { 424 | return false; 425 | } 426 | return true; 427 | } 428 | 429 | } 430 | -------------------------------------------------------------------------------- /test-data/AndroidManifest-normal.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/AndroidManifest-normal.xml -------------------------------------------------------------------------------- /test-data/AndroidManifest.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/AndroidManifest.xml -------------------------------------------------------------------------------- /test-data/abc.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/abc.apk -------------------------------------------------------------------------------- /test-data/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/app-debug.apk -------------------------------------------------------------------------------- /test-data/app-release-normal.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaratustraN/axml-parser/fb1501185346b3c8e73baec222055d62452c5fb6/test-data/app-release-normal.apk --------------------------------------------------------------------------------