From ada5aaf17d025a004b373ae49397bbe5927b229f Mon Sep 17 00:00:00 2001 From: REAndroid Date: Tue, 9 May 2023 18:03:02 +0200 Subject: [PATCH] fix multi-package xml compiling #40 --- .../reandroid/apk/ApkModuleXmlEncoder.java | 22 + .../apk/xmlencoder/EncodeMaterials.java | 100 ++- .../apk/xmlencoder/FilePathEncoder.java | 59 +- .../apk/xmlencoder/PackageCreator.java | 35 +- .../reandroid/apk/xmlencoder/RESEncoder.java | 581 +++++++++--------- .../apk/xmlencoder/XMLEncodeSource.java | 41 +- 6 files changed, 497 insertions(+), 341 deletions(-) diff --git a/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java b/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java index 122d2a9..3fe6984 100644 --- a/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java +++ b/src/main/java/com/reandroid/apk/ApkModuleXmlEncoder.java @@ -18,8 +18,11 @@ package com.reandroid.apk; import com.reandroid.archive.APKArchive; import com.reandroid.archive.FileInputSource; import com.reandroid.apk.xmlencoder.RESEncoder; +import com.reandroid.archive.InputSource; +import com.reandroid.archive.InputSourceUtil; import com.reandroid.archive2.block.ApkSignatureBlock; import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; import com.reandroid.json.JSONArray; import com.reandroid.xml.XMLException; @@ -43,6 +46,7 @@ public class ApkModuleXmlEncoder { scanRootDir(rootDir); restorePathMap(mainDirectory); restoreSignatures(mainDirectory); + sortFiles(); } private void restoreSignatures(File dir) throws IOException { File sigDir = new File(dir, ApkUtil.SIGNATURE_DIR_NAME); @@ -79,6 +83,24 @@ public class ApkModuleXmlEncoder { archive.add(inputSource); } } + private void sortFiles(){ + APKArchive archive=getApkModule().getApkArchive(); + int i = 1; + for(InputSource inputSource:archive.listInputSources()){ + if(inputSource.getSort() == 0){ + inputSource.setSort(i); + i++; + } + } + InputSource manifest = archive.getInputSource(AndroidManifestBlock.FILE_NAME); + if(manifest != null){ + manifest.setSort(0); + } + List sourceList = archive.listInputSources(); + InputSourceUtil.sort(sourceList); + archive.clear(); + archive.addAll(sourceList); + } private void loadUncompressedFiles(File mainDirectory) throws IOException, XMLException { File file=new File(mainDirectory, UncompressedFiles.JSON_FILE); UncompressedFiles uncompressedFiles = getApkModule().getUncompressedFiles(); diff --git a/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java b/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java index b424d93..6a2482e 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/EncodeMaterials.java @@ -29,7 +29,6 @@ import com.reandroid.arsc.util.FrameworkTable; import com.reandroid.arsc.util.HexUtil; import com.reandroid.arsc.util.ResNameMap; import com.reandroid.arsc.value.Entry; -import com.reandroid.common.Frameworks; import java.util.HashSet; import java.util.Set; @@ -38,6 +37,7 @@ import java.util.regex.Matcher; public class EncodeMaterials { private final Set packageIdSet = new HashSet<>(); private PackageBlock currentPackage; + private ResourceIds.Table.Package currentLocalPackage; private final Set frameworkTables = new HashSet<>(); private APKLogger apkLogger; private boolean mForceCreateNamespaces = true; @@ -99,6 +99,10 @@ public class EncodeMaterials { } String type = matcher.group(4); String name = matcher.group(5); + if(isLocalPackageName(packageName)){ + return resolveLocalResourceId(packageName, type, name); + } + if(EncodeUtil.isEmpty(packageName) || packageName.equals(getCurrentPackageName()) || !isFrameworkPackageName(packageName)){ @@ -106,12 +110,35 @@ public class EncodeMaterials { } return resolveFrameworkResourceId(packageName, type, name); } + private int resolveLocalResourceId(String packageName, String type, String name){ + ResourceIds.Table.Package pkg = getLocalPackage(packageName); + Integer resourceId = pkg.getResourceId(type, name); + if(resourceId != null){ + return resourceId; + } + EntryGroup entryGroup=getLocalEntryGroup(type, name); + if(entryGroup!=null){ + return entryGroup.getResourceId(); + } + throw new EncodeException("Local entry not found: " + + "package=" + packageName + + ", type=" + type + + ", name=" + name); + } public int resolveLocalResourceId(String type, String name){ - for(ResourceIds.Table.Package pkg:packageIdSet){ - Integer resId = pkg.getResourceId(type, name); - if(resId!=null){ + ResourceIds.Table.Package current = this.currentLocalPackage; + if(current != null){ + Integer resId = current.getResourceId(type, name); + if(resId != null){ return resId; } + }else { + for(ResourceIds.Table.Package pkg:packageIdSet){ + Integer resId = pkg.getResourceId(type, name); + if(resId!=null){ + return resId; + } + } } EntryGroup entryGroup=getLocalEntryGroup(type, name); if(entryGroup!=null){ @@ -286,8 +313,73 @@ public class EncodeMaterials { } public EncodeMaterials setCurrentPackage(PackageBlock currentPackage) { this.currentPackage = currentPackage; + onCurrentPackageChanged(currentPackage); return this; } + public EncodeMaterials setCurrentLocalPackage(ResourceIds.Table.Package currentLocalPackage) { + this.currentLocalPackage = currentLocalPackage; + return this; + } + private void onCurrentPackageChanged(PackageBlock currentPackage){ + if(currentPackage == null){ + return; + } + ResourceIds.Table.Package current = null; + if(isUniquePackageIds()){ + current = getLocalPackage(currentPackage.getId()); + } + if(current == null && isUniquePackageNames()){ + current = getLocalPackage(currentPackage.getName()); + } + if(current != null){ + this.currentLocalPackage = current; + } + } + private ResourceIds.Table.Package getLocalPackage(int packageId){ + byte id = (byte) packageId; + for(ResourceIds.Table.Package pkg : packageIdSet){ + if(id == pkg.id){ + return pkg; + } + } + return null; + } + private ResourceIds.Table.Package getLocalPackage(String name){ + if(name == null){ + return null; + } + for(ResourceIds.Table.Package pkg : packageIdSet){ + if(name.equals(pkg.name)){ + return pkg; + } + } + return null; + } + private boolean isLocalPackageName(String packageName){ + if(packageName == null){ + return false; + } + for(ResourceIds.Table.Package pkg : packageIdSet){ + if(packageName.equals(pkg.name)){ + return true; + } + } + return false; + } + private boolean isUniquePackageNames(){ + Set names = new HashSet<>(); + for(ResourceIds.Table.Package pkg : packageIdSet){ + names.add(pkg.name); + } + return names.size() == packageIdSet.size(); + } + private boolean isUniquePackageIds(){ + Set ids = new HashSet<>(); + for(ResourceIds.Table.Package pkg : packageIdSet){ + ids.add(pkg.id); + } + return ids.size() == packageIdSet.size(); + } public EncodeMaterials addFramework(FrameworkApk frameworkApk) { if(frameworkApk!=null){ addFramework(frameworkApk.getTableBlock()); diff --git a/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java index 10cefb4..662c2a5 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/FilePathEncoder.java @@ -1,35 +1,35 @@ - /* - * 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. - */ +/* + * 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.apk.xmlencoder; - import com.reandroid.archive.APKArchive; - import com.reandroid.archive.FileInputSource; - import com.reandroid.archive.InputSource; - import com.reandroid.apk.ApkUtil; - import com.reandroid.apk.UncompressedFiles; - import com.reandroid.arsc.chunk.PackageBlock; - import com.reandroid.arsc.chunk.TypeBlock; - import com.reandroid.arsc.value.Entry; - import com.reandroid.xml.source.XMLFileSource; - import com.reandroid.xml.source.XMLSource; +import com.reandroid.archive.APKArchive; +import com.reandroid.archive.FileInputSource; +import com.reandroid.archive.InputSource; +import com.reandroid.apk.ApkUtil; +import com.reandroid.apk.UncompressedFiles; +import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.chunk.TypeBlock; +import com.reandroid.arsc.value.Entry; +import com.reandroid.xml.source.XMLFileSource; +import com.reandroid.xml.source.XMLSource; - import java.io.File; - import java.util.List; +import java.io.File; +import java.util.List; - public class FilePathEncoder { +public class FilePathEncoder { private final EncodeMaterials materials; private APKArchive apkArchive; private UncompressedFiles uncompressedFiles; @@ -78,6 +78,9 @@ package com.reandroid.apk.xmlencoder; entry.setValueAsString(path); entry.setSpecReference(materials.getSpecString(name)); InputSource inputSource=createInputSource(path, resFile); + if(inputSource instanceof XMLEncodeSource){ + ((XMLEncodeSource)inputSource).setEntry(entry); + } addInputSource(inputSource); return inputSource; } diff --git a/src/main/java/com/reandroid/apk/xmlencoder/PackageCreator.java b/src/main/java/com/reandroid/apk/xmlencoder/PackageCreator.java index 64145e4..6cbb0ff 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/PackageCreator.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/PackageCreator.java @@ -1,18 +1,18 @@ - /* - * 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. - */ +/* + * 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.apk.xmlencoder; import com.reandroid.apk.APKLogger; @@ -45,14 +45,13 @@ class PackageCreator { } public PackageBlock createNew(TableBlock tableBlock, ResourceIds.Table.Package pkgResourceIds){ loadNames(pkgResourceIds); - PackageBlock packageBlock=new PackageBlock(); + PackageBlock packageBlock = tableBlock.getPackageArray().createNext(); packageBlock.setName(mPackageName); packageBlock.setId(mPackageId); loadPackageInfoJson(packageBlock); + this.mPackageName = packageBlock.getName(); - tableBlock.getPackageArray() - .add(packageBlock); packageBlock.getSpecStringPool() .addStrings(mSpecNames); diff --git a/src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java index 3b8c69b..c3a2be5 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/RESEncoder.java @@ -1,306 +1,315 @@ - /* - * 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.apk.xmlencoder; +/* + * 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.apk.xmlencoder; - import com.reandroid.apk.*; - import com.reandroid.archive.APKArchive; - import com.reandroid.arsc.chunk.PackageBlock; - import com.reandroid.arsc.chunk.TableBlock; - import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; - import com.reandroid.xml.XMLDocument; - import com.reandroid.xml.XMLException; - import com.reandroid.xml.source.XMLFileSource; - import com.reandroid.xml.source.XMLSource; +import com.reandroid.apk.*; +import com.reandroid.archive.APKArchive; +import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; +import com.reandroid.xml.XMLDocument; +import com.reandroid.xml.XMLException; +import com.reandroid.xml.source.XMLFileSource; +import com.reandroid.xml.source.XMLSource; - import java.io.File; - import java.io.IOException; - import java.util.*; - import java.util.zip.ZipEntry; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.zip.ZipEntry; - public class RESEncoder { - private APKLogger apkLogger; - private final TableBlock tableBlock; - private final Set parsedFiles = new HashSet<>(); - private final ApkModule apkModule; - public RESEncoder(){ - this(new ApkModule("encoded", - new APKArchive()), new TableBlock()); - } - public RESEncoder(ApkModule module, TableBlock block){ - this.apkModule = module; - this.tableBlock = block; - if(!module.hasTableBlock()){ - BlockInputSource inputSource= - new BlockInputSource<>(TableBlock.FILE_NAME, block); - inputSource.setMethod(ZipEntry.STORED); - this.apkModule.getUncompressedFiles().addPath(inputSource); - this.apkModule.getApkArchive().add(inputSource); - } - } - public TableBlock getTableBlock(){ - return tableBlock; - } - public ApkModule getApkModule(){ - return apkModule; - } - public void scanDirectory(File mainDir) throws IOException, XMLException { - scanResourceFiles(mainDir); - } - private void scanResourceFiles(File mainDir) throws IOException, XMLException { - List pubXmlFileList = searchPublicXmlFiles(mainDir); - if(pubXmlFileList.size()==0){ - throw new IOException("No .*/values/" - + ApkUtil.FILE_NAME_PUBLIC_XML+" file found in '"+mainDir); - } - preloadStringPool(pubXmlFileList); +public class RESEncoder { + private APKLogger apkLogger; + private final TableBlock tableBlock; + private final Set parsedFiles = new HashSet<>(); + private final ApkModule apkModule; + public RESEncoder(){ + this(new ApkModule("encoded", + new APKArchive()), new TableBlock()); + } + public RESEncoder(ApkModule module, TableBlock block){ + this.apkModule = module; + this.tableBlock = block; + if(!module.hasTableBlock()){ + BlockInputSource inputSource= + new BlockInputSource<>(TableBlock.FILE_NAME, block); + inputSource.setMethod(ZipEntry.STORED); + this.apkModule.getUncompressedFiles().addPath(inputSource); + this.apkModule.getApkArchive().add(inputSource); + } + } + public TableBlock getTableBlock(){ + return tableBlock; + } + public ApkModule getApkModule(){ + return apkModule; + } + public void scanDirectory(File mainDir) throws IOException, XMLException { + scanResourceFiles(mainDir); + } + private void scanResourceFiles(File mainDir) throws IOException, XMLException { + List pubXmlFileList = searchPublicXmlFiles(mainDir); + if(pubXmlFileList.size()==0){ + throw new IOException("No .*/values/" + + ApkUtil.FILE_NAME_PUBLIC_XML+" file found in '"+mainDir); + } + preloadStringPool(pubXmlFileList); - EncodeMaterials encodeMaterials=new EncodeMaterials(); + EncodeMaterials encodeMaterials=new EncodeMaterials(); - Map map = - initializeEncodeMaterials(pubXmlFileList, encodeMaterials); + Map map = + initializeEncodeMaterials(pubXmlFileList, encodeMaterials); - Map packageBlockMap=new HashMap<>(); + Map packageBlockMap=new HashMap<>(); - for(File pubXmlFile:pubXmlFileList){ - ResourceIds.Table.Package pkgResourceIds=map.get(pubXmlFile); - if(pkgResourceIds==null){ - continue; - } - addParsedFiles(pubXmlFile); + for(File pubXmlFile:pubXmlFileList){ + ResourceIds.Table.Package pkgResourceIds = map.get(pubXmlFile); + if(pkgResourceIds==null){ + continue; + } + addParsedFiles(pubXmlFile); - PackageBlock packageBlock = createPackage(pkgResourceIds, pubXmlFile); - encodeMaterials.setCurrentPackage(packageBlock); - packageBlockMap.put(pubXmlFile, packageBlock); + PackageBlock packageBlock = createPackage(pkgResourceIds, pubXmlFile); + encodeMaterials.setCurrentPackage(packageBlock); + encodeMaterials.setCurrentLocalPackage(pkgResourceIds); + packageBlockMap.put(pubXmlFile, packageBlock); - ValuesEncoder valuesEncoder = new ValuesEncoder(encodeMaterials); - File fileIds = toId(pubXmlFile); - if(fileIds.isFile()){ - valuesEncoder.encodeValuesXml(fileIds); - packageBlock.sortTypes(); - packageBlock.refresh(); - addParsedFiles(fileIds); - } - File fileAttrs = toAttr(pubXmlFile); - if(fileAttrs.isFile()){ - valuesEncoder.encodeValuesXml(fileAttrs); - packageBlock.sortTypes(); - packageBlock.refresh(); - addParsedFiles(fileAttrs); - } - } - for(File pubXmlFile:pubXmlFileList){ - ResourceIds.Table.Package pkgResourceIds=map.get(pubXmlFile); - if(pkgResourceIds==null){ - continue; - } - addParsedFiles(pubXmlFile); + ValuesEncoder valuesEncoder = new ValuesEncoder(encodeMaterials); + File fileIds = toId(pubXmlFile); + if(fileIds.isFile()){ + valuesEncoder.encodeValuesXml(fileIds); + packageBlock.sortTypes(); + packageBlock.refresh(); + addParsedFiles(fileIds); + } + File fileAttrs = toAttr(pubXmlFile); + if(fileAttrs.isFile()){ + valuesEncoder.encodeValuesXml(fileAttrs); + packageBlock.sortTypes(); + packageBlock.refresh(); + addParsedFiles(fileAttrs); + } + } + File manifestFile = null; + for(File pubXmlFile:pubXmlFileList){ + ResourceIds.Table.Package pkgResourceIds = map.get(pubXmlFile); + if(pkgResourceIds==null){ + continue; + } + addParsedFiles(pubXmlFile); + if(manifestFile == null){ + manifestFile = toAndroidManifest(pubXmlFile); + } - PackageBlock packageBlock=packageBlockMap.get(pubXmlFile); + PackageBlock packageBlock=packageBlockMap.get(pubXmlFile); - if(packageBlock==null){ - packageBlock = createPackage(pkgResourceIds, pubXmlFile); - } - encodeMaterials.setCurrentPackage(packageBlock); + if(packageBlock==null){ + packageBlock = createPackage(pkgResourceIds, pubXmlFile); + } + encodeMaterials.setCurrentPackage(packageBlock); + encodeMaterials.setCurrentLocalPackage(pkgResourceIds); - File resDir=toResDirectory(pubXmlFile); - encodeResDir(encodeMaterials, resDir); - FilePathEncoder filePathEncoder = new FilePathEncoder(encodeMaterials); - filePathEncoder.setApkArchive(getApkModule().getApkArchive()); - filePathEncoder.setUncompressedFiles(getApkModule().getUncompressedFiles()); - filePathEncoder.encodeResDir(resDir); + File resDir=toResDirectory(pubXmlFile); + encodeResDir(encodeMaterials, resDir); + FilePathEncoder filePathEncoder = new FilePathEncoder(encodeMaterials); + filePathEncoder.setApkArchive(getApkModule().getApkArchive()); + filePathEncoder.setUncompressedFiles(getApkModule().getUncompressedFiles()); + filePathEncoder.encodeResDir(resDir); - packageBlock.sortTypes(); - packageBlock.refresh(); + packageBlock.sortTypes(); + packageBlock.refresh(); + } + tableBlock.refresh(); + PackageBlock packageBlock = tableBlock.pickOne(); + if(manifestFile != null){ + if(packageBlock != null){ + encodeMaterials.setCurrentPackage(packageBlock); + } + XMLSource xmlSource = + new XMLFileSource(AndroidManifestBlock.FILE_NAME, manifestFile); + XMLEncodeSource xmlEncodeSource = + new XMLEncodeSource(encodeMaterials, xmlSource); + getApkModule().getApkArchive().add(xmlEncodeSource); + } + } + private PackageBlock createPackage(ResourceIds.Table.Package pkgResourceIds + , File pubXmlFile){ + PackageCreator packageCreator = new PackageCreator(); + packageCreator.setPackageName(pkgResourceIds.name); + packageCreator.setAPKLogger(apkLogger); + packageCreator.setPackageDirectory(toPackageDirectory(pubXmlFile)); + return packageCreator.createNew(this.tableBlock, pkgResourceIds); + } + private void preloadStringPool(List pubXmlFileList){ + logMessage("Loading string pool ..."); + ValuesStringPoolBuilder poolBuilder=new ValuesStringPoolBuilder(); + for(File pubXml:pubXmlFileList){ + File resDir=toResDirectory(pubXml); + List valuesDirList = listValuesDir(resDir); + for(File dir:valuesDirList){ + logVerbose(poolBuilder.size()+" building pool: "+dir.getName()); + poolBuilder.scanValuesDirectory(dir); + } + } + poolBuilder.addTo(tableBlock.getTableStringPool()); + } - File manifestFile=toAndroidManifest(pubXmlFile); - XMLSource xmlSource = - new XMLFileSource(AndroidManifestBlock.FILE_NAME, manifestFile); - XMLEncodeSource xmlEncodeSource = - new XMLEncodeSource(encodeMaterials, xmlSource); + private Map initializeEncodeMaterials( + List pubXmlFileList, EncodeMaterials encodeMaterials) + throws IOException, XMLException { - getApkModule().getApkArchive().add(xmlEncodeSource); - } - tableBlock.refresh(); - } - private PackageBlock createPackage(ResourceIds.Table.Package pkgResourceIds - , File pubXmlFile){ - PackageCreator packageCreator = new PackageCreator(); - packageCreator.setPackageName(pkgResourceIds.name); - packageCreator.setAPKLogger(apkLogger); - packageCreator.setPackageDirectory(toPackageDirectory(pubXmlFile)); - return packageCreator.createNew(this.tableBlock, pkgResourceIds); - } - private void preloadStringPool(List pubXmlFileList){ - logMessage("Loading string pool ..."); - ValuesStringPoolBuilder poolBuilder=new ValuesStringPoolBuilder(); - for(File pubXml:pubXmlFileList){ - File resDir=toResDirectory(pubXml); - List valuesDirList = listValuesDir(resDir); - for(File dir:valuesDirList){ - logVerbose(poolBuilder.size()+" building pool: "+dir.getName()); - poolBuilder.scanValuesDirectory(dir); - } - } - poolBuilder.addTo(tableBlock.getTableStringPool()); - } + encodeMaterials.setAPKLogger(apkLogger); - private Map initializeEncodeMaterials( - List pubXmlFileList, EncodeMaterials encodeMaterials) - throws IOException, XMLException { + Map results = new HashMap<>(); - encodeMaterials.setAPKLogger(apkLogger); + String packageName=null; + for(File pubXmlFile:pubXmlFileList){ + if(packageName==null){ + File manifestFile = toAndroidManifest(pubXmlFile); + packageName=readManifestPackageName(manifestFile); + FrameworkApk frameworkApk = getApkModule() + .initializeAndroidFramework(XMLDocument.load(manifestFile)); + encodeMaterials.addFramework(frameworkApk); + } + ResourceIds resourceIds=new ResourceIds(); + resourceIds.fromXml(pubXmlFile); + List pkgList = resourceIds.getTable() + .listPackages(); + if(pkgList.size()==0){ + continue; + } + ResourceIds.Table.Package pkg = pkgList.get(0); + if(pkg.name==null){ + pkg.name=packageName; + } + encodeMaterials.addPackageIds(pkg); + results.put(pubXmlFile, pkg); + } - Map results = new HashMap<>(); + encodeMaterials.setAPKLogger(apkLogger); + return results; + } + private String readManifestPackageName(File manifestFile) throws XMLException { + XMLDocument manifestDocument = XMLDocument.load(manifestFile); + return manifestDocument + .getDocumentElement().getAttributeValue("package"); + } + private void encodeResDir(EncodeMaterials materials, File resDir) throws XMLException { - String packageName=null; - for(File pubXmlFile:pubXmlFileList){ - if(packageName==null){ - File manifestFile = toAndroidManifest(pubXmlFile); - packageName=readManifestPackageName(manifestFile); - FrameworkApk frameworkApk = getApkModule() - .initializeAndroidFramework(XMLDocument.load(manifestFile)); - encodeMaterials.addFramework(frameworkApk); - } - ResourceIds resourceIds=new ResourceIds(); - resourceIds.fromXml(pubXmlFile); - List pkgList = resourceIds.getTable() - .listPackages(); - if(pkgList.size()==0){ - continue; - } - ResourceIds.Table.Package pkg = pkgList.get(0); - if(pkg.name==null){ - pkg.name=packageName; - } - encodeMaterials.addPackageIds(pkg); - results.put(pubXmlFile, pkg); - } + List valuesDirList = listValuesDir(resDir); + for(File valuesDir:valuesDirList){ + encodeValuesDir(materials, valuesDir); + } + } + private void encodeValuesDir(EncodeMaterials materials, File valuesDir) throws XMLException { + ValuesEncoder valuesEncoder = new ValuesEncoder(materials); + List xmlFiles = ApkUtil.listFiles(valuesDir, ".xml"); + EncodeUtil.sortValuesXml(xmlFiles); + for(File file:xmlFiles){ + if(isAlreadyParsed(file)){ + continue; + } + addParsedFiles(file); + valuesEncoder.encodeValuesXml(file); + } + } + private File toAndroidManifest(File pubXmlFile){ + File resDirectory = toResDirectory(pubXmlFile); + File packageDirectory = resDirectory.getParentFile(); + File root = packageDirectory.getParentFile(); + return new File(root, AndroidManifestBlock.FILE_NAME); + } + private File toPackageDirectory(File pubXmlFile){ + return toResDirectory(pubXmlFile) + .getParentFile(); + } + private File toResDirectory(File pubXmlFile){ + return pubXmlFile + .getParentFile() + .getParentFile(); + } + private File toId(File pubXmlFile){ + return new File(pubXmlFile.getParentFile(), "ids.xml"); + } + private File toAttr(File pubXmlFile){ + return new File(pubXmlFile.getParentFile(), "attrs.xml"); + } + private List listValuesDir(File resDir){ + List results=new ArrayList<>(); + File def=new File(resDir, "values"); + results.add(def); + File[] dirList=resDir.listFiles(); + if(dirList!=null){ + for(File dir:dirList){ + if(def.equals(dir) || !dir.isDirectory()){ + continue; + } + if(dir.getName().startsWith("values-")){ + results.add(dir); + } + } + } + return results; + } + private List searchPublicXmlFiles(File mainDir){ + logVerbose("Searching public.xml: "+mainDir); + List dirList=ApkUtil.listDirectories(mainDir); + List xmlFiles = new ArrayList<>(); + for(File dir:dirList){ + if(dir.getName().equals("root")){ + continue; + } + xmlFiles.addAll( + ApkUtil.recursiveFiles(dir, ApkUtil.FILE_NAME_PUBLIC_XML)); + } + List results = new ArrayList<>(); + for(File file:xmlFiles){ + if(!EncodeUtil.isPublicXml(file)){ + continue; + } + if(toAndroidManifest(file).isFile()){ + results.add(file); + } + } + EncodeUtil.sortPublicXml(results); + return results; + } - encodeMaterials.setAPKLogger(apkLogger); - return results; - } - private String readManifestPackageName(File manifestFile) throws XMLException { - XMLDocument manifestDocument = XMLDocument.load(manifestFile); - return manifestDocument - .getDocumentElement().getAttributeValue("package"); - } - private void encodeResDir(EncodeMaterials materials, File resDir) throws XMLException { - - List valuesDirList = listValuesDir(resDir); - for(File valuesDir:valuesDirList){ - encodeValuesDir(materials, valuesDir); - } - } - private void encodeValuesDir(EncodeMaterials materials, File valuesDir) throws XMLException { - ValuesEncoder valuesEncoder = new ValuesEncoder(materials); - List xmlFiles = ApkUtil.listFiles(valuesDir, ".xml"); - EncodeUtil.sortValuesXml(xmlFiles); - for(File file:xmlFiles){ - if(isAlreadyParsed(file)){ - continue; - } - addParsedFiles(file); - valuesEncoder.encodeValuesXml(file); - } - } - private File toAndroidManifest(File pubXmlFile){ - File resDirectory = toResDirectory(pubXmlFile); - File packageDirectory = resDirectory.getParentFile(); - File root = packageDirectory.getParentFile(); - return new File(root, AndroidManifestBlock.FILE_NAME); - } - private File toPackageDirectory(File pubXmlFile){ - return toResDirectory(pubXmlFile) - .getParentFile(); - } - private File toResDirectory(File pubXmlFile){ - return pubXmlFile - .getParentFile() - .getParentFile(); - } - private File toId(File pubXmlFile){ - return new File(pubXmlFile.getParentFile(), "ids.xml"); - } - private File toAttr(File pubXmlFile){ - return new File(pubXmlFile.getParentFile(), "attrs.xml"); - } - private List listValuesDir(File resDir){ - List results=new ArrayList<>(); - File def=new File(resDir, "values"); - results.add(def); - File[] dirList=resDir.listFiles(); - if(dirList!=null){ - for(File dir:dirList){ - if(def.equals(dir) || !dir.isDirectory()){ - continue; - } - if(dir.getName().startsWith("values-")){ - results.add(dir); - } - } - } - return results; - } - private List searchPublicXmlFiles(File mainDir){ - logVerbose("Searching public.xml: "+mainDir); - List dirList=ApkUtil.listDirectories(mainDir); - List xmlFiles = new ArrayList<>(); - for(File dir:dirList){ - if(dir.getName().equals("root")){ - continue; - } - xmlFiles.addAll( - ApkUtil.recursiveFiles(dir, ApkUtil.FILE_NAME_PUBLIC_XML)); - } - List results = new ArrayList<>(); - for(File file:xmlFiles){ - if(!EncodeUtil.isPublicXml(file)){ - continue; - } - if(toAndroidManifest(file).isFile()){ - results.add(file); - } - } - EncodeUtil.sortPublicXml(results); - return results; - } - - private boolean isAlreadyParsed(File file){ - return parsedFiles.contains(file); - } - private void addParsedFiles(File file){ - parsedFiles.add(file); - } - public void setAPKLogger(APKLogger logger) { - this.apkLogger = logger; - this.apkModule.setAPKLogger(logger); - } - private void logMessage(String msg) { - if(apkLogger!=null){ - apkLogger.logMessage(msg); - } - } - private void logError(String msg, Throwable tr) { - if(apkLogger!=null){ - apkLogger.logError(msg, tr); - } - } - private void logVerbose(String msg) { - if(apkLogger!=null){ - apkLogger.logVerbose(msg); - } - } - } + private boolean isAlreadyParsed(File file){ + return parsedFiles.contains(file); + } + private void addParsedFiles(File file){ + parsedFiles.add(file); + } + public void setAPKLogger(APKLogger logger) { + this.apkLogger = logger; + this.apkModule.setAPKLogger(logger); + } + private void logMessage(String msg) { + if(apkLogger!=null){ + apkLogger.logMessage(msg); + } + } + private void logError(String msg, Throwable tr) { + if(apkLogger!=null){ + apkLogger.logError(msg, tr); + } + } + private void logVerbose(String msg) { + if(apkLogger!=null){ + apkLogger.logVerbose(msg); + } + } +} diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java index bc16f3e..2c703b1 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLEncodeSource.java @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2022 github.com/REAndroid * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,9 @@ package com.reandroid.apk.xmlencoder; import com.reandroid.archive.ByteInputSource; import com.reandroid.apk.CrcOutputStream; +import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.xml.ResXmlDocument; +import com.reandroid.arsc.value.Entry; import com.reandroid.xml.XMLException; import com.reandroid.xml.source.XMLSource; @@ -28,11 +30,27 @@ public class XMLEncodeSource extends ByteInputSource { private final EncodeMaterials encodeMaterials; private final XMLSource xmlSource; private ResXmlDocument resXmlDocument; - public XMLEncodeSource(EncodeMaterials encodeMaterials, XMLSource xmlSource){ + private Entry mEntry; + public XMLEncodeSource(EncodeMaterials encodeMaterials, XMLSource xmlSource, Entry entry){ super(new byte[0], xmlSource.getPath()); - this.encodeMaterials=encodeMaterials; - this.xmlSource=xmlSource; + this.encodeMaterials = encodeMaterials; + this.xmlSource = xmlSource; + this.mEntry = entry; } + public XMLEncodeSource(EncodeMaterials encodeMaterials, XMLSource xmlSource){ + this(encodeMaterials, xmlSource, null); + } + + public XMLSource getXmlSource() { + return xmlSource; + } + public Entry getEntry(){ + return mEntry; + } + public void setEntry(Entry entry) { + this.mEntry = entry; + } + @Override public long getLength() throws IOException{ return getResXmlBlock().countBytes(); @@ -64,7 +82,13 @@ public class XMLEncodeSource extends ByteInputSource { try { XMLFileEncoder xmlFileEncoder=new XMLFileEncoder(encodeMaterials); xmlFileEncoder.setCurrentPath(xmlSource.getPath()); - encodeMaterials.logVerbose("Encoding xml: "+xmlSource.getPath()); + EncodeMaterials encodeMaterials = this.encodeMaterials; + encodeMaterials.logVerbose("Encoding xml: " + xmlSource.getPath()); + PackageBlock currentPackage = encodeMaterials.getCurrentPackage(); + PackageBlock packageBlock = getEntryPackageBlock(); + if(packageBlock != null && packageBlock != currentPackage){ + encodeMaterials.setCurrentPackage(packageBlock); + } resXmlDocument = xmlFileEncoder.encode(xmlSource.getXMLDocument()); } catch (XMLException ex) { throw new EncodeException("XMLException on: '"+xmlSource.getPath() @@ -72,6 +96,13 @@ public class XMLEncodeSource extends ByteInputSource { } return resXmlDocument; } + private PackageBlock getEntryPackageBlock(){ + Entry entry = getEntry(); + if(entry != null){ + return entry.getPackageBlock(); + } + return null; + } @Override public void disposeInputSource(){ this.xmlSource.disposeXml();