aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android
diff options
context:
space:
mode:
authorGravatar corysmith <corysmith@google.com>2018-04-03 08:35:51 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-04-03 08:37:36 -0700
commit5437b080f3ba8b51e502027cb2df15ec80cd2fbf (patch)
treee31160a5783e484b3098a0682464ae79876d02b8 /src/tools/android
parenta8023b796db7d05e329d0eb9a51cb4ce8222e4a0 (diff)
Automated rollback of commit e8bed799d59526541afa2a0e9ef5d4c49e3ba390.
*** Reason for rollback *** Rolling forward with improved handling and testing for Styleables, and correct package management. *** Original change description *** Automated rollback of commit a76f7db51a90cc2e35c1d66782056c310729eef0. *** Reason for rollback *** Breaks Kix. *** Original change description *** Modify the .flat decompilation to account for multiple configurations by converting the aapt2 proto ConfigValue to a FolderConfiguration. Adds new aapt2 compiled deserialization test. RELNOTES: None PiperOrigin-RevId: 191444658
Diffstat (limited to 'src/tools/android')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java433
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java9
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java29
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java3
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java29
5 files changed, 424 insertions, 79 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
index fb4d93dcc6..ec29c67fb7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidCompiledDataDeserializer.java
@@ -13,8 +13,19 @@
// limitations under the License.
package com.google.devtools.build.android;
+import static java.util.stream.Collectors.toList;
+
import android.aapt.pb.internal.ResourcesInternal.CompiledFile;
import com.android.SdkConstants;
+import com.android.aapt.ConfigurationOuterClass.Configuration;
+import com.android.aapt.ConfigurationOuterClass.Configuration.KeysHidden;
+import com.android.aapt.ConfigurationOuterClass.Configuration.NavHidden;
+import com.android.aapt.ConfigurationOuterClass.Configuration.Orientation;
+import com.android.aapt.ConfigurationOuterClass.Configuration.ScreenLayoutLong;
+import com.android.aapt.ConfigurationOuterClass.Configuration.ScreenLayoutSize;
+import com.android.aapt.ConfigurationOuterClass.Configuration.Touchscreen;
+import com.android.aapt.ConfigurationOuterClass.Configuration.UiModeNight;
+import com.android.aapt.ConfigurationOuterClass.Configuration.UiModeType;
import com.android.aapt.Resources;
import com.android.aapt.Resources.ConfigValue;
import com.android.aapt.Resources.Package;
@@ -22,9 +33,47 @@ import com.android.aapt.Resources.ResourceTable;
import com.android.aapt.Resources.Type;
import com.android.aapt.Resources.Value;
import com.android.aapt.Resources.Visibility.Level;
+import com.android.ide.common.resources.configuration.CountryCodeQualifier;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.LayoutDirectionQualifier;
+import com.android.ide.common.resources.configuration.LocaleQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NavigationStateQualifier;
+import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
+import com.android.ide.common.resources.configuration.NightModeQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenHeightQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenRoundQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.ScreenWidthQualifier;
+import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
+import com.android.ide.common.resources.configuration.UiModeQualifier;
+import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.resources.Density;
+import com.android.resources.Keyboard;
+import com.android.resources.KeyboardState;
+import com.android.resources.LayoutDirection;
+import com.android.resources.Navigation;
+import com.android.resources.NavigationState;
+import com.android.resources.NightMode;
import com.android.resources.ResourceType;
+import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenRound;
+import com.android.resources.ScreenSize;
+import com.android.resources.TouchScreen;
+import com.android.resources.UiMode;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.LittleEndianDataInputStream;
import com.google.devtools.build.android.FullyQualifiedName.Factory;
@@ -40,14 +89,14 @@ import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
@@ -58,6 +107,117 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
private static final Logger logger =
Logger.getLogger(AndroidCompiledDataDeserializer.class.getName());
+ static final ImmutableMap<Configuration.LayoutDirection, LayoutDirection> LAYOUT_DIRECTION_MAP =
+ ImmutableMap.of(
+ Configuration.LayoutDirection.LAYOUT_DIRECTION_LTR,
+ LayoutDirection.LTR,
+ Configuration.LayoutDirection.LAYOUT_DIRECTION_RTL,
+ LayoutDirection.RTL);
+
+ static final ImmutableMap<Configuration.ScreenLayoutSize, ScreenSize> LAYOUT_SIZE_MAP =
+ ImmutableMap.of(
+ ScreenLayoutSize.SCREEN_LAYOUT_SIZE_SMALL,
+ ScreenSize.SMALL,
+ ScreenLayoutSize.SCREEN_LAYOUT_SIZE_NORMAL,
+ ScreenSize.NORMAL,
+ ScreenLayoutSize.SCREEN_LAYOUT_SIZE_LARGE,
+ ScreenSize.LARGE,
+ ScreenLayoutSize.SCREEN_LAYOUT_SIZE_XLARGE,
+ ScreenSize.XLARGE);
+
+ static final ImmutableMap<Configuration.ScreenLayoutLong, ScreenRatio> SCREEN_LONG_MAP =
+ ImmutableMap.of(
+ ScreenLayoutLong.SCREEN_LAYOUT_LONG_LONG,
+ ScreenRatio.LONG,
+ ScreenLayoutLong.SCREEN_LAYOUT_LONG_NOTLONG,
+ ScreenRatio.NOTLONG);
+
+ static final ImmutableMap<Configuration.ScreenRound, ScreenRound> SCREEN_ROUND_MAP =
+ ImmutableMap.of(
+ Configuration.ScreenRound.SCREEN_ROUND_ROUND, ScreenRound.ROUND,
+ Configuration.ScreenRound.SCREEN_ROUND_NOTROUND, ScreenRound.NOTROUND);
+
+ private static final ImmutableMap<Configuration.Orientation, ScreenOrientation>
+ SCREEN_ORIENTATION_MAP =
+ ImmutableMap.of(
+ Orientation.ORIENTATION_LAND, ScreenOrientation.LANDSCAPE,
+ Orientation.ORIENTATION_PORT, ScreenOrientation.PORTRAIT,
+ Orientation.ORIENTATION_SQUARE, ScreenOrientation.SQUARE);
+
+ private static final ImmutableMap<UiModeType, UiMode> SCREEN_UI_MODE =
+ ImmutableMap.<UiModeType, UiMode>builder()
+ .put(UiModeType.UI_MODE_TYPE_APPLIANCE, UiMode.APPLIANCE)
+ .put(UiModeType.UI_MODE_TYPE_CAR, UiMode.CAR)
+ .put(UiModeType.UI_MODE_TYPE_DESK, UiMode.DESK)
+ .put(UiModeType.UI_MODE_TYPE_NORMAL, UiMode.NORMAL)
+ .put(UiModeType.UI_MODE_TYPE_TELEVISION, UiMode.TELEVISION)
+ .put(UiModeType.UI_MODE_TYPE_VRHEADSET, UiMode.NORMAL)
+ .put(UiModeType.UI_MODE_TYPE_WATCH, UiMode.WATCH)
+ .build();
+
+ static final ImmutableMap<Configuration.UiModeNight, NightMode> NIGHT_MODE_MAP =
+ ImmutableMap.of(
+ UiModeNight.UI_MODE_NIGHT_NIGHT, NightMode.NIGHT,
+ UiModeNight.UI_MODE_NIGHT_NOTNIGHT, NightMode.NOTNIGHT);
+
+ static final ImmutableMap<Configuration.KeysHidden, KeyboardState> KEYBOARD_STATE_MAP =
+ ImmutableMap.of(
+ KeysHidden.KEYS_HIDDEN_KEYSEXPOSED,
+ KeyboardState.EXPOSED,
+ KeysHidden.KEYS_HIDDEN_KEYSSOFT,
+ KeyboardState.SOFT,
+ KeysHidden.KEYS_HIDDEN_KEYSHIDDEN,
+ KeyboardState.HIDDEN);
+
+ static final ImmutableMap<Configuration.Touchscreen, TouchScreen> TOUCH_TYPE_MAP =
+ ImmutableMap.of(
+ Touchscreen.TOUCHSCREEN_FINGER,
+ TouchScreen.FINGER,
+ Touchscreen.TOUCHSCREEN_NOTOUCH,
+ TouchScreen.NOTOUCH,
+ Touchscreen.TOUCHSCREEN_STYLUS,
+ TouchScreen.STYLUS);
+
+ static final ImmutableMap<Configuration.Keyboard, Keyboard> KEYBOARD_MAP =
+ ImmutableMap.of(
+ Configuration.Keyboard.KEYBOARD_NOKEYS,
+ Keyboard.NOKEY,
+ Configuration.Keyboard.KEYBOARD_QWERTY,
+ Keyboard.QWERTY,
+ Configuration.Keyboard.KEYBOARD_TWELVEKEY,
+ Keyboard.TWELVEKEY);
+
+ static final ImmutableMap<Configuration.NavHidden, NavigationState> NAV_STATE_MAP =
+ ImmutableMap.of(
+ NavHidden.NAV_HIDDEN_NAVHIDDEN,
+ NavigationState.HIDDEN,
+ NavHidden.NAV_HIDDEN_NAVEXPOSED,
+ NavigationState.EXPOSED);
+
+ static final ImmutableMap<Configuration.Navigation, Navigation> NAVIGATION_MAP =
+ ImmutableMap.of(
+ Configuration.Navigation.NAVIGATION_DPAD,
+ Navigation.DPAD,
+ Configuration.Navigation.NAVIGATION_NONAV,
+ Navigation.NONAV,
+ Configuration.Navigation.NAVIGATION_TRACKBALL,
+ Navigation.TRACKBALL,
+ Configuration.Navigation.NAVIGATION_WHEEL,
+ Navigation.WHEEL);
+
+ static final ImmutableMap<Integer, Density> DENSITY_MAP =
+ ImmutableMap.<Integer, Density>builder()
+ .put(0xfffe, Density.ANYDPI)
+ .put(0xffff, Density.NODPI)
+ .put(120, Density.LOW)
+ .put(160, Density.MEDIUM)
+ .put(213, Density.TV)
+ .put(240, Density.HIGH)
+ .put(320, Density.XHIGH)
+ .put(480, Density.XXHIGH)
+ .put(640, Density.XXXHIGH)
+ .build();
+
private final ImmutableSet<String> filteredResources;
/**
@@ -78,9 +238,8 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
}
private void readResourceTable(
- LittleEndianDataInputStream resourceTableStream,
- KeyValueConsumers consumers,
- Factory fqnFactory) throws IOException {
+ LittleEndianDataInputStream resourceTableStream, KeyValueConsumers consumers)
+ throws IOException {
long alignedSize = resourceTableStream.readLong();
Preconditions.checkArgument(alignedSize <= Integer.MAX_VALUE);
@@ -91,66 +250,218 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
List<String> sourcePool =
decodeSourcePool(resourceTable.getSourcePool().getData().toByteArray());
- Map<String, Entry<FullyQualifiedName, Boolean>> fullyQualifiedNames = new HashMap<>();
+ Map<String, Boolean> qualifiedReferenceInlineStatus = new HashMap<>();
for (int i = resourceTable.getPackageCount() - 1; i >= 0; i--) {
Package resourceTablePackage = resourceTable.getPackage(i);
- String packageName = "";
- if (!resourceTablePackage.getPackageName().isEmpty()) {
- packageName = resourceTablePackage.getPackageName() + ":";
- }
+ String packageName = resourceTablePackage.getPackageName();
for (Type resourceFormatType : resourceTablePackage.getTypeList()) {
ResourceType resourceType = ResourceType.getEnum(resourceFormatType.getName());
for (Resources.Entry resource : resourceFormatType.getEntryList()) {
- String resourceName = packageName + resource.getName();
-
- FullyQualifiedName fqn = fqnFactory.create(resourceType, resourceName);
- fullyQualifiedNames.put(
- String.format("%s%s/%s", packageName, resourceType, resource.getName()),
- new SimpleEntry<FullyQualifiedName, Boolean>(fqn, packageName.isEmpty()));
-
- List<ConfigValue> configValues = resource.getConfigValueList();
- if (configValues.isEmpty()
+ if (resource.getConfigValueList().isEmpty()
&& resource.getVisibility().getLevel() == Level.PUBLIC) {
- int sourceIndex = resource.getVisibility().getSource().getPathIdx();
-
- String source = sourcePool.get(sourceIndex);
- DataSource dataSource = DataSource.of(Paths.get(source));
- DataResourceXml dataResourceXml = DataResourceXml
- .fromPublic(dataSource, resourceType, resource.getEntryId().getId());
- consumers.combiningConsumer.accept(fqn, dataResourceXml);
- } else if (packageName.isEmpty()) {// This means this resource is not in the android sdk
- Preconditions.checkArgument(configValues.size() == 1);
- int sourceIndex =
- configValues.get(0)
- .getValue()
- .getSource()
- .getPathIdx();
+ // Public resource definition.
+ int sourceIndex = resource.getVisibility().getSource().getPathIdx();
String source = sourcePool.get(sourceIndex);
DataSource dataSource = DataSource.of(Paths.get(source));
- Value resourceValue = resource.getConfigValue(0).getValue();
DataResourceXml dataResourceXml =
- DataResourceXml
- .from(resourceValue, dataSource, resourceType, fullyQualifiedNames);
-
- if (resourceType == ResourceType.ID
- || resourceType == ResourceType.STYLEABLE) {
- consumers.combiningConsumer.accept(fqn, dataResourceXml);
- } else {
- consumers.overwritingConsumer.accept(fqn, dataResourceXml);
+ DataResourceXml.fromPublic(dataSource, resourceType, resource.getEntryId().getId());
+ final FullyQualifiedName fqn =
+ createAndRecordFqn(
+ qualifiedReferenceInlineStatus,
+ packageName,
+ resourceType,
+ resource,
+ ImmutableList.of());
+ consumers.combiningConsumer.accept(fqn, dataResourceXml);
+ } else if (!"android".equals(packageName)) {
+ // This means this resource is not in the android sdk, add it to the set.
+ for (ConfigValue configValue : resource.getConfigValueList()) {
+ FullyQualifiedName fqn =
+ createAndRecordFqn(
+ qualifiedReferenceInlineStatus,
+ packageName,
+ resourceType,
+ resource,
+ convertToQualifiers(configValue));
+
+ int sourceIndex = configValue.getValue().getSource().getPathIdx();
+
+ String source = sourcePool.get(sourceIndex);
+ DataSource dataSource = DataSource.of(Paths.get(source));
+
+ Value resourceValue = resource.getConfigValue(0).getValue();
+ DataResourceXml dataResourceXml =
+ DataResourceXml.from(
+ resourceValue, dataSource, resourceType, qualifiedReferenceInlineStatus);
+
+ if (!fqn.isOverwritable()) {
+ consumers.combiningConsumer.accept(fqn, dataResourceXml);
+ } else {
+ consumers.overwritingConsumer.accept(fqn, dataResourceXml);
+ }
}
+ } else {
+ // In the sdk, just add the fqn for styleables
+ createAndRecordFqn(
+ qualifiedReferenceInlineStatus,
+ packageName,
+ resourceType,
+ resource,
+ ImmutableList.of())
+ .toPrettyString();
}
}
}
}
}
+ private FullyQualifiedName createAndRecordFqn(
+ Map<String, Boolean> qualifiedReferenceInlineStatus,
+ String packageName,
+ ResourceType resourceType,
+ Resources.Entry resource,
+ List<String> of) {
+ Preconditions.checkArgument(!packageName.contains(":"));
+ final FullyQualifiedName fqn =
+ FullyQualifiedName.of(
+ packageName.isEmpty() ? FullyQualifiedName.DEFAULT_PACKAGE : packageName,
+ of,
+ resourceType,
+ resource.getName());
+ // Record if the definition of the attr is defined in styleable.
+ // Currently, we consider any reference without a package as being defined inline.
+ qualifiedReferenceInlineStatus.put(fqn.asQualifiedReference(), packageName.isEmpty());
+ return fqn;
+ }
+
+ private List<String> convertToQualifiers(ConfigValue configValue) {
+ FolderConfiguration configuration = new FolderConfiguration();
+ final Configuration protoConfig = configValue.getConfig();
+ if (protoConfig.getMcc() > 0) {
+ configuration.setCountryCodeQualifier(new CountryCodeQualifier(protoConfig.getMcc()));
+ }
+ // special code for 0, as MNC can be zero
+ // https://android.googlesource.com/platform/frameworks/native/+/master/include/android/configuration.h#473
+ if (protoConfig.getMnc() != 0) {
+ configuration.setNetworkCodeQualifier(
+ NetworkCodeQualifier.getQualifier(
+ String.format(
+ Locale.US,
+ "mnc%1$03d",
+ protoConfig.getMnc() == 0xffff ? 0 : protoConfig.getMnc())));
+ }
+
+ // locales are the wild, wild west: no enums.
+ if (!protoConfig.getLocale().isEmpty()) {
+ new LocaleQualifier().checkAndSet(protoConfig.getLocale(), configuration);
+ }
+
+ if (LAYOUT_DIRECTION_MAP.containsKey(protoConfig.getLayoutDirection())) {
+ configuration.setLayoutDirectionQualifier(
+ new LayoutDirectionQualifier(LAYOUT_DIRECTION_MAP.get(protoConfig.getLayoutDirection())));
+ }
+
+ if (protoConfig.getSmallestScreenWidthDp() > 0) {
+ configuration.setSmallestScreenWidthQualifier(
+ new SmallestScreenWidthQualifier(protoConfig.getSmallestScreenWidthDp()));
+ }
+
+ // screen dimension is defined if one number is greater than 0
+ if (Math.max(protoConfig.getScreenHeight(), protoConfig.getScreenWidth()) > 0) {
+ configuration.setScreenDimensionQualifier(
+ new ScreenDimensionQualifier(
+ Math.max(
+ protoConfig.getScreenHeight(),
+ protoConfig.getScreenWidth()), // biggest is always first
+ Math.min(protoConfig.getScreenHeight(), protoConfig.getScreenWidth())));
+ }
+
+ if (protoConfig.getScreenWidthDp() > 0) {
+ configuration.setScreenWidthQualifier(
+ new ScreenWidthQualifier(protoConfig.getScreenWidthDp()));
+ }
+
+ if (protoConfig.getScreenHeightDp() > 0) {
+ configuration.setScreenHeightQualifier(
+ new ScreenHeightQualifier(protoConfig.getScreenHeightDp()));
+ }
+
+ if (LAYOUT_SIZE_MAP.containsKey(protoConfig.getScreenLayoutSize())) {
+ configuration.setScreenSizeQualifier(
+ new ScreenSizeQualifier(LAYOUT_SIZE_MAP.get(protoConfig.getScreenLayoutSize())));
+ }
+
+ if (SCREEN_LONG_MAP.containsKey(protoConfig.getScreenLayoutLong())) {
+ configuration.setScreenRatioQualifier(
+ new ScreenRatioQualifier(SCREEN_LONG_MAP.get(protoConfig.getScreenLayoutLong())));
+ }
+
+ if (SCREEN_ROUND_MAP.containsKey(protoConfig.getScreenRound())) {
+ configuration.setScreenRoundQualifier(
+ new ScreenRoundQualifier(SCREEN_ROUND_MAP.get(protoConfig.getScreenRound())));
+ }
+
+ if (SCREEN_ORIENTATION_MAP.containsKey(protoConfig.getOrientation())) {
+ configuration.setScreenOrientationQualifier(
+ new ScreenOrientationQualifier(SCREEN_ORIENTATION_MAP.get(protoConfig.getOrientation())));
+ }
+
+ if (SCREEN_UI_MODE.containsKey(protoConfig.getUiModeType())) {
+ configuration.setUiModeQualifier(
+ new UiModeQualifier(SCREEN_UI_MODE.get(protoConfig.getUiModeType())));
+ }
+
+ if (NIGHT_MODE_MAP.containsKey(protoConfig.getUiModeNight())) {
+ configuration.setNightModeQualifier(
+ new NightModeQualifier(NIGHT_MODE_MAP.get(protoConfig.getUiModeNight())));
+ }
+
+ if (DENSITY_MAP.containsKey(protoConfig.getDensity())) {
+ configuration.setDensityQualifier(
+ new DensityQualifier(DENSITY_MAP.get(protoConfig.getDensity())));
+ }
+
+ if (TOUCH_TYPE_MAP.containsKey(protoConfig.getTouchscreen())) {
+ configuration.setTouchTypeQualifier(
+ new TouchScreenQualifier(TOUCH_TYPE_MAP.get(protoConfig.getTouchscreen())));
+ }
+
+ if (KEYBOARD_STATE_MAP.containsKey(protoConfig.getKeysHidden())) {
+ configuration.setKeyboardStateQualifier(
+ new KeyboardStateQualifier(KEYBOARD_STATE_MAP.get(protoConfig.getKeysHidden())));
+ }
+
+ if (KEYBOARD_MAP.containsKey(protoConfig.getKeyboard())) {
+ configuration.setTextInputMethodQualifier(
+ new TextInputMethodQualifier(KEYBOARD_MAP.get(protoConfig.getKeyboard())));
+ }
+
+ if (NAV_STATE_MAP.containsKey(protoConfig.getNavHidden())) {
+ configuration.setNavigationStateQualifier(
+ new NavigationStateQualifier(NAV_STATE_MAP.get(protoConfig.getNavHidden())));
+ }
+
+ if (NAVIGATION_MAP.containsKey(protoConfig.getNavigation())) {
+ configuration.setNavigationMethodQualifier(
+ new NavigationMethodQualifier(NAVIGATION_MAP.get(protoConfig.getNavigation())));
+ }
+
+ if (protoConfig.getSdkVersion() > 0) {
+ configuration.setVersionQualifier(new VersionQualifier(protoConfig.getSdkVersion()));
+ }
+
+ return Arrays.stream(configuration.getQualifiers())
+ .map(ResourceQualifier::getFolderSegment)
+ .collect(toList());
+ }
+
/**
* Reads compiled resource data files and adds them to consumers
*
@@ -165,13 +476,14 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
private void readCompiledFile(
LittleEndianDataInputStream compiledFileStream,
KeyValueConsumers consumers,
- Factory fqnFactory) throws IOException {
- //Skip aligned size. We don't need it here.
+ Factory fqnFactory)
+ throws IOException {
+ // Skip aligned size. We don't need it here.
Preconditions.checkArgument(compiledFileStream.skipBytes(8) == 8);
int resFileHeaderSize = compiledFileStream.readInt();
- //Skip data payload size. We don't need it here.
+ // Skip data payload size. We don't need it here.
Preconditions.checkArgument(compiledFileStream.skipBytes(8) == 8);
byte[] file = new byte[resFileHeaderSize];
@@ -201,9 +513,8 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
}
private void readAttributesFile(
- InputStream resourceFileStream,
- FileSystem fileSystem,
- KeyValueConsumers consumers) throws IOException {
+ InputStream resourceFileStream, FileSystem fileSystem, KeyValueConsumers consumers)
+ throws IOException {
Header header = Header.parseDelimitedFrom(resourceFileStream);
List<DataKey> fullyQualifiedNames = new ArrayList<>();
@@ -219,10 +530,8 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
SerializeFormat.DataValue protoValue =
SerializeFormat.DataValue.parseDelimitedFrom(resourceFileStream);
DataSource source = sourceTable.sourceFromId(protoValue.getSourceId());
- DataResourceXml dataResourceXml =
- (DataResourceXml) DataResourceXml.from(protoValue, source);
- AttributeType attributeType =
- AttributeType.valueOf(protoValue.getXmlValue().getValueType());
+ DataResourceXml dataResourceXml = (DataResourceXml) DataResourceXml.from(protoValue, source);
+ AttributeType attributeType = AttributeType.valueOf(protoValue.getXmlValue().getValueType());
if (attributeType.isCombining()) {
consumers.combiningConsumer.accept(fullyQualifiedName, dataResourceXml);
@@ -276,18 +585,20 @@ public class AndroidCompiledDataDeserializer implements AndroidDataDeserializer
int resourceType = dataInputStream.readInt();
if (resourceType == 0) { // 0 is a resource table
- readResourceTable(dataInputStream, consumers, fqnFactory);
+ readResourceTable(dataInputStream, consumers);
} else if (resourceType == 1) { // 1 is a resource file
readCompiledFile(dataInputStream, consumers, fqnFactory);
} else {
- throw new DeserializationException("aapt2 version mismatch.",
- new DeserializationException(String.format(
- "Unexpected tag for resourceType %s expected 0 or 1 in %s."
- + "\n Last known good values:"
- + "\n\tmagicNumber 1414545729 (is %s)"
- + "\n\tformatVersion 1 (is %s)"
- + "\n\tnumberOfEntries 1 (is %s)",
- resourceType, fileZipPath, magicNumber, formatVersion, numberOfEntries)));
+ throw new DeserializationException(
+ "aapt2 version mismatch.",
+ new DeserializationException(
+ String.format(
+ "Unexpected tag for resourceType %s expected 0 or 1 in %s."
+ + "\n Last known good values:"
+ + "\n\tmagicNumber 1414545729 (is %s)"
+ + "\n\tformatVersion 1 (is %s)"
+ + "\n\tnumberOfEntries 1 (is %s)",
+ resourceType, fileZipPath, magicNumber, formatVersion, numberOfEntries)));
}
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java b/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
index 2b39c449e6..59a6fa3504 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
@@ -46,7 +46,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Objects;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
@@ -174,7 +173,7 @@ public class DataResourceXml implements DataResource {
Value protoValue,
DataSource source,
ResourceType resourceType,
- Map<String, Entry<FullyQualifiedName, Boolean>> fullyQualifiedNames)
+ Map<String, Boolean> fullyQualifiedNames)
throws InvalidProtocolBufferException {
DataResourceXml dataResourceXml =
createWithNamespaces(
@@ -212,9 +211,7 @@ public class DataResourceXml implements DataResource {
}
private static XmlResourceValue valueFromProto(
- Value proto,
- ResourceType resourceType,
- Map<String, Entry<FullyQualifiedName, Boolean>> fullyQualifiedNames)
+ Value proto, ResourceType resourceType, Map<String, Boolean> qualifiedReferenceToInlineStatus)
throws InvalidProtocolBufferException {
switch (resourceType) {
case STYLE:
@@ -226,7 +223,7 @@ public class DataResourceXml implements DataResource {
case ATTR:
return AttrXmlResourceValue.from(proto);
case STYLEABLE:
- return StyleableXmlResourceValue.from(proto, fullyQualifiedNames);
+ return StyleableXmlResourceValue.from(proto, qualifiedReferenceToInlineStatus);
case ID:
return IdXmlResourceValue.of();
case DIMEN:
diff --git a/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java b/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java
index 9344412390..3663c34673 100644
--- a/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java
+++ b/src/tools/android/java/com/google/devtools/build/android/FullyQualifiedName.java
@@ -20,6 +20,7 @@ import com.android.ide.common.resources.configuration.ResourceQualifier;
import com.android.resources.ResourceType;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Iterators;
@@ -33,6 +34,7 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -130,6 +132,23 @@ public class FullyQualifiedName implements DataKey {
protoKey.getKeyValue());
}
+ static final Pattern QUALIFIED_REFERENCE =
+ Pattern.compile("((?<package>[^:]+):)?(?<type>\\w+)/(?<name>\\w+)");
+
+ public static FullyQualifiedName fromReference(String qualifiedReference) {
+ final Matcher matcher = QUALIFIED_REFERENCE.matcher(qualifiedReference);
+ Preconditions.checkArgument(
+ matcher.find(),
+ "%s is not a reference. Expected %s",
+ qualifiedReference,
+ QUALIFIED_REFERENCE.pattern());
+ return of(
+ Optional.ofNullable(matcher.group("package")).orElse(DEFAULT_PACKAGE),
+ ImmutableList.of(),
+ ResourceType.getEnum(matcher.group("type")),
+ matcher.group("name"));
+ }
+
public static void logCacheUsage(Logger logger) {
logger.fine(
String.format(
@@ -186,6 +205,16 @@ public class FullyQualifiedName implements DataKey {
return name;
}
+ /** Provides the name qualified by the package it belongs to. */
+ public String qualifiedName() {
+ return (pkg.equals(DEFAULT_PACKAGE) ? "" : pkg + ":") + name;
+ }
+
+ public String asQualifiedReference() {
+ return String.format(
+ "%s%s/%s", (pkg.equals(DEFAULT_PACKAGE) ? "" : pkg + ":"), type.getName(), name);
+ }
+
public ResourceType type() {
if (type instanceof ResourceTypeWrapper) {
return ((ResourceTypeWrapper) type).resourceType;
diff --git a/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java b/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java
index 12b7abe4ae..ab1dee800d 100644
--- a/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java
+++ b/src/tools/android/java/com/google/devtools/build/android/PlaceholderIdFieldInitializerBuilder.java
@@ -165,6 +165,7 @@ class PlaceholderIdFieldInitializerBuilder {
private static String normalizeAttrName(String attrName) {
// In addition to ".", attributes can have ":", e.g., for "android:textColor".
+ Preconditions.checkArgument(!attrName.contains("::"), "invalid name %s", attrName);
return normalizeName(attrName).replace(':', '_');
}
@@ -240,7 +241,7 @@ class PlaceholderIdFieldInitializerBuilder {
styleableAttrs.put(normalizedStyleableName, normalizedAttrs);
}
for (Map.Entry<FullyQualifiedName, Boolean> attrEntry : attrs.entrySet()) {
- String normalizedAttrName = normalizeAttrName(attrEntry.getKey().name());
+ String normalizedAttrName = normalizeAttrName(attrEntry.getKey().qualifiedName());
normalizedAttrs.put(normalizedAttrName, attrEntry.getValue());
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java b/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java
index c9661690cf..72707cf87d 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/StyleableXmlResourceValue.java
@@ -18,6 +18,7 @@ import com.android.aapt.Resources.Value;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Iterables;
@@ -165,16 +166,22 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
}
public static XmlResourceValue from(
- Value proto, Map<String, Entry<FullyQualifiedName, Boolean>> fullyQualifiedNames) {
+ Value proto, Map<String, Boolean> qualifiedReferenceInlineStatus) {
Map<FullyQualifiedName, Boolean> attributes = new HashMap<>();
Styleable styleable = proto.getCompoundValue().getStyleable();
for (Styleable.Entry entry : styleable.getEntryList()) {
- String attrName = entry.getAttr().getName();
-
- Entry<FullyQualifiedName, Boolean> fqnEntry = fullyQualifiedNames.get(attrName);
- attributes.put(fqnEntry.getKey(), fqnEntry.getValue());
- fqnEntry.setValue(false);
+ final FullyQualifiedName reference =
+ FullyQualifiedName.fromReference(entry.getAttr().getName());
+ final String qualifiedReference = reference.asQualifiedReference();
+ Preconditions.checkArgument(
+ qualifiedReferenceInlineStatus.containsKey(qualifiedReference),
+ "Styleable reference %s is not in %s",
+ qualifiedReference,
+ qualifiedReferenceInlineStatus.keySet());
+
+ attributes.put(reference, qualifiedReferenceInlineStatus.get(qualifiedReference));
+ qualifiedReferenceInlineStatus.put(qualifiedReference, false);
}
return of(ImmutableMap.copyOf(attributes));
@@ -202,12 +209,12 @@ public class StyleableXmlResourceValue implements XmlResourceValue {
/**
* Combines this instance with another {@link StyleableXmlResourceValue}.
*
- * Defining two Styleables (undocumented in the official Android Docs) with the same
- * {@link FullyQualifiedName} results in a single Styleable containing a union of all the
- * attribute references.
+ * <p>Defining two Styleables (undocumented in the official Android Docs) with the same {@link
+ * FullyQualifiedName} results in a single Styleable containing a union of all the attribute
+ * references.
*
- * @param value Another {@link StyleableXmlResourceValue} with the same
- * {@link FullyQualifiedName}.
+ * @param value Another {@link StyleableXmlResourceValue} with the same {@link
+ * FullyQualifiedName}.
* @return {@link StyleableXmlResourceValue} containing a sorted union of the attribute
* references.
* @throws IllegalArgumentException if value is not an {@link StyleableXmlResourceValue}.