diff --git a/README.md b/README.md index 2f8e925..3e921c3 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Get apk-parser from maven central repo: net.dongliu apk-parser - 2.5.3 + 2.6.0 ``` From version 2.0, apk-parser requires java7. The last version support java6 is 1.7.4. diff --git a/pom.xml b/pom.xml index 43d55ec..28524dc 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ apk-parser apk-parser jar - 2.5.3 + 2.6.0 https://github.com/xiaxiaocao/apk-parser @@ -141,7 +141,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 1.6 sign-artifacts @@ -149,6 +149,12 @@ sign + + + --pinentry-mode + loopback + + diff --git a/src/main/java/net/dongliu/apk/parser/AbstractApkFile.java b/src/main/java/net/dongliu/apk/parser/AbstractApkFile.java index fdb7633..aacf64e 100644 --- a/src/main/java/net/dongliu/apk/parser/AbstractApkFile.java +++ b/src/main/java/net/dongliu/apk/parser/AbstractApkFile.java @@ -241,9 +241,58 @@ private void transBinaryXml(byte[] data, XmlStreamer xmlStreamer) throws IOExcep binaryXmlParser.parse(); } + /** + * This method return icons specified in android manifest file, application. + * The icons could be file icon, color icon, or adaptive icon, etc. + * + * @return icon files. + */ + public List getAllIcons() throws IOException { + List iconPaths = getIconPaths(); + if (iconPaths.isEmpty()) { + return Collections.emptyList(); + } + List iconFaces = new ArrayList<>(iconPaths.size()); + for (IconPath iconPath : iconPaths) { + String filePath = iconPath.getPath(); + if (filePath.endsWith(".xml")) { + // adaptive icon? + byte[] data = getFileData(filePath); + if (data == null) { + continue; + } + parseResourceTable(); + + AdaptiveIconParser iconParser = new AdaptiveIconParser(); + transBinaryXml(data, iconParser); + Icon backgroundIcon = null; + if (iconParser.getBackground() != null) { + backgroundIcon = newFileIcon(iconParser.getBackground(), iconPath.getDensity()); + } + Icon foregroundIcon = null; + if (iconParser.getForeground() != null) { + foregroundIcon = newFileIcon(iconParser.getForeground(), iconPath.getDensity()); + } + AdaptiveIcon icon = new AdaptiveIcon(backgroundIcon, foregroundIcon); + iconFaces.add(icon); + } else { + Icon icon = newFileIcon(filePath, iconPath.getDensity()); + iconFaces.add(icon); + } + } + return iconFaces; + } + + private Icon newFileIcon(String filePath, int density) throws IOException { + return new Icon(filePath, density, getFileData(filePath)); + } + /** * Get the default apk icon file. + * + * @deprecated use {@link #getAllIcons()} */ + @Deprecated public Icon getIconFile() throws IOException { ApkMeta apkMeta = getApkMeta(); String iconPath = apkMeta.getIcon(); @@ -255,7 +304,10 @@ public Icon getIconFile() throws IOException { /** * Get all the icon paths, for different densities. + * + * @deprecated using {@link #getAllIcons()} instead */ + @Deprecated public List getIconPaths() throws IOException { parseManifest(); return this.iconPaths; @@ -263,12 +315,15 @@ public List getIconPaths() throws IOException { /** * Get all the icons, for different densities. + * + * @deprecated using {@link #getAllIcons()} instead */ + @Deprecated public List getIconFiles() throws IOException { List iconPaths = getIconPaths(); List icons = new ArrayList<>(iconPaths.size()); for (IconPath iconPath : iconPaths) { - Icon icon = new Icon(iconPath.getPath(), iconPath.getDensity(), getFileData(iconPath.getPath())); + Icon icon = newFileIcon(iconPath.getPath(), iconPath.getDensity()); icons.add(icon); } return icons; @@ -353,6 +408,9 @@ public void close() throws IOException { this.iconPaths = null; } + /** + * The local used to parse apk + */ public Locale getPreferredLocale() { return preferredLocale; } diff --git a/src/main/java/net/dongliu/apk/parser/Main.java b/src/main/java/net/dongliu/apk/parser/Main.java index d10116b..e8d7326 100644 --- a/src/main/java/net/dongliu/apk/parser/Main.java +++ b/src/main/java/net/dongliu/apk/parser/Main.java @@ -1,7 +1,9 @@ package net.dongliu.apk.parser; +import net.dongliu.apk.parser.bean.IconFace; + import java.io.IOException; -import java.security.cert.CertificateException; +import java.util.List; /** * Main method for parser apk @@ -9,9 +11,10 @@ * @author Liu Dong {@literal } */ public class Main { - public static void main(String[] args) throws IOException, CertificateException { + public static void main(String[] args) throws IOException { try (ApkFile apkFile = new ApkFile(args[0])) { - System.out.println(apkFile.getApkSingers().get(0).getCertificateMetas()); + List allIcons = apkFile.getAllIcons(); + System.out.println(allIcons); } } } diff --git a/src/main/java/net/dongliu/apk/parser/bean/AdaptiveIcon.java b/src/main/java/net/dongliu/apk/parser/bean/AdaptiveIcon.java new file mode 100644 index 0000000..7b258d6 --- /dev/null +++ b/src/main/java/net/dongliu/apk/parser/bean/AdaptiveIcon.java @@ -0,0 +1,55 @@ +package net.dongliu.apk.parser.bean; + +import java.io.Serializable; + +/** + * Android adaptive icon, from android 8.0 + */ +public class AdaptiveIcon implements IconFace, Serializable { + private static final long serialVersionUID = 4185750290211529320L; + private final Icon foreground; + private final Icon background; + + public AdaptiveIcon(Icon foreground, Icon background) { + this.foreground = foreground; + this.background = background; + } + + + /** + * The foreground icon + */ + public Icon getForeground() { + return foreground; + } + + /** + * The background icon + */ + public Icon getBackground() { + return background; + } + + @Override + public String toString() { + return "AdaptiveIcon{" + + "foreground=" + foreground + + ", background=" + background + + '}'; + } + + @Override + public boolean isFile() { + return foreground.isFile(); + } + + @Override + public byte[] getData() { + return foreground.getData(); + } + + @Override + public String getPath() { + return foreground.getPath(); + } +} diff --git a/src/main/java/net/dongliu/apk/parser/bean/ApkMeta.java b/src/main/java/net/dongliu/apk/parser/bean/ApkMeta.java index 033d0b5..6075801 100644 --- a/src/main/java/net/dongliu/apk/parser/bean/ApkMeta.java +++ b/src/main/java/net/dongliu/apk/parser/bean/ApkMeta.java @@ -1,5 +1,7 @@ package net.dongliu.apk.parser.bean; +import net.dongliu.apk.parser.AbstractApkFile; + import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; @@ -92,11 +94,14 @@ public void addUsesPermission(String permission) { * the icon file path in apk * * @return null if not found + * @deprecated use {@link AbstractApkFile#getAllIcons()} instead. */ + @Deprecated public String getIcon() { return icon; } + @Deprecated public void setIcon(String icon) { this.icon = icon; } diff --git a/src/main/java/net/dongliu/apk/parser/bean/ColorIcon.java b/src/main/java/net/dongliu/apk/parser/bean/ColorIcon.java new file mode 100644 index 0000000..a963588 --- /dev/null +++ b/src/main/java/net/dongliu/apk/parser/bean/ColorIcon.java @@ -0,0 +1,27 @@ +package net.dongliu.apk.parser.bean; + +import java.io.Serializable; + +/** + * The plain icon, using color drawable resource. + */ +//to be implemented +public class ColorIcon implements IconFace, Serializable { + private static final long serialVersionUID = -7913024425268466186L; + + @Override + public boolean isFile() { + return false; + } + + @Override + public byte[] getData() { + throw new UnsupportedOperationException(); + } + + @Override + public String getPath() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/net/dongliu/apk/parser/bean/Icon.java b/src/main/java/net/dongliu/apk/parser/bean/Icon.java index 0bb6e5b..41399d2 100644 --- a/src/main/java/net/dongliu/apk/parser/bean/Icon.java +++ b/src/main/java/net/dongliu/apk/parser/bean/Icon.java @@ -1,16 +1,18 @@ package net.dongliu.apk.parser.bean; import javax.annotation.Nullable; +import java.io.Serializable; /** - * The apk icon file path, and data + * The plain file apk icon. * * @author Liu Dong */ -public class Icon { +public class Icon implements IconFace, Serializable { + private static final long serialVersionUID = 8680309892249769701L; private final String path; - private int density; + private final int density; private final byte[] data; public Icon(String path, int density, byte[] data) { @@ -34,6 +36,11 @@ public int getDensity() { return density; } + @Override + public boolean isFile() { + return true; + } + /** * Icon data may be null, due to some apk missing the icon file. */ diff --git a/src/main/java/net/dongliu/apk/parser/bean/IconFace.java b/src/main/java/net/dongliu/apk/parser/bean/IconFace.java new file mode 100644 index 0000000..454a34c --- /dev/null +++ b/src/main/java/net/dongliu/apk/parser/bean/IconFace.java @@ -0,0 +1,27 @@ +package net.dongliu.apk.parser.bean; + +import java.io.Serializable; + +/** + * The icon interface + */ +public interface IconFace extends Serializable { + + /** + * If icon is file resource + */ + boolean isFile(); + + /** + * Return the icon file as bytes. This method is valid only when {@link #isFile()} return true. + * Otherwise, {@link UnsupportedOperationException} should be thrown. + */ + byte[] getData(); + + + /** + * Return the icon file path in apk file. This method is valid only when {@link #isFile()} return true. + * Otherwise, {@link UnsupportedOperationException} should be thrown. + */ + String getPath(); +} diff --git a/src/main/java/net/dongliu/apk/parser/parser/AdaptiveIconParser.java b/src/main/java/net/dongliu/apk/parser/parser/AdaptiveIconParser.java new file mode 100644 index 0000000..32ef7fa --- /dev/null +++ b/src/main/java/net/dongliu/apk/parser/parser/AdaptiveIconParser.java @@ -0,0 +1,61 @@ +package net.dongliu.apk.parser.parser; + +import net.dongliu.apk.parser.struct.xml.*; + +/** + * Parse adaptive icon xml file. + * + * @author Liu Dong dongliu@live.cn + */ +public class AdaptiveIconParser implements XmlStreamer { + + private String foreground; + private String background; + + public String getForeground() { + return foreground; + } + + public String getBackground() { + return background; + } + + @Override + public void onStartTag(XmlNodeStartTag xmlNodeStartTag) { + if (xmlNodeStartTag.getName().equals("background")) { + background = getDrawable(xmlNodeStartTag); + } else if (xmlNodeStartTag.getName().equals("foreground")) { + foreground = getDrawable(xmlNodeStartTag); + } + } + + private String getDrawable(XmlNodeStartTag xmlNodeStartTag) { + Attributes attributes = xmlNodeStartTag.getAttributes(); + for (Attribute attribute : attributes.values()) { + if (attribute.getName().equals("drawable")) { + return attribute.getValue(); + } + } + return null; + } + + @Override + public void onEndTag(XmlNodeEndTag xmlNodeEndTag) { + + } + + @Override + public void onCData(XmlCData xmlCData) { + + } + + @Override + public void onNamespaceStart(XmlNamespaceStartTag tag) { + + } + + @Override + public void onNamespaceEnd(XmlNamespaceEndTag tag) { + + } +}