am c33062ff: Merge "AAPT2: Proguard rules generation added." into mnc-dev
* commit 'c33062ff0c7e9eb92ce7c70340d5f489f23aaafe': AAPT2: Proguard rules generation added.
This commit is contained in:
@@ -40,6 +40,7 @@ sources := \
|
||||
ManifestParser.cpp \
|
||||
ManifestValidator.cpp \
|
||||
Png.cpp \
|
||||
ProguardRules.cpp \
|
||||
ResChunkPullParser.cpp \
|
||||
Resource.cpp \
|
||||
ResourceParser.cpp \
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ManifestValidator.h"
|
||||
#include "NameMangler.h"
|
||||
#include "Png.h"
|
||||
#include "ProguardRules.h"
|
||||
#include "ResourceParser.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceTableResolver.h"
|
||||
@@ -300,6 +301,9 @@ struct AaptOptions {
|
||||
// Directory to in which to generate R.java.
|
||||
Maybe<Source> generateJavaClass;
|
||||
|
||||
// File in which to produce proguard rules.
|
||||
Maybe<Source> generateProguardRules;
|
||||
|
||||
// Whether to output verbose details about
|
||||
// compilation.
|
||||
bool verbose = false;
|
||||
@@ -417,7 +421,8 @@ bool shouldGenerateVersionedResource(const std::shared_ptr<const ResourceTable>&
|
||||
|
||||
bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
|
||||
const std::shared_ptr<IResolver>& resolver, const LinkItem& item,
|
||||
const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue) {
|
||||
const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue,
|
||||
proguard::KeepSet* keepSet) {
|
||||
SourceLogger logger(item.source);
|
||||
std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
|
||||
if (!root) {
|
||||
@@ -435,6 +440,10 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
|
||||
xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
|
||||
}
|
||||
|
||||
if (options.generateProguardRules) {
|
||||
proguard::collectProguardRules(item.name.type, item.source, root.get(), keepSet);
|
||||
}
|
||||
|
||||
BigBuffer outBuffer(1024);
|
||||
Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
|
||||
item.originalPackage, resolver,
|
||||
@@ -509,7 +518,7 @@ bool copyFile(const AaptOptions& options, const CompileItem& item, ZipFile* outA
|
||||
|
||||
bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
|
||||
const std::map<std::shared_ptr<ResourceTable>, StaticLibraryData>& libApks,
|
||||
const android::ResTable& table, ZipFile* outApk) {
|
||||
const android::ResTable& table, ZipFile* outApk, proguard::KeepSet* keepSet) {
|
||||
if (options.verbose) {
|
||||
Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
|
||||
}
|
||||
@@ -557,6 +566,11 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver
|
||||
}
|
||||
}
|
||||
|
||||
if (options.generateProguardRules) {
|
||||
proguard::collectProguardRulesForManifest(options.manifest, merger.getMergedXml(),
|
||||
keepSet);
|
||||
}
|
||||
|
||||
BigBuffer outBuffer(1024);
|
||||
if (!xml::flattenAndLink(options.manifest, merger.getMergedXml(), options.appInfo.package,
|
||||
resolver, {}, &outBuffer)) {
|
||||
@@ -805,8 +819,10 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
return false;
|
||||
}
|
||||
|
||||
proguard::KeepSet keepSet;
|
||||
|
||||
android::ResTable binTable;
|
||||
if (!compileManifest(options, resolver, apkFiles, binTable, &outApk)) {
|
||||
if (!compileManifest(options, resolver, apkFiles, binTable, &outApk, &keepSet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -826,7 +842,7 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
assert(uncompressedData);
|
||||
|
||||
if (!linkXml(options, outTable, resolver, item, uncompressedData,
|
||||
entry->getUncompressedLen(), &outApk, &linkQueue)) {
|
||||
entry->getUncompressedLen(), &outApk, &linkQueue, &keepSet)) {
|
||||
Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
|
||||
<< std::endl;
|
||||
return false;
|
||||
@@ -883,6 +899,26 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the Proguard rules file.
|
||||
if (options.generateProguardRules) {
|
||||
const Source& outPath = options.generateProguardRules.value();
|
||||
|
||||
if (options.verbose) {
|
||||
Logger::note(outPath) << "writing proguard rules." << std::endl;
|
||||
}
|
||||
|
||||
std::ofstream fout(outPath.path);
|
||||
if (!fout) {
|
||||
Logger::error(outPath) << strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!proguard::writeKeepSet(&fout, keepSet)) {
|
||||
Logger::error(outPath) << "failed to write proguard rules." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
outTable->getValueStringPool().prune();
|
||||
outTable->getValueStringPool().sort(
|
||||
[](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
|
||||
@@ -1072,6 +1108,11 @@ static AaptOptions prepareArgs(int argc, char** argv) {
|
||||
options.generateJavaClass = Source{ arg.toString() };
|
||||
});
|
||||
|
||||
flag::optionalFlag("--proguard", "file in which to output proguard rules",
|
||||
[&options](const StringPiece& arg) {
|
||||
options.generateProguardRules = Source{ arg.toString() };
|
||||
});
|
||||
|
||||
flag::optionalSwitch("--static-lib", "generate a static Android library", true,
|
||||
&isStaticLib);
|
||||
|
||||
|
||||
240
tools/aapt2/ProguardRules.cpp
Normal file
240
tools/aapt2/ProguardRules.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ProguardRules.h"
|
||||
#include "Util.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace aapt {
|
||||
namespace proguard {
|
||||
|
||||
constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
|
||||
|
||||
class BaseVisitor : public xml::Visitor {
|
||||
public:
|
||||
BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
|
||||
}
|
||||
|
||||
virtual void visit(xml::Text*) override {};
|
||||
|
||||
virtual void visit(xml::Namespace* node) override {
|
||||
for (const auto& child : node->children) {
|
||||
child->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(xml::Element* node) override {
|
||||
if (!node->namespaceUri.empty()) {
|
||||
Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
|
||||
node->namespaceUri);
|
||||
if (maybePackage) {
|
||||
// This is a custom view, let's figure out the class name from this.
|
||||
std::u16string package = maybePackage.value() + u"." + node->name;
|
||||
if (util::isJavaClassName(package)) {
|
||||
addClass(node->lineNumber, package);
|
||||
}
|
||||
}
|
||||
} else if (util::isJavaClassName(node->name)) {
|
||||
addClass(node->lineNumber, node->name);
|
||||
}
|
||||
|
||||
for (const auto& child: node->children) {
|
||||
child->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void addClass(size_t lineNumber, const std::u16string& className) {
|
||||
mKeepSet->addClass(mSource.line(lineNumber), className);
|
||||
}
|
||||
|
||||
void addMethod(size_t lineNumber, const std::u16string& methodName) {
|
||||
mKeepSet->addMethod(mSource.line(lineNumber), methodName);
|
||||
}
|
||||
|
||||
private:
|
||||
Source mSource;
|
||||
KeepSet* mKeepSet;
|
||||
};
|
||||
|
||||
struct LayoutVisitor : public BaseVisitor {
|
||||
LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
|
||||
}
|
||||
|
||||
virtual void visit(xml::Element* node) override {
|
||||
bool checkClass = false;
|
||||
bool checkName = false;
|
||||
if (node->namespaceUri.empty()) {
|
||||
checkClass = node->name == u"view" || node->name == u"fragment";
|
||||
} else if (node->namespaceUri == kSchemaAndroid) {
|
||||
checkName = node->name == u"fragment";
|
||||
}
|
||||
|
||||
for (const auto& attr : node->attributes) {
|
||||
if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
|
||||
util::isJavaClassName(attr.value)) {
|
||||
addClass(node->lineNumber, attr.value);
|
||||
} else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
|
||||
util::isJavaClassName(attr.value)) {
|
||||
addClass(node->lineNumber, attr.value);
|
||||
} else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
|
||||
addMethod(node->lineNumber, attr.value);
|
||||
}
|
||||
}
|
||||
|
||||
BaseVisitor::visit(node);
|
||||
}
|
||||
};
|
||||
|
||||
struct XmlResourceVisitor : public BaseVisitor {
|
||||
XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
|
||||
}
|
||||
|
||||
virtual void visit(xml::Element* node) override {
|
||||
bool checkFragment = false;
|
||||
if (node->namespaceUri.empty()) {
|
||||
checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
|
||||
}
|
||||
|
||||
if (checkFragment) {
|
||||
xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
|
||||
if (attr && util::isJavaClassName(attr->value)) {
|
||||
addClass(node->lineNumber, attr->value);
|
||||
}
|
||||
}
|
||||
|
||||
BaseVisitor::visit(node);
|
||||
}
|
||||
};
|
||||
|
||||
struct TransitionVisitor : public BaseVisitor {
|
||||
TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
|
||||
}
|
||||
|
||||
virtual void visit(xml::Element* node) override {
|
||||
bool checkClass = node->namespaceUri.empty() &&
|
||||
(node->name == u"transition" || node->name == u"pathMotion");
|
||||
if (checkClass) {
|
||||
xml::Attribute* attr = node->findAttribute({}, u"class");
|
||||
if (attr && util::isJavaClassName(attr->value)) {
|
||||
addClass(node->lineNumber, attr->value);
|
||||
}
|
||||
}
|
||||
|
||||
BaseVisitor::visit(node);
|
||||
}
|
||||
};
|
||||
|
||||
struct ManifestVisitor : public BaseVisitor {
|
||||
ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
|
||||
}
|
||||
|
||||
virtual void visit(xml::Element* node) override {
|
||||
if (node->namespaceUri.empty()) {
|
||||
bool getName = false;
|
||||
if (node->name == u"manifest") {
|
||||
xml::Attribute* attr = node->findAttribute({}, u"package");
|
||||
if (attr) {
|
||||
mPackage = attr->value;
|
||||
}
|
||||
} else if (node->name == u"application") {
|
||||
getName = true;
|
||||
xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
|
||||
if (attr) {
|
||||
Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
|
||||
attr->value);
|
||||
if (result) {
|
||||
addClass(node->lineNumber, result.value());
|
||||
}
|
||||
}
|
||||
} else if (node->name == u"activity" || node->name == u"service" ||
|
||||
node->name == u"receiver" || node->name == u"provider" ||
|
||||
node->name == u"instrumentation") {
|
||||
getName = true;
|
||||
}
|
||||
|
||||
if (getName) {
|
||||
xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
|
||||
if (attr) {
|
||||
Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
|
||||
attr->value);
|
||||
if (result) {
|
||||
addClass(node->lineNumber, result.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BaseVisitor::visit(node);
|
||||
}
|
||||
|
||||
std::u16string mPackage;
|
||||
};
|
||||
|
||||
bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet) {
|
||||
ManifestVisitor visitor(source, keepSet);
|
||||
node->accept(&visitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
|
||||
KeepSet* keepSet) {
|
||||
switch (type) {
|
||||
case ResourceType::kLayout: {
|
||||
LayoutVisitor visitor(source, keepSet);
|
||||
node->accept(&visitor);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResourceType::kXml: {
|
||||
XmlResourceVisitor visitor(source, keepSet);
|
||||
node->accept(&visitor);
|
||||
break;
|
||||
}
|
||||
|
||||
case ResourceType::kTransition: {
|
||||
TransitionVisitor visitor(source, keepSet);
|
||||
node->accept(&visitor);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
|
||||
for (const auto& entry : keepSet.mKeepSet) {
|
||||
for (const SourceLine& source : entry.second) {
|
||||
*out << "// Referenced at " << source << "\n";
|
||||
}
|
||||
*out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
|
||||
}
|
||||
|
||||
for (const auto& entry : keepSet.mKeepMethodSet) {
|
||||
for (const SourceLine& source : entry.second) {
|
||||
*out << "// Referenced at " << source << "\n";
|
||||
}
|
||||
*out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace proguard
|
||||
} // namespace aapt
|
||||
58
tools/aapt2/ProguardRules.h
Normal file
58
tools/aapt2/ProguardRules.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef AAPT_PROGUARD_RULES_H
|
||||
#define AAPT_PROGUARD_RULES_H
|
||||
|
||||
#include "Resource.h"
|
||||
#include "Source.h"
|
||||
#include "XmlDom.h"
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace aapt {
|
||||
namespace proguard {
|
||||
|
||||
class KeepSet {
|
||||
public:
|
||||
inline void addClass(const SourceLine& source, const std::u16string& className) {
|
||||
mKeepSet[className].insert(source);
|
||||
}
|
||||
|
||||
inline void addMethod(const SourceLine& source, const std::u16string& methodName) {
|
||||
mKeepMethodSet[methodName].insert(source);
|
||||
}
|
||||
|
||||
private:
|
||||
friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
|
||||
|
||||
std::map<std::u16string, std::set<SourceLine>> mKeepSet;
|
||||
std::map<std::u16string, std::set<SourceLine>> mKeepMethodSet;
|
||||
};
|
||||
|
||||
bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet);
|
||||
bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
|
||||
KeepSet* keepSet);
|
||||
|
||||
bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
|
||||
|
||||
} // namespace proguard
|
||||
} // namespace aapt
|
||||
|
||||
#endif // AAPT_PROGUARD_RULES_H
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -80,6 +81,10 @@ inline ::std::ostream& operator<<(::std::ostream& out, const SourceLineColumn& s
|
||||
return out << source.path << ":" << source.line << ":" << source.column;
|
||||
}
|
||||
|
||||
inline bool operator<(const SourceLine& lhs, const SourceLine& rhs) {
|
||||
return std::tie(lhs.path, lhs.line) < std::tie(rhs.path, rhs.line);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
#endif // AAPT_SOURCE_H
|
||||
|
||||
@@ -102,6 +102,51 @@ StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16
|
||||
return endIter;
|
||||
}
|
||||
|
||||
bool isJavaClassName(const StringPiece16& str) {
|
||||
size_t pieces = 0;
|
||||
for (const StringPiece16& piece : tokenize(str, u'.')) {
|
||||
pieces++;
|
||||
if (piece.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Can't have starting or trailing $ character.
|
||||
if (piece.data()[0] == u'$' || piece.data()[piece.size() - 1] == u'$') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (findNonAlphaNumericAndNotInSet(piece, u"$_") != piece.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return pieces >= 2;
|
||||
}
|
||||
|
||||
Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
|
||||
const StringPiece16& className) {
|
||||
if (className.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (util::isJavaClassName(className)) {
|
||||
return className.toString();
|
||||
}
|
||||
|
||||
if (package.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::u16string result(package.data(), package.size());
|
||||
if (className.data()[0] != u'.') {
|
||||
result += u'.';
|
||||
}
|
||||
result.append(className.data(), className.size());
|
||||
if (!isJavaClassName(result)) {
|
||||
return {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char16_t* end) {
|
||||
char16_t code = 0;
|
||||
for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
|
||||
|
||||
@@ -77,6 +77,23 @@ inline bool isspace16(char16_t c) {
|
||||
StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str,
|
||||
const StringPiece16& allowedChars);
|
||||
|
||||
/**
|
||||
* Tests that the string is a valid Java class name.
|
||||
*/
|
||||
bool isJavaClassName(const StringPiece16& str);
|
||||
|
||||
/**
|
||||
* Converts the class name to a fully qualified class name from the given `package`. Ex:
|
||||
*
|
||||
* asdf --> package.asdf
|
||||
* .asdf --> package.asdf
|
||||
* .a.b --> package.a.b
|
||||
* asdf.adsf --> asdf.adsf
|
||||
*/
|
||||
Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
|
||||
const StringPiece16& className);
|
||||
|
||||
|
||||
/**
|
||||
* Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
|
||||
* This will be present in C++14 and can be removed then.
|
||||
|
||||
@@ -93,4 +93,44 @@ TEST(UtilTest, TokenizeInput) {
|
||||
ASSERT_EQ(tokenizer.end(), iter);
|
||||
}
|
||||
|
||||
TEST(UtilTest, IsJavaClassName) {
|
||||
EXPECT_TRUE(util::isJavaClassName(u"android.test.Class"));
|
||||
EXPECT_TRUE(util::isJavaClassName(u"android.test.Class$Inner"));
|
||||
EXPECT_TRUE(util::isJavaClassName(u"android_test.test.Class"));
|
||||
EXPECT_TRUE(util::isJavaClassName(u"_android_.test._Class_"));
|
||||
EXPECT_FALSE(util::isJavaClassName(u"android.test.$Inner"));
|
||||
EXPECT_FALSE(util::isJavaClassName(u"android.test.Inner$"));
|
||||
EXPECT_FALSE(util::isJavaClassName(u".test.Class"));
|
||||
EXPECT_FALSE(util::isJavaClassName(u"android"));
|
||||
}
|
||||
|
||||
TEST(UtilTest, FullyQualifiedClassName) {
|
||||
Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(res.value(), u"android.asdf");
|
||||
|
||||
res = util::getFullyQualifiedClassName(u"android", u".asdf");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(res.value(), u"android.asdf");
|
||||
|
||||
res = util::getFullyQualifiedClassName(u"android", u".a.b");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(res.value(), u"android.a.b");
|
||||
|
||||
res = util::getFullyQualifiedClassName(u"android", u"a.b");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(res.value(), u"a.b");
|
||||
|
||||
res = util::getFullyQualifiedClassName(u"", u"a.b");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(res.value(), u"a.b");
|
||||
|
||||
res = util::getFullyQualifiedClassName(u"", u"");
|
||||
ASSERT_FALSE(res);
|
||||
|
||||
res = util::getFullyQualifiedClassName(u"android", u"./Apple");
|
||||
ASSERT_FALSE(res);
|
||||
}
|
||||
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.app">
|
||||
<application>
|
||||
<application
|
||||
android:name=".Activity">
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -15,6 +15,7 @@ LOCAL_RESOURCE_DIR := res
|
||||
LOCAL_LIBS := lib/out/package.apk
|
||||
LOCAL_OUT := out
|
||||
LOCAL_GEN := out/gen
|
||||
LOCAL_PROGUARD := out/proguard.rule
|
||||
|
||||
##
|
||||
# AAPT2 custom rules.
|
||||
@@ -57,7 +58,7 @@ $(foreach d,$(PRIVATE_RESOURCE_TYPES),$(eval $(call make-collect-rule,$d)))
|
||||
|
||||
# Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
|
||||
$(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_INCLUDES) $(LOCAL_LIBS) AndroidManifest.xml
|
||||
$(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS)
|
||||
$(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS) --proguard $(LOCAL_PROGUARD) -v
|
||||
|
||||
# R.java: gen/com/android/app/R.java <- out/resources.arsc
|
||||
# No action since R.java is generated when out/resources.arsc is.
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<fragment class="android.test.sample.App$Inner" />
|
||||
|
||||
<variable name="user" type="com.android.User" />
|
||||
|
||||
<View xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/me"
|
||||
android:layout_width="1dp"
|
||||
android:onClick="doClick"
|
||||
android:text="@{user.name}"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_width="@support:bool/allow"
|
||||
|
||||
Reference in New Issue
Block a user