mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-05-05 00:14:25 +02:00
fix issue: Styled strings merging. #5
This commit is contained in:
parent
8c7b70fd3a
commit
ac88f79acc
@ -17,6 +17,8 @@ package com.reandroid.lib.apk;
|
||||
|
||||
import com.reandroid.archive.APKArchive;
|
||||
import com.reandroid.lib.arsc.chunk.TableBlock;
|
||||
import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
import com.reandroid.lib.arsc.pool.builder.StringPoolMerger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@ -35,8 +37,11 @@ public class ApkBundle {
|
||||
if(moduleList.size()==0){
|
||||
throw new FileNotFoundException("Nothing to merge, empty modules");
|
||||
}
|
||||
ApkModule result=new ApkModule("merged", new APKArchive());
|
||||
ApkModule result=new ApkModule(generateMergedModuleName(), new APKArchive());
|
||||
result.setAPKLogger(apkLogger);
|
||||
|
||||
mergeStringPools(result);
|
||||
|
||||
ApkModule base=getBaseModule();
|
||||
if(base==null){
|
||||
base=getLargestTableModule();
|
||||
@ -56,6 +61,43 @@ public class ApkBundle {
|
||||
result.getApkArchive().sortApkFiles();
|
||||
return result;
|
||||
}
|
||||
private void mergeStringPools(ApkModule mergedModule) throws IOException {
|
||||
if(!hasOneTableBlock() || mergedModule.hasTableBlock()){
|
||||
return;
|
||||
}
|
||||
logMessage("Merging string pools ... ");
|
||||
TableBlock createdTable = new TableBlock();
|
||||
BlockInputSource<TableBlock> inputSource=
|
||||
new BlockInputSource<>(TableBlock.FILE_NAME, createdTable);
|
||||
mergedModule.getApkArchive().add(inputSource);
|
||||
|
||||
StringPoolMerger poolMerger = new StringPoolMerger();
|
||||
|
||||
for(ApkModule apkModule:getModules()){
|
||||
if(!apkModule.hasTableBlock()){
|
||||
continue;
|
||||
}
|
||||
TableStringPool stringPool = apkModule.getVolatileTableStringPool();
|
||||
poolMerger.add(stringPool);
|
||||
}
|
||||
|
||||
poolMerger.mergeTo(createdTable.getTableStringPool());
|
||||
|
||||
logMessage("Merged string pools="+poolMerger.getMergedPools()
|
||||
+", style="+poolMerger.getMergedStyleStrings()
|
||||
+", strings="+poolMerger.getMergedStrings());
|
||||
}
|
||||
private String generateMergedModuleName(){
|
||||
Set<String> moduleNames=mModulesMap.keySet();
|
||||
String merged="merged";
|
||||
int i=1;
|
||||
String name=merged;
|
||||
while (moduleNames.contains(name)){
|
||||
name=merged+"_"+i;
|
||||
i++;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
private ApkModule getLargestTableModule() throws IOException {
|
||||
ApkModule apkModule=null;
|
||||
int chunkSize=0;
|
||||
@ -123,6 +165,14 @@ public class ApkBundle {
|
||||
public Collection<ApkModule> getModules(){
|
||||
return mModulesMap.values();
|
||||
}
|
||||
private boolean hasOneTableBlock(){
|
||||
for(ApkModule apkModule:getModules()){
|
||||
if(apkModule.hasTableBlock()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void setAPKLogger(APKLogger logger) {
|
||||
this.apkLogger = logger;
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ public class TableBlock extends BaseChunk implements JSONConvert<JSONObject> {
|
||||
if(tableBlock==null||tableBlock==this){
|
||||
return;
|
||||
}
|
||||
if(getPackageArray().childesCount()==0){
|
||||
if(getPackageArray().childesCount()==0 && getTableStringPool().countStrings()==0){
|
||||
getTableStringPool().merge(tableBlock.getTableStringPool());
|
||||
}
|
||||
getPackageArray().merge(tableBlock.getPackageArray());
|
||||
|
@ -160,7 +160,7 @@ public class StyleItem extends IntegerArray implements JSONConvert<JSONObject> {
|
||||
}
|
||||
};
|
||||
}
|
||||
final List<StyleSpanInfo> getSpanInfoList(){
|
||||
public final List<StyleSpanInfo> getSpanInfoList(){
|
||||
if(mSpanInfoList!=null){
|
||||
return mSpanInfoList;
|
||||
}
|
||||
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* 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.arsc.pool.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SpannedText {
|
||||
private String mTag;
|
||||
private String mText;
|
||||
private int mStart;
|
||||
private int mEnd;
|
||||
private List<SpannedText> mChildes=new ArrayList<>();
|
||||
public SpannedText(){
|
||||
}
|
||||
public void parse(String text){
|
||||
char[] allChars=text.toCharArray();
|
||||
int len=allChars.length;
|
||||
String firstTag=null;
|
||||
String endTag=null;
|
||||
StringBuilder tag=null;
|
||||
boolean firstTagFound=false;
|
||||
int posFirst=0;
|
||||
int posSecond=0;
|
||||
for(int i=0;i<len;i++){
|
||||
char ch=allChars[i];
|
||||
if(tag!=null){
|
||||
tag.append(ch);
|
||||
if(ch=='>'){
|
||||
if(!firstTagFound){
|
||||
firstTag=tag.toString();
|
||||
firstTagFound=true;
|
||||
tag=null;
|
||||
}else {
|
||||
endTag=tag.toString();
|
||||
if(isTagsMatch(firstTag, endTag)){
|
||||
break;
|
||||
}
|
||||
endTag=null;
|
||||
tag=null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(ch=='<'){
|
||||
if(isClosing(allChars, i+1)){
|
||||
if(!firstTagFound){
|
||||
tag=null;
|
||||
continue;
|
||||
}
|
||||
}else if(firstTagFound){
|
||||
firstTagFound=false;
|
||||
firstTag=null;
|
||||
}
|
||||
tag=new StringBuilder();
|
||||
tag.append(ch);
|
||||
if(!firstTagFound){
|
||||
posFirst=i;
|
||||
}else {
|
||||
posSecond=i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(firstTag==null || endTag==null){
|
||||
return;
|
||||
}
|
||||
mStart=posFirst;
|
||||
mEnd=posSecond;
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(text, 0, posFirst);
|
||||
builder.append(text, posFirst+firstTag.length(), posSecond);
|
||||
builder.append(text.substring(posSecond+endTag.length()));
|
||||
mText=builder.toString();
|
||||
}
|
||||
private boolean isClosing(char[] allChars, int pos){
|
||||
for(int i=pos;i<allChars.length;i++){
|
||||
char ch=allChars[i];
|
||||
if(ch=='/'){
|
||||
return true;
|
||||
}
|
||||
if(ch!=' '){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private boolean isTagsMatch(String start, String end){
|
||||
start=trimStart(start);
|
||||
end=trimEndTag(end);
|
||||
return start.equals(end);
|
||||
}
|
||||
private String trimStart(String start){
|
||||
start=start.substring(1, start.length()-1);
|
||||
int i=start.indexOf(' ');
|
||||
if(i>0){
|
||||
start=start.substring(0,i);
|
||||
}
|
||||
start=start.trim();
|
||||
return start;
|
||||
}
|
||||
private String trimEndTag(String end){
|
||||
end=end.substring(1, end.length()-1).trim();
|
||||
end=end.substring(1).trim();
|
||||
return end;
|
||||
}
|
||||
private static final Pattern PATTERN_TAG=Pattern.compile("^(.*)(<[^/<>]+>)([^<]+)(</[^<>]+>)(.*)$");
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.arsc.pool.builder;
|
||||
|
||||
import com.reandroid.lib.arsc.array.StringArray;
|
||||
import com.reandroid.lib.arsc.array.StyleArray;
|
||||
import com.reandroid.lib.arsc.item.StyleItem;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
import com.reandroid.lib.arsc.model.StyleSpanInfo;
|
||||
import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class StringPoolMerger implements Comparator<String> {
|
||||
private final Set<TableStringPool> mPools;
|
||||
private int mMergedPools;
|
||||
private int mMergedStrings;
|
||||
private int mMergedStyleStrings;
|
||||
public StringPoolMerger(){
|
||||
this.mPools=new HashSet<>();
|
||||
}
|
||||
public void mergeTo(TableStringPool destination){
|
||||
mMergedPools=0;
|
||||
mMergedStrings=0;
|
||||
mMergedStyleStrings=0;
|
||||
if(destination.countStrings()>0 || destination.countStyles()>0){
|
||||
throw new IllegalArgumentException("Destination string pool is not empty");
|
||||
}
|
||||
mergeStyledStrings(destination);
|
||||
mergeNonStyledStrings(destination);
|
||||
mMergedPools = mPools.size();
|
||||
mPools.clear();
|
||||
destination.refresh();
|
||||
}
|
||||
public void add(TableStringPool stringPool){
|
||||
mPools.add(stringPool);
|
||||
}
|
||||
public int getMergedPools() {
|
||||
return mMergedPools;
|
||||
}
|
||||
public int getMergedStyleStrings() {
|
||||
return mMergedStyleStrings;
|
||||
}
|
||||
public int getMergedStrings() {
|
||||
return mMergedStrings;
|
||||
}
|
||||
|
||||
private void mergeStyledStrings(TableStringPool destination){
|
||||
List<TableString> styledStrings = getStyledStrings();
|
||||
Map<String, TableString> mapTableStrings =
|
||||
destination.insertStrings(toStringList(styledStrings));
|
||||
Map<String, TableString> mapTags =
|
||||
destination.insertStrings(listStyleTags(styledStrings));
|
||||
|
||||
StyleArray styleArray = destination.getStyleArray();
|
||||
styleArray.setChildesCount(styledStrings.size());
|
||||
|
||||
for(TableString tableString:styledStrings){
|
||||
TableString createdString = mapTableStrings.get(tableString.get());
|
||||
StyleItem createdStyle = styleArray.get(createdString.getIndex());
|
||||
|
||||
StyleItem styleItem = tableString.getStyle();
|
||||
for(StyleSpanInfo spanInfo:styleItem.getSpanInfoList()){
|
||||
if(spanInfo!=null && createdStyle!=null){
|
||||
int tagReference = mapTags.get(spanInfo.getTag())
|
||||
.getIndex();
|
||||
createdStyle.addStylePiece(
|
||||
tagReference,
|
||||
spanInfo.getFirst(),
|
||||
spanInfo.getLast());
|
||||
}
|
||||
}
|
||||
}
|
||||
mMergedStyleStrings=styledStrings.size();
|
||||
}
|
||||
private void mergeNonStyledStrings(TableStringPool destination){
|
||||
List<String> nonStyledStrings=getNonStyledStrings();
|
||||
destination.insertStrings(nonStyledStrings);
|
||||
mMergedStrings=nonStyledStrings.size();
|
||||
}
|
||||
private List<TableString> getStyledStrings(){
|
||||
Map<String, TableString> mapUniqueHtml = new HashMap<>();
|
||||
for(TableStringPool pool:mPools){
|
||||
int styleCount = pool.countStyles();
|
||||
StringArray<TableString> stringArray = pool.getStringsArray();
|
||||
for(int i=0;i<styleCount;i++){
|
||||
TableString tableString = stringArray.get(i);
|
||||
if(tableString==null || !tableString.hasStyle()){
|
||||
continue;
|
||||
}
|
||||
mapUniqueHtml.put(tableString.getHtml(), tableString);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(mapUniqueHtml.values());
|
||||
}
|
||||
private List<String> getNonStyledStrings(){
|
||||
Set<String> uniqueSet = new HashSet<>();
|
||||
for(TableStringPool pool:mPools){
|
||||
TableString[] tableStrings = pool.getStrings();
|
||||
if(tableStrings==null){
|
||||
continue;
|
||||
}
|
||||
for(int i=0;i<tableStrings.length;i++){
|
||||
TableString tableString=tableStrings[i];
|
||||
if(tableString==null || tableString.hasStyle()){
|
||||
continue;
|
||||
}
|
||||
uniqueSet.add(tableString.get());
|
||||
}
|
||||
}
|
||||
List<String> results=new ArrayList<>(uniqueSet);
|
||||
results.sort(this);
|
||||
return results;
|
||||
}
|
||||
private List<String> toStringList(Collection<TableString> tableStringList){
|
||||
List<String> results=new ArrayList<>(tableStringList.size());
|
||||
for(TableString tableString:tableStringList){
|
||||
String str=tableString.get();
|
||||
if(str!=null){
|
||||
results.add(str);
|
||||
}
|
||||
}
|
||||
results.sort(this);
|
||||
return results;
|
||||
}
|
||||
private List<String> listStyleTags(List<TableString> styledStrings){
|
||||
Set<String> resultSet=new HashSet<>();
|
||||
for(TableString tableString:styledStrings){
|
||||
StyleItem style = tableString.getStyle();
|
||||
if(style==null){
|
||||
continue;
|
||||
}
|
||||
for(StyleSpanInfo spanInfo:style.getSpanInfoList()){
|
||||
if(spanInfo!=null){
|
||||
resultSet.add(spanInfo.getTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
List<String> results=new ArrayList<>(resultSet);
|
||||
results.sort(this);
|
||||
return results;
|
||||
}
|
||||
@Override
|
||||
public int compare(String s1, String s2) {
|
||||
return s1.compareTo(s2);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user