fix multi-package xml compiling #40

This commit is contained in:
REAndroid 2023-05-09 18:03:02 +02:00
parent e136a8d570
commit ada5aaf17d
6 changed files with 497 additions and 341 deletions

View File

@ -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<InputSource> 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();

View File

@ -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<ResourceIds.Table.Package> packageIdSet = new HashSet<>();
private PackageBlock currentPackage;
private ResourceIds.Table.Package currentLocalPackage;
private final Set<FrameworkTable> 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<String> names = new HashSet<>();
for(ResourceIds.Table.Package pkg : packageIdSet){
names.add(pkg.name);
}
return names.size() == packageIdSet.size();
}
private boolean isUniquePackageIds(){
Set<Byte> 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());

View File

@ -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;
}

View File

@ -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);

View File

@ -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<File> 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<TableBlock> 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<File> 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<File> 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<TableBlock> 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<File> 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<File, ResourceIds.Table.Package> map =
initializeEncodeMaterials(pubXmlFileList, encodeMaterials);
Map<File, ResourceIds.Table.Package> map =
initializeEncodeMaterials(pubXmlFileList, encodeMaterials);
Map<File, PackageBlock> packageBlockMap=new HashMap<>();
Map<File, PackageBlock> 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<File> pubXmlFileList){
logMessage("Loading string pool ...");
ValuesStringPoolBuilder poolBuilder=new ValuesStringPoolBuilder();
for(File pubXml:pubXmlFileList){
File resDir=toResDirectory(pubXml);
List<File> 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<File, ResourceIds.Table.Package> initializeEncodeMaterials(
List<File> 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<File> pubXmlFileList){
logMessage("Loading string pool ...");
ValuesStringPoolBuilder poolBuilder=new ValuesStringPoolBuilder();
for(File pubXml:pubXmlFileList){
File resDir=toResDirectory(pubXml);
List<File> 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<File, ResourceIds.Table.Package> initializeEncodeMaterials(
List<File> pubXmlFileList, EncodeMaterials encodeMaterials)
throws IOException, XMLException {
Map<File, ResourceIds.Table.Package> 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<ResourceIds.Table.Package> 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<File, ResourceIds.Table.Package> 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<ResourceIds.Table.Package> 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<File> 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<File> 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<File> listValuesDir(File resDir){
List<File> 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<File> searchPublicXmlFiles(File mainDir){
logVerbose("Searching public.xml: "+mainDir);
List<File> dirList=ApkUtil.listDirectories(mainDir);
List<File> xmlFiles = new ArrayList<>();
for(File dir:dirList){
if(dir.getName().equals("root")){
continue;
}
xmlFiles.addAll(
ApkUtil.recursiveFiles(dir, ApkUtil.FILE_NAME_PUBLIC_XML));
}
List<File> 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<File> 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<File> 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<File> listValuesDir(File resDir){
List<File> 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<File> searchPublicXmlFiles(File mainDir){
logVerbose("Searching public.xml: "+mainDir);
List<File> dirList=ApkUtil.listDirectories(mainDir);
List<File> xmlFiles = new ArrayList<>();
for(File dir:dirList){
if(dir.getName().equals("root")){
continue;
}
xmlFiles.addAll(
ApkUtil.recursiveFiles(dir, ApkUtil.FILE_NAME_PUBLIC_XML));
}
List<File> 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);
}
}
}

View File

@ -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();