Merge branch 'master' into patch-1

This commit is contained in:
Connor Tumbleson
2019-02-27 18:26:02 -05:00
committed by GitHub
58 changed files with 481 additions and 57 deletions

View File

@ -416,7 +416,7 @@ public class Androlib {
if (apkOptions.forceBuildAll || isModified(smaliDir, dex)) {
LOGGER.info("Smaling " + folder + " folder into " + filename + "...");
dex.delete();
SmaliBuilder.build(smaliDir, dex, mMinSdkVersion);
SmaliBuilder.build(smaliDir, dex, apkOptions.forceApi > 0 ? apkOptions.forceApi : mMinSdkVersion);
}
return true;
}

View File

@ -377,6 +377,25 @@ public class ApkDecoder {
private void putSdkInfo(MetaInfo meta) throws AndrolibException {
Map<String, String> info = getResTable().getSdkInfo();
if (info.size() > 0) {
String refValue;
if (info.get("minSdkVersion") != null) {
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("minSdkVersion"));
if (refValue != null) {
info.put("minSdkVersion", refValue);
}
}
if (info.get("targetSdkVersion") != null) {
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("targetSdkVersion"));
if (refValue != null) {
info.put("targetSdkVersion", refValue);
}
}
if (info.get("maxSdkVersion") != null) {
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("maxSdkVersion"));
if (refValue != null) {
info.put("maxSdkVersion", refValue);
}
}
meta.sdkInfo = info;
}
}

View File

