diff --git a/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java b/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java index 5be68a7..6172d6e 100644 --- a/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java +++ b/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java @@ -25,6 +25,7 @@ import com.reandroid.lib.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock; import com.reandroid.lib.arsc.container.SpecTypePair; import com.reandroid.lib.arsc.decoder.ValueDecoder; +import com.reandroid.lib.arsc.item.TableString; import com.reandroid.lib.arsc.value.*; import com.reandroid.lib.common.EntryStore; import com.reandroid.lib.common.Frameworks; @@ -238,18 +239,17 @@ import java.util.*; attribute.setNameId(resourceId); element.setResourceId(resourceId); if(!entryBlock.isEntryTypeBag()){ - String value; ResValueInt resValueInt=(ResValueInt) entryBlock.getResValue(); if(resValueInt.getValueType()== ValueType.STRING){ - value=ValueDecoder.escapeSpecialCharacter( - resValueInt.getValueAsString()); + XmlHelper.setTextContent(element, + resValueInt.getValueAsPoolString()); }else { - value= ValueDecoder.decodeEntryValue(entryStore, + String value = ValueDecoder.decodeEntryValue(entryStore, entryBlock.getPackageBlock(), resValueInt.getValueType(), resValueInt.getData()); + element.setTextContent(value); } - element.setTextContent(value); }else { ResValueBag resValueBag=(ResValueBag) entryBlock.getResValue(); xmlBagDecoder.decode(resValueBag, element); diff --git a/src/main/java/com/reandroid/lib/apk/XmlHelper.java b/src/main/java/com/reandroid/lib/apk/XmlHelper.java new file mode 100644 index 0000000..5b7d967 --- /dev/null +++ b/src/main/java/com/reandroid/lib/apk/XmlHelper.java @@ -0,0 +1,33 @@ + /* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.lib.apk; + +import com.reandroid.lib.arsc.item.StringItem; +import com.reandroid.xml.XMLElement; + +public class XmlHelper { + public static void setTextContent(XMLElement element, StringItem stringItem){ + if(stringItem==null){ + element.clearChildNodes(); + return; + } + if(!stringItem.hasStyle()){ + element.setTextContent(stringItem.get()); + }else { + element.setSpannableText(stringItem.getHtml()); + } + } +} diff --git a/src/main/java/com/reandroid/lib/apk/xmldecoder/XMLArrayDecoder.java b/src/main/java/com/reandroid/lib/apk/xmldecoder/XMLArrayDecoder.java index 7728842..90df2d4 100644 --- a/src/main/java/com/reandroid/lib/apk/xmldecoder/XMLArrayDecoder.java +++ b/src/main/java/com/reandroid/lib/apk/xmldecoder/XMLArrayDecoder.java @@ -16,6 +16,7 @@ package com.reandroid.lib.apk.xmldecoder; import com.reandroid.lib.apk.ApkUtil; +import com.reandroid.lib.apk.XmlHelper; import com.reandroid.lib.arsc.decoder.ValueDecoder; import com.reandroid.lib.arsc.value.ResValueBag; import com.reandroid.lib.arsc.value.ResValueBagItem; @@ -38,11 +39,16 @@ import java.util.Set; Set valueTypes = new HashSet<>(); for(int i=0;i stringList; + private final Set styleList; public ValuesStringPoolBuilder(){ this.stringList=new HashSet<>(); + this.styleList=new HashSet<>(); } public void addTo(TableStringPool stringPool){ if(stringPool.getStringsArray().childesCount()==0){ @@ -39,6 +41,7 @@ import java.util.*; } stringPool.addStrings(stringList); stringList.clear(); + styleList.clear(); stringPool.refresh(); } private void buildWithStyles(TableStringPool stringPool){ @@ -74,11 +77,13 @@ import java.util.*; private List buildSpannable(){ List results=new ArrayList<>(); Set removeList=new HashSet<>(); - for(String text:stringList){ + for(String text:styleList){ XMLSpannable spannable=XMLSpannable.parse(text); if(spannable!=null){ results.add(spannable); removeList.add(text); + }else { + stringList.add(text); } } stringList.removeAll(removeList); @@ -143,14 +148,21 @@ import java.util.*; } } private void addStrings(XMLElement element){ - String text = ValueDecoder - .unEscapeSpecialCharacter(element.getTextContent()); - addString(text); + if(element.hasChildElements()){ + addStyleElement(element); + }else { + String text = ValueDecoder + .unEscapeSpecialCharacter(element.getTextContent()); + addString(text); + } } private void addString(String text){ if(text!=null && text.length()>0 && text.charAt(0)!='@'){ stringList.add(text); } } + private void addStyleElement(XMLElement element){ + styleList.add(element.buildTextContent()); + } } diff --git a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java index 673becd..ae4d3e9 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java +++ b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java @@ -27,6 +27,10 @@ public abstract class BaseResValueItem extends BaseResValue implements ResValueI BaseResValueItem(int bytesLength) { super(bytesLength); } + + public TableString getValueAsPoolString(){ + return getTableString(getData()); + } String getString(int ref){ TableString tableString=getTableString(ref); if(tableString==null){ diff --git a/src/main/java/com/reandroid/xml/XMLComment.java b/src/main/java/com/reandroid/xml/XMLComment.java index d46d4e5..ebddc58 100755 --- a/src/main/java/com/reandroid/xml/XMLComment.java +++ b/src/main/java/com/reandroid/xml/XMLComment.java @@ -62,6 +62,9 @@ public class XMLComment extends XMLElement { return XMLUtil.isEmpty(getTextContent()); } + + void buildTextContent(Writer writer) throws IOException{ + } @Override public boolean write(Writer writer, boolean newLineAttributes) throws IOException { if(isHidden()){ diff --git a/src/main/java/com/reandroid/xml/XMLElement.java b/src/main/java/com/reandroid/xml/XMLElement.java index ea4a5c7..1de7ba7 100755 --- a/src/main/java/com/reandroid/xml/XMLElement.java +++ b/src/main/java/com/reandroid/xml/XMLElement.java @@ -16,6 +16,8 @@ package com.reandroid.xml; +import com.reandroid.xml.parser.XMLSpanParser; + import java.io.IOException; import java.io.StringWriter; import java.io.Writer; @@ -26,7 +28,7 @@ public class XMLElement extends XMLNode{ private String mTagName; private final List mAttributes = new ArrayList<>(); private final List mChildElements = new ArrayList<>(); - private List mComments; + private final List mComments = new ArrayList<>(); private final List mTexts = new ArrayList<>(); private XMLElement mParent; private int mIndent; @@ -196,7 +198,7 @@ public class XMLElement extends XMLNode{ mTexts.clear(); } public XMLComment getCommentAt(int index){ - if(mComments==null || index<0){ + if(index<0){ return null; } if(index>=mComments.size()){ @@ -213,17 +215,11 @@ public class XMLElement extends XMLNode{ } } private void hideComments(boolean hide){ - if(mComments==null){ - return; - } for(XMLComment ce:mComments){ ce.setHidden(hide); } } public int getCommentsCount(){ - if(mComments==null){ - return 0; - } return mComments.size(); } public void addComments(Collection commentElements){ @@ -235,11 +231,7 @@ public class XMLElement extends XMLNode{ } } public void clearComments(){ - if(mComments==null){ - return; - } mComments.clear(); - mComments=null; } public void addComment(XMLComment commentElement) { addCommentInternal(commentElement, true); @@ -248,9 +240,6 @@ public class XMLElement extends XMLNode{ if(commentElement==null){ return; } - if(mComments==null){ - mComments=new ArrayList<>(); - } mComments.add(commentElement); commentElement.setIndent(getIndent()); commentElement.setParent(this); @@ -258,8 +247,12 @@ public class XMLElement extends XMLNode{ super.addChildNodeInternal(commentElement); } } - public void removeChildElements(){ + @Override + void clearChildNodesInternal(){ + super.clearChildNodesInternal(); mChildElements.clear(); + mComments.clear(); + mTexts.clear(); } public List listAttributes(){ return mAttributes; @@ -538,33 +531,41 @@ public class XMLElement extends XMLNode{ mTag =tag; } public String getTextContent(){ - return getTextContent(true); - } - public String getTextContent(boolean unEscape){ - String text=buildTextContent(); - if(unEscape){ - text=XMLUtil.unEscapeXmlChars(text); - } - return text; - } - private String buildTextContent(){ if(!hasTextContent()){ return null; } + return buildTextContent(); + } + public String buildTextContent(){ StringWriter writer=new StringWriter(); - for(XMLNode child:getChildNodes()){ - try { - child.write(writer, false); - } catch (IOException ignored) { - } - } - writer.flush(); try { + for(XMLNode node:getChildNodes()){ + node.buildTextContent(writer); + } + writer.flush(); writer.close(); } catch (IOException ignored) { } return writer.toString(); } + void buildTextContent(Writer writer) throws IOException { + writer.write("<"); + writer.write(getTagName()); + appendAttributes(writer, false); + if(!hasChildNodes()){ + writer.write("/>"); + return; + } + writer.write('>'); + for(XMLNode node:getChildNodes()){ + node.buildTextContent(writer); + } + if(hasChildNodes()){ + writer.write("'); + } + } private void appendTextContent(Writer writer) throws IOException { for(XMLNode child:getChildNodes()){ if(child instanceof XMLElement){ @@ -573,6 +574,9 @@ public class XMLElement extends XMLNode{ child.write(writer, false); } } + public boolean hasChildElements(){ + return mChildElements.size()>0; + } public boolean hasTextContent() { return mTexts.size()>0; } @@ -582,6 +586,17 @@ public class XMLElement extends XMLNode{ } return mTexts.get(0).getText(); } + public void setSpannableText(String text){ + clearChildNodes(); + XMLElement element = parseSpanSafe(text); + if(element==null){ + addText( new XMLText(text)); + return; + } + for(XMLNode xmlNode:element.getChildNodes()){ + super.addChildNode(xmlNode); + } + } public void setTextContent(String text){ setTextContent(text, true); } @@ -775,4 +790,16 @@ public class XMLElement extends XMLNode{ return strWriter.toString(); } + private static XMLElement parseSpanSafe(String spanText){ + if(spanText==null){ + return null; + } + try { + XMLSpanParser spanParser = new XMLSpanParser(); + return spanParser.parse(spanText); + } catch (XMLException ignored) { + return null; + } + } + } diff --git a/src/main/java/com/reandroid/xml/XMLNode.java b/src/main/java/com/reandroid/xml/XMLNode.java index 7ff67f5..bb799f8 100644 --- a/src/main/java/com/reandroid/xml/XMLNode.java +++ b/src/main/java/com/reandroid/xml/XMLNode.java @@ -66,8 +66,20 @@ public abstract class XMLNode { } mChildNodes.remove(xmlNode); } + public void clearChildNodes(){ + clearChildNodesInternal(); + } + void clearChildNodesInternal(){ + mChildNodes.clear(); + } public List getChildNodes() { return mChildNodes; + } + boolean hasChildNodes(){ + return mChildNodes.size()>0; + } + void buildTextContent(Writer writer) throws IOException{ + } public boolean write(Writer writer) throws IOException { return write(writer, false); diff --git a/src/main/java/com/reandroid/xml/XMLText.java b/src/main/java/com/reandroid/xml/XMLText.java index 8d5b7a8..5ebf73b 100644 --- a/src/main/java/com/reandroid/xml/XMLText.java +++ b/src/main/java/com/reandroid/xml/XMLText.java @@ -44,6 +44,10 @@ public class XMLText extends XMLNode{ this.text=XMLUtil.escapeXmlChars(text); } @Override + void buildTextContent(Writer writer) throws IOException{ + writer.write(this.text); + } + @Override public boolean write(Writer writer, boolean newLineAttributes) throws IOException { if(!XMLUtil.isEmpty(this.text)){ writer.write(this.text); diff --git a/src/main/java/com/reandroid/xml/XmlHeaderElement.java b/src/main/java/com/reandroid/xml/XmlHeaderElement.java index bc44564..1c7794a 100755 --- a/src/main/java/com/reandroid/xml/XmlHeaderElement.java +++ b/src/main/java/com/reandroid/xml/XmlHeaderElement.java @@ -15,7 +15,10 @@ */ package com.reandroid.xml; -public class XmlHeaderElement extends XMLElement { + import java.io.IOException; + import java.io.Writer; + + public class XmlHeaderElement extends XMLElement { private static final String ATTR_VERSION="version"; private static final String ATTR_ENCODING="encoding"; private static final String ATTR_STANDALONE="standalone"; @@ -100,4 +103,8 @@ public class XmlHeaderElement extends XMLElement { int getIndent(){ return 0; } + @Override + void buildTextContent(Writer writer) throws IOException { + + } }