Merge branch 'master' into issue-1498
21
INTERNAL.md
@ -200,7 +200,7 @@ to get the source downloaded. This is no small download, expect to use 40-60GB.
|
||||
After that, you need to build AOSP via this [documentation](https://source.android.com/source/building.html) guide. Now
|
||||
we aren't building the entire AOSP package, the initial build is to just see if you are capable of building it.
|
||||
|
||||
We check out a certain tag. Currently we use `android-7.1.1_r4`.
|
||||
We check out a certain tag. Currently we use `android-7.1.2_r11`.
|
||||
|
||||
### Including our modified `frameworks/base` package.
|
||||
|
||||
@ -239,3 +239,22 @@ we lose the ability to quickly build just the aapt binary. So the Windows proced
|
||||
1. `source build/envsetup.sh`
|
||||
2. `lunch sdk-eng`
|
||||
3. `make OUT_DIR=out-x64 LOCAL_MULTILIB=64 USE_NINJA=false aapt`
|
||||
|
||||
# Gradle Tips n Tricks
|
||||
|
||||
./gradlew build shadowJar proguard -x test
|
||||
|
||||
This skips the testing suite (which currently takes 2-4 minutes). Use this when making quick builds and save the testing
|
||||
suite before pushing to GitHub.
|
||||
|
||||
./gradlew build shadowJar proguard -Dtest.debug
|
||||
|
||||
This enables debugging on the test suite. This starts the debugger on port 5005 which you can connect with IntelliJ.
|
||||
|
||||
./gradlew :brut.apktool:apktool-lib:test ---tests "*BuildAndDecodeTest"
|
||||
|
||||
This runs the library project of Apktool, selecting a specific test to run. Comes in handy when writing a new test and
|
||||
only wanting to run that one. The asterisk is used to the full path to the test can be ignored. You can additionally
|
||||
match this with the debugging parameter to debug a specific test. This command can be found below.
|
||||
|
||||
./gradlew :brut.apktool:apktool-lib:test --tests "*BuildAndDecodeTest" -Dtest.debug
|
||||
|
@ -13,6 +13,10 @@ It is NOT intended for piracy and other non-legal uses. It could be used for loc
|
||||
- [Project Page](http://ibotpeaches.github.io/Apktool/)
|
||||
- [#apktool on freenode](http://webchat.freenode.net/?channels=apktool)
|
||||
|
||||
#### Sponsored by
|
||||
|
||||
* [Sourcetoad](https://www.sourcetoad.com/cool-tools/apktool/) - helping with a weekly sponsorship for continued improvement and maintenance of the project.
|
||||
|
||||
#### Links
|
||||
- [Downloads](https://bitbucket.org/iBotPeaches/apktool/downloads)
|
||||
- [Downloads Mirror](http://connortumbleson.com/apktool/)
|
||||
|
@ -19,6 +19,7 @@ package brut.androlib;
|
||||
import brut.androlib.meta.MetaInfo;
|
||||
import brut.androlib.meta.UsesFramework;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.androlib.res.data.ResConfigFlags;
|
||||
import brut.androlib.res.data.ResPackage;
|
||||
import brut.androlib.res.data.ResTable;
|
||||
import brut.androlib.res.data.ResUnknownFiles;
|
||||
@ -279,7 +280,22 @@ public class Androlib {
|
||||
mAndRes.setSharedLibrary(meta.sharedLibrary);
|
||||
|
||||
if (meta.sdkInfo != null && meta.sdkInfo.get("minSdkVersion") != null) {
|
||||
mMinSdkVersion = Integer.parseInt(meta.sdkInfo.get("minSdkVersion"));
|
||||
String minSdkVersion = meta.sdkInfo.get("minSdkVersion");
|
||||
|
||||
// Preview builds use short letter for API versions
|
||||
switch (minSdkVersion) {
|
||||
case "M":
|
||||
mMinSdkVersion = ResConfigFlags.SDK_MNC;
|
||||
break;
|
||||
case "N":
|
||||
mMinSdkVersion = ResConfigFlags.SDK_NOUGAT;
|
||||
break;
|
||||
case "O":
|
||||
mMinSdkVersion = ResConfigFlags.SDK_O;
|
||||
break;
|
||||
default:
|
||||
mMinSdkVersion = Integer.parseInt(meta.sdkInfo.get("minSdkVersion"));
|
||||
}
|
||||
}
|
||||
|
||||
if (outFile == null) {
|
||||
|
@ -507,6 +507,9 @@ public class ResConfigFlags {
|
||||
public final static byte SDK_LOLLIPOP = 21;
|
||||
public final static byte SDK_LOLLIPOP_MR1 = 22;
|
||||
public final static byte SDK_MNC = 23;
|
||||
public final static byte SDK_NOUGAT = 24;
|
||||
public final static byte SDK_NOUGAT_MR1 = 25;
|
||||
public final static byte SDK_O = 26;
|
||||
|
||||
public final static byte ORIENTATION_ANY = 0;
|
||||
public final static byte ORIENTATION_PORT = 1;
|
||||
|
@ -90,7 +90,8 @@ public class ResValueFactory {
|
||||
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
|
||||
return ResAttr.factory(parentVal, items, this, mPackage);
|
||||
}
|
||||
if (key == ResArrayValue.BAG_KEY_ARRAY_START) {
|
||||
// Android O Preview added an unknown enum for ResTable_map. This is hardcoded as 0 for now.
|
||||
if (key == ResArrayValue.BAG_KEY_ARRAY_START || key == 0) {
|
||||
return new ResArrayValue(parentVal, items);
|
||||
}
|
||||
if (key >= ResPluralsValue.BAG_KEY_PLURALS_START && key <= ResPluralsValue.BAG_KEY_PLURALS_END) {
|
||||
|
@ -20,6 +20,8 @@ import brut.androlib.AndrolibException;
|
||||
import brut.androlib.err.CantFind9PatchChunk;
|
||||
import brut.util.ExtDataInput;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.*;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
@ -39,8 +41,24 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
|
||||
int w = im.getWidth(), h = im.getHeight();
|
||||
|
||||
BufferedImage im2 = new BufferedImage(w+2, h+2, BufferedImage.TYPE_INT_ARGB);
|
||||
im2.createGraphics().drawImage(im, 1, 1, w, h, null);
|
||||
BufferedImage im2 = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB);
|
||||
if (im.getType() == BufferedImage.TYPE_CUSTOM) {
|
||||
//TODO: Ensure this is gray + alpha case?
|
||||
Raster srcRaster = im.getRaster();
|
||||
WritableRaster dstRaster = im2.getRaster();
|
||||
int[] gray = null, alpha = null;
|
||||
for (int y = 0; y < im.getHeight(); y++) {
|
||||
gray = srcRaster.getSamples(0, y, w, 1, 0, gray);
|
||||
alpha = srcRaster.getSamples(0, y, w, 1, 1, alpha);
|
||||
|
||||
dstRaster.setSamples(1, y + 1, w, 1, 0, gray);
|
||||
dstRaster.setSamples(1, y + 1, w, 1, 1, gray);
|
||||
dstRaster.setSamples(1, y + 1, w, 1, 2, gray);
|
||||
dstRaster.setSamples(1, y + 1, w, 1, 3, alpha);
|
||||
}
|
||||
} else {
|
||||
im2.createGraphics().drawImage(im, 1, 1, w, h, null);
|
||||
}
|
||||
|
||||
NinePatch np = getNinePatch(data);
|
||||
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
||||
@ -56,6 +74,34 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
|
||||
}
|
||||
|
||||
// Some images additionally use Optical Bounds
|
||||
// https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
|
||||
try {
|
||||
OpticalInset oi = getOpticalInset(data);
|
||||
|
||||
for (int i = 0; i < oi.layoutBoundsLeft; i++) {
|
||||
int x = 1 + i;
|
||||
im2.setRGB(x, h + 1, OI_COLOR);
|
||||
}
|
||||
|
||||
for (int i = 0; i < oi.layoutBoundsRight; i++) {
|
||||
int x = w - i;
|
||||
im2.setRGB(x, h + 1, OI_COLOR);
|
||||
}
|
||||
|
||||
for (int i = 0; i < oi.layoutBoundsTop; i++) {
|
||||
int y = 1 + i;
|
||||
im2.setRGB(w + 1, y, OI_COLOR);
|
||||
}
|
||||
|
||||
for (int i = 0; i < oi.layoutBoundsBottom; i++) {
|
||||
int y = h - i;
|
||||
im2.setRGB(w + 1, y, OI_COLOR);
|
||||
}
|
||||
} catch (CantFind9PatchChunk t) {
|
||||
// This chunk might not exist
|
||||
}
|
||||
|
||||
ImageIO.write(im2, "png", out);
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
@ -70,11 +116,18 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||
private NinePatch getNinePatch(byte[] data) throws AndrolibException,
|
||||
IOException {
|
||||
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
||||
find9patchChunk(di);
|
||||
find9patchChunk(di, NP_CHUNK_TYPE);
|
||||
return NinePatch.decode(di);
|
||||
}
|
||||
|
||||
private void find9patchChunk(DataInput di) throws AndrolibException,
|
||||
private OpticalInset getOpticalInset(byte[] data) throws AndrolibException,
|
||||
IOException {
|
||||
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
||||
find9patchChunk(di, OI_CHUNK_TYPE);
|
||||
return OpticalInset.decode(di);
|
||||
}
|
||||
|
||||
private void find9patchChunk(DataInput di, int magic) throws AndrolibException,
|
||||
IOException {
|
||||
di.skipBytes(8);
|
||||
while (true) {
|
||||
@ -84,7 +137,7 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||
} catch (IOException ex) {
|
||||
throw new CantFind9PatchChunk("Cant find nine patch chunk", ex);
|
||||
}
|
||||
if (di.readInt() == NP_CHUNK_TYPE) {
|
||||
if (di.readInt() == magic) {
|
||||
return;
|
||||
}
|
||||
di.skipBytes(size + 4);
|
||||
@ -104,7 +157,9 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||
}
|
||||
|
||||
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
|
||||
private static final int OI_CHUNK_TYPE = 0x6e704c62; // npLb
|
||||
private static final int NP_COLOR = 0xff000000;
|
||||
private static final int OI_COLOR = 0xffff0000;
|
||||
|
||||
private static class NinePatch {
|
||||
public final int padLeft, padRight, padTop, padBottom;
|
||||
@ -121,16 +176,16 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||
}
|
||||
|
||||
public static NinePatch decode(ExtDataInput di) throws IOException {
|
||||
di.skipBytes(1);
|
||||
di.skipBytes(1); // wasDeserialized
|
||||
byte numXDivs = di.readByte();
|
||||
byte numYDivs = di.readByte();
|
||||
di.skipBytes(1);
|
||||
di.skipBytes(8);
|
||||
di.skipBytes(1); // numColors
|
||||
di.skipBytes(8); // xDivs/yDivs offset
|
||||
int padLeft = di.readInt();
|
||||
int padRight = di.readInt();
|
||||
int padTop = di.readInt();
|
||||
int padBottom = di.readInt();
|
||||
di.skipBytes(4);
|
||||
di.skipBytes(4); // colorsOffset
|
||||
int[] xDivs = di.readIntArray(numXDivs);
|
||||
int[] yDivs = di.readIntArray(numYDivs);
|
||||
|
||||
@ -138,4 +193,25 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
||||
yDivs);
|
||||
}
|
||||
}
|
||||
|
||||
private static class OpticalInset {
|
||||
public final int layoutBoundsLeft, layoutBoundsTop, layoutBoundsRight, layoutBoundsBottom;
|
||||
|
||||
public OpticalInset(int layoutBoundsLeft, int layoutBoundsTop,
|
||||
int layoutBoundsRight, int layoutBoundsBottom) {
|
||||
this.layoutBoundsLeft = layoutBoundsLeft;
|
||||
this.layoutBoundsTop = layoutBoundsTop;
|
||||
this.layoutBoundsRight = layoutBoundsRight;
|
||||
this.layoutBoundsBottom = layoutBoundsBottom;
|
||||
}
|
||||
|
||||
public static OpticalInset decode(ExtDataInput di) throws IOException {
|
||||
int layoutBoundsLeft = Integer.reverseBytes(di.readInt());
|
||||
int layoutBoundsTop = Integer.reverseBytes(di.readInt());
|
||||
int layoutBoundsRight = Integer.reverseBytes(di.readInt());
|
||||
int layoutBoundsBottom = Integer.reverseBytes(di.readInt());
|
||||
return new OpticalInset(layoutBoundsLeft, layoutBoundsTop,
|
||||
layoutBoundsRight, layoutBoundsBottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,8 +131,14 @@ public class StringBlock {
|
||||
if (style == null) {
|
||||
return ResXmlEncoders.escapeXmlChars(raw);
|
||||
}
|
||||
|
||||
// If the returned style is further in string, than string length. Lets skip it.
|
||||
if (style[1] > raw.length()) {
|
||||
return ResXmlEncoders.escapeXmlChars(raw);
|
||||
}
|
||||
StringBuilder html = new StringBuilder(raw.length() + 32);
|
||||
int[] opened = new int[style.length / 3];
|
||||
boolean[] unclosed = new boolean[style.length / 3];
|
||||
int offset = 0, depth = 0;
|
||||
while (true) {
|
||||
int i = -1, j;
|
||||
@ -149,6 +155,9 @@ public class StringBlock {
|
||||
int last = opened[j];
|
||||
int end = style[last + 2];
|
||||
if (end >= start) {
|
||||
if (style[last + 1] == -1 && end != -1) {
|
||||
unclosed[j] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (offset <= end) {
|
||||
@ -160,6 +169,11 @@ public class StringBlock {
|
||||
depth = j + 1;
|
||||
if (offset < start) {
|
||||
html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset, start)));
|
||||
if (j >= 0 && unclosed.length >= j && unclosed[j]) {
|
||||
if (unclosed.length > (j + 1) && unclosed[j + 1] || unclosed.length == 1) {
|
||||
outputStyleTag(getString(style[opened[j]]), html, true);
|
||||
}
|
||||
}
|
||||
offset = start;
|
||||
}
|
||||
if (i == -1) {
|
||||
|
@ -246,6 +246,8 @@ public final class ResXmlPatcher {
|
||||
throws IOException, SAXException, ParserConfigurationException {
|
||||
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
docFactory.setFeature(FEATURE_DISABLE_DOCTYPE_DECL, true);
|
||||
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
return docBuilder.parse(file);
|
||||
}
|
||||
@ -264,10 +266,10 @@ public final class ResXmlPatcher {
|
||||
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
transformer.setOutputProperty(OutputKeys.STANDALONE,"yes");
|
||||
DOMSource source = new DOMSource(doc);
|
||||
StreamResult result = new StreamResult(file);
|
||||
transformer.transform(source, result);
|
||||
}
|
||||
|
||||
private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import brut.common.BrutException;
|
||||
import brut.util.OS;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
@ -29,10 +28,10 @@ import static org.junit.Assert.*;
|
||||
public class AndResGuardTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1170/", sTmpDir);
|
||||
TestUtils.copyResourceDir(AndResGuardTest.class, "brut/apktool/issue1170/", sTmpDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@ -62,6 +61,4 @@ public class AndResGuardTest {
|
||||
|
||||
private static ExtFile sTmpDir;
|
||||
private static ExtFile sTestOrigDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeTest.class.getName());
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ import static org.junit.Assert.assertTrue;
|
||||
public class BuildAndDecodeJarTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
sTestOrigDir = new ExtFile(sTmpDir, "testjar-orig");
|
||||
@ -66,4 +66,4 @@ public class BuildAndDecodeJarTest {
|
||||
private static ExtFile sTestNewDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName());
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ import javax.imageio.ImageIO;
|
||||
public class BuildAndDecodeTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
@ -253,6 +253,11 @@ public class BuildAndDecodeTest {
|
||||
compareValuesFiles("values-ast/strings.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void androidOStringTest() throws BrutException, IOException {
|
||||
compareValuesFiles("values-ast/strings.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoLetterNotHandledAsBcpTest() throws BrutException, IOException {
|
||||
checkFolderExists("res/values-fr");
|
||||
@ -345,6 +350,89 @@ public class BuildAndDecodeTest {
|
||||
assertEquals(controlImage.getRGB(30, 30), testImage.getRGB(30, 30));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void issue1508Test() throws BrutException, IOException {
|
||||
char slash = File.separatorChar;
|
||||
String location = slash + "res" + slash + "drawable-xhdpi" + slash;
|
||||
|
||||
File control = new File((sTestOrigDir + location), "btn_zoom_up_normal.9.png");
|
||||
File test = new File((sTestNewDir + location), "btn_zoom_up_normal.9.png");
|
||||
|
||||
BufferedImage controlImage = ImageIO.read(control);
|
||||
BufferedImage testImage = ImageIO.read(test);
|
||||
|
||||
// 0, 0 = clear
|
||||
assertEquals(controlImage.getRGB(0, 0), testImage.getRGB(0, 0));
|
||||
|
||||
// 30, 0 = black line
|
||||
assertEquals(controlImage.getRGB(0, 30), testImage.getRGB(0, 30));
|
||||
|
||||
// 30, 30 = greyish button
|
||||
assertEquals(controlImage.getRGB(30, 30), testImage.getRGB(30, 30));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void issue1511Test() throws BrutException, IOException {
|
||||
char slash = File.separatorChar;
|
||||
String location = slash + "res" + slash + "drawable-xxhdpi" + slash;
|
||||
|
||||
File control = new File((sTestOrigDir + location), "textfield_activated_holo_dark.9.png");
|
||||
File test = new File((sTestNewDir + location), "textfield_activated_holo_dark.9.png");
|
||||
|
||||
BufferedImage controlImage = ImageIO.read(control);
|
||||
BufferedImage testImage = ImageIO.read(test);
|
||||
|
||||
// Check entire image as we cannot mess this up
|
||||
final int w = controlImage.getWidth(),
|
||||
h = controlImage.getHeight();
|
||||
|
||||
final int[] controlImageGrid = controlImage.getRGB(0, 0, w, h, null, 0, w);
|
||||
final int[] testImageGrid = testImage.getRGB(0, 0, w, h, null, 0, w);
|
||||
|
||||
for (int i = 0; i < controlImageGrid.length; i++) {
|
||||
assertEquals("Image lost Optical Bounds at i = " + i, controlImageGrid[i], testImageGrid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void robust9patchTest() throws BrutException, IOException {
|
||||
String[] ninePatches = {"ic_notification_overlay.9.png", "status_background.9.png",
|
||||
"search_bg_transparent.9.png", "screenshot_panel.9.png", "recents_lower_gradient.9.png"};
|
||||
|
||||
char slash = File.separatorChar;
|
||||
String location = slash + "res" + slash + "drawable-xxhdpi" + slash;
|
||||
|
||||
for (String ninePatch : ninePatches) {
|
||||
File control = new File((sTestOrigDir + location), ninePatch);
|
||||
File test = new File((sTestNewDir + location), ninePatch);
|
||||
|
||||
BufferedImage controlImage = ImageIO.read(control);
|
||||
BufferedImage testImage = ImageIO.read(test);
|
||||
|
||||
int w = controlImage.getWidth(), h = controlImage.getHeight();
|
||||
|
||||
// Check the entire horizontal line
|
||||
for (int i = 1; i < w; i++) {
|
||||
if (isTransparent(controlImage.getRGB(i, 0))) {
|
||||
assertTrue(isTransparent(testImage.getRGB(i, 0)));
|
||||
} else {
|
||||
assertEquals("Image lost npTc chunk on image " + ninePatch + " at (x, y) (" + i + "," + 0 + ")",
|
||||
controlImage.getRGB(i, 0), testImage.getRGB(i, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// Check the entire vertical line
|
||||
for (int i = 1; i < h; i++) {
|
||||
if (isTransparent(controlImage.getRGB(0, i))) {
|
||||
assertTrue(isTransparent(testImage.getRGB(0, i)));
|
||||
} else {
|
||||
assertEquals("Image lost npTc chunk on image " + ninePatch + " at (x, y) (" + 0 + "," + i + ")",
|
||||
controlImage.getRGB(0, i), testImage.getRGB(0, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drawableXxhdpiTest() throws BrutException, IOException {
|
||||
compareResFolder("drawable-xxhdpi");
|
||||
@ -465,6 +553,10 @@ public class BuildAndDecodeTest {
|
||||
assertTrue(f.isDirectory());
|
||||
}
|
||||
|
||||
private boolean isTransparent(int pixel) {
|
||||
return pixel >> 24 == 0x00;
|
||||
}
|
||||
|
||||
private void compareXmlFiles(String path, ElementQualifier qualifier) throws BrutException {
|
||||
DetailedDiff diff;
|
||||
try {
|
||||
|
@ -37,7 +37,7 @@ import static org.junit.Assert.assertTrue;
|
||||
public class DebugTagRetainedTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
sTestOrigDir = new ExtFile(sTmpDir, "issue1235-orig");
|
||||
|
@ -18,12 +18,12 @@ import static org.junit.Assert.assertEquals;
|
||||
public class DefaultBaksmaliVariableTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
sTestOrigDir = new ExtFile(sTmpDir, "testjar-orig");
|
||||
sTestNewDir = new ExtFile(sTmpDir, "testjar-new");
|
||||
LOGGER.info("Unpacking testjar...");
|
||||
TestUtils.copyResourceDir(BuildAndDecodeJarTest.class, "brut/apktool/issue1481/", sTestOrigDir);
|
||||
TestUtils.copyResourceDir(DefaultBaksmaliVariableTest.class, "brut/apktool/issue1481/", sTestOrigDir);
|
||||
|
||||
LOGGER.info("Building issue1481.jar...");
|
||||
File testJar = new File(sTmpDir, "issue1481.jar");
|
||||
@ -99,5 +99,5 @@ public class DefaultBaksmaliVariableTest {
|
||||
private static ExtFile sTestOrigDir;
|
||||
private static ExtFile sTestNewDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName());
|
||||
private final static Logger LOGGER = Logger.getLogger(DefaultBaksmaliVariableTest.class.getName());
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@ -37,10 +36,10 @@ import static org.junit.Assert.assertTrue;
|
||||
public class DoubleExtensionUnknownFileTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1244/", sTmpDir);
|
||||
TestUtils.copyResourceDir(DoubleExtensionUnknownFileTest.class, "brut/apktool/issue1244/", sTmpDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@ -67,6 +66,4 @@ public class DoubleExtensionUnknownFileTest {
|
||||
}
|
||||
|
||||
private static ExtFile sTmpDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName());
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* Copyright 2016 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
*
|
||||
* 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 brut.androlib;
|
||||
|
||||
import brut.directory.ExtFile;
|
||||
import brut.common.BrutException;
|
||||
import brut.util.OS;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
*/
|
||||
public class ExternalEntityTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
sOrigDir = new ExtFile(OS.createTempDirectory());
|
||||
TestUtils.copyResourceDir(ExternalEntityTest.class, "brut/apktool/doctype/", sOrigDir);
|
||||
|
||||
LOGGER.info("Building doctype.apk...");
|
||||
File testApk = new File(sOrigDir, "doctype.apk");
|
||||
new Androlib().build(sOrigDir, testApk);
|
||||
|
||||
LOGGER.info("Decoding doctype.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(new File(sOrigDir + File.separator + "output"));
|
||||
apkDecoder.decode();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws BrutException {
|
||||
OS.rmdir(sOrigDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doctypeTest() throws BrutException, IOException {
|
||||
|
||||
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
|
||||
"<manifest android:versionCode=\"1\" android:versionName=\"1.0\" hardwareAccelerated=\"true\" package=\"com.ibotpeaches.doctype\" platformBuildVersionCode=\"23\" platformBuildVersionName=\"6.0-2438415\"\n" +
|
||||
" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
|
||||
" <supports-screens android:anyDensity=\"true\" android:smallScreens=\"true\" android:normalScreens=\"true\" android:largeScreens=\"true\" android:resizeable=\"true\" android:xlargeScreens=\"true\" />\n" +
|
||||
"</manifest>");
|
||||
|
||||
byte[] encoded = Files.readAllBytes(Paths.get(sOrigDir + File.separator + "output" + File.separator + "AndroidManifest.xml"));
|
||||
String obtained = TestUtils.replaceNewlines(new String(encoded));
|
||||
assertEquals(expected, obtained);
|
||||
}
|
||||
|
||||
private static ExtFile sOrigDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(ExternalEntityTest.class.getName());
|
||||
}
|
@ -22,7 +22,6 @@ import brut.util.OS;
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.custommonkey.xmlunit.*;
|
||||
import org.junit.*;
|
||||
@ -32,7 +31,7 @@ import org.xml.sax.SAXException;
|
||||
public class LargeIntsInManifestTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue767/", sTmpDir);
|
||||
@ -94,6 +93,4 @@ public class LargeIntsInManifestTest {
|
||||
private static ExtFile sTmpDir;
|
||||
private static ExtFile sTestOrigDir;
|
||||
private static ExtFile sTestNewDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeTest.class.getName());
|
||||
}
|
||||
|
@ -35,10 +35,10 @@ import static org.junit.Assert.assertEquals;
|
||||
public class MissingVersionManifestTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1264/", sTmpDir);
|
||||
TestUtils.copyResourceDir(MissingVersionManifestTest.class, "brut/apktool/issue1264/", sTmpDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@ -61,6 +61,4 @@ public class MissingVersionManifestTest {
|
||||
}
|
||||
|
||||
private static ExtFile sTmpDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName());
|
||||
}
|
@ -17,7 +17,6 @@ package brut.androlib;
|
||||
|
||||
import brut.androlib.res.xml.ResXmlEncoders;
|
||||
import org.junit.Test;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@ -54,6 +53,4 @@ public class PositionalEnumerationTest {
|
||||
private String enumerateArguments(String value) {
|
||||
return ResXmlEncoders.enumerateNonPositionalSubstitutionsIfRequired(value);
|
||||
}
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName());
|
||||
}
|
@ -84,4 +84,4 @@ public class ProviderAttributeTest {
|
||||
}
|
||||
|
||||
private static ExtFile sTmpDir;
|
||||
}
|
||||
}
|
@ -25,7 +25,6 @@ import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@ -35,10 +34,10 @@ import static org.junit.Assert.assertEquals;
|
||||
public class ReferenceVersionCodeTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/issue1234/", sTmpDir);
|
||||
TestUtils.copyResourceDir(ReferenceVersionCodeTest.class, "brut/apktool/issue1234/", sTmpDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@ -61,6 +60,4 @@ public class ReferenceVersionCodeTest {
|
||||
}
|
||||
|
||||
private static ExtFile sTmpDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName());
|
||||
}
|
@ -25,7 +25,6 @@ import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
@ -36,10 +35,10 @@ import static org.junit.Assert.assertNotSame;
|
||||
public class UnknownCompressionTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception, BrutException {
|
||||
public static void beforeClass() throws Exception {
|
||||
TestUtils.cleanFrameworkFile();
|
||||
sTmpDir = new ExtFile(OS.createTempDirectory());
|
||||
TestUtils.copyResourceDir(LargeIntsInManifestTest.class, "brut/apktool/unknown_compression/", sTmpDir);
|
||||
TestUtils.copyResourceDir(UnknownCompressionTest.class, "brut/apktool/unknown_compression/", sTmpDir);
|
||||
|
||||
String apk = "deflated_unknowns.apk";
|
||||
ApkOptions apkOptions = new ApkOptions();
|
||||
@ -90,6 +89,4 @@ public class UnknownCompressionTest {
|
||||
|
||||
private static ExtFile sOriginalFile;
|
||||
private static ExtFile sBuiltFile;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName());
|
||||
}
|
@ -25,7 +25,6 @@ import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@ -66,6 +65,4 @@ public class VectorDrawableTest {
|
||||
|
||||
private static ExtFile sTmpDir;
|
||||
private static ExtFile sTestOrigDir;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(VectorDrawableTest.class.getName());
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE manifest [<!ENTITY e1 SYSTEM 'http://ibotpeaches.com?z=APKTOOLXXE;'>]>
|
||||
<manifest hardwareAccelerated="true" package="com.ibotpeaches.doctype" platformBuildVersionCode="24" platformBuildVersionName="6.0-2456767" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
&e1;
|
||||
<supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true"/>
|
||||
</manifest>
|
@ -0,0 +1,12 @@
|
||||
version: 2.0.0
|
||||
apkFileName: doctype.apk
|
||||
isFrameworkApk: false
|
||||
usesFramework:
|
||||
ids:
|
||||
- 1
|
||||
packageInfo:
|
||||
forced-package-id: '127'
|
||||
versionInfo:
|
||||
versionCode: '1'
|
||||
versionName: '1.0'
|
||||
compressionType: false
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 265 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 179 B |
After Width: | Height: | Size: 327 B |
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="test1">"Forgot your username or password?\nVisit google.com/accounts/recovery."</string>
|
||||
<string name="test2">Forgot your username or password?\n.Visit google.com/accounts/recovery</string>
|
||||
<string name="test3"> (string8) "Forgot your username or password?\nVisit google.com/accounts/recovery."</string>
|
||||
<string name="test4">Forgot your username or password?\nVisit google.com/accounts/recovery.</string>
|
||||
<string name="test5">Forgot your username or password?
|
||||
Visit google.com/accounts/recovery.</string>
|
||||
</resources>
|
@ -31,4 +31,14 @@ bar"</string>
|
||||
<string name="test_string29" formatted="false">category=temp%temp%foo</string>
|
||||
<string name="test_string30">res/foo/</string>
|
||||
<string name="test_string31">res/foo</string>
|
||||
<string name="test_string32">[<font size="17">TEST STRING</font>]</string>
|
||||
<string name="test_string33"><font size="17">[TEST STRING]</font></string>
|
||||
<string name="test_string34">[<font size="17">TEST STRING]</font></string>
|
||||
<string name="test_string35"><font size="17">[TEST STRING</font>]</string>
|
||||
<string name="test_string36"><font size="17">TEST STRING</font></string>
|
||||
<string name="test_string37">[<font size="17">Ţåþ ţö ţýþé þåššŵöŕð one two three]</font></string>
|
||||
<string name="test_string38">[<font size="17">Ţåþ ţö ţýþé þåššŵöŕð one two three</font>]</string>
|
||||
<string name="test_string39"><font size="17">[Ţåþ ţö ţýþé þåššŵöŕð one two three</font>]</string>
|
||||
<string name="test_string40">[<font size="17">]Ţåþ ţö ţýþé þåššŵöŕð one two three</font></string>
|
||||
<string name="test_string41"><font size="17">[Ţåþ ţö ţýþé þåššŵöŕð one two three]</font></string>
|
||||
</resources>
|
||||
|
@ -82,7 +82,7 @@ subprojects {
|
||||
|
||||
ext {
|
||||
depends = [
|
||||
baksmali: 'org.smali:baksmali:2.2.0',
|
||||
baksmali: 'org.smali:baksmali:2.2.1',
|
||||
commons_cli: 'commons-cli:commons-cli:1.4',
|
||||
commons_io: 'commons-io:commons-io:2.4',
|
||||
commons_lang: 'org.apache.commons:commons-lang3:3.1',
|
||||
@ -91,7 +91,7 @@ subprojects {
|
||||
junit: 'junit:junit:4.12',
|
||||
proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
|
||||
snakeyaml: 'org.yaml:snakeyaml:1.17',
|
||||
smali: 'org.smali:smali:2.2.0',
|
||||
smali: 'org.smali:smali:2.2.1',
|
||||
xmlpull: 'xpp3:xpp3:1.1.4c',
|
||||
xmlunit: 'xmlunit:xmlunit:1.3',
|
||||
]
|
||||
|