diff --git a/src/main/java/com/reandroid/apkeditor/info/Info.java b/src/main/java/com/reandroid/apkeditor/info/Info.java index e401509..1af01e4 100644 --- a/src/main/java/com/reandroid/apkeditor/info/Info.java +++ b/src/main/java/com/reandroid/apkeditor/info/Info.java @@ -31,17 +31,16 @@ import com.reandroid.arsc.coder.ReferenceString; import com.reandroid.arsc.coder.ValueCoder; import com.reandroid.arsc.model.ResourceEntry; +import com.reandroid.arsc.value.*; import com.reandroid.dex.model.DexDirectory; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.HexUtil; -import com.reandroid.arsc.value.AttributeDataFormat; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResValue; -import com.reandroid.arsc.value.ValueType; import com.reandroid.utils.collection.CollectionUtil; +import com.reandroid.utils.collection.ComputeIterator; import java.io.*; import java.util.*; +import java.util.function.Function; public class Info extends CommandExecutor { private InfoWriter mInfoWriter; @@ -100,8 +99,12 @@ private void print(ApkModule apkModule) throws IOException { printXmlTree(apkModule); printXmlStrings(apkModule); + printStrings(apkModule); listFiles(apkModule); listXmlFiles(apkModule); + printConfigurations(apkModule); + printLanguages(apkModule); + printLocales(apkModule); } private void printSourceFile() throws IOException { InfoOptions options = getOptions(); @@ -357,15 +360,25 @@ private void printAppClass(ApkModule apkModule) throws IOException { } private void printXmlStrings(ApkModule apkModule) throws IOException { InfoOptions options = getOptions(); - String xmlStrings = options.xmlStrings; - if (xmlStrings == null) { + List xmlStrings = options.xmlStrings; + if (xmlStrings.isEmpty()) { return; } InfoWriter infoWriter = getInfoWriter(); - ResXmlDocument document = apkModule.loadResXmlDocument(xmlStrings); - document.setApkFile(null); - document.setPackageBlock(null); - infoWriter.writeStringPool(xmlStrings, document.getStringPool()); + for (String path : xmlStrings) { + ResXmlDocument document = apkModule.loadResXmlDocument(path); + document.setApkFile(null); + document.setPackageBlock(null); + infoWriter.writeStringPool(path, document.getStringPool()); + } + } + private void printStrings(ApkModule apkModule) throws IOException { + InfoOptions options = getOptions(); + if (!options.strings || !apkModule.hasTableBlock()) { + return; + } + InfoWriter infoWriter = getInfoWriter(); + infoWriter.writeStringPool(TableBlock.FILE_NAME, apkModule.getTableBlock().getStringPool()); } private void printXmlTree(ApkModule apkModule) throws IOException { InfoOptions options = getOptions(); @@ -405,6 +418,53 @@ private void listXmlFiles(ApkModule apkModule) throws IOException { } getInfoWriter().writeArray("CompiledXmlFiles", names.toArray(new String[0])); } + private void printConfigurations(ApkModule apkModule) throws IOException { + InfoOptions options = getOptions(); + if (!options.configurations || !apkModule.hasTableBlock()) { + return; + } + Iterator iterator = apkModule.getTableBlock().getResConfigs(); + List qualifiers = CollectionUtil.toUniqueList( + ComputeIterator.of(iterator, config -> { + String qualifier = config.getQualifiers(); + if (qualifier.length() != 0) { + qualifier = qualifier.substring(1); + } + return qualifier; + })); + + qualifiers.sort(CompareUtil.getComparableComparator()); + + getInfoWriter().writeArray("configurations", qualifiers.toArray(new String[0])); + } + private void printLanguages(ApkModule apkModule) throws IOException { + InfoOptions options = getOptions(); + if (!options.languages || !apkModule.hasTableBlock()) { + return; + } + Iterator iterator = apkModule.getTableBlock().getResConfigs(); + List languages = CollectionUtil.toUniqueList( + ComputeIterator.of(iterator, ResConfig::getLanguage)); + + languages.sort(CompareUtil.getComparableComparator()); + + getInfoWriter().writeArray("languages", languages.toArray(new String[0])); + } + private void printLocales(ApkModule apkModule) throws IOException { + InfoOptions options = getOptions(); + if (!options.locales || !apkModule.hasTableBlock()) { + return; + } + Iterator iterator = apkModule.getTableBlock().getResConfigs(); + List locales = CollectionUtil.toUniqueList( + ComputeIterator.of(iterator, ResConfig::getLocale)); + + locales.remove(""); + + locales.sort(CompareUtil.getComparableComparator()); + + getInfoWriter().writeArray("locales", locales.toArray(new String[0])); + } private String getValueOfName(ResXmlElement element){ ResXmlAttribute attribute = element .searchAttributeByResourceId(AndroidManifest.ID_name); diff --git a/src/main/java/com/reandroid/apkeditor/info/InfoOptions.java b/src/main/java/com/reandroid/apkeditor/info/InfoOptions.java index 56c94dd..6d64746 100644 --- a/src/main/java/com/reandroid/apkeditor/info/InfoOptions.java +++ b/src/main/java/com/reandroid/apkeditor/info/InfoOptions.java @@ -37,51 +37,85 @@ public class InfoOptions extends OptionsWithFramework { @ChoiceArg(name = "-t", description = "info_print_types", values = {TYPE_TEXT, TYPE_JSON, TYPE_XML}) public String type = TYPE_TEXT; + @OptionArg(name = "-v", description = "info_verbose_mode", flag = true) public boolean verbose = false; + @OptionArg(name = "-package", description = "info_package_name", flag = true) public boolean packageName = false; + @OptionArg(name = "-version-code", description = "info_app_version_code", flag = true) public boolean versionCode = false; + @OptionArg(name = "-version-name", description = "info_app_version_name", flag = true) public boolean versionName = false; + @OptionArg(name = "-min-sdk-version", description = "info_min_sdk_version", flag = true) public boolean minSdkVersion = false; + @OptionArg(name = "-target-sdk-version", description = "info_target_sdk_version", flag = true) public boolean targetSdkVersion = false; + @OptionArg(name = "-app-name", description = "info_app_name", flag = true) public boolean appName = false; + @OptionArg(name = "-app-icon", description = "info_app_icon", flag = true) public boolean appIcon = false; + @OptionArg(name = "-app-round-icon", description = "info_app_icon_round", flag = true) public boolean appRoundIcon = false; + @OptionArg(name = "-permissions", description = "info_permissions", flag = true) public boolean permissions = false; + @OptionArg(name = "-app-class", description = "info_app_class_name", flag = true) public boolean appClass = false; + @OptionArg(name = "-activities", description = "info_activities", flag = true) public boolean activities = false; + @OptionArg(name = "-res", description = "info_res") public final List resList = new ArrayList<>(); + @OptionArg(name = "-resources", description = "info_resources", flag = true) public boolean resources = false; + @OptionArg(name = "-filter-type", description = "info_filter_type") public final List typeFilterList = new ArrayList<>(); + @OptionArg(name = "-dex", description = "info_dex", flag = true) public boolean dex = false; + @OptionArg(name = "-signatures", description = "info_signatures", flag = true) public boolean signatures = false; + @OptionArg(name = "-signatures-base64", description = "info_signatures_base64", flag = true) public boolean signatures_base64 = false; + @OptionArg(name = "-xmlstrings", description = "info_xml_strings") - public String xmlStrings; + public List xmlStrings = new ArrayList<>(); + + @OptionArg(name = "-strings", description = "info_strings", flag = true) + public boolean strings = false; + @OptionArg(name = "-xmltree", description = "info_xml_tree") public final List xmlTree = new ArrayList<>(); + @OptionArg(name = "-list-files", description = "info_list_files", flag = true) public boolean listFiles = false; + @OptionArg(name = "-list-xml-files", description = "info_list_xml_files", flag = true) public boolean listXmlFiles = false; + @OptionArg(name = "-configurations", description = "info_configurations", flag = true) + public boolean configurations = false; + + @OptionArg(name = "-languages", description = "info_languages", flag = true) + public boolean languages = false; + + @OptionArg(name = "-locales", description = "info_locales", flag = true) + public boolean locales = false; + public InfoOptions(){ super(); } @@ -148,8 +182,9 @@ private boolean isDefault() { boolean flagsChanged = activities || appClass || appIcon || appName || appRoundIcon || dex || minSdkVersion || packageName || permissions || targetSdkVersion || resources || signatures || signatures_base64 || versionCode || versionName || - listFiles || listXmlFiles || xmlStrings != null; + listFiles || listXmlFiles || configurations || languages || locales || strings; - return !flagsChanged && resList.isEmpty() && typeFilterList.isEmpty() && xmlTree.isEmpty(); + return !flagsChanged && resList.isEmpty() && typeFilterList.isEmpty() && + xmlTree.isEmpty() && xmlStrings.isEmpty(); } } diff --git a/src/main/resources/strings/strings.properties b/src/main/resources/strings/strings.properties index 374db3c..afd3249 100644 --- a/src/main/resources/strings/strings.properties +++ b/src/main/resources/strings/strings.properties @@ -64,6 +64,9 @@ info_example_2=[Specify output and type]\n java -jar APKEditor.jar info -i path info_example_3=[Print only specific type]\n java -jar APKEditor.jar info -i path/input.apk -resources -filter-type drawable info_filter_type=Prints only the specified resource type names\n *This applies only when flag '-resources' used.\n *Can be multiple. info_invalid_output_extension=Invalid file extension! Expected '%s', '%s' +info_configurations=Print the configurations in the APK. +info_languages=Print the languages in the APK. +info_locales=Print the locales in the APK. info_list_files=List files inside apk. info_list_xml_files=List compiled xml files inside apk. info_min_sdk_version=Minimum SDK version. @@ -74,10 +77,11 @@ info_res=Prints resource entries specified by either of\:\n 1) Hex or decimal r info_resources=Prints all resources info_signatures=Prints signature information. info_signatures_base64=Prints signature information with base64 certificates. +info_strings=Print the contents of the resource table string pool in the APK. info_target_sdk_version=Target SDK version. info_verbose_mode=Verbose mode. info_xml_tree=Prints the compiled xmls in the given assets.\n *Can be multiple -info_xml_strings=Print the strings of the given compiled xml assets. +info_xml_strings=Print the strings of the given compiled xml assets.\n *Can be multiple input_path=Input path. invalid_sig_parameter_combination=Invalid parameter combination!\nSignatures directory provided but missing: -t sig invalid_type_format=Invalid <%s> string '%s'