├── .gitignore ├── libs └── android.jar ├── pom.xml ├── src └── main │ └── java │ ├── android │ ├── content │ │ ├── Context.java │ │ └── res │ │ │ ├── AXmlResourceParser.java │ │ │ ├── ChunkUtil.java │ │ │ ├── IntReader.java │ │ │ ├── Resources.java │ │ │ ├── StringBlock.java │ │ │ └── XmlResourceParser.java │ ├── graphics │ │ └── Color.java │ ├── text │ │ └── TextUtils.java │ └── util │ │ ├── AttributeSet.java │ │ └── TypedValue.java │ └── com │ └── bigzhao │ └── xml2axml │ ├── AxmlUtils.java │ ├── ComplexConsts.java │ ├── DefaultReferenceResolver.java │ ├── Encoder.java │ ├── IntWriter.java │ ├── NotImplementedException.java │ ├── ReferenceResolver.java │ ├── ValueType.java │ ├── chunks │ ├── AttrChunk.java │ ├── Chunk.java │ ├── ChunkType.java │ ├── EndNameSpaceChunk.java │ ├── EndTagChunk.java │ ├── ResourceMapChunk.java │ ├── StartNameSpaceChunk.java │ ├── StartTagChunk.java │ ├── StringPoolChunk.java │ ├── TagChunk.java │ ├── ValueChunk.java │ └── XmlChunk.java │ └── test │ ├── AXMLPrinter.java │ └── Main.java └── xml2axml.iml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | data/ 4 | -------------------------------------------------------------------------------- /libs/android.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l741589/xml2axml/72fffbea84251bf08668b9144f709a0347b82945/libs/android.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.bigzhao.axml 8 | xml2axml 9 | 1.0.2-SNAPSHOT 10 | 11 | 12 | 13 | commons-io 14 | commons-io 15 | 1.4 16 | compile 17 | 18 | 19 | net.sf.kxml 20 | kxml2 21 | 2.3.0 22 | compile 23 | 24 | 25 | org.apache.commons 26 | commons-lang3 27 | 3.4 28 | compile 29 | 30 | 31 | com.google.android 32 | android 33 | 21.0.0 34 | system 35 | ${project.basedir}\libs\android.jar 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-compiler-plugin 44 | 2.0.2 45 | 46 | 1.7 47 | 1.7 48 | UTF-8 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/android/content/Context.java: -------------------------------------------------------------------------------- 1 | package android.content; 2 | 3 | import android.content.res.Resources; 4 | 5 | /** 6 | * Created by Roy on 15-10-6. 7 | */ 8 | public class Context { 9 | private Resources resources=new Resources(); 10 | public Resources getResources() { 11 | return resources; 12 | } 13 | 14 | public String getPackageName() { 15 | return "com.bighzao.xml2axml"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/android/content/res/AXmlResourceParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.content.res; 17 | 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.io.Reader; 21 | import org.xmlpull.v1.XmlPullParserException; 22 | import android.util.TypedValue; 23 | 24 | /** 25 | * @author Dmitry Skiba 26 | * 27 | * Binary xml files parser. 28 | * 29 | * Parser has only two states: 30 | * (1) Operational state, which parser obtains after first successful call 31 | * to next() and retains until open(), close(), or failed call to next(). 32 | * (2) Closed state, which parser obtains after open(), close(), or failed 33 | * call to next(). In this state methods return invalid values or throw exceptions. 34 | * 35 | * TODO: 36 | * * check all methods in closed state 37 | * 38 | */ 39 | public class AXmlResourceParser implements XmlResourceParser { 40 | 41 | public AXmlResourceParser() { 42 | resetEventInfo(); 43 | } 44 | 45 | public void open(InputStream stream) { 46 | close(); 47 | if (stream!=null) { 48 | m_reader=new IntReader(stream,false); 49 | } 50 | } 51 | 52 | public void close() { 53 | if (!m_operational) { 54 | return; 55 | } 56 | m_operational=false; 57 | m_reader.close(); 58 | m_reader=null; 59 | m_strings=null; 60 | m_resourceIDs=null; 61 | m_namespaces.reset(); 62 | resetEventInfo(); 63 | } 64 | 65 | /////////////////////////////////// iteration 66 | 67 | public int next() throws XmlPullParserException,IOException { 68 | if (m_reader==null) { 69 | throw new XmlPullParserException("Parser is not opened.",this,null); 70 | } 71 | try { 72 | doNext(); 73 | return m_event; 74 | } 75 | catch (IOException e) { 76 | close(); 77 | throw e; 78 | } 79 | } 80 | 81 | public int nextToken() throws XmlPullParserException,IOException { 82 | return next(); 83 | } 84 | 85 | public int nextTag() throws XmlPullParserException,IOException { 86 | int eventType=next(); 87 | if (eventType==TEXT && isWhitespace()) { 88 | eventType=next(); 89 | } 90 | if (eventType!=START_TAG && eventType!=END_TAG) { 91 | throw new XmlPullParserException("Expected start or end tag.",this,null); 92 | } 93 | return eventType; 94 | } 95 | 96 | public String nextText() throws XmlPullParserException,IOException { 97 | if(getEventType()!=START_TAG) { 98 | throw new XmlPullParserException("Parser must be on START_TAG to read next text.",this,null); 99 | } 100 | int eventType=next(); 101 | if (eventType==TEXT) { 102 | String result=getText(); 103 | eventType=next(); 104 | if (eventType!=END_TAG) { 105 | throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.",this,null); 106 | } 107 | return result; 108 | } else if (eventType==END_TAG) { 109 | return ""; 110 | } else { 111 | throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.",this,null); 112 | } 113 | } 114 | 115 | public void require(int type,String namespace,String name) throws XmlPullParserException,IOException { 116 | if (type!=getEventType() || 117 | (namespace!=null && !namespace.equals(getNamespace())) || 118 | (name!=null && !name.equals(getName()))) 119 | { 120 | throw new XmlPullParserException(TYPES[type]+" is expected.",this,null); 121 | } 122 | } 123 | 124 | public int getDepth() { 125 | return m_namespaces.getDepth()-1; 126 | } 127 | 128 | public int getEventType() throws XmlPullParserException { 129 | return m_event; 130 | } 131 | 132 | public int getLineNumber() { 133 | return m_lineNumber; 134 | } 135 | 136 | public String getName() { 137 | if (m_name==-1 || (m_event!=START_TAG && m_event!=END_TAG)) { 138 | return null; 139 | } 140 | return m_strings.getString(m_name); 141 | } 142 | 143 | public String getText() { 144 | if (m_name==-1 || m_event!=TEXT) { 145 | return null; 146 | } 147 | return m_strings.getString(m_name); 148 | } 149 | 150 | public char[] getTextCharacters(int[] holderForStartAndLength) { 151 | String text=getText(); 152 | if (text==null) { 153 | return null; 154 | } 155 | holderForStartAndLength[0]=0; 156 | holderForStartAndLength[1]=text.length(); 157 | char[] chars=new char[text.length()]; 158 | text.getChars(0,text.length(),chars,0); 159 | return chars; 160 | } 161 | 162 | public String getNamespace() { 163 | return m_strings.getString(m_namespaceUri); 164 | } 165 | 166 | public String getPrefix() { 167 | int prefix=m_namespaces.findPrefix(m_namespaceUri); 168 | return m_strings.getString(prefix); 169 | } 170 | 171 | public String getPositionDescription() { 172 | return "XML line #"+getLineNumber(); 173 | } 174 | 175 | public int getNamespaceCount(int depth) throws XmlPullParserException { 176 | return m_namespaces.getAccumulatedCount(depth); 177 | } 178 | 179 | public String getNamespacePrefix(int pos) throws XmlPullParserException { 180 | int prefix=m_namespaces.getPrefix(pos); 181 | return m_strings.getString(prefix); 182 | } 183 | 184 | public String getNamespaceUri(int pos) throws XmlPullParserException { 185 | int uri=m_namespaces.getUri(pos); 186 | return m_strings.getString(uri); 187 | } 188 | 189 | /////////////////////////////////// attributes 190 | 191 | public String getClassAttribute() { 192 | if (m_classAttribute==-1) { 193 | return null; 194 | } 195 | int offset=getAttributeOffset(m_classAttribute); 196 | int value=m_attributes[offset+ATTRIBUTE_IX_VALUE_STRING]; 197 | return m_strings.getString(value); 198 | } 199 | 200 | public String getIdAttribute() { 201 | if (m_idAttribute==-1) { 202 | return null; 203 | } 204 | int offset=getAttributeOffset(m_idAttribute); 205 | int value=m_attributes[offset+ATTRIBUTE_IX_VALUE_STRING]; 206 | return m_strings.getString(value); 207 | } 208 | 209 | public int getIdAttributeResourceValue(int defaultValue) { 210 | if (m_idAttribute==-1) { 211 | return defaultValue; 212 | } 213 | int offset=getAttributeOffset(m_idAttribute); 214 | int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE]; 215 | if (valueType!=TypedValue.TYPE_REFERENCE) { 216 | return defaultValue; 217 | } 218 | return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 219 | } 220 | 221 | public int getStyleAttribute() { 222 | if (m_styleAttribute==-1) { 223 | return 0; 224 | } 225 | int offset=getAttributeOffset(m_styleAttribute); 226 | return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 227 | } 228 | 229 | public int getAttributeCount() { 230 | if (m_event!=START_TAG) { 231 | return -1; 232 | } 233 | return m_attributes.length/ATTRIBUTE_LENGHT; 234 | } 235 | 236 | public String getAttributeNamespace(int index) { 237 | int offset=getAttributeOffset(index); 238 | int namespace=m_attributes[offset+ATTRIBUTE_IX_NAMESPACE_URI]; 239 | if (namespace==-1) { 240 | return ""; 241 | } 242 | return m_strings.getString(namespace); 243 | } 244 | 245 | public String getAttributePrefix(int index) { 246 | int offset=getAttributeOffset(index); 247 | int uri=m_attributes[offset+ATTRIBUTE_IX_NAMESPACE_URI]; 248 | int prefix=m_namespaces.findPrefix(uri); 249 | if (prefix==-1) { 250 | return ""; 251 | } 252 | return m_strings.getString(prefix); 253 | } 254 | 255 | public String getAttributeName(int index) { 256 | int offset=getAttributeOffset(index); 257 | int name=m_attributes[offset+ATTRIBUTE_IX_NAME]; 258 | if (name==-1) { 259 | return ""; 260 | } 261 | return m_strings.getString(name); 262 | } 263 | 264 | public int getAttributeNameResource(int index) { 265 | int offset=getAttributeOffset(index); 266 | int name=m_attributes[offset+ATTRIBUTE_IX_NAME]; 267 | if (m_resourceIDs==null || 268 | name<0 || name>=m_resourceIDs.length) 269 | { 270 | return 0; 271 | } 272 | return m_resourceIDs[name]; 273 | } 274 | 275 | public int getAttributeValueType(int index) { 276 | int offset=getAttributeOffset(index); 277 | return m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE]; 278 | } 279 | 280 | public int getAttributeValueData(int index) { 281 | int offset=getAttributeOffset(index); 282 | return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 283 | } 284 | 285 | public String getAttributeValue(int index) { 286 | int offset=getAttributeOffset(index); 287 | int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE]; 288 | if (valueType==TypedValue.TYPE_STRING) { 289 | int valueString=m_attributes[offset+ATTRIBUTE_IX_VALUE_STRING]; 290 | return m_strings.getString(valueString); 291 | } 292 | int valueData=m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 293 | return "";//TypedValue.coerceToString(valueType,valueData); 294 | } 295 | 296 | public boolean getAttributeBooleanValue(int index,boolean defaultValue) { 297 | return getAttributeIntValue(index,defaultValue?1:0)!=0; 298 | } 299 | 300 | public float getAttributeFloatValue(int index,float defaultValue) { 301 | int offset=getAttributeOffset(index); 302 | int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE]; 303 | if (valueType==TypedValue.TYPE_FLOAT) { 304 | int valueData=m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 305 | return Float.intBitsToFloat(valueData); 306 | } 307 | return defaultValue; 308 | } 309 | 310 | public int getAttributeIntValue(int index,int defaultValue) { 311 | int offset=getAttributeOffset(index); 312 | int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE]; 313 | if (valueType>=TypedValue.TYPE_FIRST_INT && 314 | valueType<=TypedValue.TYPE_LAST_INT) 315 | { 316 | return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 317 | } 318 | return defaultValue; 319 | } 320 | 321 | public int getAttributeUnsignedIntValue(int index,int defaultValue) { 322 | return getAttributeIntValue(index,defaultValue); 323 | } 324 | 325 | public int getAttributeResourceValue(int index,int defaultValue) { 326 | int offset=getAttributeOffset(index); 327 | int valueType=m_attributes[offset+ATTRIBUTE_IX_VALUE_TYPE]; 328 | if (valueType==TypedValue.TYPE_REFERENCE) { 329 | return m_attributes[offset+ATTRIBUTE_IX_VALUE_DATA]; 330 | } 331 | return defaultValue; 332 | } 333 | 334 | public String getAttributeValue(String namespace,String attribute) { 335 | int index=findAttribute(namespace,attribute); 336 | if (index==-1) { 337 | return null; 338 | } 339 | return getAttributeValue(index); 340 | } 341 | 342 | public boolean getAttributeBooleanValue(String namespace,String attribute,boolean defaultValue) { 343 | int index=findAttribute(namespace,attribute); 344 | if (index==-1) { 345 | return defaultValue; 346 | } 347 | return getAttributeBooleanValue(index,defaultValue); 348 | } 349 | 350 | public float getAttributeFloatValue(String namespace,String attribute,float defaultValue) { 351 | int index=findAttribute(namespace,attribute); 352 | if (index==-1) { 353 | return defaultValue; 354 | } 355 | return getAttributeFloatValue(index,defaultValue); 356 | } 357 | 358 | public int getAttributeIntValue(String namespace,String attribute,int defaultValue) { 359 | int index=findAttribute(namespace,attribute); 360 | if (index==-1) { 361 | return defaultValue; 362 | } 363 | return getAttributeIntValue(index,defaultValue); 364 | } 365 | 366 | public int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue) { 367 | int index=findAttribute(namespace,attribute); 368 | if (index==-1) { 369 | return defaultValue; 370 | } 371 | return getAttributeUnsignedIntValue(index,defaultValue); 372 | } 373 | 374 | public int getAttributeResourceValue(String namespace,String attribute,int defaultValue) { 375 | int index=findAttribute(namespace,attribute); 376 | if (index==-1) { 377 | return defaultValue; 378 | } 379 | return getAttributeResourceValue(index,defaultValue); 380 | } 381 | 382 | public int getAttributeListValue(int index,String[] options,int defaultValue) { 383 | // TODO implement 384 | return 0; 385 | } 386 | 387 | public int getAttributeListValue(String namespace,String attribute,String[] options,int defaultValue) { 388 | // TODO implement 389 | return 0; 390 | } 391 | 392 | public String getAttributeType(int index) { 393 | return "CDATA"; 394 | } 395 | 396 | public boolean isAttributeDefault(int index) { 397 | return false; 398 | } 399 | 400 | /////////////////////////////////// dummies 401 | 402 | public void setInput(InputStream stream,String inputEncoding) throws XmlPullParserException { 403 | throw new XmlPullParserException(E_NOT_SUPPORTED); 404 | } 405 | public void setInput(Reader reader) throws XmlPullParserException { 406 | throw new XmlPullParserException(E_NOT_SUPPORTED); 407 | } 408 | 409 | public String getInputEncoding() { 410 | return null; 411 | } 412 | 413 | public int getColumnNumber() { 414 | return -1; 415 | } 416 | 417 | public boolean isEmptyElementTag() throws XmlPullParserException { 418 | return false; 419 | } 420 | 421 | public boolean isWhitespace() throws XmlPullParserException { 422 | return false; 423 | } 424 | 425 | public void defineEntityReplacementText(String entityName,String replacementText) throws XmlPullParserException { 426 | throw new XmlPullParserException(E_NOT_SUPPORTED); 427 | } 428 | 429 | public String getNamespace(String prefix) { 430 | throw new RuntimeException(E_NOT_SUPPORTED); 431 | } 432 | 433 | public Object getProperty(String name) { 434 | return null; 435 | } 436 | public void setProperty(String name,Object value) throws XmlPullParserException { 437 | throw new XmlPullParserException(E_NOT_SUPPORTED); 438 | } 439 | 440 | public boolean getFeature(String feature) { 441 | return false; 442 | } 443 | public void setFeature(String name,boolean value) throws XmlPullParserException { 444 | throw new XmlPullParserException(E_NOT_SUPPORTED); 445 | } 446 | 447 | ///////////////////////////////////////////// implementation 448 | 449 | /** 450 | * Namespace stack, holds prefix+uri pairs, as well as 451 | * depth information. 452 | * All information is stored in one int[] array. 453 | * Array consists of depth frames: 454 | * Data=DepthFrame*; 455 | * DepthFrame=Count+[Prefix+Uri]*+Count; 456 | * Count='count of Prefix+Uri pairs'; 457 | * Yes, count is stored twice, to enable bottom-up traversal. 458 | * increaseDepth adds depth frame, decreaseDepth removes it. 459 | * push/pop operations operate only in current depth frame. 460 | * decreaseDepth removes any remaining (not pop'ed) namespace pairs. 461 | * findXXX methods search all depth frames starting 462 | * from the last namespace pair of current depth frame. 463 | * All functions that operate with int, use -1 as 'invalid value'. 464 | * 465 | * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! 466 | * 467 | */ 468 | private static final class NamespaceStack { 469 | public NamespaceStack() { 470 | m_data=new int[32]; 471 | } 472 | 473 | public final void reset() { 474 | m_dataLength=0; 475 | m_count=0; 476 | m_depth=0; 477 | } 478 | 479 | public final int getTotalCount() { 480 | return m_count; 481 | } 482 | 483 | public final int getCurrentCount() { 484 | if (m_dataLength==0) { 485 | return 0; 486 | } 487 | int offset=m_dataLength-1; 488 | return m_data[offset]; 489 | } 490 | 491 | public final int getAccumulatedCount(int depth) { 492 | if (m_dataLength==0 || depth<0) { 493 | return 0; 494 | } 495 | if (depth>m_depth) { 496 | depth=m_depth; 497 | } 498 | int accumulatedCount=0; 499 | int offset=0; 500 | for (;depth!=0;--depth) { 501 | int count=m_data[offset]; 502 | accumulatedCount+=count; 503 | offset+=(2+count*2); 504 | } 505 | return accumulatedCount; 506 | } 507 | 508 | public final void push(int prefix,int uri) { 509 | if (m_depth==0) { 510 | increaseDepth(); 511 | } 512 | ensureDataCapacity(2); 513 | int offset=m_dataLength-1; 514 | int count=m_data[offset]; 515 | m_data[offset-1-count*2]=count+1; 516 | m_data[offset]=prefix; 517 | m_data[offset+1]=uri; 518 | m_data[offset+2]=count+1; 519 | m_dataLength+=2; 520 | m_count+=1; 521 | } 522 | 523 | public final boolean pop(int prefix,int uri) { 524 | if (m_dataLength==0) { 525 | return false; 526 | } 527 | int offset=m_dataLength-1; 528 | int count=m_data[offset]; 529 | for (int i=0,o=offset-2;i!=count;++i,o-=2) { 530 | if (m_data[o]!=prefix || m_data[o+1]!=uri) { 531 | continue; 532 | } 533 | count-=1; 534 | if (i==0) { 535 | m_data[o]=count; 536 | o-=(1+count*2); 537 | m_data[o]=count; 538 | } else { 539 | m_data[offset]=count; 540 | offset-=(1+2+count*2); 541 | m_data[offset]=count; 542 | System.arraycopy( 543 | m_data,o+2, 544 | m_data,o, 545 | m_dataLength-o); 546 | } 547 | m_dataLength-=2; 548 | m_count-=1; 549 | return true; 550 | } 551 | return false; 552 | } 553 | 554 | public final boolean pop() { 555 | if (m_dataLength==0) { 556 | return false; 557 | } 558 | int offset=m_dataLength-1; 559 | int count=m_data[offset]; 560 | if (count==0) { 561 | return false; 562 | } 563 | count-=1; 564 | offset-=2; 565 | m_data[offset]=count; 566 | offset-=(1+count*2); 567 | m_data[offset]=count; 568 | m_dataLength-=2; 569 | m_count-=1; 570 | return true; 571 | } 572 | 573 | public final int getPrefix(int index) { 574 | return get(index,true); 575 | } 576 | 577 | public final int getUri(int index) { 578 | return get(index,false); 579 | } 580 | 581 | public final int findPrefix(int uri) { 582 | return find(uri,false); 583 | } 584 | 585 | public final int findUri(int prefix) { 586 | return find(prefix,true); 587 | } 588 | 589 | public final int getDepth() { 590 | return m_depth; 591 | } 592 | 593 | public final void increaseDepth() { 594 | ensureDataCapacity(2); 595 | int offset=m_dataLength; 596 | m_data[offset]=0; 597 | m_data[offset+1]=0; 598 | m_dataLength+=2; 599 | m_depth+=1; 600 | } 601 | public final void decreaseDepth() { 602 | if (m_dataLength==0) { 603 | return; 604 | } 605 | int offset=m_dataLength-1; 606 | int count=m_data[offset]; 607 | if ((offset-1-count*2)==0) { 608 | return; 609 | } 610 | m_dataLength-=2+count*2; 611 | m_count-=count; 612 | m_depth-=1; 613 | } 614 | 615 | private void ensureDataCapacity(int capacity) { 616 | int available=(m_data.length-m_dataLength); 617 | if (available>capacity) { 618 | return; 619 | } 620 | int newLength=(m_data.length+available)*2; 621 | int[] newData=new int[newLength]; 622 | System.arraycopy(m_data,0,newData,0,m_dataLength); 623 | m_data=newData; 624 | } 625 | 626 | private final int find(int prefixOrUri,boolean prefix) { 627 | if (m_dataLength==0) { 628 | return -1; 629 | } 630 | int offset=m_dataLength-1; 631 | for (int i=m_depth;i!=0;--i) { 632 | int count=m_data[offset]; 633 | offset-=2; 634 | for (;count!=0;--count) { 635 | if (prefix) { 636 | if (m_data[offset]==prefixOrUri) { 637 | return m_data[offset+1]; 638 | } 639 | } else { 640 | if (m_data[offset+1]==prefixOrUri) { 641 | return m_data[offset]; 642 | } 643 | } 644 | offset-=2; 645 | } 646 | } 647 | return -1; 648 | } 649 | 650 | private final int get(int index,boolean prefix) { 651 | if (m_dataLength==0 || index<0) { 652 | return -1; 653 | } 654 | int offset=0; 655 | for (int i=m_depth;i!=0;--i) { 656 | int count=m_data[offset]; 657 | if (index>=count) { 658 | index-=count; 659 | offset+=(2+count*2); 660 | continue; 661 | } 662 | offset+=(1+index*2); 663 | if (!prefix) { 664 | offset+=1; 665 | } 666 | return m_data[offset]; 667 | } 668 | return -1; 669 | } 670 | 671 | private int[] m_data; 672 | private int m_dataLength; 673 | private int m_count; 674 | private int m_depth; 675 | } 676 | 677 | /////////////////////////////////// package-visible 678 | 679 | // final void fetchAttributes(int[] styleableIDs,TypedArray result) { 680 | // result.resetIndices(); 681 | // if (m_attributes==null || m_resourceIDs==null) { 682 | // return; 683 | // } 684 | // boolean needStrings=false; 685 | // for (int i=0,e=styleableIDs.length;i!=e;++i) { 686 | // int id=styleableIDs[i]; 687 | // for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { 688 | // int name=m_attributes[o+ATTRIBUTE_IX_NAME]; 689 | // if (name>=m_resourceIDs.length || 690 | // m_resourceIDs[name]!=id) 691 | // { 692 | // continue; 693 | // } 694 | // int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; 695 | // int valueData; 696 | // int assetCookie; 697 | // if (valueType==TypedValue.TYPE_STRING) { 698 | // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; 699 | // assetCookie=-1; 700 | // needStrings=true; 701 | // } else { 702 | // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; 703 | // assetCookie=0; 704 | // } 705 | // result.addValue(i,valueType,valueData,assetCookie,id,0); 706 | // } 707 | // } 708 | // if (needStrings) { 709 | // result.setStrings(m_strings); 710 | // } 711 | // } 712 | 713 | final StringBlock getStrings() { 714 | return m_strings; 715 | } 716 | 717 | /////////////////////////////////// 718 | 719 | private final int getAttributeOffset(int index) { 720 | if (m_event!=START_TAG) { 721 | throw new IndexOutOfBoundsException("Current event is not START_TAG."); 722 | } 723 | int offset=index*5; 724 | if (offset>=m_attributes.length) { 725 | throw new IndexOutOfBoundsException("Invalid attribute index ("+index+")."); 726 | } 727 | return offset; 728 | } 729 | 730 | private final int findAttribute(String namespace,String attribute) { 731 | if (m_strings==null || attribute==null) { 732 | return -1; 733 | } 734 | int name=m_strings.find(attribute); 735 | if (name==-1) { 736 | return -1; 737 | } 738 | int uri=(namespace!=null)? 739 | m_strings.find(namespace): 740 | -1; 741 | for (int o=0;o!=m_attributes.length;++o) { 742 | if (name==m_attributes[o+ATTRIBUTE_IX_NAME] && 743 | (uri==-1 || uri==m_attributes[o+ATTRIBUTE_IX_NAMESPACE_URI])) 744 | { 745 | return o/ATTRIBUTE_LENGHT; 746 | } 747 | } 748 | return -1; 749 | } 750 | 751 | private final void resetEventInfo() { 752 | m_event=-1; 753 | m_lineNumber=-1; 754 | m_name=-1; 755 | m_namespaceUri=-1; 756 | m_attributes=null; 757 | m_idAttribute=-1; 758 | m_classAttribute=-1; 759 | m_styleAttribute=-1; 760 | } 761 | 762 | private final void doNext() throws IOException { 763 | // Delayed initialization. 764 | if (m_strings==null) { 765 | ChunkUtil.readCheckType(m_reader,CHUNK_AXML_FILE); 766 | /*chunkSize*/m_reader.skipInt(); 767 | m_strings=StringBlock.read(m_reader); 768 | m_namespaces.increaseDepth(); 769 | m_operational=true; 770 | } 771 | 772 | if (m_event==END_DOCUMENT) { 773 | return; 774 | } 775 | 776 | int event=m_event; 777 | resetEventInfo(); 778 | 779 | while (true) { 780 | if (m_decreaseDepth) { 781 | m_decreaseDepth=false; 782 | m_namespaces.decreaseDepth(); 783 | } 784 | 785 | // Fake END_DOCUMENT event. 786 | if (event==END_TAG && 787 | m_namespaces.getDepth()==1 && 788 | m_namespaces.getCurrentCount()==0) 789 | { 790 | m_event=END_DOCUMENT; 791 | break; 792 | } 793 | 794 | int chunkType; 795 | if (event==START_DOCUMENT) { 796 | // Fake event, see CHUNK_XML_START_TAG handler. 797 | chunkType=CHUNK_XML_START_TAG; 798 | } else { 799 | chunkType=m_reader.readInt(); 800 | } 801 | 802 | if (chunkType==CHUNK_RESOURCEIDS) { 803 | int chunkSize=m_reader.readInt(); 804 | if (chunkSize<8 || (chunkSize%4)!=0) { 805 | throw new IOException("Invalid resource ids size ("+chunkSize+")."); 806 | } 807 | m_resourceIDs=m_reader.readIntArray(chunkSize/4-2); 808 | continue; 809 | } 810 | 811 | if (chunkTypeCHUNK_XML_LAST) { 812 | throw new IOException("Invalid chunk type ("+chunkType+")."); 813 | } 814 | 815 | // Fake START_DOCUMENT event. 816 | if (chunkType==CHUNK_XML_START_TAG && event==-1) { 817 | m_event=START_DOCUMENT; 818 | break; 819 | } 820 | 821 | // Common header. 822 | /*chunkSize*/m_reader.skipInt(); 823 | int lineNumber=m_reader.readInt(); 824 | /*0xFFFFFFFF*/m_reader.skipInt(); 825 | 826 | if (chunkType==CHUNK_XML_START_NAMESPACE || 827 | chunkType==CHUNK_XML_END_NAMESPACE) 828 | { 829 | if (chunkType==CHUNK_XML_START_NAMESPACE) { 830 | int prefix=m_reader.readInt(); 831 | int uri=m_reader.readInt(); 832 | m_namespaces.push(prefix,uri); 833 | } else { 834 | /*prefix*/m_reader.skipInt(); 835 | /*uri*/m_reader.skipInt(); 836 | m_namespaces.pop(); 837 | } 838 | continue; 839 | } 840 | 841 | m_lineNumber=lineNumber; 842 | 843 | if (chunkType==CHUNK_XML_START_TAG) { 844 | m_namespaceUri=m_reader.readInt(); 845 | m_name=m_reader.readInt(); 846 | /*flags?*/m_reader.skipInt(); 847 | int attributeCount=m_reader.readInt(); 848 | m_idAttribute=(attributeCount>>>16)-1; 849 | attributeCount&=0xFFFF; 850 | m_classAttribute=m_reader.readInt(); 851 | m_styleAttribute=(m_classAttribute>>>16)-1; 852 | m_classAttribute=(m_classAttribute & 0xFFFF)-1; 853 | m_attributes=m_reader.readIntArray(attributeCount*ATTRIBUTE_LENGHT); 854 | for (int i=ATTRIBUTE_IX_VALUE_TYPE;i>>24); 856 | i+=ATTRIBUTE_LENGHT; 857 | } 858 | m_namespaces.increaseDepth(); 859 | m_event=START_TAG; 860 | break; 861 | } 862 | 863 | if (chunkType==CHUNK_XML_END_TAG) { 864 | m_namespaceUri=m_reader.readInt(); 865 | m_name=m_reader.readInt(); 866 | m_event=END_TAG; 867 | m_decreaseDepth=true; 868 | break; 869 | } 870 | 871 | if (chunkType==CHUNK_XML_TEXT) { 872 | m_name=m_reader.readInt(); 873 | /*?*/m_reader.skipInt(); 874 | /*?*/m_reader.skipInt(); 875 | m_event=TEXT; 876 | break; 877 | } 878 | } 879 | } 880 | 881 | /////////////////////////////////// data 882 | 883 | /* 884 | * All values are essentially indices, e.g. m_name is 885 | * an index of name in m_strings. 886 | */ 887 | 888 | private IntReader m_reader; 889 | private boolean m_operational=false; 890 | 891 | private StringBlock m_strings; 892 | private int[] m_resourceIDs; 893 | private NamespaceStack m_namespaces=new NamespaceStack(); 894 | 895 | private boolean m_decreaseDepth; 896 | 897 | private int m_event; 898 | private int m_lineNumber; 899 | private int m_name; 900 | private int m_namespaceUri; 901 | private int[] m_attributes; 902 | private int m_idAttribute; 903 | private int m_classAttribute; 904 | private int m_styleAttribute; 905 | 906 | private static final String 907 | E_NOT_SUPPORTED ="Method is not supported."; 908 | 909 | private static final int 910 | ATTRIBUTE_IX_NAMESPACE_URI =0, 911 | ATTRIBUTE_IX_NAME =1, 912 | ATTRIBUTE_IX_VALUE_STRING =2, 913 | ATTRIBUTE_IX_VALUE_TYPE =3, 914 | ATTRIBUTE_IX_VALUE_DATA =4, 915 | ATTRIBUTE_LENGHT =5; 916 | 917 | private static final int 918 | CHUNK_AXML_FILE =0x00080003, 919 | CHUNK_RESOURCEIDS =0x00080180, 920 | CHUNK_XML_FIRST =0x00100100, 921 | CHUNK_XML_START_NAMESPACE =0x00100100, 922 | CHUNK_XML_END_NAMESPACE =0x00100101, 923 | CHUNK_XML_START_TAG =0x00100102, 924 | CHUNK_XML_END_TAG =0x00100103, 925 | CHUNK_XML_TEXT =0x00100104, 926 | CHUNK_XML_LAST =0x00100104; 927 | } 928 | -------------------------------------------------------------------------------- /src/main/java/android/content/res/ChunkUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.content.res; 17 | 18 | import java.io.IOException; 19 | 20 | /** 21 | * @author Dmitry Skiba 22 | * 23 | */ 24 | class ChunkUtil { 25 | 26 | public static final void readCheckType(IntReader reader,int expectedType) throws IOException { 27 | int type=reader.readInt(); 28 | if (type!=expectedType) { 29 | throw new IOException( 30 | "Expected chunk of type 0x"+Integer.toHexString(expectedType)+ 31 | ", read 0x"+Integer.toHexString(type)+"."); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/android/content/res/IntReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.content.res; 17 | 18 | import java.io.EOFException; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | 22 | /** 23 | * @author Dmitry Skiba 24 | * 25 | * Simple helper class that allows reading of integers. 26 | * 27 | * TODO: 28 | * * implement buffering 29 | * 30 | */ 31 | public final class IntReader { 32 | 33 | public IntReader() { 34 | } 35 | public IntReader(InputStream stream,boolean bigEndian) { 36 | reset(stream,bigEndian); 37 | } 38 | 39 | public final void reset(InputStream stream,boolean bigEndian) { 40 | m_stream=stream; 41 | m_bigEndian=bigEndian; 42 | m_position=0; 43 | } 44 | 45 | public final void close() { 46 | if (m_stream==null) { 47 | return; 48 | } 49 | try { 50 | m_stream.close(); 51 | } 52 | catch (IOException e) { 53 | } 54 | reset(null,false); 55 | } 56 | 57 | public final InputStream getStream() { 58 | return m_stream; 59 | } 60 | 61 | public final boolean isBigEndian() { 62 | return m_bigEndian; 63 | } 64 | public final void setBigEndian(boolean bigEndian) { 65 | m_bigEndian=bigEndian; 66 | } 67 | 68 | public final int readByte() throws IOException { 69 | return readInt(1); 70 | } 71 | public final int readShort() throws IOException { 72 | return readInt(2); 73 | } 74 | public final int readInt() throws IOException { 75 | return readInt(4); 76 | } 77 | 78 | public final int readInt(int length) throws IOException { 79 | if (length<0 || length>4) { 80 | throw new IllegalArgumentException(); 81 | } 82 | int result=0; 83 | if (m_bigEndian) { 84 | for (int i=(length-1)*8;i>=0;i-=8) { 85 | int b=m_stream.read(); 86 | if (b==-1) { 87 | throw new EOFException(); 88 | } 89 | m_position+=1; 90 | result|=(b<0;length-=1) { 114 | array[offset++]=readInt(); 115 | } 116 | } 117 | 118 | public final byte[] readByteArray(int length) throws IOException { 119 | byte[] array=new byte[length]; 120 | int read=m_stream.read(array); 121 | m_position+=read; 122 | if (read!=length) { 123 | throw new EOFException(); 124 | } 125 | return array; 126 | } 127 | 128 | public final void skip(int bytes) throws IOException { 129 | if (bytes<=0) { 130 | return; 131 | } 132 | long skipped=m_stream.skip(bytes); 133 | m_position+=skipped; 134 | if (skipped!=bytes) { 135 | throw new EOFException(); 136 | } 137 | } 138 | 139 | public final void skipInt() throws IOException { 140 | skip(4); 141 | } 142 | 143 | public final int available() throws IOException { 144 | return m_stream.available(); 145 | } 146 | 147 | public final int getPosition() { 148 | return m_position; 149 | } 150 | 151 | /////////////////////////////////// data 152 | 153 | private InputStream m_stream; 154 | private boolean m_bigEndian; 155 | private int m_position; 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/android/content/res/Resources.java: -------------------------------------------------------------------------------- 1 | package android.content.res; 2 | 3 | import javax.annotation.Resource; 4 | import java.lang.reflect.Field; 5 | import java.util.HashMap; 6 | 7 | /** 8 | * Created by Roy on 15-10-6. 9 | */ 10 | public class Resources { 11 | 12 | public HashMap attrMap=new HashMap<>(); 13 | 14 | private void init(){ 15 | try { 16 | Field[] fs = android.R.attr.class.getFields(); 17 | for (Field f : fs) attrMap.put(f.getName(), (Integer)f.get(null)); 18 | }catch (Exception e){ 19 | throw new RuntimeException(e); 20 | } 21 | } 22 | 23 | public Resources(){ 24 | init(); 25 | } 26 | 27 | public int getIdentifier(String name, String type, String pkg) { 28 | if ("android".equals(pkg)&&"attr".equals(type)){ 29 | Integer x=attrMap.get(name); 30 | if (x==null) System.out.println("attr not found: " + name); 31 | else { 32 | System.out.format("@%s:%s/%s=0x%x\n",pkg,type,name,x); 33 | return x; 34 | } 35 | }else{ 36 | System.out.format("@%s:%s/%s=0x%x\n",pkg,type,name,0); 37 | } 38 | return 0; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/android/content/res/StringBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.content.res; 17 | 18 | import java.io.IOException; 19 | 20 | /** 21 | * @author Dmitry Skiba 22 | * 23 | * Block of strings, used in binary xml and arsc. 24 | * 25 | * TODO: 26 | * - implement get() 27 | * 28 | */ 29 | public class StringBlock { 30 | 31 | /** 32 | * Reads whole (including chunk type) string block from stream. 33 | * Stream must be at the chunk type. 34 | */ 35 | public static StringBlock read(IntReader reader) throws IOException { 36 | ChunkUtil.readCheckType(reader,CHUNK_TYPE); 37 | int chunkSize=reader.readInt(); 38 | int stringCount=reader.readInt(); 39 | int styleOffsetCount=reader.readInt(); 40 | /*?*/reader.readInt(); 41 | int stringsOffset=reader.readInt(); 42 | int stylesOffset=reader.readInt(); 43 | 44 | StringBlock block=new StringBlock(); 45 | block.m_stringOffsets=reader.readIntArray(stringCount); 46 | if (styleOffsetCount!=0) { 47 | block.m_styleOffsets=reader.readIntArray(styleOffsetCount); 48 | } 49 | { 50 | int size=((stylesOffset==0)?chunkSize:stylesOffset)-stringsOffset; 51 | if ((size%4)!=0) { 52 | throw new IOException("String data size is not multiple of 4 ("+size+")."); 53 | } 54 | block.m_strings=reader.readIntArray(size/4); 55 | } 56 | if (stylesOffset!=0) { 57 | int size=(chunkSize-stylesOffset); 58 | if ((size%4)!=0) { 59 | throw new IOException("Style data size is not multiple of 4 ("+size+")."); 60 | } 61 | block.m_styles=reader.readIntArray(size/4); 62 | } 63 | 64 | return block; 65 | } 66 | 67 | /** 68 | * Returns number of strings in block. 69 | */ 70 | public int getCount() { 71 | return m_stringOffsets!=null? 72 | m_stringOffsets.length: 73 | 0; 74 | } 75 | 76 | /** 77 | * Returns raw string (without any styling information) at specified index. 78 | */ 79 | public String getString(int index) { 80 | if (index<0 || 81 | m_stringOffsets==null || 82 | index>=m_stringOffsets.length) 83 | { 84 | return null; 85 | } 86 | int offset=m_stringOffsets[index]; 87 | int length=getShort(m_strings,offset); 88 | StringBuilder result=new StringBuilder(length); 89 | for (;length!=0;length-=1) { 90 | offset+=2; 91 | result.append((char)getShort(m_strings,offset)); 92 | } 93 | return result.toString(); 94 | } 95 | 96 | /** 97 | * Not yet implemented. 98 | * 99 | * Returns string with style information (if any). 100 | */ 101 | public CharSequence get(int index) { 102 | return getString(index); 103 | } 104 | 105 | /** 106 | * Returns string with style tags (html-like). 107 | */ 108 | public String getHTML(int index) { 109 | String raw=getString(index); 110 | if (raw==null) { 111 | return raw; 112 | } 113 | int[] style=getStyle(index); 114 | if (style==null) { 115 | return raw; 116 | } 117 | StringBuilder html=new StringBuilder(raw.length()+32); 118 | int offset=0; 119 | while (true) { 120 | int i=-1; 121 | for (int j=0;j!=style.length;j+=3) { 122 | if (style[j+1]==-1) { 123 | continue; 124 | } 125 | if (i==-1 || style[i+1]>style[j+1]) { 126 | i=j; 127 | } 128 | } 129 | int start=((i!=-1)?style[i+1]:raw.length()); 130 | for (int j=0;j!=style.length;j+=3) { 131 | int end=style[j+2]; 132 | if (end==-1 || end>=start) { 133 | continue; 134 | } 135 | if (offset<=end) { 136 | html.append(raw,offset,end+1); 137 | offset=end+1; 138 | } 139 | style[j+2]=-1; 140 | html.append('<'); 141 | html.append('/'); 142 | html.append(getString(style[j])); 143 | html.append('>'); 144 | } 145 | if (offset'); 155 | style[i+1]=-1; 156 | } 157 | return html.toString(); 158 | } 159 | 160 | /** 161 | * Finds index of the string. 162 | * Returns -1 if the string was not found. 163 | */ 164 | public int find(String string) { 165 | if (string==null) { 166 | return -1; 167 | } 168 | for (int i=0;i!=m_stringOffsets.length;++i) { 169 | int offset=m_stringOffsets[i]; 170 | int length=getShort(m_strings,offset); 171 | if (length!=string.length()) { 172 | continue; 173 | } 174 | int j=0; 175 | for (;j!=length;++j) { 176 | offset+=2; 177 | if (string.charAt(j)!=getShort(m_strings,offset)) { 178 | break; 179 | } 180 | } 181 | if (j==length) { 182 | return i; 183 | } 184 | } 185 | return -1; 186 | } 187 | 188 | ///////////////////////////////////////////// implementation 189 | 190 | private StringBlock() { 191 | } 192 | 193 | /** 194 | * Returns style information - array of int triplets, 195 | * where in each triplet: 196 | * * first int is index of tag name ('b','i', etc.) 197 | * * second int is tag start index in string 198 | * * third int is tag end index in string 199 | */ 200 | private int[] getStyle(int index) { 201 | if (m_styleOffsets==null || m_styles==null || 202 | index>=m_styleOffsets.length) 203 | { 204 | return null; 205 | } 206 | int offset=m_styleOffsets[index]/4; 207 | int style[]; 208 | { 209 | int count=0; 210 | for (int i=offset;i>> 16); 236 | } 237 | } 238 | 239 | private int[] m_stringOffsets; 240 | private int[] m_strings; 241 | private int[] m_styleOffsets; 242 | private int[] m_styles; 243 | 244 | private static final int CHUNK_TYPE=0x001C0001; 245 | } 246 | -------------------------------------------------------------------------------- /src/main/java/android/content/res/XmlResourceParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.content.res; 17 | 18 | import org.xmlpull.v1.XmlPullParser; 19 | import android.util.AttributeSet; 20 | 21 | /** 22 | * @author Dmitry Skiba 23 | * 24 | */ 25 | public interface XmlResourceParser extends XmlPullParser, AttributeSet { 26 | void close(); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/android/graphics/Color.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.graphics; 18 | 19 | import java.util.HashMap; 20 | import java.util.Locale; 21 | 22 | /** 23 | * The Color class defines methods for creating and converting color ints. 24 | * Colors are represented as packed ints, made up of 4 bytes: alpha, red, 25 | * green, blue. The values are unpremultiplied, meaning any transparency is 26 | * stored solely in the alpha component, and not in the color components. The 27 | * components are stored as follows (alpha << 24) | (red << 16) | 28 | * (green << 8) | blue. Each component ranges between 0..255 with 0 29 | * meaning no contribution for that component, and 255 meaning 100% 30 | * contribution. Thus opaque-black would be 0xFF000000 (100% opaque but 31 | * no contributions from red, green, or blue), and opaque-white would be 32 | * 0xFFFFFFFF 33 | */ 34 | public class Color { 35 | public static final int BLACK = 0xFF000000; 36 | public static final int DKGRAY = 0xFF444444; 37 | public static final int GRAY = 0xFF888888; 38 | public static final int LTGRAY = 0xFFCCCCCC; 39 | public static final int WHITE = 0xFFFFFFFF; 40 | public static final int RED = 0xFFFF0000; 41 | public static final int GREEN = 0xFF00FF00; 42 | public static final int BLUE = 0xFF0000FF; 43 | public static final int YELLOW = 0xFFFFFF00; 44 | public static final int CYAN = 0xFF00FFFF; 45 | public static final int MAGENTA = 0xFFFF00FF; 46 | public static final int TRANSPARENT = 0; 47 | 48 | /** 49 | * Return the alpha component of a color int. This is the same as saying 50 | * color >>> 24 51 | */ 52 | public static int alpha(int color) { 53 | return color >>> 24; 54 | } 55 | 56 | /** 57 | * Return the red component of a color int. This is the same as saying 58 | * (color >> 16) & 0xFF 59 | */ 60 | public static int red(int color) { 61 | return (color >> 16) & 0xFF; 62 | } 63 | 64 | /** 65 | * Return the green component of a color int. This is the same as saying 66 | * (color >> 8) & 0xFF 67 | */ 68 | public static int green(int color) { 69 | return (color >> 8) & 0xFF; 70 | } 71 | 72 | /** 73 | * Return the blue component of a color int. This is the same as saying 74 | * color & 0xFF 75 | */ 76 | public static int blue(int color) { 77 | return color & 0xFF; 78 | } 79 | 80 | /** 81 | * Return a color-int from red, green, blue components. 82 | * The alpha component is implicity 255 (fully opaque). 83 | * These component values should be [0..255], but there is no 84 | * range check performed, so if they are out of range, the 85 | * returned color is undefined. 86 | * @param red Red component [0..255] of the color 87 | * @param green Green component [0..255] of the color 88 | * @param blue Blue component [0..255] of the color 89 | */ 90 | public static int rgb(int red, int green, int blue) { 91 | return (0xFF << 24) | (red << 16) | (green << 8) | blue; 92 | } 93 | 94 | /** 95 | * Return a color-int from alpha, red, green, blue components. 96 | * These component values should be [0..255], but there is no 97 | * range check performed, so if they are out of range, the 98 | * returned color is undefined. 99 | * @param alpha Alpha component [0..255] of the color 100 | * @param red Red component [0..255] of the color 101 | * @param green Green component [0..255] of the color 102 | * @param blue Blue component [0..255] of the color 103 | */ 104 | public static int argb(int alpha, int red, int green, int blue) { 105 | return (alpha << 24) | (red << 16) | (green << 8) | blue; 106 | } 107 | 108 | /** 109 | * Returns the hue component of a color int. 110 | * 111 | * @return A value between 0.0f and 1.0f 112 | * 113 | * @hide Pending API council 114 | */ 115 | public static float hue(int color) { 116 | int r = (color >> 16) & 0xFF; 117 | int g = (color >> 8) & 0xFF; 118 | int b = color & 0xFF; 119 | 120 | int V = Math.max(b, Math.max(r, g)); 121 | int temp = Math.min(b, Math.min(r, g)); 122 | 123 | float H; 124 | 125 | if (V == temp) { 126 | H = 0; 127 | } else { 128 | final float vtemp = (float) (V - temp); 129 | final float cr = (V - r) / vtemp; 130 | final float cg = (V - g) / vtemp; 131 | final float cb = (V - b) / vtemp; 132 | 133 | if (r == V) { 134 | H = cb - cg; 135 | } else if (g == V) { 136 | H = 2 + cr - cb; 137 | } else { 138 | H = 4 + cg - cr; 139 | } 140 | 141 | H /= 6.f; 142 | if (H < 0) { 143 | H++; 144 | } 145 | } 146 | 147 | return H; 148 | } 149 | 150 | /** 151 | * Returns the saturation component of a color int. 152 | * 153 | * @return A value between 0.0f and 1.0f 154 | * 155 | * @hide Pending API council 156 | */ 157 | public static float saturation(int color) { 158 | int r = (color >> 16) & 0xFF; 159 | int g = (color >> 8) & 0xFF; 160 | int b = color & 0xFF; 161 | 162 | 163 | int V = Math.max(b, Math.max(r, g)); 164 | int temp = Math.min(b, Math.min(r, g)); 165 | 166 | float S; 167 | 168 | if (V == temp) { 169 | S = 0; 170 | } else { 171 | S = (V - temp) / (float) V; 172 | } 173 | 174 | return S; 175 | } 176 | 177 | /** 178 | * Returns the brightness component of a color int. 179 | * 180 | * @return A value between 0.0f and 1.0f 181 | * 182 | * @hide Pending API council 183 | */ 184 | public static float brightness(int color) { 185 | int r = (color >> 16) & 0xFF; 186 | int g = (color >> 8) & 0xFF; 187 | int b = color & 0xFF; 188 | 189 | int V = Math.max(b, Math.max(r, g)); 190 | 191 | return (V / 255.f); 192 | } 193 | 194 | /** 195 | * Parse the color string, and return the corresponding color-int. 196 | * If the string cannot be parsed, throws an IllegalArgumentException 197 | * exception. Supported formats are: 198 | * #RRGGBB 199 | * #AARRGGBB 200 | * 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta', 201 | * 'yellow', 'lightgray', 'darkgray', 'grey', 'lightgrey', 'darkgrey', 202 | * 'aqua', 'fuschia', 'lime', 'maroon', 'navy', 'olive', 'purple', 203 | * 'silver', 'teal' 204 | */ 205 | public static int parseColor(String colorString) { 206 | if (colorString.charAt(0) == '#') { 207 | // Use a long to avoid rollovers on #ffXXXXXX 208 | long color = Long.parseLong(colorString.substring(1), 16); 209 | if (colorString.length() == 7) { 210 | // Set the alpha value 211 | color |= 0x00000000ff000000; 212 | } else if (colorString.length() != 9) { 213 | throw new IllegalArgumentException("Unknown color"); 214 | } 215 | return (int)color; 216 | } else { 217 | Integer color = sColorNameMap.get(colorString.toLowerCase(Locale.ROOT)); 218 | if (color != null) { 219 | return color; 220 | } 221 | } 222 | throw new IllegalArgumentException("Unknown color"); 223 | } 224 | 225 | 226 | 227 | /** 228 | * Convert RGB components to HSV. 229 | * hsv[0] is Hue [0 .. 360) 230 | * hsv[1] is Saturation [0...1] 231 | * hsv[2] is Value [0...1] 232 | * @param red red component value [0..255] 233 | * @param green green component value [0..255] 234 | * @param blue blue component value [0..255] 235 | * @param hsv 3 element array which holds the resulting HSV components. 236 | */ 237 | public static void RGBToHSV(int red, int green, int blue, float hsv[]) { 238 | if (hsv.length < 3) { 239 | throw new RuntimeException("3 components required for hsv"); 240 | } 241 | nativeRGBToHSV(red, green, blue, hsv); 242 | } 243 | 244 | /** 245 | * Convert the argb color to its HSV components. 246 | * hsv[0] is Hue [0 .. 360) 247 | * hsv[1] is Saturation [0...1] 248 | * hsv[2] is Value [0...1] 249 | * @param color the argb color to convert. The alpha component is ignored. 250 | * @param hsv 3 element array which holds the resulting HSV components. 251 | */ 252 | public static void colorToHSV(int color, float hsv[]) { 253 | RGBToHSV((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsv); 254 | } 255 | 256 | /** 257 | * Convert HSV components to an ARGB color. Alpha set to 0xFF. 258 | * hsv[0] is Hue [0 .. 360) 259 | * hsv[1] is Saturation [0...1] 260 | * hsv[2] is Value [0...1] 261 | * If hsv values are out of range, they are pinned. 262 | * @param hsv 3 element array which holds the input HSV components. 263 | * @return the resulting argb color 264 | */ 265 | public static int HSVToColor(float hsv[]) { 266 | return HSVToColor(0xFF, hsv); 267 | } 268 | 269 | /** 270 | * Convert HSV components to an ARGB color. The alpha component is passed 271 | * through unchanged. 272 | * hsv[0] is Hue [0 .. 360) 273 | * hsv[1] is Saturation [0...1] 274 | * hsv[2] is Value [0...1] 275 | * If hsv values are out of range, they are pinned. 276 | * @param alpha the alpha component of the returned argb color. 277 | * @param hsv 3 element array which holds the input HSV components. 278 | * @return the resulting argb color 279 | */ 280 | public static int HSVToColor(int alpha, float hsv[]) { 281 | if (hsv.length < 3) { 282 | throw new RuntimeException("3 components required for hsv"); 283 | } 284 | return nativeHSVToColor(alpha, hsv); 285 | } 286 | 287 | private static native void nativeRGBToHSV(int red, int greed, int blue, float hsv[]); 288 | private static native int nativeHSVToColor(int alpha, float hsv[]); 289 | 290 | private static final HashMap sColorNameMap; 291 | 292 | static { 293 | sColorNameMap = new HashMap(); 294 | sColorNameMap.put("black", BLACK); 295 | sColorNameMap.put("darkgray", DKGRAY); 296 | sColorNameMap.put("gray", GRAY); 297 | sColorNameMap.put("lightgray", LTGRAY); 298 | sColorNameMap.put("white", WHITE); 299 | sColorNameMap.put("red", RED); 300 | sColorNameMap.put("green", GREEN); 301 | sColorNameMap.put("blue", BLUE); 302 | sColorNameMap.put("yellow", YELLOW); 303 | sColorNameMap.put("cyan", CYAN); 304 | sColorNameMap.put("magenta", MAGENTA); 305 | sColorNameMap.put("aqua", 0xFF00FFFF); 306 | sColorNameMap.put("fuchsia", 0xFFFF00FF); 307 | sColorNameMap.put("darkgrey", DKGRAY); 308 | sColorNameMap.put("grey", GRAY); 309 | sColorNameMap.put("lightgrey", LTGRAY); 310 | sColorNameMap.put("lime", 0xFF00FF00); 311 | sColorNameMap.put("maroon", 0xFF800000); 312 | sColorNameMap.put("navy", 0xFF000080); 313 | sColorNameMap.put("olive", 0xFF808000); 314 | sColorNameMap.put("purple", 0xFF800080); 315 | sColorNameMap.put("silver", 0xFFC0C0C0); 316 | sColorNameMap.put("teal", 0xFF008080); 317 | 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/main/java/android/text/TextUtils.java: -------------------------------------------------------------------------------- 1 | package android.text; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | /** 6 | * Created by Roy on 15-10-6. 7 | */ 8 | public class TextUtils extends StringUtils{ 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/android/util/AttributeSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.util; 17 | 18 | /** 19 | * @author Dmitry Skiba 20 | * 21 | */ 22 | public interface AttributeSet { 23 | int getAttributeCount(); 24 | String getAttributeName(int index); 25 | String getAttributeValue(int index); 26 | String getPositionDescription(); 27 | int getAttributeNameResource(int index); 28 | int getAttributeListValue(int index, String options[], int defaultValue); 29 | boolean getAttributeBooleanValue(int index, boolean defaultValue); 30 | int getAttributeResourceValue(int index, int defaultValue); 31 | int getAttributeIntValue(int index, int defaultValue); 32 | int getAttributeUnsignedIntValue(int index, int defaultValue); 33 | float getAttributeFloatValue(int index, float defaultValue); 34 | String getIdAttribute(); 35 | String getClassAttribute(); 36 | int getIdAttributeResourceValue(int index); 37 | int getStyleAttribute(); 38 | String getAttributeValue(String namespace, String attribute); 39 | int getAttributeListValue(String namespace, String attribute, String options[], int defaultValue); 40 | boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue); 41 | int getAttributeResourceValue(String namespace, String attribute, int defaultValue); 42 | int getAttributeIntValue(String namespace, String attribute, int defaultValue); 43 | int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue); 44 | float getAttributeFloatValue(String namespace, String attribute, float defaultValue); 45 | 46 | //TODO: remove 47 | int getAttributeValueType(int index); 48 | int getAttributeValueData(int index); 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/android/util/TypedValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.util; 17 | 18 | /** 19 | * @author Dmitry Skiba 20 | * 21 | */ 22 | public class TypedValue { 23 | 24 | public int type; 25 | public CharSequence string; 26 | public int data; 27 | public int assetCookie; 28 | public int resourceId; 29 | public int changingConfigurations; 30 | 31 | public static final int 32 | TYPE_NULL =0, 33 | TYPE_REFERENCE =1, 34 | TYPE_ATTRIBUTE =2, 35 | TYPE_STRING =3, 36 | TYPE_FLOAT =4, 37 | TYPE_DIMENSION =5, 38 | TYPE_FRACTION =6, 39 | TYPE_FIRST_INT =16, 40 | TYPE_INT_DEC =16, 41 | TYPE_INT_HEX =17, 42 | TYPE_INT_BOOLEAN =18, 43 | TYPE_FIRST_COLOR_INT =28, 44 | TYPE_INT_COLOR_ARGB8 =28, 45 | TYPE_INT_COLOR_RGB8 =29, 46 | TYPE_INT_COLOR_ARGB4 =30, 47 | TYPE_INT_COLOR_RGB4 =31, 48 | TYPE_LAST_COLOR_INT =31, 49 | TYPE_LAST_INT =31; 50 | 51 | public static final int 52 | COMPLEX_UNIT_PX =0, 53 | COMPLEX_UNIT_DIP =1, 54 | COMPLEX_UNIT_SP =2, 55 | COMPLEX_UNIT_PT =3, 56 | COMPLEX_UNIT_IN =4, 57 | COMPLEX_UNIT_MM =5, 58 | COMPLEX_UNIT_SHIFT =0, 59 | COMPLEX_UNIT_MASK =15, 60 | COMPLEX_UNIT_FRACTION =0, 61 | COMPLEX_UNIT_FRACTION_PARENT=1, 62 | COMPLEX_RADIX_23p0 =0, 63 | COMPLEX_RADIX_16p7 =1, 64 | COMPLEX_RADIX_8p15 =2, 65 | COMPLEX_RADIX_0p23 =3, 66 | COMPLEX_RADIX_SHIFT =4, 67 | COMPLEX_RADIX_MASK =3, 68 | COMPLEX_MANTISSA_SHIFT =8, 69 | COMPLEX_MANTISSA_MASK =0xFFFFFF; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/AxmlUtils.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml; 2 | 3 | import org.apache.commons.io.FileUtils; 4 | import org.apache.commons.io.IOUtils; 5 | 6 | import com.bigzhao.xml2axml.test.AXMLPrinter; 7 | 8 | import java.io.*; 9 | 10 | /** 11 | * Created by Roy on 16-4-27. 12 | */ 13 | public class AxmlUtils { 14 | 15 | public static String decode(byte[] data) { 16 | try(InputStream is=new ByteArrayInputStream(data)) { 17 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 18 | AXMLPrinter.out = new PrintStream(os); 19 | AXMLPrinter.decode(is); 20 | byte[] bs = os.toByteArray(); 21 | IOUtils.closeQuietly(os); 22 | AXMLPrinter.out.close(); 23 | return new String(bs, "UTF-8"); 24 | }catch (Exception e){ 25 | e.printStackTrace(); 26 | return null; 27 | } 28 | } 29 | public static String decode(File file) throws IOException { 30 | return decode(FileUtils.readFileToByteArray(file)); 31 | } 32 | 33 | public static byte[] encode(String xml){ 34 | try { 35 | Encoder encoder = new Encoder(); 36 | return encoder.encodeString(null, xml); 37 | }catch (Exception e){ 38 | e.printStackTrace(); 39 | return null; 40 | } 41 | } 42 | public static byte[] encode(File file){ 43 | try { 44 | Encoder encoder = new Encoder(); 45 | return encoder.encodeFile(null, file.getAbsolutePath()); 46 | }catch (Exception e){ 47 | e.printStackTrace(); 48 | return null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/ComplexConsts.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml; 2 | 3 | /** 4 | * Created by Roy on 15-10-5. 5 | */ 6 | public class ComplexConsts { 7 | // Where the unit type information is. This gives us 16 possible 8 | // types, as defined below. 9 | public static final int UNIT_SHIFT = 0; 10 | public static final int UNIT_MASK = 0xf; 11 | 12 | // TYPE_DIMENSION: Value is raw pixels. 13 | public static final int UNIT_PX = 0; 14 | // TYPE_DIMENSION: Value is Device Independent Pixels. 15 | public static final int UNIT_DIP = 1; 16 | // TYPE_DIMENSION: Value is a Scaled device independent Pixels. 17 | public static final int UNIT_SP = 2; 18 | // TYPE_DIMENSION: Value is in points. 19 | public static final int UNIT_PT = 3; 20 | // TYPE_DIMENSION: Value is in inches. 21 | public static final int UNIT_IN = 4; 22 | // TYPE_DIMENSION: Value is in millimeters. 23 | public static final int UNIT_MM = 5; 24 | 25 | // TYPE_FRACTION: A basic fraction of the overall size. 26 | public static final int UNIT_FRACTION = 0; 27 | // TYPE_FRACTION: A fraction of the parent size. 28 | public static final int UNIT_FRACTION_PARENT = 1; 29 | 30 | // Where the radix information is, telling where the decimal place 31 | // appears in the mantissa. This give us 4 possible fixed point 32 | // representations as defined below. 33 | public static final int RADIX_SHIFT = 4; 34 | public static final int RADIX_MASK = 0x3; 35 | 36 | // The mantissa is an integral number -- i.e., 0xnnnnnn.0 37 | public static final int RADIX_23p0 = 0; 38 | // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn 39 | public static final int RADIX_16p7 = 1; 40 | // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn 41 | public static final int RADIX_8p15 = 2; 42 | // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn 43 | public static final int RADIX_0p23 = 3; 44 | 45 | // Where the actual value is. This gives us 23 bits of 46 | // precision. The top bit is the sign. 47 | public static final int MANTISSA_SHIFT = 8; 48 | public static final int MANTISSA_MASK = 0xffffff; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/DefaultReferenceResolver.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml; 2 | 3 | import com.bigzhao.xml2axml.ReferenceResolver; 4 | import com.bigzhao.xml2axml.chunks.ValueChunk; 5 | 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * Created by Roy on 16-4-27. 11 | */ 12 | public class DefaultReferenceResolver implements ReferenceResolver { 13 | static Pattern pat = Pattern.compile("^@\\+?(?:(\\w+):)?(?:(\\w+)/)?(\\w+)$"); 14 | 15 | public int resolve(ValueChunk value, String ref) { 16 | Matcher m=pat.matcher(ref); 17 | if (!m.matches()) throw new RuntimeException("invalid reference"); 18 | String pkg=m.group(1); 19 | String type=m.group(2); 20 | String name=m.group(3); 21 | try { 22 | return Integer.parseInt(name, Encoder.Config.defaultReferenceRadix); 23 | }catch (Exception e){ 24 | e.printStackTrace(); 25 | } 26 | int id=value.getContext().getResources().getIdentifier(name,type,pkg); 27 | return id; 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/Encoder.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml; 2 | 3 | import android.content.Context; 4 | 5 | import com.bigzhao.xml2axml.chunks.Chunk; 6 | import com.bigzhao.xml2axml.chunks.StringPoolChunk; 7 | import com.bigzhao.xml2axml.chunks.TagChunk; 8 | import com.bigzhao.xml2axml.chunks.XmlChunk; 9 | import org.xmlpull.v1.XmlPullParser; 10 | import org.xmlpull.v1.XmlPullParserException; 11 | import org.xmlpull.v1.XmlPullParserFactory; 12 | 13 | import java.io.*; 14 | import java.util.HashSet; 15 | 16 | /** 17 | * Created by Roy on 15-10-4. 18 | */ 19 | public class Encoder { 20 | 21 | public static class Config{ 22 | public static StringPoolChunk.Encoding encoding= StringPoolChunk.Encoding.UNICODE; 23 | public static int defaultReferenceRadix=16; 24 | } 25 | 26 | public byte[] encodeFile(Context context,String filename) throws XmlPullParserException, IOException { 27 | XmlPullParserFactory f=XmlPullParserFactory.newInstance(); 28 | f.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,true); 29 | XmlPullParser p=f.newPullParser(); 30 | p.setInput(new FileInputStream(filename),"UTF-8"); 31 | return encode(context,p); 32 | } 33 | 34 | public byte[] encodeString(Context context,String xml) throws XmlPullParserException, IOException { 35 | XmlPullParserFactory f=XmlPullParserFactory.newInstance(); 36 | f.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,true); 37 | XmlPullParser p=f.newPullParser(); 38 | p.setInput(new StringReader(xml)); 39 | return encode(context,p); 40 | } 41 | 42 | public byte[] encode(Context context,XmlPullParser p) throws XmlPullParserException, IOException { 43 | if (context==null) context=new Context(); 44 | XmlChunk chunk=new XmlChunk(context); 45 | //HashSet strings=new HashSet(); 46 | TagChunk current=null; 47 | for (int i=p.getEventType();i!=XmlPullParser.END_DOCUMENT;i=p.next()){ 48 | switch (i){ 49 | case XmlPullParser.START_DOCUMENT: 50 | break; 51 | case XmlPullParser.START_TAG: 52 | /* strings.add(p.getName()); 53 | strings.add(p.getPrefix()); 54 | strings.add(p.getNamespace()); 55 | int ac=p.getAttributeCount(); 56 | for (int j=0;j>>8)&0xff); 28 | }else{ 29 | os.write((s>>>8)&0xff); 30 | os.write(s&0xff); 31 | } 32 | pos+=2; 33 | } 34 | 35 | public void write(char x) throws IOException { 36 | write((short)x); 37 | } 38 | 39 | public void write(int x) throws IOException { 40 | if (!bigEndian){ 41 | os.write(x&0xff); 42 | x>>>=8; 43 | os.write(x&0xff); 44 | x>>>=8; 45 | os.write(x&0xff); 46 | x>>>=8; 47 | os.write(x&0xff); 48 | }else{ 49 | throw new NotImplementedException(); 50 | } 51 | pos+=4; 52 | } 53 | 54 | public void writePlaceHolder(int len,String name) throws IOException { 55 | os.write(new byte[len]); 56 | } 57 | 58 | public void close() throws IOException { 59 | os.close(); 60 | } 61 | 62 | public int getPos() { 63 | return pos; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/NotImplementedException.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml; 2 | 3 | /** 4 | * Created by Roy on 15-10-6. 5 | */ 6 | public class NotImplementedException extends RuntimeException { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/ReferenceResolver.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml; 2 | 3 | import com.bigzhao.xml2axml.chunks.AttrChunk; 4 | import com.bigzhao.xml2axml.chunks.ValueChunk; 5 | 6 | /** 7 | * Created by Roy on 15-10-5. 8 | */ 9 | public interface ReferenceResolver { 10 | int resolve(ValueChunk value, String ref); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/ValueType.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml; 2 | 3 | /** 4 | * Created by Roy on 15-10-5. 5 | */ 6 | public class ValueType { 7 | // Contains no data. 8 | public static final byte NULL = 0x00; 9 | // The 'data' holds a ResTable_ref, a reference to another resource 10 | // table entry. 11 | public static final byte REFERENCE = 0x01; 12 | // The 'data' holds an attribute resource identifier. 13 | public static final byte ATTRIBUTE = 0x02; 14 | // The 'data' holds an index into the containing resource table's 15 | // global value string pool. 16 | public static final byte STRING = 0x03; 17 | // The 'data' holds a single-precision floating point number. 18 | public static final byte FLOAT = 0x04; 19 | // The 'data' holds a complex number encoding a dimension value; 20 | // such as "100in". 21 | public static final byte DIMENSION = 0x05; 22 | // The 'data' holds a complex number encoding a fraction of a 23 | // container. 24 | public static final byte FRACTION = 0x06; 25 | 26 | // Beginning of integer flavors... 27 | public static final byte FIRST_INT = 0x10; 28 | 29 | // The 'data' is a raw integer value of the form n..n. 30 | public static final byte INT_DEC = 0x10; 31 | // The 'data' is a raw integer value of the form 0xn..n. 32 | public static final byte INT_HEX = 0x11; 33 | // The 'data' is either 0 or 1, for input "false" or "true" respectively. 34 | public static final byte INT_BOOLEAN = 0x12; 35 | 36 | // Beginning of color integer flavors... 37 | public static final byte FIRST_COLOR_INT = 0x1c; 38 | 39 | // The 'data' is a raw integer value of the form #aarrggbb. 40 | public static final byte INT_COLOR_ARGB8 = 0x1c; 41 | // The 'data' is a raw integer value of the form #rrggbb. 42 | public static final byte INT_COLOR_RGB8 = 0x1d; 43 | // The 'data' is a raw integer value of the form #argb. 44 | public static final byte INT_COLOR_ARGB4 = 0x1e; 45 | // The 'data' is a raw integer value of the form #rgb. 46 | public static final byte INT_COLOR_RGB4 = 0x1f; 47 | 48 | // ...end of integer flavors. 49 | public static final byte LAST_COLOR_INT = 0x1f; 50 | 51 | // ...end of integer flavors. 52 | public static final byte LAST_INT = 0x1f; 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/AttrChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import android.graphics.Color; 4 | 5 | import com.bigzhao.xml2axml.ComplexConsts; 6 | import com.bigzhao.xml2axml.IntWriter; 7 | import com.bigzhao.xml2axml.NotImplementedException; 8 | import com.bigzhao.xml2axml.ValueType; 9 | 10 | import java.io.IOException; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * Created by Roy on 15-10-5. 16 | */ 17 | public class AttrChunk extends Chunk{ 18 | private StartTagChunk startTagChunk; 19 | public String prefix; 20 | public String name; 21 | public String namespace; 22 | public String rawValue; 23 | 24 | public AttrChunk(StartTagChunk startTagChunk) { 25 | super(startTagChunk); 26 | this.startTagChunk = startTagChunk; 27 | header.size=20; 28 | } 29 | 30 | 31 | 32 | public ValueChunk value = new ValueChunk(this); 33 | 34 | @Override 35 | public void preWrite() { 36 | value.calc(); 37 | } 38 | 39 | @Override 40 | public void writeEx(IntWriter w) throws IOException { 41 | w.write(startTagChunk.stringIndex(null,namespace)); 42 | w.write(startTagChunk.stringIndex(namespace,name)); 43 | //w.write(-1); 44 | if (value.type==ValueType.STRING) 45 | w.write(startTagChunk.stringIndex(null,rawValue)); 46 | else 47 | w.write(-1); 48 | value.write(w); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/Chunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import android.content.Context; 4 | 5 | import com.bigzhao.xml2axml.IntWriter; 6 | import com.bigzhao.xml2axml.ReferenceResolver; 7 | 8 | import java.io.IOException; 9 | import java.lang.reflect.Constructor; 10 | import java.lang.reflect.ParameterizedType; 11 | import java.lang.reflect.Type; 12 | import java.util.HashMap; 13 | 14 | /** 15 | * Created by Roy on 15-10-4. 16 | */ 17 | public abstract class Chunk { 18 | 19 | public abstract class Header { 20 | public short type; 21 | public short headerSize; 22 | public int size; 23 | 24 | public Header(ChunkType ct){ 25 | type=ct.type; 26 | headerSize=ct.headerSize; 27 | } 28 | 29 | public void write(IntWriter w) throws IOException { 30 | w.write(type); 31 | w.write(headerSize); 32 | w.write(size); 33 | writeEx(w); 34 | } 35 | 36 | public abstract void writeEx(IntWriter w) throws IOException; 37 | } 38 | 39 | public abstract class NodeHeader extends Header{ 40 | 41 | public int lineNo=1; 42 | public int comment=-1; 43 | 44 | public NodeHeader(ChunkType ct) { 45 | super(ct); 46 | headerSize=0x10; 47 | } 48 | 49 | @Override 50 | public void write(IntWriter w) throws IOException { 51 | w.write(type); 52 | w.write(headerSize); 53 | w.write(size); 54 | w.write(lineNo); 55 | w.write(comment); 56 | writeEx(w); 57 | } 58 | 59 | @Override 60 | public void writeEx(IntWriter w) throws IOException { 61 | 62 | } 63 | } 64 | 65 | public class EmptyHeader extends Header{ 66 | public EmptyHeader() { 67 | super(ChunkType.Null); 68 | } 69 | @Override 70 | public void writeEx(IntWriter w) throws IOException {} 71 | @Override 72 | public void write(IntWriter w) throws IOException {} 73 | } 74 | 75 | 76 | public Chunk(Chunk parent){ 77 | this.parent=parent; 78 | try { 79 | Class t = (Class)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 80 | Constructor[] cs=t.getConstructors(); 81 | for (Constructor c:cs) { 82 | Type[] ts=c.getParameterTypes(); 83 | if (ts.length==1&&Chunk.class.isAssignableFrom((Class>)ts[0])) 84 | header = (H) c.newInstance(this); 85 | } 86 | }catch (Exception e){ 87 | 88 | throw new RuntimeException(e); 89 | } 90 | } 91 | 92 | protected Context context; 93 | private Chunk parent; 94 | public H header; 95 | 96 | public void write(IntWriter w) throws IOException { 97 | int pos=w.getPos(); 98 | calc(); 99 | header.write(w); 100 | writeEx(w); 101 | assert w.getPos()-pos==header.size:(w.getPos()-pos)+" instead of "+header.size+" bytes were written:"+getClass().getName(); 102 | } 103 | 104 | public Chunk getParent() { 105 | return parent; 106 | } 107 | 108 | public Context getContext() { 109 | if (context!=null) return context; 110 | return getParent().getContext(); 111 | } 112 | 113 | private boolean isCalculated=false; 114 | public int calc(){ 115 | if (!isCalculated){ 116 | preWrite(); 117 | isCalculated=true; 118 | } 119 | return header.size; 120 | } 121 | 122 | private XmlChunk root; 123 | public XmlChunk root(){ 124 | if (root!=null) return root; 125 | return getParent().root(); 126 | } 127 | public int stringIndex(String namespace,String s){ 128 | return stringPool().stringIndex(namespace, s); 129 | } 130 | 131 | private StringPoolChunk stringPool=null; 132 | public StringPoolChunk stringPool(){ 133 | return root().stringPool; 134 | } 135 | 136 | public ReferenceResolver getReferenceResolver() { 137 | return root().getReferenceResolver(); 138 | } 139 | 140 | public void preWrite(){} 141 | public abstract void writeEx(IntWriter w) throws IOException; 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/ChunkType.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | enum ChunkType { 4 | Null(0x0000, -1), 5 | StringPool(0x0001, 28), 6 | Table(0x0002, -1), 7 | Xml(0x0003, 8), 8 | 9 | // chunk types in resxmlType 10 | XmlFirstChunk(0x0100, -1), 11 | XmlStartNamespace(0x0100, 0x10), 12 | XmlEndNamespace(0x0101, 0x10), 13 | XmlStartElement(0x0102, 0x10), 14 | XmlEndElement(0x0103, 0x10), 15 | XmlCdata(0x0104, -1), 16 | XmlLastChunk(0x017f, -1), 17 | // this Contains a uint32t array mapping strings in the string 18 | // pool Back to resource identifiers. it is optional. 19 | XmlResourceMap(0x0180, 8), 20 | 21 | // chunk types in restableType 22 | TablePackage(0x0200, -1), 23 | TableType(0x0201, -1), 24 | TableTypeSpec(0x0202, -1); 25 | 26 | public final short type; 27 | public final short headerSize; 28 | 29 | ChunkType(int type, int headerSize) { 30 | this.type = (short)type; 31 | this.headerSize = (short)headerSize; 32 | } 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/EndNameSpaceChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import com.bigzhao.xml2axml.IntWriter; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by Roy on 15-10-5. 9 | */ 10 | public class EndNameSpaceChunk extends Chunk{ 11 | 12 | 13 | 14 | public class H extends Chunk.NodeHeader{ 15 | public H() { 16 | super(ChunkType.XmlEndNamespace); 17 | size=0x18; 18 | } 19 | } 20 | public StartNameSpaceChunk start; 21 | public EndNameSpaceChunk(Chunk parent, StartNameSpaceChunk start) { 22 | super(parent); 23 | this.start=start; 24 | } 25 | @Override 26 | public void writeEx(IntWriter w) throws IOException { 27 | start.writeEx(w); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/EndTagChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import com.bigzhao.xml2axml.IntWriter; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by Roy on 15-10-5. 9 | */ 10 | public class EndTagChunk extends Chunk{ 11 | public class H extends Chunk.NodeHeader{ 12 | 13 | public H() { 14 | super(ChunkType.XmlEndElement); 15 | this.size=24; 16 | } 17 | } 18 | 19 | public StartTagChunk start; 20 | public EndTagChunk(Chunk parent,StartTagChunk start) { 21 | super(parent); 22 | this.start=start; 23 | } 24 | 25 | @Override 26 | public void writeEx(IntWriter w) throws IOException { 27 | w.write(stringIndex(null,start.namespace)); 28 | w.write(stringIndex(null,start.name)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/ResourceMapChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import com.bigzhao.xml2axml.IntWriter; 4 | 5 | import java.io.IOException; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Roy on 15-10-6. 11 | */ 12 | public class ResourceMapChunk extends Chunk{ 13 | 14 | public class H extends Chunk.Header{ 15 | 16 | public H() { 17 | super(ChunkType.XmlResourceMap); 18 | } 19 | 20 | @Override 21 | public void writeEx(IntWriter w) throws IOException { 22 | 23 | } 24 | } 25 | 26 | public ResourceMapChunk(Chunk parent) { 27 | super(parent); 28 | } 29 | 30 | private LinkedList ids; 31 | 32 | @Override 33 | public void preWrite() { 34 | List rss=stringPool().rawStrings; 35 | ids=new LinkedList(); 36 | for (StringPoolChunk.RawString r:rss){ 37 | if (r.origin.id>=0){ 38 | ids.add(r.origin.id); 39 | }else{ 40 | break; 41 | } 42 | } 43 | header.size=ids.size()*4+header.headerSize; 44 | } 45 | 46 | @Override 47 | public void writeEx(IntWriter w) throws IOException { 48 | for (int i:ids) w.write(i); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/StartNameSpaceChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import com.bigzhao.xml2axml.IntWriter; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by Roy on 15-10-5. 9 | */ 10 | public class StartNameSpaceChunk extends Chunk{ 11 | 12 | public StartNameSpaceChunk(Chunk parent) { 13 | super(parent); 14 | } 15 | 16 | public class H extends Chunk.NodeHeader{ 17 | public H() { 18 | super(ChunkType.XmlStartNamespace); 19 | size=0x18; 20 | } 21 | } 22 | 23 | public String prefix; 24 | public String uri; 25 | 26 | @Override 27 | public void writeEx(IntWriter w) throws IOException { 28 | w.write(stringIndex(null,prefix)); 29 | w.write(stringIndex(null,uri)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/StartTagChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import com.bigzhao.xml2axml.IntWriter; 4 | import org.xmlpull.v1.XmlPullParser; 5 | import org.xmlpull.v1.XmlPullParserException; 6 | 7 | import java.io.IOException; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Stack; 11 | 12 | /** 13 | * Created by Roy on 15-10-5. 14 | */ 15 | public class StartTagChunk extends Chunk{ 16 | public class H extends Chunk.NodeHeader{ 17 | 18 | public H() { 19 | super(ChunkType.XmlStartElement); 20 | } 21 | } 22 | 23 | public String name; 24 | public String prefix; 25 | public String namespace; 26 | public short attrStart=20; 27 | public short attrSize=20; 28 | public short idIndex=0; 29 | public short styleIndex=0; 30 | public short classIndex=0; 31 | public LinkedList attrs=new LinkedList(); 32 | public List startNameSpace=new Stack(); 33 | 34 | public StartTagChunk(Chunk parent,XmlPullParser p) throws XmlPullParserException { 35 | super(parent); 36 | name = p.getName(); 37 | stringPool().addString(name); 38 | prefix = p.getPrefix(); 39 | namespace = p.getNamespace(); 40 | int ac = p.getAttributeCount(); 41 | for (short i = 0; i < ac; ++i) { 42 | String prefix = p.getAttributePrefix(i); 43 | String namespace = p.getAttributeNamespace(i); 44 | String name = p.getAttributeName(i); 45 | String val = p.getAttributeValue(i); 46 | AttrChunk attr = new AttrChunk(this); 47 | attr.prefix = prefix; 48 | attr.namespace = namespace; 49 | attr.rawValue = val; 50 | attr.name = name; 51 | stringPool().addString(namespace,name); 52 | attrs.add(attr); 53 | if ("id".equals(name)&&"http://schemas.android.com/apk/res/android".equals(namespace)){ 54 | idIndex=i; 55 | }else if (prefix==null&&"style".equals(name)){ 56 | styleIndex=i; 57 | }else if (prefix==null&&"class".equals(name)){ 58 | classIndex=i; 59 | } 60 | } 61 | int nsStart = p.getNamespaceCount(p.getDepth() - 1); 62 | int nsEnd = p.getNamespaceCount(p.getDepth()); 63 | for (int i = nsStart; i < nsEnd; i++) { 64 | StartNameSpaceChunk snc=new StartNameSpaceChunk(parent); 65 | snc.prefix = p.getNamespacePrefix(i); 66 | stringPool().addString(null,snc.prefix); 67 | snc.uri = p.getNamespaceUri(i); 68 | stringPool().addString(null,snc.uri); 69 | startNameSpace.add(snc); 70 | } 71 | } 72 | 73 | @Override 74 | public void preWrite() { 75 | for (AttrChunk a:attrs) a.calc(); 76 | header.size=36+20*attrs.size(); 77 | } 78 | 79 | @Override 80 | public void writeEx(IntWriter w) throws IOException { 81 | w.write(stringIndex(null,namespace)); 82 | w.write(stringIndex(null,name)); 83 | w.write(attrStart); 84 | w.write(attrSize); 85 | w.write((short)attrs.size()); 86 | w.write(idIndex); 87 | w.write(classIndex); 88 | w.write(styleIndex); 89 | for (AttrChunk a:attrs){ 90 | a.write(w); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/StringPoolChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.bigzhao.xml2axml.Encoder; 6 | import com.bigzhao.xml2axml.IntWriter; 7 | import com.bigzhao.xml2axml.NotImplementedException; 8 | 9 | import java.io.IOException; 10 | import java.nio.charset.Charset; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Collections; 14 | import java.util.Comparator; 15 | import java.util.HashMap; 16 | import java.util.LinkedList; 17 | 18 | /** 19 | * Created by Roy on 15-10-4. 20 | */ 21 | 22 | public class StringPoolChunk extends Chunk{ 23 | 24 | public StringPoolChunk(Chunk parent) { 25 | super(parent); 26 | } 27 | 28 | public class H extends Chunk.Header { 29 | public int stringCount; 30 | public int styleCount; 31 | public int flags; 32 | public int stringPoolOffset; 33 | public int stylePoolOffset; 34 | 35 | public H() {super(ChunkType.StringPool);} 36 | 37 | @Override 38 | public void writeEx(IntWriter w) throws IOException { 39 | w.write(stringCount); 40 | w.write(styleCount); 41 | w.write(flags); 42 | w.write(stringPoolOffset); 43 | w.write(stylePoolOffset); 44 | } 45 | } 46 | 47 | public static class RawString{ 48 | 49 | StringItem origin; 50 | int byteLength; 51 | char[] cdata; 52 | byte[] bdata; 53 | 54 | int length(){ 55 | if (cdata!=null)return cdata.length; 56 | return origin.string.length(); 57 | }; 58 | 59 | int padding(){ 60 | if (cdata!=null){ 61 | return (cdata.length*2+4)&2; 62 | }else{ 63 | //return (4-((bdata.length+3)&3))&3; 64 | return 0; 65 | } 66 | } 67 | 68 | int size(){ 69 | if (cdata!=null){ 70 | return cdata.length*2+4+padding(); 71 | }else{ 72 | return bdata.length+3+padding(); 73 | } 74 | } 75 | 76 | void write(IntWriter w) throws IOException { 77 | if (cdata!=null) { 78 | int pos=w.getPos(); 79 | w.write((short)length()); 80 | for (char c:cdata) w.write(c); 81 | w.write((short)0); 82 | if (padding()==2)w.write((short)0); 83 | assert size()==w.getPos()-pos:size()+","+(w.getPos()-pos); 84 | }else{ 85 | int pos=w.getPos(); 86 | w.write((byte)length()); 87 | w.write((byte)bdata.length); 88 | for (byte c:bdata) w.write(c); 89 | w.write((byte)0); 90 | int p=padding(); 91 | for (int i=0;i rawStrings; 101 | 102 | public Encoding encoding= Encoder.Config.encoding; 103 | 104 | @Override 105 | public void preWrite() { 106 | rawStrings=new ArrayList(); 107 | LinkedList offsets=new LinkedList(); 108 | int off=0; 109 | int i=0; 110 | if (encoding==Encoding.UNICODE) { 111 | for (LinkedList ss: map.values()) { 112 | for (StringItem s:ss) { 113 | RawString r = new RawString(); 114 | r.cdata = s.string.toCharArray(); 115 | r.origin = s; 116 | rawStrings.add(r); 117 | } 118 | } 119 | }else{ 120 | for (LinkedList ss: map.values()) { 121 | for (StringItem s:ss) { 122 | RawString r = new RawString(); 123 | r.bdata = s.string.getBytes(Charset.forName("UTF-8")); 124 | r.origin = s; 125 | rawStrings.add(r); 126 | } 127 | } 128 | 129 | } 130 | Collections.sort(rawStrings, new Comparator() { 131 | 132 | public int compare(RawString lhs, RawString rhs) { 133 | int l = lhs.origin.id; 134 | int r = rhs.origin.id; 135 | if (l == -1) l = Integer.MAX_VALUE; 136 | if (r == -1) r = Integer.MAX_VALUE; 137 | return l - r; 138 | } 139 | }); 140 | for (RawString r:rawStrings) { 141 | offsets.add(off); 142 | off += r.size(); 143 | } 144 | header.stringCount=rawStrings.size(); 145 | header.styleCount=0; 146 | header.size=off+header.headerSize+header.stringCount*4+header.styleCount*4; 147 | header.stringPoolOffset=offsets.size()*4+header.headerSize; 148 | header.stylePoolOffset =0; 149 | stringsOffset=new int[offsets.size()]; 150 | if (encoding==Encoding.UTF8) header.flags|=0x100; 151 | i=0; 152 | for (int x:offsets) stringsOffset[i++]=x; 153 | stylesOffset=new int[0]; 154 | } 155 | 156 | @Override 157 | public void writeEx(IntWriter w) throws IOException { 158 | for (int i:stringsOffset) w.write(i); 159 | for (int i:stylesOffset) w.write(i); 160 | for (RawString r:rawStrings) r.write(w); 161 | //TODO styles; 162 | } 163 | 164 | 165 | public class StringItem{ 166 | public String namespace; 167 | public String string; 168 | public int id=-1;; 169 | 170 | public StringItem(String s){ 171 | string=s; 172 | namespace=null; 173 | } 174 | 175 | public StringItem(String namespace,String s){ 176 | string=s; 177 | this.namespace=namespace; 178 | genId(); 179 | } 180 | 181 | public void setNamespace(String namespace) { 182 | this.namespace = namespace; 183 | genId(); 184 | } 185 | 186 | public void genId(){ 187 | if (namespace==null) return; 188 | String pkg="http://schemas.android.com/apk/res-auto".equals(namespace)?getContext().getPackageName(): 189 | namespace.startsWith("http://schemas.android.com/apk/res/")?namespace.substring("http://schemas.android.com/apk/res/".length()):null; 190 | if (pkg==null) return; 191 | id=getContext().getResources().getIdentifier(string,"attr",pkg); 192 | } 193 | } 194 | 195 | private HashMap> map=new HashMap>(); 196 | private String preHandleString(String s){ 197 | return s; 198 | } 199 | public void addString(String s){ 200 | s=preHandleString(s); 201 | LinkedList list=map.get(s); 202 | if (list==null) map.put(s,list=new LinkedList()); 203 | if (!list.isEmpty()) return; 204 | StringItem item=new StringItem(s); 205 | list.add(item); 206 | } 207 | 208 | public void addString(String namespace, String s){ 209 | namespace=preHandleString(namespace); 210 | s=preHandleString(s); 211 | LinkedList list=map.get(s); 212 | if (list==null) map.put(s,list=new LinkedList()); 213 | for (StringItem e:list) if (e.namespace==null||e.namespace.equals(namespace)) { 214 | e.setNamespace(namespace); 215 | return; 216 | } 217 | StringItem item=new StringItem(namespace,s); 218 | list.add(item); 219 | } 220 | 221 | @Override 222 | public int stringIndex(String namespace, String s) { 223 | namespace=preHandleString(namespace); 224 | s=preHandleString(s); 225 | if (TextUtils.isEmpty(s)) return -1; 226 | int l=rawStrings.size(); 227 | for (int i=0;i{ 15 | 16 | 17 | 18 | public List startNameSpace; 19 | public StartTagChunk startTag; 20 | public List content=new LinkedList(); 21 | public EndTagChunk endTag; 22 | public List endNameSpace; 23 | 24 | public TagChunk(Chunk parent,XmlPullParser p) throws XmlPullParserException { 25 | super(parent); 26 | if (parent instanceof TagChunk){ 27 | ((TagChunk) parent).content.add(this); 28 | }else if (parent instanceof XmlChunk){ 29 | ((XmlChunk) parent).content=this; 30 | }else{ 31 | throw new IllegalArgumentException("parent must be XmlChunk or TagChunk"); 32 | } 33 | startTag=new StartTagChunk(this,p); 34 | endTag=new EndTagChunk(this,startTag); 35 | startNameSpace=startTag.startNameSpace; 36 | endNameSpace=new LinkedList(); 37 | for (StartNameSpaceChunk c:startNameSpace) endNameSpace.add(new EndNameSpaceChunk(this,c)); 38 | endTag.header.lineNo=startTag.header.lineNo=p.getLineNumber(); 39 | } 40 | 41 | @Override 42 | public void preWrite() { 43 | int sum=0; 44 | for (StartNameSpaceChunk e:startNameSpace) sum+=e.calc(); 45 | for (EndNameSpaceChunk e:endNameSpace) sum+=e.calc(); 46 | sum+=startTag.calc(); 47 | sum+=endTag.calc(); 48 | for (TagChunk c:content) sum+=c.calc(); 49 | header.size=sum; 50 | } 51 | 52 | @Override 53 | public void writeEx(IntWriter w) throws IOException { 54 | for(StartNameSpaceChunk c:startNameSpace) c.write(w); 55 | startTag.write(w); 56 | for (TagChunk c:content) c.write(w); 57 | endTag.write(w); 58 | for(EndNameSpaceChunk c:endNameSpace) c.write(w); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/ValueChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import android.graphics.Color; 4 | 5 | import com.bigzhao.xml2axml.ComplexConsts; 6 | import com.bigzhao.xml2axml.IntWriter; 7 | import com.bigzhao.xml2axml.NotImplementedException; 8 | import com.bigzhao.xml2axml.ValueType; 9 | 10 | import java.io.IOException; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * Created by Roy on 15-10-6. 16 | */ 17 | public class ValueChunk extends Chunk { 18 | 19 | class ValPair { 20 | int pos; 21 | String val; 22 | 23 | public ValPair(Matcher m) { 24 | int c = m.groupCount(); 25 | for (int i = 1; i <= c; ++i) { 26 | String s = m.group(i); 27 | if (s == null || s.isEmpty()) continue; 28 | pos = i; 29 | val = s; 30 | return; 31 | } 32 | pos = -1; 33 | val = m.group(); 34 | } 35 | } 36 | 37 | private AttrChunk attrChunk; 38 | private String realString; 39 | short size = 8; 40 | byte res0 = 0; 41 | byte type = -1; 42 | int data = -1; 43 | 44 | Pattern explicitType = Pattern.compile("!(?:(\\w+)!)?(.*)"); 45 | Pattern types = Pattern.compile(("^(?:" + 46 | "(@null)" + 47 | "|(@\\+?(?:\\w+:)?\\w+/\\w+|@(?:\\w+:)?[0-9a-zA-Z]+)" + 48 | "|(true|false)" + 49 | "|([-+]?\\d+)" + 50 | "|(0x[0-9a-zA-Z]+)" + 51 | "|([-+]?\\d+(?:\\.\\d+)?)" + 52 | "|([-+]?\\d+(?:\\.\\d+)?(?:dp|dip|in|px|sp|pt|mm))" + 53 | "|([-+]?\\d+(?:\\.\\d+)?(?:%))" + 54 | "|(\\#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8}))" + 55 | "|(match_parent|wrap_content|fill_parent)" + 56 | ")$").replaceAll("\\s+", "")); 57 | 58 | public ValueChunk(AttrChunk parent) { 59 | super(parent); 60 | header.size=8; 61 | this.attrChunk = parent; 62 | } 63 | 64 | @Override 65 | public void preWrite() { 66 | evaluate(); 67 | } 68 | 69 | @Override 70 | public void writeEx(IntWriter w) throws IOException { 71 | w.write(size); 72 | w.write(res0); 73 | if (type==ValueType.STRING){ 74 | data=stringIndex(null,realString); 75 | } 76 | w.write(type); 77 | w.write(data); 78 | } 79 | 80 | public int evalcomplex(String val) { 81 | int unit; 82 | int radix; 83 | int base; 84 | String num; 85 | 86 | if (val.endsWith("%")) { 87 | num = val.substring(0, val.length() - 1); 88 | unit = ComplexConsts.UNIT_FRACTION; 89 | } else if (val.endsWith("dp")) { 90 | unit = ComplexConsts.UNIT_DIP; 91 | num = val.substring(0, val.length() - 2); 92 | } else if (val.endsWith("dip")) { 93 | unit = ComplexConsts.UNIT_DIP; 94 | num = val.substring(0, val.length() - 3); 95 | } else if (val.endsWith("sp")) { 96 | unit = ComplexConsts.UNIT_SP; 97 | num = val.substring(0, val.length() - 2); 98 | } else if (val.endsWith("px")) { 99 | unit = ComplexConsts.UNIT_PX; 100 | num = val.substring(0, val.length() - 2); 101 | } else if (val.endsWith("pt")) { 102 | unit = ComplexConsts.UNIT_PT; 103 | num = val.substring(0, val.length() - 2); 104 | } else if (val.endsWith("in")) { 105 | unit = ComplexConsts.UNIT_IN; 106 | num = val.substring(0, val.length() - 2); 107 | } else if (val.endsWith("mm")) { 108 | unit = ComplexConsts.UNIT_MM; 109 | num = val.substring(0, val.length() - 2); 110 | } else { 111 | throw new RuntimeException("invalid unit"); 112 | } 113 | double f = Double.parseDouble(num); 114 | if (f < 1 && f > -1) { 115 | base = (int) (f * (1 << 23)); 116 | radix = ComplexConsts.RADIX_0p23; 117 | } else if (f < 0x100 && f > -0x100) { 118 | base = (int) (f * (1 << 15)); 119 | radix = ComplexConsts.RADIX_8p15; 120 | } else if (f < 0x10000 && f > -0x10000) { 121 | base = (int) (f * (1 << 7)); 122 | radix = ComplexConsts.RADIX_16p7; 123 | } else { 124 | base = (int) f; 125 | radix = ComplexConsts.RADIX_23p0; 126 | } 127 | return (base << 8) | (radix << 4) | unit; 128 | } 129 | 130 | public void evaluate() { 131 | Matcher m = explicitType.matcher(attrChunk.rawValue); 132 | if (m.find()) { 133 | String t = m.group(1); 134 | String v = m.group(2); 135 | if (t == null || t.isEmpty() || t.equals("string") || t.equals("str")) { 136 | type = ValueType.STRING; 137 | realString=v; 138 | stringPool().addString(realString); 139 | //data = stringIndex(null, v); 140 | } else { 141 | //TODO resolve other type 142 | throw new NotImplementedException(); 143 | } 144 | } else { 145 | m = types.matcher(attrChunk.rawValue); 146 | if (m.find()) { 147 | ValPair vp = new ValPair(m); 148 | switch (vp.pos) { 149 | case 1: 150 | type = ValueType.NULL; 151 | data = 0; 152 | break; 153 | case 2: 154 | type = ValueType.REFERENCE; 155 | data = getReferenceResolver().resolve(this, vp.val); 156 | break; 157 | case 3: 158 | type = ValueType.INT_BOOLEAN; 159 | data = "true".equalsIgnoreCase(vp.val) ? 1 : 0; 160 | break; 161 | case 4: 162 | type = ValueType.INT_DEC; 163 | data = Integer.parseInt(vp.val); 164 | break; 165 | case 5: 166 | type = ValueType.INT_HEX; 167 | data = Integer.parseInt(vp.val.substring(2),16); 168 | break; 169 | case 6: 170 | type = ValueType.FLOAT; 171 | data = Float.floatToIntBits(Float.parseFloat(vp.val)); 172 | break; 173 | case 7: 174 | type = ValueType.DIMENSION; 175 | data = evalcomplex(vp.val); 176 | break; 177 | case 8: 178 | type = ValueType.FRACTION; 179 | data = evalcomplex(vp.val); 180 | break; 181 | case 9: 182 | type = ValueType.INT_COLOR_ARGB8; 183 | data = Color.parseColor(vp.val); 184 | break; 185 | case 10: 186 | type = ValueType.INT_DEC; 187 | data = "wrap_content".equalsIgnoreCase(vp.val) ? -2 : -1; 188 | break; 189 | default: 190 | type = ValueType.STRING; 191 | realString=vp.val; 192 | stringPool().addString(realString); 193 | //data = stringIndex(null, attrChunk.rawValue); 194 | break; 195 | } 196 | } else { 197 | type = ValueType.STRING; 198 | realString=attrChunk.rawValue; 199 | stringPool().addString(realString); 200 | //data = stringIndex(null, attrChunk.rawValue); 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/XmlChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import android.content.Context; 4 | 5 | import com.bigzhao.xml2axml.DefaultReferenceResolver; 6 | import com.bigzhao.xml2axml.IntWriter; 7 | import com.bigzhao.xml2axml.ReferenceResolver; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by Roy on 15-10-4. 13 | */ 14 | 15 | 16 | 17 | public class XmlChunk extends Chunk{ 18 | public XmlChunk(Context context) { 19 | super(null); 20 | this.context=context; 21 | } 22 | 23 | public class H extends Chunk.Header { 24 | 25 | public H() { 26 | super(ChunkType.Xml); 27 | } 28 | 29 | @Override 30 | public void writeEx(IntWriter w) throws IOException { 31 | 32 | } 33 | } 34 | public StringPoolChunk stringPool=new StringPoolChunk(this); 35 | public ResourceMapChunk resourceMap=new ResourceMapChunk(this); 36 | public TagChunk content; 37 | 38 | @Override 39 | public void preWrite() { 40 | header.size=header.headerSize+content.calc()+stringPool.calc()+resourceMap.calc(); 41 | } 42 | 43 | @Override 44 | public void writeEx(IntWriter w) throws IOException { 45 | stringPool.write(w); 46 | resourceMap.write(w); 47 | content.write(w); 48 | } 49 | 50 | @Override 51 | public XmlChunk root() { 52 | return this; 53 | } 54 | 55 | private ReferenceResolver referenceResolver; 56 | @Override 57 | public ReferenceResolver getReferenceResolver() { 58 | if (referenceResolver==null) referenceResolver= new DefaultReferenceResolver(); 59 | return referenceResolver; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/test/AXMLPrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bigzhao.xml2axml.test; 17 | 18 | import java.io.FileInputStream; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.PrintStream; 22 | 23 | import android.content.res.AXmlResourceParser; 24 | import org.xmlpull.v1.XmlPullParser; 25 | import android.content.res.XmlResourceParser; 26 | import android.util.TypedValue; 27 | import org.xmlpull.v1.XmlPullParserException; 28 | 29 | /** 30 | * @author Dmitry Skiba 31 | * 32 | * This is example usage of AXMLParser class. 33 | * 34 | * Prints xml document from Android's binary xml file. 35 | */ 36 | public class AXMLPrinter { 37 | 38 | public static PrintStream out=System.out; 39 | 40 | public static void main(String[] arguments) { 41 | if (arguments.length<1) { 42 | log("Usage: AXMLPrinter "); 43 | return; 44 | } 45 | try { 46 | FileInputStream inf=new FileInputStream(arguments[0]); 47 | decode(inf); 48 | } catch (Exception e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | public static void decode(InputStream inf) throws XmlPullParserException, IOException { 54 | AXmlResourceParser parser=new AXmlResourceParser(); 55 | parser.open(inf); 56 | //byte[] bs=FileUtils.readFileToByteArray(new File(arguments[0])); 57 | //String b64=new String(Base64.getEncoder().encode(bs)); 58 | StringBuilder indent=new StringBuilder(10); 59 | //FileUtils.writeStringToFile(new File(arguments[0]+".base64.txt"),b64); 60 | final String indentStep=" "; 61 | while (true) { 62 | int type=parser.next(); 63 | if (type== XmlPullParser.END_DOCUMENT) { 64 | break; 65 | } 66 | switch (type) { 67 | case XmlPullParser.START_DOCUMENT: 68 | { 69 | log(""); 70 | break; 71 | } 72 | case XmlPullParser.START_TAG: 73 | { 74 | log("%s<%s%s",indent, 75 | getNamespacePrefix(parser.getPrefix()),parser.getName()); 76 | indent.append(indentStep); 77 | 78 | int namespaceCountBefore=parser.getNamespaceCount(parser.getDepth()-1); 79 | int namespaceCount=parser.getNamespaceCount(parser.getDepth()); 80 | for (int i=namespaceCountBefore;i!=namespaceCount;++i) { 81 | log("%sxmlns:%s=\"%s\"", 82 | indent, 83 | parser.getNamespacePrefix(i), 84 | parser.getNamespaceUri(i)); 85 | } 86 | 87 | for (int i=0;i!=parser.getAttributeCount();++i) { 88 | log("%s%s%s=\"%s\"",indent, 89 | getNamespacePrefix(parser.getAttributePrefix(i)), 90 | parser.getAttributeName(i), 91 | getAttributeValue(parser,i)); 92 | } 93 | log("%s>",indent); 94 | break; 95 | } 96 | case XmlPullParser.END_TAG: 97 | { 98 | indent.setLength(indent.length()-indentStep.length()); 99 | log("%s%s%s>",indent, 100 | getNamespacePrefix(parser.getPrefix()), 101 | parser.getName()); 102 | break; 103 | } 104 | case XmlPullParser.TEXT: 105 | { 106 | log("%s%s",indent,parser.getText()); 107 | break; 108 | } 109 | } 110 | } 111 | } 112 | 113 | private static String getNamespacePrefix(String prefix) { 114 | if (prefix==null || prefix.length()==0) { 115 | return ""; 116 | } 117 | return prefix+":"; 118 | } 119 | 120 | private static String getAttributeValue(AXmlResourceParser parser,int index) { 121 | int type=parser.getAttributeValueType(index); 122 | int data=parser.getAttributeValueData(index); 123 | if (type==TypedValue.TYPE_STRING) { 124 | return parser.getAttributeValue(index); 125 | } 126 | if (type==TypedValue.TYPE_ATTRIBUTE) { 127 | return String.format("?%s%08X",getPackage(data),data); 128 | } 129 | if (type==TypedValue.TYPE_REFERENCE) { 130 | return String.format("@%s%08X",getPackage(data),data); 131 | } 132 | if (type==TypedValue.TYPE_FLOAT) { 133 | return String.valueOf(Float.intBitsToFloat(data)); 134 | } 135 | if (type==TypedValue.TYPE_INT_HEX) { 136 | return String.format("0x%08X",data); 137 | } 138 | if (type==TypedValue.TYPE_INT_BOOLEAN) { 139 | return data!=0?"true":"false"; 140 | } 141 | if (type==TypedValue.TYPE_DIMENSION) { 142 | return Float.toString(complexToFloat(data))+ 143 | DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK]; 144 | } 145 | if (type==TypedValue.TYPE_FRACTION) { 146 | return Float.toString(complexToFloat(data))+ 147 | FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK]; 148 | } 149 | if (type>=TypedValue.TYPE_FIRST_COLOR_INT && type<=TypedValue.TYPE_LAST_COLOR_INT) { 150 | return String.format("#%08X",data); 151 | } 152 | if (type>=TypedValue.TYPE_FIRST_INT && type<=TypedValue.TYPE_LAST_INT) { 153 | return String.valueOf(data); 154 | } 155 | return String.format("<0x%X, type 0x%02X>",data,type); 156 | } 157 | 158 | private static String getPackage(int id) { 159 | if (id>>>24==1) { 160 | return "android:"; 161 | } 162 | return ""; 163 | } 164 | 165 | private static void log(String format,Object...arguments) { 166 | out.printf(format,arguments); 167 | out.println(); 168 | } 169 | 170 | /////////////////////////////////// ILLEGAL STUFF, DONT LOOK :) 171 | 172 | public static float complexToFloat(int complex) { 173 | return (float)(complex & 0xFFFFFF00)*RADIX_MULTS[(complex>>4) & 3]; 174 | } 175 | 176 | private static final float RADIX_MULTS[]={ 177 | 0.00390625F,3.051758E-005F,1.192093E-007F,4.656613E-010F 178 | }; 179 | private static final String DIMENSION_UNITS[]={ 180 | "px","dip","sp","pt","in","mm","","" 181 | }; 182 | private static final String FRACTION_UNITS[]={ 183 | "%","%p","","","","","","" 184 | }; 185 | } -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/test/Main.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.test; 2 | 3 | import android.content.Context; 4 | import com.bigzhao.xml2axml.Encoder; 5 | import org.apache.commons.io.FileUtils; 6 | import org.xmlpull.v1.XmlPullParserException; 7 | 8 | import java.io.File; 9 | import java.io.FileNotFoundException; 10 | import java.io.IOException; 11 | import java.io.PrintStream; 12 | 13 | /** 14 | * Created by Roy on 15-10-6. 15 | */ 16 | public class Main { 17 | public static void main(String args[]) throws IOException, XmlPullParserException { 18 | if (args[0].startsWith("e")) { 19 | encode(args[1],args[2]); 20 | }else if (args[0].startsWith("d")){ 21 | decode(args[1],args[2]); 22 | }else if (args[0].startsWith("t")) { 23 | decode(args[1], args[2]); 24 | encode(args[2], args[3]); 25 | decode(args[3], args[4]); 26 | } 27 | } 28 | 29 | public static void encode(String in,String out) throws IOException, XmlPullParserException { 30 | Encoder e = new Encoder(); 31 | byte[] bs = e.encodeFile(new Context(), in); 32 | FileUtils.writeByteArrayToFile(new File(out), bs); 33 | } 34 | 35 | public static void decode(String in,String out) throws FileNotFoundException { 36 | AXMLPrinter.out=new PrintStream(new File(out)); 37 | AXMLPrinter.main(new String[]{in}); 38 | AXMLPrinter.out.close(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /xml2axml.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | --------------------------------------------------------------------------------
rawStrings; 101 | 102 | public Encoding encoding= Encoder.Config.encoding; 103 | 104 | @Override 105 | public void preWrite() { 106 | rawStrings=new ArrayList(); 107 | LinkedList offsets=new LinkedList(); 108 | int off=0; 109 | int i=0; 110 | if (encoding==Encoding.UNICODE) { 111 | for (LinkedList ss: map.values()) { 112 | for (StringItem s:ss) { 113 | RawString r = new RawString(); 114 | r.cdata = s.string.toCharArray(); 115 | r.origin = s; 116 | rawStrings.add(r); 117 | } 118 | } 119 | }else{ 120 | for (LinkedList ss: map.values()) { 121 | for (StringItem s:ss) { 122 | RawString r = new RawString(); 123 | r.bdata = s.string.getBytes(Charset.forName("UTF-8")); 124 | r.origin = s; 125 | rawStrings.add(r); 126 | } 127 | } 128 | 129 | } 130 | Collections.sort(rawStrings, new Comparator() { 131 | 132 | public int compare(RawString lhs, RawString rhs) { 133 | int l = lhs.origin.id; 134 | int r = rhs.origin.id; 135 | if (l == -1) l = Integer.MAX_VALUE; 136 | if (r == -1) r = Integer.MAX_VALUE; 137 | return l - r; 138 | } 139 | }); 140 | for (RawString r:rawStrings) { 141 | offsets.add(off); 142 | off += r.size(); 143 | } 144 | header.stringCount=rawStrings.size(); 145 | header.styleCount=0; 146 | header.size=off+header.headerSize+header.stringCount*4+header.styleCount*4; 147 | header.stringPoolOffset=offsets.size()*4+header.headerSize; 148 | header.stylePoolOffset =0; 149 | stringsOffset=new int[offsets.size()]; 150 | if (encoding==Encoding.UTF8) header.flags|=0x100; 151 | i=0; 152 | for (int x:offsets) stringsOffset[i++]=x; 153 | stylesOffset=new int[0]; 154 | } 155 | 156 | @Override 157 | public void writeEx(IntWriter w) throws IOException { 158 | for (int i:stringsOffset) w.write(i); 159 | for (int i:stylesOffset) w.write(i); 160 | for (RawString r:rawStrings) r.write(w); 161 | //TODO styles; 162 | } 163 | 164 | 165 | public class StringItem{ 166 | public String namespace; 167 | public String string; 168 | public int id=-1;; 169 | 170 | public StringItem(String s){ 171 | string=s; 172 | namespace=null; 173 | } 174 | 175 | public StringItem(String namespace,String s){ 176 | string=s; 177 | this.namespace=namespace; 178 | genId(); 179 | } 180 | 181 | public void setNamespace(String namespace) { 182 | this.namespace = namespace; 183 | genId(); 184 | } 185 | 186 | public void genId(){ 187 | if (namespace==null) return; 188 | String pkg="http://schemas.android.com/apk/res-auto".equals(namespace)?getContext().getPackageName(): 189 | namespace.startsWith("http://schemas.android.com/apk/res/")?namespace.substring("http://schemas.android.com/apk/res/".length()):null; 190 | if (pkg==null) return; 191 | id=getContext().getResources().getIdentifier(string,"attr",pkg); 192 | } 193 | } 194 | 195 | private HashMap> map=new HashMap>(); 196 | private String preHandleString(String s){ 197 | return s; 198 | } 199 | public void addString(String s){ 200 | s=preHandleString(s); 201 | LinkedList list=map.get(s); 202 | if (list==null) map.put(s,list=new LinkedList()); 203 | if (!list.isEmpty()) return; 204 | StringItem item=new StringItem(s); 205 | list.add(item); 206 | } 207 | 208 | public void addString(String namespace, String s){ 209 | namespace=preHandleString(namespace); 210 | s=preHandleString(s); 211 | LinkedList list=map.get(s); 212 | if (list==null) map.put(s,list=new LinkedList()); 213 | for (StringItem e:list) if (e.namespace==null||e.namespace.equals(namespace)) { 214 | e.setNamespace(namespace); 215 | return; 216 | } 217 | StringItem item=new StringItem(namespace,s); 218 | list.add(item); 219 | } 220 | 221 | @Override 222 | public int stringIndex(String namespace, String s) { 223 | namespace=preHandleString(namespace); 224 | s=preHandleString(s); 225 | if (TextUtils.isEmpty(s)) return -1; 226 | int l=rawStrings.size(); 227 | for (int i=0;i{ 15 | 16 | 17 | 18 | public List startNameSpace; 19 | public StartTagChunk startTag; 20 | public List content=new LinkedList(); 21 | public EndTagChunk endTag; 22 | public List endNameSpace; 23 | 24 | public TagChunk(Chunk parent,XmlPullParser p) throws XmlPullParserException { 25 | super(parent); 26 | if (parent instanceof TagChunk){ 27 | ((TagChunk) parent).content.add(this); 28 | }else if (parent instanceof XmlChunk){ 29 | ((XmlChunk) parent).content=this; 30 | }else{ 31 | throw new IllegalArgumentException("parent must be XmlChunk or TagChunk"); 32 | } 33 | startTag=new StartTagChunk(this,p); 34 | endTag=new EndTagChunk(this,startTag); 35 | startNameSpace=startTag.startNameSpace; 36 | endNameSpace=new LinkedList(); 37 | for (StartNameSpaceChunk c:startNameSpace) endNameSpace.add(new EndNameSpaceChunk(this,c)); 38 | endTag.header.lineNo=startTag.header.lineNo=p.getLineNumber(); 39 | } 40 | 41 | @Override 42 | public void preWrite() { 43 | int sum=0; 44 | for (StartNameSpaceChunk e:startNameSpace) sum+=e.calc(); 45 | for (EndNameSpaceChunk e:endNameSpace) sum+=e.calc(); 46 | sum+=startTag.calc(); 47 | sum+=endTag.calc(); 48 | for (TagChunk c:content) sum+=c.calc(); 49 | header.size=sum; 50 | } 51 | 52 | @Override 53 | public void writeEx(IntWriter w) throws IOException { 54 | for(StartNameSpaceChunk c:startNameSpace) c.write(w); 55 | startTag.write(w); 56 | for (TagChunk c:content) c.write(w); 57 | endTag.write(w); 58 | for(EndNameSpaceChunk c:endNameSpace) c.write(w); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/ValueChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import android.graphics.Color; 4 | 5 | import com.bigzhao.xml2axml.ComplexConsts; 6 | import com.bigzhao.xml2axml.IntWriter; 7 | import com.bigzhao.xml2axml.NotImplementedException; 8 | import com.bigzhao.xml2axml.ValueType; 9 | 10 | import java.io.IOException; 11 | import java.util.regex.Matcher; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * Created by Roy on 15-10-6. 16 | */ 17 | public class ValueChunk extends Chunk { 18 | 19 | class ValPair { 20 | int pos; 21 | String val; 22 | 23 | public ValPair(Matcher m) { 24 | int c = m.groupCount(); 25 | for (int i = 1; i <= c; ++i) { 26 | String s = m.group(i); 27 | if (s == null || s.isEmpty()) continue; 28 | pos = i; 29 | val = s; 30 | return; 31 | } 32 | pos = -1; 33 | val = m.group(); 34 | } 35 | } 36 | 37 | private AttrChunk attrChunk; 38 | private String realString; 39 | short size = 8; 40 | byte res0 = 0; 41 | byte type = -1; 42 | int data = -1; 43 | 44 | Pattern explicitType = Pattern.compile("!(?:(\\w+)!)?(.*)"); 45 | Pattern types = Pattern.compile(("^(?:" + 46 | "(@null)" + 47 | "|(@\\+?(?:\\w+:)?\\w+/\\w+|@(?:\\w+:)?[0-9a-zA-Z]+)" + 48 | "|(true|false)" + 49 | "|([-+]?\\d+)" + 50 | "|(0x[0-9a-zA-Z]+)" + 51 | "|([-+]?\\d+(?:\\.\\d+)?)" + 52 | "|([-+]?\\d+(?:\\.\\d+)?(?:dp|dip|in|px|sp|pt|mm))" + 53 | "|([-+]?\\d+(?:\\.\\d+)?(?:%))" + 54 | "|(\\#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8}))" + 55 | "|(match_parent|wrap_content|fill_parent)" + 56 | ")$").replaceAll("\\s+", "")); 57 | 58 | public ValueChunk(AttrChunk parent) { 59 | super(parent); 60 | header.size=8; 61 | this.attrChunk = parent; 62 | } 63 | 64 | @Override 65 | public void preWrite() { 66 | evaluate(); 67 | } 68 | 69 | @Override 70 | public void writeEx(IntWriter w) throws IOException { 71 | w.write(size); 72 | w.write(res0); 73 | if (type==ValueType.STRING){ 74 | data=stringIndex(null,realString); 75 | } 76 | w.write(type); 77 | w.write(data); 78 | } 79 | 80 | public int evalcomplex(String val) { 81 | int unit; 82 | int radix; 83 | int base; 84 | String num; 85 | 86 | if (val.endsWith("%")) { 87 | num = val.substring(0, val.length() - 1); 88 | unit = ComplexConsts.UNIT_FRACTION; 89 | } else if (val.endsWith("dp")) { 90 | unit = ComplexConsts.UNIT_DIP; 91 | num = val.substring(0, val.length() - 2); 92 | } else if (val.endsWith("dip")) { 93 | unit = ComplexConsts.UNIT_DIP; 94 | num = val.substring(0, val.length() - 3); 95 | } else if (val.endsWith("sp")) { 96 | unit = ComplexConsts.UNIT_SP; 97 | num = val.substring(0, val.length() - 2); 98 | } else if (val.endsWith("px")) { 99 | unit = ComplexConsts.UNIT_PX; 100 | num = val.substring(0, val.length() - 2); 101 | } else if (val.endsWith("pt")) { 102 | unit = ComplexConsts.UNIT_PT; 103 | num = val.substring(0, val.length() - 2); 104 | } else if (val.endsWith("in")) { 105 | unit = ComplexConsts.UNIT_IN; 106 | num = val.substring(0, val.length() - 2); 107 | } else if (val.endsWith("mm")) { 108 | unit = ComplexConsts.UNIT_MM; 109 | num = val.substring(0, val.length() - 2); 110 | } else { 111 | throw new RuntimeException("invalid unit"); 112 | } 113 | double f = Double.parseDouble(num); 114 | if (f < 1 && f > -1) { 115 | base = (int) (f * (1 << 23)); 116 | radix = ComplexConsts.RADIX_0p23; 117 | } else if (f < 0x100 && f > -0x100) { 118 | base = (int) (f * (1 << 15)); 119 | radix = ComplexConsts.RADIX_8p15; 120 | } else if (f < 0x10000 && f > -0x10000) { 121 | base = (int) (f * (1 << 7)); 122 | radix = ComplexConsts.RADIX_16p7; 123 | } else { 124 | base = (int) f; 125 | radix = ComplexConsts.RADIX_23p0; 126 | } 127 | return (base << 8) | (radix << 4) | unit; 128 | } 129 | 130 | public void evaluate() { 131 | Matcher m = explicitType.matcher(attrChunk.rawValue); 132 | if (m.find()) { 133 | String t = m.group(1); 134 | String v = m.group(2); 135 | if (t == null || t.isEmpty() || t.equals("string") || t.equals("str")) { 136 | type = ValueType.STRING; 137 | realString=v; 138 | stringPool().addString(realString); 139 | //data = stringIndex(null, v); 140 | } else { 141 | //TODO resolve other type 142 | throw new NotImplementedException(); 143 | } 144 | } else { 145 | m = types.matcher(attrChunk.rawValue); 146 | if (m.find()) { 147 | ValPair vp = new ValPair(m); 148 | switch (vp.pos) { 149 | case 1: 150 | type = ValueType.NULL; 151 | data = 0; 152 | break; 153 | case 2: 154 | type = ValueType.REFERENCE; 155 | data = getReferenceResolver().resolve(this, vp.val); 156 | break; 157 | case 3: 158 | type = ValueType.INT_BOOLEAN; 159 | data = "true".equalsIgnoreCase(vp.val) ? 1 : 0; 160 | break; 161 | case 4: 162 | type = ValueType.INT_DEC; 163 | data = Integer.parseInt(vp.val); 164 | break; 165 | case 5: 166 | type = ValueType.INT_HEX; 167 | data = Integer.parseInt(vp.val.substring(2),16); 168 | break; 169 | case 6: 170 | type = ValueType.FLOAT; 171 | data = Float.floatToIntBits(Float.parseFloat(vp.val)); 172 | break; 173 | case 7: 174 | type = ValueType.DIMENSION; 175 | data = evalcomplex(vp.val); 176 | break; 177 | case 8: 178 | type = ValueType.FRACTION; 179 | data = evalcomplex(vp.val); 180 | break; 181 | case 9: 182 | type = ValueType.INT_COLOR_ARGB8; 183 | data = Color.parseColor(vp.val); 184 | break; 185 | case 10: 186 | type = ValueType.INT_DEC; 187 | data = "wrap_content".equalsIgnoreCase(vp.val) ? -2 : -1; 188 | break; 189 | default: 190 | type = ValueType.STRING; 191 | realString=vp.val; 192 | stringPool().addString(realString); 193 | //data = stringIndex(null, attrChunk.rawValue); 194 | break; 195 | } 196 | } else { 197 | type = ValueType.STRING; 198 | realString=attrChunk.rawValue; 199 | stringPool().addString(realString); 200 | //data = stringIndex(null, attrChunk.rawValue); 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/chunks/XmlChunk.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.chunks; 2 | 3 | import android.content.Context; 4 | 5 | import com.bigzhao.xml2axml.DefaultReferenceResolver; 6 | import com.bigzhao.xml2axml.IntWriter; 7 | import com.bigzhao.xml2axml.ReferenceResolver; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by Roy on 15-10-4. 13 | */ 14 | 15 | 16 | 17 | public class XmlChunk extends Chunk{ 18 | public XmlChunk(Context context) { 19 | super(null); 20 | this.context=context; 21 | } 22 | 23 | public class H extends Chunk.Header { 24 | 25 | public H() { 26 | super(ChunkType.Xml); 27 | } 28 | 29 | @Override 30 | public void writeEx(IntWriter w) throws IOException { 31 | 32 | } 33 | } 34 | public StringPoolChunk stringPool=new StringPoolChunk(this); 35 | public ResourceMapChunk resourceMap=new ResourceMapChunk(this); 36 | public TagChunk content; 37 | 38 | @Override 39 | public void preWrite() { 40 | header.size=header.headerSize+content.calc()+stringPool.calc()+resourceMap.calc(); 41 | } 42 | 43 | @Override 44 | public void writeEx(IntWriter w) throws IOException { 45 | stringPool.write(w); 46 | resourceMap.write(w); 47 | content.write(w); 48 | } 49 | 50 | @Override 51 | public XmlChunk root() { 52 | return this; 53 | } 54 | 55 | private ReferenceResolver referenceResolver; 56 | @Override 57 | public ReferenceResolver getReferenceResolver() { 58 | if (referenceResolver==null) referenceResolver= new DefaultReferenceResolver(); 59 | return referenceResolver; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/test/AXMLPrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Android4ME 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.bigzhao.xml2axml.test; 17 | 18 | import java.io.FileInputStream; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.PrintStream; 22 | 23 | import android.content.res.AXmlResourceParser; 24 | import org.xmlpull.v1.XmlPullParser; 25 | import android.content.res.XmlResourceParser; 26 | import android.util.TypedValue; 27 | import org.xmlpull.v1.XmlPullParserException; 28 | 29 | /** 30 | * @author Dmitry Skiba 31 | * 32 | * This is example usage of AXMLParser class. 33 | * 34 | * Prints xml document from Android's binary xml file. 35 | */ 36 | public class AXMLPrinter { 37 | 38 | public static PrintStream out=System.out; 39 | 40 | public static void main(String[] arguments) { 41 | if (arguments.length<1) { 42 | log("Usage: AXMLPrinter "); 43 | return; 44 | } 45 | try { 46 | FileInputStream inf=new FileInputStream(arguments[0]); 47 | decode(inf); 48 | } catch (Exception e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | public static void decode(InputStream inf) throws XmlPullParserException, IOException { 54 | AXmlResourceParser parser=new AXmlResourceParser(); 55 | parser.open(inf); 56 | //byte[] bs=FileUtils.readFileToByteArray(new File(arguments[0])); 57 | //String b64=new String(Base64.getEncoder().encode(bs)); 58 | StringBuilder indent=new StringBuilder(10); 59 | //FileUtils.writeStringToFile(new File(arguments[0]+".base64.txt"),b64); 60 | final String indentStep=" "; 61 | while (true) { 62 | int type=parser.next(); 63 | if (type== XmlPullParser.END_DOCUMENT) { 64 | break; 65 | } 66 | switch (type) { 67 | case XmlPullParser.START_DOCUMENT: 68 | { 69 | log(""); 70 | break; 71 | } 72 | case XmlPullParser.START_TAG: 73 | { 74 | log("%s<%s%s",indent, 75 | getNamespacePrefix(parser.getPrefix()),parser.getName()); 76 | indent.append(indentStep); 77 | 78 | int namespaceCountBefore=parser.getNamespaceCount(parser.getDepth()-1); 79 | int namespaceCount=parser.getNamespaceCount(parser.getDepth()); 80 | for (int i=namespaceCountBefore;i!=namespaceCount;++i) { 81 | log("%sxmlns:%s=\"%s\"", 82 | indent, 83 | parser.getNamespacePrefix(i), 84 | parser.getNamespaceUri(i)); 85 | } 86 | 87 | for (int i=0;i!=parser.getAttributeCount();++i) { 88 | log("%s%s%s=\"%s\"",indent, 89 | getNamespacePrefix(parser.getAttributePrefix(i)), 90 | parser.getAttributeName(i), 91 | getAttributeValue(parser,i)); 92 | } 93 | log("%s>",indent); 94 | break; 95 | } 96 | case XmlPullParser.END_TAG: 97 | { 98 | indent.setLength(indent.length()-indentStep.length()); 99 | log("%s%s%s>",indent, 100 | getNamespacePrefix(parser.getPrefix()), 101 | parser.getName()); 102 | break; 103 | } 104 | case XmlPullParser.TEXT: 105 | { 106 | log("%s%s",indent,parser.getText()); 107 | break; 108 | } 109 | } 110 | } 111 | } 112 | 113 | private static String getNamespacePrefix(String prefix) { 114 | if (prefix==null || prefix.length()==0) { 115 | return ""; 116 | } 117 | return prefix+":"; 118 | } 119 | 120 | private static String getAttributeValue(AXmlResourceParser parser,int index) { 121 | int type=parser.getAttributeValueType(index); 122 | int data=parser.getAttributeValueData(index); 123 | if (type==TypedValue.TYPE_STRING) { 124 | return parser.getAttributeValue(index); 125 | } 126 | if (type==TypedValue.TYPE_ATTRIBUTE) { 127 | return String.format("?%s%08X",getPackage(data),data); 128 | } 129 | if (type==TypedValue.TYPE_REFERENCE) { 130 | return String.format("@%s%08X",getPackage(data),data); 131 | } 132 | if (type==TypedValue.TYPE_FLOAT) { 133 | return String.valueOf(Float.intBitsToFloat(data)); 134 | } 135 | if (type==TypedValue.TYPE_INT_HEX) { 136 | return String.format("0x%08X",data); 137 | } 138 | if (type==TypedValue.TYPE_INT_BOOLEAN) { 139 | return data!=0?"true":"false"; 140 | } 141 | if (type==TypedValue.TYPE_DIMENSION) { 142 | return Float.toString(complexToFloat(data))+ 143 | DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK]; 144 | } 145 | if (type==TypedValue.TYPE_FRACTION) { 146 | return Float.toString(complexToFloat(data))+ 147 | FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK]; 148 | } 149 | if (type>=TypedValue.TYPE_FIRST_COLOR_INT && type<=TypedValue.TYPE_LAST_COLOR_INT) { 150 | return String.format("#%08X",data); 151 | } 152 | if (type>=TypedValue.TYPE_FIRST_INT && type<=TypedValue.TYPE_LAST_INT) { 153 | return String.valueOf(data); 154 | } 155 | return String.format("<0x%X, type 0x%02X>",data,type); 156 | } 157 | 158 | private static String getPackage(int id) { 159 | if (id>>>24==1) { 160 | return "android:"; 161 | } 162 | return ""; 163 | } 164 | 165 | private static void log(String format,Object...arguments) { 166 | out.printf(format,arguments); 167 | out.println(); 168 | } 169 | 170 | /////////////////////////////////// ILLEGAL STUFF, DONT LOOK :) 171 | 172 | public static float complexToFloat(int complex) { 173 | return (float)(complex & 0xFFFFFF00)*RADIX_MULTS[(complex>>4) & 3]; 174 | } 175 | 176 | private static final float RADIX_MULTS[]={ 177 | 0.00390625F,3.051758E-005F,1.192093E-007F,4.656613E-010F 178 | }; 179 | private static final String DIMENSION_UNITS[]={ 180 | "px","dip","sp","pt","in","mm","","" 181 | }; 182 | private static final String FRACTION_UNITS[]={ 183 | "%","%p","","","","","","" 184 | }; 185 | } -------------------------------------------------------------------------------- /src/main/java/com/bigzhao/xml2axml/test/Main.java: -------------------------------------------------------------------------------- 1 | package com.bigzhao.xml2axml.test; 2 | 3 | import android.content.Context; 4 | import com.bigzhao.xml2axml.Encoder; 5 | import org.apache.commons.io.FileUtils; 6 | import org.xmlpull.v1.XmlPullParserException; 7 | 8 | import java.io.File; 9 | import java.io.FileNotFoundException; 10 | import java.io.IOException; 11 | import java.io.PrintStream; 12 | 13 | /** 14 | * Created by Roy on 15-10-6. 15 | */ 16 | public class Main { 17 | public static void main(String args[]) throws IOException, XmlPullParserException { 18 | if (args[0].startsWith("e")) { 19 | encode(args[1],args[2]); 20 | }else if (args[0].startsWith("d")){ 21 | decode(args[1],args[2]); 22 | }else if (args[0].startsWith("t")) { 23 | decode(args[1], args[2]); 24 | encode(args[2], args[3]); 25 | decode(args[3], args[4]); 26 | } 27 | } 28 | 29 | public static void encode(String in,String out) throws IOException, XmlPullParserException { 30 | Encoder e = new Encoder(); 31 | byte[] bs = e.encodeFile(new Context(), in); 32 | FileUtils.writeByteArrayToFile(new File(out), bs); 33 | } 34 | 35 | public static void decode(String in,String out) throws FileNotFoundException { 36 | AXMLPrinter.out=new PrintStream(new File(out)); 37 | AXMLPrinter.main(new String[]{in}); 38 | AXMLPrinter.out.close(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /xml2axml.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | --------------------------------------------------------------------------------