mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-05-12 03:34:25 +02:00
fix: encode/decode XML styled strings properly
This commit is contained in:
parent
ee5db344af
commit
a5d71a28a4
@ -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);
|
||||
|
33
src/main/java/com/reandroid/lib/apk/XmlHelper.java
Normal file
33
src/main/java/com/reandroid/lib/apk/XmlHelper.java
Normal file
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ValueType> valueTypes = new HashSet<>();
|
||||
for(int i=0;i<bagItems.length;i++){
|
||||
ResValueBagItem bagItem = bagItems[i];
|
||||
String value = ValueDecoder.decodeIntEntry(entryStore, bagItem);
|
||||
ValueType valueType = bagItem.getValueType();
|
||||
XMLElement child = new XMLElement("item");
|
||||
child.setTextContent(value);
|
||||
if(valueType == ValueType.STRING){
|
||||
XmlHelper.setTextContent(child, bagItem.getValueAsPoolString());
|
||||
}else {
|
||||
String value = ValueDecoder.decodeIntEntry(entryStore, bagItem);
|
||||
child.setTextContent(value);
|
||||
}
|
||||
parentElement.addChild(child);
|
||||
valueTypes.add(bagItem.getValueType());
|
||||
valueTypes.add(valueType);
|
||||
}
|
||||
if(valueTypes.contains(ValueType.STRING)){
|
||||
parentElement.setTagName(ApkUtil.TAG_STRING_ARRAY);
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.reandroid.lib.apk.xmldecoder;
|
||||
|
||||
import com.reandroid.lib.apk.XmlHelper;
|
||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.decoder.ValueDecoder;
|
||||
import com.reandroid.lib.arsc.value.ResValueBag;
|
||||
@ -57,11 +58,14 @@ class XMLCommonBagDecoder extends BagDecoder{
|
||||
|
||||
child.setAttribute("name", name);
|
||||
|
||||
String value = ValueDecoder.decode(entryStore, currentPackageId,
|
||||
resourceId, item.getValueType(), item.getData());
|
||||
|
||||
child.setTextContent(value);
|
||||
|
||||
ValueType valueType = item.getValueType();
|
||||
if(valueType == ValueType.STRING){
|
||||
XmlHelper.setTextContent(child, item.getValueAsPoolString());
|
||||
}else {
|
||||
String value = ValueDecoder.decode(entryStore, currentPackageId,
|
||||
resourceId, item.getValueType(), item.getData());
|
||||
child.setTextContent(value);
|
||||
}
|
||||
parentElement.addChild(child);
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.reandroid.lib.apk.xmldecoder;
|
||||
|
||||
import com.reandroid.lib.apk.XmlHelper;
|
||||
import com.reandroid.lib.arsc.decoder.ValueDecoder;
|
||||
import com.reandroid.lib.arsc.value.BaseResValue;
|
||||
import com.reandroid.lib.arsc.value.ResValueBag;
|
||||
import com.reandroid.lib.arsc.value.ResValueBagItem;
|
||||
import com.reandroid.lib.arsc.value.ValueType;
|
||||
import com.reandroid.lib.arsc.value.plurals.PluralsQuantity;
|
||||
import com.reandroid.lib.common.EntryStore;
|
||||
import com.reandroid.xml.XMLElement;
|
||||
@ -36,11 +38,15 @@ class XMLPluralsDecoder extends BagDecoder{
|
||||
ResValueBagItem item = bagItems[i];
|
||||
|
||||
PluralsQuantity quantity = PluralsQuantity.valueOf(item.getIdLow());
|
||||
|
||||
String value = ValueDecoder.decodeIntEntry(entryStore, item);
|
||||
XMLElement child=new XMLElement("item");
|
||||
child.setAttribute("quantity", quantity.toString());
|
||||
child.setTextContent(value);
|
||||
|
||||
if(item.getValueType() == ValueType.STRING){
|
||||
XmlHelper.setTextContent(child, item.getValueAsPoolString());
|
||||
}else {
|
||||
String value = ValueDecoder.decodeIntEntry(entryStore, item);
|
||||
child.setTextContent(value);
|
||||
}
|
||||
|
||||
parentElement.addChild(child);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.reandroid.lib.apk.xmlencoder;
|
||||
|
||||
import com.reandroid.lib.apk.ApkUtil;
|
||||
import com.reandroid.xml.XMLDocument;
|
||||
import com.reandroid.xml.XMLElement;
|
||||
import com.reandroid.xml.XMLException;
|
||||
@ -85,6 +86,9 @@ class ValuesEncoder {
|
||||
if(type.startsWith("plurals")){
|
||||
return true;
|
||||
}
|
||||
if(type.startsWith("array")){
|
||||
return true;
|
||||
}
|
||||
if(type.startsWith("string")){
|
||||
return false;
|
||||
}
|
||||
@ -108,7 +112,13 @@ class ValuesEncoder {
|
||||
if(type==null){
|
||||
type=first.getTagName();
|
||||
}
|
||||
if(type==null||type.equals("item")){
|
||||
if(type==null){
|
||||
return def;
|
||||
}
|
||||
if(type.endsWith("-array")){
|
||||
return "array";
|
||||
}
|
||||
if(type.equals("item")){
|
||||
return def;
|
||||
}
|
||||
return type;
|
||||
|
@ -30,8 +30,10 @@ import java.util.*;
|
||||
|
||||
public class ValuesStringPoolBuilder {
|
||||
private final Set<String> stringList;
|
||||
private final Set<String> 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<XMLSpannable> buildSpannable(){
|
||||
List<XMLSpannable> results=new ArrayList<>();
|
||||
Set<String> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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){
|
||||
|
@ -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()){
|
||||
|
@ -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<XMLAttribute> mAttributes = new ArrayList<>();
|
||||
private final List<XMLElement> mChildElements = new ArrayList<>();
|
||||
private List<XMLComment> mComments;
|
||||
private final List<XMLComment> mComments = new ArrayList<>();
|
||||
private final List<XMLText> 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<XMLComment> 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<XMLAttribute> 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("</");
|
||||
writer.write(getTagName());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,8 +66,20 @@ public abstract class XMLNode {
|
||||
}
|
||||
mChildNodes.remove(xmlNode);
|
||||
}
|
||||
public void clearChildNodes(){
|
||||
clearChildNodesInternal();
|
||||
}
|
||||
void clearChildNodesInternal(){
|
||||
mChildNodes.clear();
|
||||
}
|
||||
public List<XMLNode> 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);
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user