Merge "Copy common resources between styles"
This commit is contained in:
committed by
Android (Google) Code Review
commit
3064067908
@@ -1064,6 +1064,17 @@ public final class AssetManager implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) {
|
||||
synchronized (this) {
|
||||
ensureValidLocked();
|
||||
synchronized (srcAssetManager) {
|
||||
srcAssetManager.ensureValidLocked();
|
||||
nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (DEBUG_REFS && mNumRefs != 0) {
|
||||
@@ -1375,7 +1386,8 @@ public final class AssetManager implements AutoCloseable {
|
||||
private static native void nativeThemeDestroy(long themePtr);
|
||||
private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
|
||||
boolean force);
|
||||
static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
|
||||
private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr,
|
||||
long srcAssetManagerPtr, long srcThemePtr);
|
||||
static native void nativeThemeClear(long themePtr);
|
||||
private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
|
||||
@AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
|
||||
|
||||
@@ -1336,7 +1336,7 @@ public class ResourcesImpl {
|
||||
void setTo(ThemeImpl other) {
|
||||
synchronized (mKey) {
|
||||
synchronized (other.mKey) {
|
||||
AssetManager.nativeThemeCopy(mTheme, other.mTheme);
|
||||
mAssets.setThemeTo(mTheme, other.mAssets, other.mTheme);
|
||||
|
||||
mThemeResId = other.mThemeResId;
|
||||
mKey.setTo(other.getKey());
|
||||
|
||||
@@ -1208,13 +1208,23 @@ static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlon
|
||||
// jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
|
||||
}
|
||||
|
||||
static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
|
||||
jlong src_theme_ptr) {
|
||||
static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
|
||||
jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) {
|
||||
Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
|
||||
Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
|
||||
if (!dst_theme->SetTo(*src_theme)) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"Themes are from different AssetManagers");
|
||||
|
||||
if (dst_asset_manager_ptr != src_asset_manager_ptr) {
|
||||
ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
|
||||
CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
|
||||
(void) dst_assetmanager;
|
||||
|
||||
ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
|
||||
CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
|
||||
(void) src_assetmanager;
|
||||
|
||||
dst_theme->SetTo(*src_theme);
|
||||
} else {
|
||||
dst_theme->SetTo(*src_theme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1255,10 +1265,11 @@ static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong
|
||||
Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
|
||||
CHECK(theme->GetAssetManager() == &(*assetmanager));
|
||||
(void) assetmanager;
|
||||
(void) theme;
|
||||
(void) priority;
|
||||
(void) tag;
|
||||
(void) prefix;
|
||||
|
||||
theme->Dump();
|
||||
}
|
||||
|
||||
static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
|
||||
@@ -1377,7 +1388,7 @@ static const JNINativeMethod gAssetManagerMethods[] = {
|
||||
{"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
|
||||
{"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
|
||||
{"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
|
||||
{"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy},
|
||||
{"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
|
||||
{"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
|
||||
{"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
|
||||
(void*)NativeThemeGetAttributeValue},
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
@@ -869,6 +870,17 @@ void AssetManager2::InvalidateCaches(uint32_t diff) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) {
|
||||
for (auto& package_group : package_groups_) {
|
||||
for (auto& package2 : package_group.packages_) {
|
||||
if (package2.loaded_package_ == package) {
|
||||
return package_group.dynamic_ref_table.mAssignedPackageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<Theme> AssetManager2::NewTheme() {
|
||||
return std::unique_ptr<Theme>(new Theme(this));
|
||||
}
|
||||
@@ -1054,44 +1066,231 @@ void Theme::Clear() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Theme::SetTo(const Theme& o) {
|
||||
void Theme::SetTo(const Theme& o) {
|
||||
if (this == &o) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
type_spec_flags_ = o.type_spec_flags_;
|
||||
|
||||
const bool copy_only_system = asset_manager_ != o.asset_manager_;
|
||||
|
||||
for (size_t p = 0; p < packages_.size(); p++) {
|
||||
const Package* package = o.packages_[p].get();
|
||||
if (package == nullptr || (copy_only_system && p != 0x01)) {
|
||||
// The other theme doesn't have this package, clear ours.
|
||||
packages_[p].reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packages_[p] == nullptr) {
|
||||
// The other theme has this package, but we don't. Make one.
|
||||
packages_[p].reset(new Package());
|
||||
}
|
||||
|
||||
for (size_t t = 0; t < package->types.size(); t++) {
|
||||
const ThemeType* type = package->types[t].get();
|
||||
if (type == nullptr) {
|
||||
// The other theme doesn't have this type, clear ours.
|
||||
packages_[p]->types[t].reset();
|
||||
if (asset_manager_ == o.asset_manager_) {
|
||||
// The theme comes from the same asset manager so all theme data can be copied exactly
|
||||
for (size_t p = 0; p < packages_.size(); p++) {
|
||||
const Package *package = o.packages_[p].get();
|
||||
if (package == nullptr) {
|
||||
// The other theme doesn't have this package, clear ours.
|
||||
packages_[p].reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a new type and update it to theirs.
|
||||
const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
|
||||
void* copied_data = malloc(type_alloc_size);
|
||||
memcpy(copied_data, type, type_alloc_size);
|
||||
packages_[p]->types[t].reset(reinterpret_cast<ThemeType*>(copied_data));
|
||||
if (packages_[p] == nullptr) {
|
||||
// The other theme has this package, but we don't. Make one.
|
||||
packages_[p].reset(new Package());
|
||||
}
|
||||
|
||||
for (size_t t = 0; t < package->types.size(); t++) {
|
||||
const ThemeType *type = package->types[t].get();
|
||||
if (type == nullptr) {
|
||||
// The other theme doesn't have this type, clear ours.
|
||||
packages_[p]->types[t].reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a new type and update it to theirs.
|
||||
const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
|
||||
void *copied_data = malloc(type_alloc_size);
|
||||
memcpy(copied_data, type, type_alloc_size);
|
||||
packages_[p]->types[t].reset(reinterpret_cast<ThemeType *>(copied_data));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
|
||||
typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
|
||||
std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
|
||||
|
||||
// Determine which ApkAssets are loaded in both theme AssetManagers
|
||||
std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets();
|
||||
for (size_t i = 0; i < src_assets.size(); i++) {
|
||||
const ApkAssets* src_asset = src_assets[i];
|
||||
|
||||
std::vector<const ApkAssets*> dest_assets = asset_manager_->GetApkAssets();
|
||||
for (size_t j = 0; j < dest_assets.size(); j++) {
|
||||
const ApkAssets* dest_asset = dest_assets[j];
|
||||
|
||||
// Map the runtime package of the source apk asset to the destination apk asset
|
||||
if (src_asset->GetPath() == dest_asset->GetPath()) {
|
||||
const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages =
|
||||
src_asset->GetLoadedArsc()->GetPackages();
|
||||
const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages =
|
||||
dest_asset->GetLoadedArsc()->GetPackages();
|
||||
|
||||
SourceToDestinationRuntimePackageMap package_map;
|
||||
|
||||
// The source and destination package should have the same number of packages loaded in
|
||||
// the same order.
|
||||
const size_t N = src_packages.size();
|
||||
CHECK(N == dest_packages.size())
|
||||
<< " LoadedArsc " << src_asset->GetPath() << " differs number of packages.";
|
||||
for (size_t p = 0; p < N; p++) {
|
||||
auto& src_package = src_packages[p];
|
||||
auto& dest_package = dest_packages[p];
|
||||
CHECK(src_package->GetPackageName() == dest_package->GetPackageName())
|
||||
<< " Package " << src_package->GetPackageName() << " differs in load order.";
|
||||
|
||||
int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get());
|
||||
int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get());
|
||||
package_map[src_package_id] = dest_package_id;
|
||||
}
|
||||
|
||||
src_to_dest_asset_cookies.insert(std::pair<ApkAssetsCookie, ApkAssetsCookie>(i, j));
|
||||
src_asset_cookie_id_map.insert(
|
||||
std::pair<ApkAssetsCookie, SourceToDestinationRuntimePackageMap>(i, package_map));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the data in the destination theme
|
||||
for (size_t p = 0; p < packages_.size(); p++) {
|
||||
if (packages_[p] != nullptr) {
|
||||
packages_[p].reset();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t p = 0; p < packages_.size(); p++) {
|
||||
const Package *package = o.packages_[p].get();
|
||||
if (package == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t t = 0; t < package->types.size(); t++) {
|
||||
const ThemeType *type = package->types[t].get();
|
||||
if (type == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t e = 0; e < type->entry_count; e++) {
|
||||
const ThemeEntry &entry = type->entries[e];
|
||||
if (entry.value.dataType == Res_value::TYPE_NULL &&
|
||||
entry.value.data != Res_value::DATA_NULL_EMPTY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The package id of the attribute needs to be rewritten to the package id of the value in
|
||||
// the destination
|
||||
int attribute_dest_package_id = p;
|
||||
if (attribute_dest_package_id != 0x01) {
|
||||
// Find the cookie of the attribute resource id
|
||||
FindEntryResult attribute_entry_result;
|
||||
ApkAssetsCookie attribute_cookie =
|
||||
o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false,
|
||||
&attribute_entry_result);
|
||||
|
||||
// Determine the package id of the attribute in the destination AssetManager
|
||||
auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie);
|
||||
if (attribute_package_map == src_asset_cookie_id_map.end()) {
|
||||
continue;
|
||||
}
|
||||
auto attribute_dest_package = attribute_package_map->second.find(
|
||||
attribute_dest_package_id);
|
||||
if (attribute_dest_package == attribute_package_map->second.end()) {
|
||||
continue;
|
||||
}
|
||||
attribute_dest_package_id = attribute_dest_package->second;
|
||||
}
|
||||
|
||||
// If the attribute value represents an attribute or reference, the package id of the
|
||||
// value needs to be rewritten to the package id of the value in the destination
|
||||
uint32_t attribue_data = entry.value.data;
|
||||
if (entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
|
||||
|| entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE
|
||||
|| entry.value.dataType == Res_value::TYPE_ATTRIBUTE
|
||||
|| entry.value.dataType == Res_value::TYPE_REFERENCE) {
|
||||
|
||||
// Determine the package id of the reference in the destination AssetManager
|
||||
auto value_package_map = src_asset_cookie_id_map.find(entry.cookie);
|
||||
if (value_package_map == src_asset_cookie_id_map.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value_dest_package = value_package_map->second.find(
|
||||
get_package_id(entry.value.data));
|
||||
if (value_dest_package == value_package_map->second.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attribue_data = fix_package_id(entry.value.data, value_dest_package->second);
|
||||
}
|
||||
|
||||
// Lazily instantiate the destination package
|
||||
std::unique_ptr<Package>& dest_package = packages_[attribute_dest_package_id];
|
||||
if (dest_package == nullptr) {
|
||||
dest_package.reset(new Package());
|
||||
}
|
||||
|
||||
// Lazily instantiate and resize the destination type
|
||||
util::unique_cptr<ThemeType>& dest_type = dest_package->types[t];
|
||||
if (dest_type == nullptr || dest_type->entry_count < type->entry_count) {
|
||||
const size_t type_alloc_size = sizeof(ThemeType)
|
||||
+ (type->entry_count * sizeof(ThemeEntry));
|
||||
void* dest_data = malloc(type_alloc_size);
|
||||
memset(dest_data, 0, type->entry_count * sizeof(ThemeEntry));
|
||||
|
||||
// Copy the existing destination type values if the type is resized
|
||||
if (dest_type != nullptr) {
|
||||
memcpy(dest_data, type, sizeof(ThemeType)
|
||||
+ (dest_type->entry_count * sizeof(ThemeEntry)));
|
||||
}
|
||||
|
||||
dest_type.reset(reinterpret_cast<ThemeType *>(dest_data));
|
||||
dest_type->entry_count = type->entry_count;
|
||||
}
|
||||
|
||||
// Find the cookie of the value in the destination
|
||||
auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie);
|
||||
if (value_dest_cookie == src_to_dest_asset_cookies.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dest_type->entries[e].cookie = value_dest_cookie->second;
|
||||
dest_type->entries[e].value.dataType = entry.value.dataType;
|
||||
dest_type->entries[e].value.data = attribue_data;
|
||||
dest_type->entries[e].type_spec_flags = entry.type_spec_flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Theme::Dump() const {
|
||||
base::ScopedLogSeverity _log(base::INFO);
|
||||
LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
|
||||
|
||||
for (int p = 0; p < packages_.size(); p++) {
|
||||
auto& package = packages_[p];
|
||||
if (package == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int t = 0; t < package->types.size(); t++) {
|
||||
auto& type = package->types[t];
|
||||
if (type == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int e = 0; e < type->entry_count; e++) {
|
||||
auto& entry = type->entries[e];
|
||||
if (entry.value.dataType == Res_value::TYPE_NULL &&
|
||||
entry.value.data != Res_value::DATA_NULL_EMPTY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
|
||||
make_resid(p, t, e), entry.value.data,
|
||||
entry.value.dataType, entry.cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -74,6 +74,8 @@ struct FindEntryResult;
|
||||
// AssetManager2 is the main entry point for accessing assets and resources.
|
||||
// AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
|
||||
class AssetManager2 {
|
||||
friend Theme;
|
||||
|
||||
public:
|
||||
struct ResourceName {
|
||||
const char* package = nullptr;
|
||||
@@ -285,6 +287,9 @@ class AssetManager2 {
|
||||
// been seen while traversing bag parents.
|
||||
const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
|
||||
|
||||
// Retrieve the assigned package id of the package if loaded into this AssetManager
|
||||
uint8_t GetAssignedPackageId(const LoadedPackage* package);
|
||||
|
||||
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
|
||||
// have a longer lifetime.
|
||||
std::vector<const ApkAssets*> apk_assets_;
|
||||
@@ -355,11 +360,14 @@ class Theme {
|
||||
bool ApplyStyle(uint32_t resid, bool force = false);
|
||||
|
||||
// Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
|
||||
// Returns false if the AssetManagers of the Themes were not compatible.
|
||||
bool SetTo(const Theme& o);
|
||||
// If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
|
||||
// into both AssetManagers will be copied to this theme.
|
||||
void SetTo(const Theme& o);
|
||||
|
||||
void Clear();
|
||||
|
||||
void Dump() const;
|
||||
|
||||
inline const AssetManager2* GetAssetManager() const {
|
||||
return asset_manager_;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
#include "TestHelpers.h"
|
||||
#include "androidfw/ResourceUtils.h"
|
||||
#include "data/lib_one/R.h"
|
||||
#include "data/lib_two/R.h"
|
||||
#include "data/libclient/R.h"
|
||||
#include "data/styles/R.h"
|
||||
#include "data/system/R.h"
|
||||
|
||||
namespace app = com::android::app;
|
||||
namespace lib_one = com::android::lib_one;
|
||||
namespace lib_two = com::android::lib_two;
|
||||
namespace libclient = com::android::libclient;
|
||||
|
||||
namespace android {
|
||||
@@ -263,7 +265,7 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) {
|
||||
ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
|
||||
|
||||
// Copy the theme to theme_one.
|
||||
ASSERT_TRUE(theme_one->SetTo(*theme_two));
|
||||
theme_one->SetTo(*theme_two);
|
||||
|
||||
// Clear theme_two to make sure we test that there WAS a copy.
|
||||
theme_two->Clear();
|
||||
@@ -279,12 +281,14 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) {
|
||||
EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
|
||||
}
|
||||
|
||||
TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) {
|
||||
TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
|
||||
AssetManager2 assetmanager_one;
|
||||
assetmanager_one.SetApkAssets({system_assets_.get(), style_assets_.get()});
|
||||
assetmanager_one.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(),
|
||||
libclient_assets_.get()});
|
||||
|
||||
AssetManager2 assetmanager_two;
|
||||
assetmanager_two.SetApkAssets({system_assets_.get(), style_assets_.get()});
|
||||
assetmanager_two.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(),
|
||||
style_assets_.get()});
|
||||
|
||||
auto theme_one = assetmanager_one.NewTheme();
|
||||
ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
|
||||
@@ -292,17 +296,34 @@ TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) {
|
||||
auto theme_two = assetmanager_two.NewTheme();
|
||||
ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One));
|
||||
ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
|
||||
ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03),
|
||||
false /*force*/));
|
||||
ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
|
||||
false /*force*/));
|
||||
|
||||
EXPECT_TRUE(theme_one->SetTo(*theme_two));
|
||||
theme_one->SetTo(*theme_two);
|
||||
|
||||
Res_value value;
|
||||
uint32_t flags;
|
||||
|
||||
// No app resources.
|
||||
EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
|
||||
// System resources (present in destination asset manager)
|
||||
EXPECT_EQ(0, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
|
||||
|
||||
// Only system.
|
||||
EXPECT_NE(kInvalidCookie, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
|
||||
// The cookie of the style asset is 3 in the source and 2 in the destination.
|
||||
// Check that the cookie has been rewritten to the destination values
|
||||
EXPECT_EQ(2, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
|
||||
|
||||
// The cookie of the lib_one asset is 2 in the source and 1 in the destination.
|
||||
// The package id of the lib_one package is 0x03 in the source and 0x02 in the destination
|
||||
// Check that the cookie and packages have been rewritten to the destination values
|
||||
EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value,
|
||||
&flags));
|
||||
EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value,
|
||||
&flags));
|
||||
|
||||
// attr2 references an attribute in lib_one. Check that the resolution of the attribute value is
|
||||
// correct after the value of attr2 had its package id rewritten to the destination package id
|
||||
EXPECT_EQ(700, value.data);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -24,12 +24,24 @@ namespace android {
|
||||
namespace lib_two {
|
||||
|
||||
struct R {
|
||||
struct attr {
|
||||
enum : uint32_t {
|
||||
attr3 = 0x02010000, // default
|
||||
};
|
||||
};
|
||||
|
||||
struct string {
|
||||
enum : uint32_t {
|
||||
LibraryString = 0x02020000, // default
|
||||
foo = 0x02020001, // default
|
||||
};
|
||||
};
|
||||
|
||||
struct style {
|
||||
enum : uint32_t {
|
||||
Theme = 0x02030000, // default
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace lib_two
|
||||
|
||||
Binary file not shown.
@@ -15,9 +15,17 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<public type="attr" name="attr3" id="0x00010000" />
|
||||
<attr name="attr3" format="integer" />
|
||||
|
||||
<public type="string" name="LibraryString" id="0x00020000" />
|
||||
<string name="LibraryString">Hi from library two</string>
|
||||
|
||||
<public type="string" name="foo" id="0x00020001" />
|
||||
<string name="foo">Foo from lib_two</string>
|
||||
|
||||
<public type="style" name="Theme" id="0x02030000" />
|
||||
<style name="Theme">
|
||||
<item name="com.android.lib_two:attr3">800</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user