blob: 11fcc5d6274d9c1201073faf644d1258ac91c4d4 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ConfigDescription.h"
18#include "ResourceTable.h"
19#include "SdkConstants.h"
20#include "ValueVisitor.h"
21
22#include "link/Linkers.h"
Adam Lesinskie78fd612015-10-22 12:48:43 -070023#include "util/Comparators.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024
25#include <algorithm>
26#include <cassert>
27
28namespace aapt {
29
Adam Lesinski1ab598f2015-08-14 14:26:04 -070030bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
31 const int sdkVersionToGenerate) {
32 assert(sdkVersionToGenerate > config.sdkVersion);
33 const auto endIter = entry->values.end();
Adam Lesinskie78fd612015-10-22 12:48:43 -070034 auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThan);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070035
36 // The source config came from this list, so it should be here.
37 assert(iter != entry->values.end());
38 ++iter;
39
40 // The next configuration either only varies in sdkVersion, or it is completely different
41 // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
42
43 // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
44 // qualifiers, so we need to iterate through the entire list to be sure there
45 // are no higher sdk level versions of this resource.
46 ConfigDescription tempConfig(config);
47 for (; iter != endIter; ++iter) {
48 tempConfig.sdkVersion = iter->config.sdkVersion;
49 if (tempConfig == iter->config) {
50 // The two configs are the same, check the sdk version.
51 return sdkVersionToGenerate < iter->config.sdkVersion;
52 }
53 }
54
55 // No match was found, so we should generate the versioned resource.
56 return true;
57}
58
59bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
60 for (auto& package : table->packages) {
61 for (auto& type : package->types) {
62 if (type->type != ResourceType::kStyle) {
63 continue;
64 }
65
66 for (auto& entry : type->entries) {
67 for (size_t i = 0; i < entry->values.size(); i++) {
68 ResourceConfigValue& configValue = entry->values[i];
69 if (configValue.config.sdkVersion >= SDK_LOLLIPOP_MR1) {
70 // If this configuration is only used on L-MR1 then we don't need
71 // to do anything since we use private attributes since that version.
72 continue;
73 }
74
75 if (Style* style = valueCast<Style>(configValue.value.get())) {
76 Maybe<size_t> minSdkStripped;
77 std::vector<Style::Entry> stripped;
78
79 auto iter = style->entries.begin();
80 while (iter != style->entries.end()) {
81 assert(iter->key.id && "IDs must be assigned and linked");
82
83 // Find the SDK level that is higher than the configuration allows.
84 const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value());
85 if (sdkLevel > std::max<size_t>(configValue.config.sdkVersion, 1)) {
86 // Record that we are about to strip this.
87 stripped.emplace_back(std::move(*iter));
88
89 // We use the smallest SDK level to generate the new style.
90 if (minSdkStripped) {
91 minSdkStripped = std::min(minSdkStripped.value(), sdkLevel);
92 } else {
93 minSdkStripped = sdkLevel;
94 }
95
96 // Erase this from this style.
97 iter = style->entries.erase(iter);
98 continue;
99 }
100 ++iter;
101 }
102
103 if (minSdkStripped && !stripped.empty()) {
104 // We found attributes from a higher SDK level. Check that
105 // there is no other defined resource for the version we want to
106 // generate.
Adam Lesinskie78fd612015-10-22 12:48:43 -0700107 if (shouldGenerateVersionedResource(entry.get(),
108 configValue.config,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700109 minSdkStripped.value())) {
110 // Let's create a new Style for this versioned resource.
111 ConfigDescription newConfig(configValue.config);
112 newConfig.sdkVersion = minSdkStripped.value();
113
Adam Lesinskie78fd612015-10-22 12:48:43 -0700114 std::unique_ptr<Style> newStyle(style->clone(&table->stringPool));
115 newStyle->setComment(style->getComment());
116 newStyle->setSource(style->getSource());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700117
118 // Move the previously stripped attributes into this style.
119 newStyle->entries.insert(newStyle->entries.end(),
120 std::make_move_iterator(stripped.begin()),
121 std::make_move_iterator(stripped.end()));
122
123 // Insert the new Resource into the correct place.
124 auto iter = std::lower_bound(entry->values.begin(),
Adam Lesinskie78fd612015-10-22 12:48:43 -0700125 entry->values.end(),
126 newConfig,
127 cmp::lessThan);
128
129 entry->values.insert(
130 iter,
131 ResourceConfigValue{ newConfig, std::move(newStyle) });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700132 }
133 }
134 }
135 }
136 }
137 }
138 }
139 return true;
140}
141
142} // namespace aapt