@ -28,6 +28,8 @@ public class ApkOptions {
public boolean isFramework = false;
public boolean resourcesAreCompressed = false;
public boolean useAapt2 = false;
public boolean noCrunch = false;
public int forceApi = 0;
public Collection<String> doNotCompress;
public String frameworkFolderLocation = null;

View File

@ -0,0 +1,37 @@
/**
* Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2018 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.err;
import brut.androlib.AndrolibException;
public class AXmlDecodingException extends AndrolibException {
public AXmlDecodingException(Throwable cause) {
super(cause);
}
public AXmlDecodingException(String message, Throwable cause) {
super(message, cause);
}
public AXmlDecodingException(String message) {
super(message);
}
public AXmlDecodingException() {
}
}

View File

@ -0,0 +1,37 @@
/**
* Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2018 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.err;
import brut.androlib.AndrolibException;
public class RawXmlEncounteredException extends AndrolibException {
public RawXmlEncounteredException(Throwable cause) {
super(cause);
}
public RawXmlEncounteredException(String message, Throwable cause) {
super(message, cause);
}
public RawXmlEncounteredException(String message) {
super(message);
}
public RawXmlEncounteredException() {
}
}

View File

@ -29,14 +29,14 @@ import org.jf.smali.*;
*/
public class SmaliMod {
public static boolean assembleSmaliFile(String smali, DexBuilder dexBuilder, boolean verboseErrors,
public static boolean assembleSmaliFile(String smali, DexBuilder dexBuilder, int apiLevel, boolean verboseErrors,
boolean printTokens, File smaliFile) throws IOException, RuntimeException, RecognitionException {
InputStream is = new ByteArrayInputStream(smali.getBytes());
return assembleSmaliFile(is, dexBuilder, verboseErrors, printTokens, smaliFile);
return assembleSmaliFile(is, dexBuilder, apiLevel, verboseErrors, printTokens, smaliFile);
}
public static boolean assembleSmaliFile(InputStream is,DexBuilder dexBuilder, boolean verboseErrors,
public static boolean assembleSmaliFile(InputStream is,DexBuilder dexBuilder, int apiLevel, boolean verboseErrors,
boolean printTokens, File smaliFile) throws IOException, RecognitionException {
// copy our filestream into a tmp file, so we don't overwrite
@ -47,10 +47,10 @@ public class SmaliMod {
IOUtils.copy(is, os);
os.close();
return assembleSmaliFile(tmp,dexBuilder, verboseErrors, printTokens);
return assembleSmaliFile(tmp,dexBuilder, apiLevel, verboseErrors, printTokens);
}
public static boolean assembleSmaliFile(File smaliFile,DexBuilder dexBuilder, boolean verboseErrors,
public static boolean assembleSmaliFile(File smaliFile,DexBuilder dexBuilder, int apiLevel, boolean verboseErrors,
boolean printTokens) throws IOException, RecognitionException {
CommonTokenStream tokens;
@ -77,6 +77,7 @@ public class SmaliMod {
}
smaliParser parser = new smaliParser(tokens);
parser.setApiLevel(apiLevel);
parser.setVerboseErrors(verboseErrors);
smaliParser.smali_file_return result = parser.smali_file();
@ -93,7 +94,7 @@ public class SmaliMod {
treeStream.setTokenStream(tokens);
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
dexGen.setApiLevel(apiLevel);
dexGen.setVerboseErrors(verboseErrors);
dexGen.setDexBuilder(dexBuilder);
dexGen.smali_file();

View File

@ -350,6 +350,10 @@ final public class AndrolibResources {
cmd.add("-v");
}
if (apkOptions.noCrunch) {
cmd.add("--no-crunch");
}
try {
OS.exec(cmd.toArray(new String[0]));
LOGGER.fine("aapt2 compile command ran: ");
@ -485,6 +489,9 @@ final public class AndrolibResources {
if (apkOptions.debugMode) { // inject debuggable="true" into manifest
cmd.add("--debug-mode");
}
if (apkOptions.noCrunch) {
cmd.add("--no-crunch");
}
// force package id so that some frameworks build with correct id
// disable if user adds own aapt (can't know if they have this feature)
if (mPackageId != null && ! customAapt && ! mSharedLibrary) {

View File

@ -24,6 +24,11 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public final class ResTypeSpec {
public static final String RES_TYPE_NAME_ARRAY = "array";
public static final String RES_TYPE_NAME_PLURALS = "plurals";
public static final String RES_TYPE_NAME_STYLES = "style";
private final String mName;
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<String, ResResSpec>();

View File

@ -83,6 +83,13 @@ public abstract class ResScalarValue extends ResIntBasedValue implements
}
}
// Android does not allow values (false) for ids.xml anymore
// https://issuetracker.google.com/issues/80475496
// But it decodes as a ResBoolean, which makes no sense. So force it to empty
if (type.equalsIgnoreCase("id") && !body.isEmpty()) {
body = "";
}
// check for using attrib as node or item
String tagName = item ? "item" : type;

View File

@ -19,6 +19,7 @@ package brut.androlib.res.data.value;
import android.util.TypedValue;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTypeSpec;
import brut.util.Duo;
/**
@ -41,7 +42,7 @@ public class ResValueFactory {
}
return new ResReferenceValue(mPackage, 0, null);
case TypedValue.TYPE_REFERENCE:
return newReference(value, rawValue);
return newReference(value, null);
case TypedValue.TYPE_ATTRIBUTE:
return newReference(value, rawValue, true);
case TypedValue.TYPE_STRING:
@ -83,7 +84,7 @@ public class ResValueFactory {
return new ResStringValue(value, rawValue);
}
public ResBagValue bagFactory(int parent, Duo<Integer, ResScalarValue>[] items) throws AndrolibException {
public ResBagValue bagFactory(int parent, Duo<Integer, ResScalarValue>[] items, ResTypeSpec resTypeSpec) throws AndrolibException {
ResReferenceValue parentVal = newReference(parent, null);
if (items.length == 0) {
@ -93,14 +94,25 @@ public class ResValueFactory {
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
return ResAttr.factory(parentVal, items, this, mPackage);
}
// 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) {
String resTypeName = resTypeSpec.getName();
// Android O Preview added an unknown enum for c. This is hardcoded as 0 for now.
if (ResTypeSpec.RES_TYPE_NAME_ARRAY.equals(resTypeName)
|| 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) {
if (ResTypeSpec.RES_TYPE_NAME_PLURALS.equals(resTypeName) ||
(key >= ResPluralsValue.BAG_KEY_PLURALS_START && key <= ResPluralsValue.BAG_KEY_PLURALS_END)) {
return new ResPluralsValue(parentVal, items);
}
return new ResStyleValue(parentVal, items, this);
if (ResTypeSpec.RES_TYPE_NAME_STYLES.equals(resTypeName)) {
return new ResStyleValue(parentVal, items, this);
}
throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName);
}
public ResReferenceValue newReference(int resID, String rawValue) {

View File

@ -347,7 +347,7 @@ public class ARSCDecoder {
}
}
return factory.bagFactory(parent, items);
return factory.bagFactory(parent, items, mTypeSpec);
}
private ResIntBasedValue readValue() throws IOException, AndrolibException {

View File

@ -852,7 +852,7 @@ public class AXmlResourceParser implements XmlResourceParser {
private final void doNext() throws IOException {
// Delayed initialization.
if (m_strings == null) {
m_reader.skipCheckInt(CHUNK_AXML_FILE);
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
/*
* chunkSize
@ -1004,7 +1004,7 @@ public class AXmlResourceParser implements XmlResourceParser {
ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_DATA = 4,
ATTRIBUTE_LENGTH = 5;
private static final int CHUNK_AXML_FILE = 0x00080003,
private static final int CHUNK_AXML_FILE = 0x00080003, CHUNK_AXML_FILE_BROKEN = 0x00080001,
CHUNK_RESOURCEIDS = 0x00080180, CHUNK_XML_FIRST = 0x00100100,
CHUNK_XML_START_NAMESPACE = 0x00100100,
CHUNK_XML_END_NAMESPACE = 0x00100101,

View File

@ -65,13 +65,21 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
int[] xDivs = np.xDivs;
for (int i = 0; i < xDivs.length; i += 2) {
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
if (xDivs.length == 0) {
drawHLine(im2, 0, 1, w);
} else {
for (int i = 0; i < xDivs.length; i += 2) {
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
}
}
int[] yDivs = np.yDivs;
for (int i = 0; i < yDivs.length; i += 2) {
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
if (yDivs.length == 0) {
drawVLine(im2, 0, 1, h);
} else {
for (int i = 0; i < yDivs.length; i += 2) {
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
}
}
// Some images additionally use Optical Bounds

View File

@ -18,6 +18,7 @@ package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import brut.androlib.err.CantFind9PatchChunk;
import brut.androlib.err.RawXmlEncounteredException;
import brut.androlib.res.data.ResResource;
import brut.androlib.res.data.value.ResBoolValue;
import brut.androlib.res.data.value.ResFileValue;
@ -118,6 +119,11 @@ public class ResFileDecoder {
}
decode(inDir, inFileName, outDir, outFileName, "xml");
} catch (RawXmlEncounteredException ex) {
// If we got an error to decode XML, lets assume the file is in raw format.
// This is a large assumption, that might increase runtime, but will save us for situations where
// XSD files are AXML`d on aapt1, but left in plaintext in aapt2.
decode(inDir, inFileName, outDir, outFileName, "raw");
} catch (AndrolibException ex) {
LOGGER.log(Level.SEVERE, String.format(
"Could not decode file, replacing by FALSE value: %s",

View File

@ -19,8 +19,9 @@ package brut.androlib.res.decoder;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;
import brut.androlib.err.AXmlDecodingException;
import brut.androlib.err.RawXmlEncounteredException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.wrapper.XmlPullParserWrapper;
@ -142,9 +143,9 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
}
ser.flush();
} catch (XmlPullParserException ex) {
throw new AndrolibException("Could not decode XML", ex);
throw new AXmlDecodingException("Could not decode XML", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode XML", ex);
throw new RawXmlEncounteredException("Could not decode XML", ex);
}
}
@ -155,6 +156,4 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
private final XmlPullParser mParser;
private final ExtXmlSerializer mSerial;
private final static Logger LOGGER = Logger.getLogger(XmlPullStreamDecoder.class.getName());
}

View File

@ -197,6 +197,41 @@ public final class ResXmlPatcher {
return null;
}
/**
* Finds key in integers.xml file and returns text value
*
* @param directory Root directory of apk
* @param key Integer reference (ie @integer/foo)
* @return String|null
* @throws AndrolibException
*/
public static String pullValueFromIntegers(File directory, String key) throws AndrolibException {
if (key == null || ! key.contains("@")) {
return null;
}
File file = new File(directory, "/res/values/integers.xml");
key = key.replace("@integer/", "");
if (file.exists()) {
try {
Document doc = loadDocument(file);
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile("/resources/integer[@name=" + '"' + key + "\"]/text()");
Object result = expression.evaluate(doc, XPathConstants.STRING);
if (result != null) {
return (String) result;
}
} catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) {
}
}
return null;
}
/**
* Removes attributes like "versionCode" and "versionName" from file.
*

View File

@ -71,7 +71,7 @@ public class SmaliBuilder {
if (fileName.endsWith(".smali")) {
try {
if (!SmaliMod.assembleSmaliFile(inFile, dexBuilder, false, false)) {
if (!SmaliMod.assembleSmaliFile(inFile, dexBuilder, mApiLevel, false, false)) {
throw new AndrolibException("Could not smali file: " + fileName);
}
} catch (IOException | RecognitionException ex) {

View File

@ -198,6 +198,16 @@ public class BuildAndDecodeTest extends BaseTest {
compareXmlFiles("res/xml/references.xml");
}
@Test
public void xmlXsdFileTest() throws BrutException {
compareXmlFiles("res/xml/ww_box_styles_schema.xsd");
}
@Test
public void xmlIdsEmptyTest() throws BrutException {
compareXmlFiles("res/values/ids.xml");
}
@Test
public void xmlReferenceAttributeTest() throws BrutException {
compareXmlFiles("res/layout/issue1040.xml");
@ -231,7 +241,7 @@ public class BuildAndDecodeTest extends BaseTest {
@Test
public void qualifiersTest() throws BrutException {
compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp"
+ "-xlarge-long-round-highdr-land-desk-night-xhdpi-finger-keyssoft-12key"
+ "-long-round-highdr-land-desk-night-xhdpi-finger-keyssoft-12key"
+ "-navhidden-dpad-v26/strings.xml");
}

View File

@ -77,8 +77,8 @@ public class DebugTagRetainedTest extends BaseTest {
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:compileSdkVersion=\"23\" " +
"android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue1235\" platformBuildVersionCode=\"23\" " +
"platformBuildVersionName=\"6.0-2438415\"> <application android:debuggable=\"true\"/></manifest>");
"android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue1235\" platformBuildVersionCode=\"20\" " +
"platformBuildVersionName=\"4.4W.2-1537038\"> <application android:debuggable=\"true\"/></manifest>");
byte[] encoded = Files.readAllBytes(Paths.get(sTmpDir + File.separator + apk + File.separator + "AndroidManifest.xml"));
String obtained = TestUtils.replaceNewlines(new String(encoded));

View File

@ -72,7 +72,7 @@ public class ProviderAttributeTest extends BaseTest {
apkDecoder.decode();
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:compileSdkVersion=\"23\" android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue636\" platformBuildVersionCode=\"23\" platformBuildVersionName=\"6.0-2438415\">\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:compileSdkVersion=\"23\" android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue636\" platformBuildVersionCode=\"22\" platformBuildVersionName=\"5.1-1756733\">\n" +
" <application android:allowBackup=\"true\" android:debuggable=\"true\" android:icon=\"@mipmap/ic_launcher\" android:label=\"@string/app_name\" android:theme=\"@style/AppTheme\">\n" +
" <provider android:authorities=\"com.ibotpeaches.issue636.Provider\" android:exported=\"false\" android:grantUriPermissions=\"true\" android:label=\"@string/app_name\" android:multiprocess=\"false\" android:name=\"com.ibotpeaches.issue636.Provider\"/>\n" +
" <provider android:authorities=\"com.ibotpeaches.issue636.ProviderTwo\" android:exported=\"false\" android:grantUriPermissions=\"true\" android:label=\"@string/app_name\" android:multiprocess=\"false\" android:name=\"com.ibotpeaches.issue636.ProviderTwo\"/>\n" +

View File

@ -84,6 +84,11 @@ public class BuildAndDecodeTest extends BaseTest {
compareXmlFiles("res/navigation/nav_graph.xml");
}
@Test
public void xmlIdsEmptyTest() throws BrutException {
compareXmlFiles("res/values/ids.xml");
}
@Test
public void leadingDollarSignResourceNameTest() throws BrutException {
compareXmlFiles("res/drawable/$avd_hide_password__0.xml");
@ -93,4 +98,9 @@ public class BuildAndDecodeTest extends BaseTest {
public void confirmManifestStructureTest() throws BrutException {
compareXmlFiles("AndroidManifest.xml");
}
@Test
public void xmlXsdFileTest() throws BrutException {
compareXmlFiles("res/xml/ww_box_styles_schema.xsd");
}
}

View File

@ -0,0 +1,55 @@
package brut.androlib.decode;
import brut.androlib.ApkDecoder;
import brut.androlib.BaseTest;
import brut.androlib.TestUtils;
import brut.androlib.res.data.ResTable;
import brut.androlib.res.data.value.ResArrayValue;
import brut.androlib.res.data.value.ResValue;
import brut.common.BrutException;
import brut.directory.ExtFile;
import brut.util.OS;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import static junit.framework.Assert.assertTrue;
public class DecodeArrayTest extends BaseTest {
@BeforeClass
public static void beforeClass() throws Exception {
TestUtils.cleanFrameworkFile();
sTmpDir = new ExtFile(OS.createTempDirectory());
TestUtils.copyResourceDir(MissingVersionManifestTest.class, "decode/issue1994/", sTmpDir);
}
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
@Test
public void decodeStringArray() throws BrutException {
String apk = "issue1994.apk";
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
ResTable resTable = apkDecoder.getResTable();
ResValue value = resTable.getResSpec(0x7f020001).getDefaultResource().getValue();
assertTrue("Not a ResArrayValue. Found: " + value.getClass(), value instanceof ResArrayValue);
}
@Test
public void decodeArray() throws BrutException {
String apk = "issue1994.apk";
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
ResTable resTable = apkDecoder.getResTable();
ResValue value = resTable.getResSpec(0x7f020000).getDefaultResource().getValue();
assertTrue("Not a ResArrayValue. Found: " + value.getClass(), value instanceof ResArrayValue);
}
}

View File

@ -64,7 +64,7 @@ public class ExternalEntityTest extends BaseTest {
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<manifest android:versionCode=\"1\" android:versionName=\"1.0\" android:compileSdkVersion=\"23\" android:compileSdkVersionCodename=\"6.0-2438415\" " +
"hardwareAccelerated=\"true\" package=\"com.ibotpeaches.doctype\" platformBuildVersionCode=\"23\" platformBuildVersionName=\"6.0-2438415\" " +
"hardwareAccelerated=\"true\" package=\"com.ibotpeaches.doctype\" platformBuildVersionCode=\"24\" platformBuildVersionName=\"6.0-2456767\" " +
"xmlns:android=\"http://schemas.android.com/apk/res/android\"> <supports-screens android:anyDensity=\"true\" android:smallScreens=\"true\" " +
"android:normalScreens=\"true\" android:largeScreens=\"true\" android:resizeable=\"true\" android:xlargeScreens=\"true\" /></manifest>");

View File

@ -0,0 +1,73 @@
/**
* Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2018 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.decode;
import brut.androlib.BaseTest;
import brut.androlib.TestUtils;
import brut.androlib.res.decoder.Res9patchStreamDecoder;
import brut.common.BrutException;
import brut.directory.ExtFile;
import brut.util.OS;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import static org.junit.Assert.*;
public class MissingDiv9PatchTest extends BaseTest {
@BeforeClass
public static void beforeClass() throws Exception {
TestUtils.cleanFrameworkFile();
sTmpDir = new ExtFile(OS.createTempDirectory());
TestUtils.copyResourceDir(MissingDiv9PatchTest.class, "decode/issue1522/", sTmpDir);
}
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
@Test
public void assertMissingDivAdded() throws Exception {
InputStream inputStream = getFileInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Res9patchStreamDecoder decoder = new Res9patchStreamDecoder();
decoder.decode(inputStream, outputStream);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(outputStream.toByteArray()));
int height = image.getHeight() - 1;
// First and last pixel will be invisible, so lets check the first column and ensure its all black
for (int y = 1; y < height; y++) {
assertEquals("y coordinate failed at: " + y, NP_COLOR, image.getRGB(0, y));
}
}
private FileInputStream getFileInputStream() throws IOException {
File file = new File(sTmpDir, "pip_dismiss_scrim.9.png");
return new FileInputStream(file.toPath().toString());
}
private static final int NP_COLOR = 0xff000000;
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<item type="id" name="test_attr1" />
</resources>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="string" name="hello_world" id="0x7f020000" />
<public type="xml" name="ww_box_styles_schema" id="0x7f1500df" />
</resources>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element name="person" type="xs:string"/>
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<attr format="integer" name="test_attr1"/>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<item type="id" name="test_attr1" />
</resources>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element name="person" type="xs:string"/>
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B