Add support for building split APKs
Build multiple APKs, each containing a disjoint subset of configurations. These can then be loaded into the device AssetManager and should operate as if they were never split. Use the idea of building multiple sets of files, where each set represents an APK. An ApkBuilder can place files in a set based on its configuration, but you can actually add directly to a set, in the case of the resources.arsc and generated AndroidManifest.xml for splits. Change-Id: Ic65d3f0ac1bbd290185695b9971d425c85ab1de3
This commit is contained in:
@@ -2452,15 +2452,19 @@ String8 ResTable_config::toString() const {
|
||||
|
||||
if (mcc != 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
res.appendFormat("%dmcc", dtohs(mcc));
|
||||
res.appendFormat("mcc%d", dtohs(mcc));
|
||||
}
|
||||
if (mnc != 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
res.appendFormat("%dmnc", dtohs(mnc));
|
||||
res.appendFormat("mnc%d", dtohs(mnc));
|
||||
}
|
||||
|
||||
char localeStr[RESTABLE_MAX_LOCALE_LEN];
|
||||
getBcp47Locale(localeStr);
|
||||
res.append(localeStr);
|
||||
if (strlen(localeStr) > 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
res.append(localeStr);
|
||||
}
|
||||
|
||||
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
@@ -2627,6 +2631,20 @@ String8 ResTable_config::toString() const {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
switch (inputFlags&MASK_KEYSHIDDEN) {
|
||||
case ResTable_config::KEYSHIDDEN_NO:
|
||||
res.append("keysexposed");
|
||||
break;
|
||||
case ResTable_config::KEYSHIDDEN_YES:
|
||||
res.append("keyshidden");
|
||||
break;
|
||||
case ResTable_config::KEYSHIDDEN_SOFT:
|
||||
res.append("keyssoft");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyboard != KEYBOARD_ANY) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
switch (keyboard) {
|
||||
@@ -2644,17 +2662,18 @@ String8 ResTable_config::toString() const {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
|
||||
if ((inputFlags&MASK_NAVHIDDEN) != 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
switch (inputFlags&MASK_KEYSHIDDEN) {
|
||||
case ResTable_config::KEYSHIDDEN_NO:
|
||||
res.append("keysexposed");
|
||||
switch (inputFlags&MASK_NAVHIDDEN) {
|
||||
case ResTable_config::NAVHIDDEN_NO:
|
||||
res.append("navexposed");
|
||||
break;
|
||||
case ResTable_config::KEYSHIDDEN_YES:
|
||||
res.append("keyshidden");
|
||||
case ResTable_config::NAVHIDDEN_YES:
|
||||
res.append("navhidden");
|
||||
break;
|
||||
case ResTable_config::KEYSHIDDEN_SOFT:
|
||||
res.append("keyssoft");
|
||||
default:
|
||||
res.appendFormat("inputFlagsNavHidden=%d",
|
||||
dtohs(inputFlags&MASK_NAVHIDDEN));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2678,21 +2697,6 @@ String8 ResTable_config::toString() const {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((inputFlags&MASK_NAVHIDDEN) != 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
switch (inputFlags&MASK_NAVHIDDEN) {
|
||||
case ResTable_config::NAVHIDDEN_NO:
|
||||
res.append("navsexposed");
|
||||
break;
|
||||
case ResTable_config::NAVHIDDEN_YES:
|
||||
res.append("navhidden");
|
||||
break;
|
||||
default:
|
||||
res.appendFormat("inputFlagsNavHidden=%d",
|
||||
dtohs(inputFlags&MASK_NAVHIDDEN));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (screenSize != 0) {
|
||||
if (res.size() > 0) res.append("-");
|
||||
res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight));
|
||||
@@ -5503,7 +5507,25 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
|
||||
if (package == NULL) {
|
||||
return (mError=NO_MEMORY);
|
||||
}
|
||||
|
||||
|
||||
if (idmap_id == 0) {
|
||||
err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
|
||||
header->dataEnd-(base+dtohl(pkg->typeStrings)));
|
||||
if (err != NO_ERROR) {
|
||||
delete group;
|
||||
delete package;
|
||||
return (mError=err);
|
||||
}
|
||||
|
||||
err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
|
||||
header->dataEnd-(base+dtohl(pkg->keyStrings)));
|
||||
if (err != NO_ERROR) {
|
||||
delete group;
|
||||
delete package;
|
||||
return (mError=err);
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0) {
|
||||
// This is a library so assign an ID
|
||||
id = mNextPackageId++;
|
||||
@@ -5521,21 +5543,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
|
||||
return (mError=NO_MEMORY);
|
||||
}
|
||||
|
||||
err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
|
||||
header->dataEnd-(base+dtohl(pkg->typeStrings)));
|
||||
if (err != NO_ERROR) {
|
||||
delete group;
|
||||
delete package;
|
||||
return (mError=err);
|
||||
}
|
||||
err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
|
||||
header->dataEnd-(base+dtohl(pkg->keyStrings)));
|
||||
if (err != NO_ERROR) {
|
||||
delete group;
|
||||
delete package;
|
||||
return (mError=err);
|
||||
}
|
||||
|
||||
//printf("Adding new package id %d at index %d\n", id, idx);
|
||||
err = mPackageGroups.add(group);
|
||||
if (err < NO_ERROR) {
|
||||
|
||||
28
tests/Split/Android.mk
Normal file
28
tests/Split/Android.mk
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
LOCAL_PACKAGE_NAME := Split
|
||||
|
||||
LOCAL_AAPT_FLAGS := --split fr,de
|
||||
LOCAL_AAPT_FLAGS += -v
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
27
tests/Split/AndroidManifest.xml
Normal file
27
tests/Split/AndroidManifest.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.example.split">
|
||||
<application android:label="@string/app_title">
|
||||
<activity android:name="ActivityMain">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
1
tests/Split/assets/blah.txt
Normal file
1
tests/Split/assets/blah.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is some useful info.
|
||||
19
tests/Split/assets/statement.xml
Normal file
19
tests/Split/assets/statement.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<statement>
|
||||
<value>Hello</value>
|
||||
</statement>
|
||||
23
tests/Split/res/layout-fr-sw600dp/main.xml
Normal file
23
tests/Split/res/layout-fr-sw600dp/main.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</FrameLayout>
|
||||
19
tests/Split/res/layout/main.xml
Normal file
19
tests/Split/res/layout/main.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
19
tests/Split/res/values-de/values.xml
Normal file
19
tests/Split/res/values-de/values.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="test">Achtung!</string>
|
||||
</resources>
|
||||
25
tests/Split/res/values-fr/values.xml
Normal file
25
tests/Split/res/values-fr/values.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_title">APK Divisé</string>
|
||||
<string name="test">Bonjour, Monde!</string>
|
||||
<string name="blah">Bleh..</string>
|
||||
<string-array name="lotsofstrings">
|
||||
<item>Hé là</item>
|
||||
<item>Au revoir</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
19
tests/Split/res/values-sw600dp/values.xml
Normal file
19
tests/Split/res/values-sw600dp/values.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<dimen name="width">230dp</dimen>
|
||||
</resources>
|
||||
47
tests/Split/res/values/values.xml
Normal file
47
tests/Split/res/values/values.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_title">Split APK</string>
|
||||
<string name="test">Hello, World!</string>
|
||||
<string name="boom">Boom!</string>
|
||||
<string name="blah">Blah...</string>
|
||||
<string-array name="lotsofstrings">
|
||||
<item>Hello there</item>
|
||||
<item>Good bye</item>
|
||||
</string-array>
|
||||
|
||||
<plurals name="plur">
|
||||
<item quantity="zero">I no haz :(</item>
|
||||
<item quantity="one">I haz 1!1! :)</item>
|
||||
<item quantity="many">I haz ALL!</item>
|
||||
</plurals>
|
||||
|
||||
<bool name="que">true</bool>
|
||||
<color name="green">#00FF00</color>
|
||||
<dimen name="width">23dp</dimen>
|
||||
<item type="id" name="identifier" />
|
||||
<integer name="number">123</integer>
|
||||
<integer-array name="numList">
|
||||
<item>1234</item>
|
||||
</integer-array>
|
||||
|
||||
<array name="ary">
|
||||
<item>@string/test</item>
|
||||
<item>@string/boom</item>
|
||||
<item>25dp</item>
|
||||
</array>
|
||||
</resources>
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
package com.android.example.split;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ActivityMain extends Activity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
TextView text = new TextView(this);
|
||||
text.setText(R.string.test);
|
||||
setContentView(text);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,22 +6,24 @@
|
||||
#ifndef __AAPT_ASSETS_H
|
||||
#define __AAPT_ASSETS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <androidfw/AssetManager.h>
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <set>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/SortedVector.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#include "AaptConfig.h"
|
||||
#include "Bundle.h"
|
||||
#include "ConfigDescription.h"
|
||||
#include "SourcePos.h"
|
||||
#include "ZipFile.h"
|
||||
|
||||
#include "Bundle.h"
|
||||
#include "SourcePos.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
|
||||
extern const char * const gDefaultIgnoreAssets;
|
||||
extern const char * gUserIgnoreAssets;
|
||||
|
||||
@@ -82,9 +84,6 @@ struct AaptLocaleValue {
|
||||
return memcmp(this, &other, sizeof(AaptLocaleValue));
|
||||
}
|
||||
|
||||
static void splitAndLowerCase(const char* const chars, Vector<String8>* parts,
|
||||
const char separator);
|
||||
|
||||
inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
|
||||
@@ -98,31 +97,6 @@ private:
|
||||
void setVariant(const char* variant);
|
||||
};
|
||||
|
||||
struct AxisValue {
|
||||
// Used for all axes except AXIS_LOCALE, which is represented
|
||||
// as a AaptLocaleValue value.
|
||||
int intValue;
|
||||
AaptLocaleValue localeValue;
|
||||
|
||||
AxisValue() : intValue(0) {
|
||||
}
|
||||
|
||||
inline int compare(const AxisValue &other) const {
|
||||
if (intValue != other.intValue) {
|
||||
return intValue - other.intValue;
|
||||
}
|
||||
|
||||
return localeValue.compare(other.localeValue);
|
||||
}
|
||||
|
||||
inline bool operator<(const AxisValue& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const AxisValue& o) const { return compare(o) == 0; }
|
||||
inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; }
|
||||
inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; }
|
||||
inline bool operator>(const AxisValue& o) const { return compare(o) > 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure contains a specific variation of a single file out
|
||||
* of all the variations it can have that we can have.
|
||||
@@ -130,23 +104,11 @@ struct AxisValue {
|
||||
struct AaptGroupEntry
|
||||
{
|
||||
public:
|
||||
AaptGroupEntry() : mParamsChanged(true) {
|
||||
memset(&mParams, 0, sizeof(ResTable_config));
|
||||
}
|
||||
|
||||
bool initFromDirName(const char* dir, String8* resType);
|
||||
|
||||
static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value);
|
||||
|
||||
static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis);
|
||||
|
||||
static bool configSameExcept(const ResTable_config& config,
|
||||
const ResTable_config& otherConfig, int axis);
|
||||
|
||||
int compare(const AaptGroupEntry& o) const;
|
||||
|
||||
const ResTable_config toParams() const;
|
||||
inline const ConfigDescription& toParams() const { return mParams; }
|
||||
|
||||
inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); }
|
||||
inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
|
||||
@@ -154,56 +116,13 @@ public:
|
||||
inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
|
||||
inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
|
||||
|
||||
String8 toString() const;
|
||||
String8 toString() const { return mParams.toString(); }
|
||||
String8 toDirName(const String8& resType) const;
|
||||
|
||||
const String8& getVersionString() const { return version; }
|
||||
const String8 getVersionString() const { return AaptConfig::getVersion(mParams); }
|
||||
|
||||
private:
|
||||
static bool getMccName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getMncName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getOrientationName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getUiModeNightName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getDensityName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getTouchscreenName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getNavigationName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getNavHiddenName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
|
||||
static bool getVersionName(const char* name, ResTable_config* out = NULL);
|
||||
|
||||
String8 mcc;
|
||||
String8 mnc;
|
||||
AaptLocaleValue locale;
|
||||
String8 vendor;
|
||||
String8 smallestScreenWidthDp;
|
||||
String8 screenWidthDp;
|
||||
String8 screenHeightDp;
|
||||
String8 screenLayoutSize;
|
||||
String8 screenLayoutLong;
|
||||
String8 orientation;
|
||||
String8 uiModeType;
|
||||
String8 uiModeNight;
|
||||
String8 density;
|
||||
String8 touchscreen;
|
||||
String8 keysHidden;
|
||||
String8 keyboard;
|
||||
String8 navHidden;
|
||||
String8 navigation;
|
||||
String8 screenSize;
|
||||
String8 layoutDirection;
|
||||
String8 version;
|
||||
|
||||
mutable bool mParamsChanged;
|
||||
mutable ResTable_config mParams;
|
||||
ConfigDescription mParams;
|
||||
};
|
||||
|
||||
inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
|
||||
|
||||
790
tools/aapt/AaptConfig.cpp
Normal file
790
tools/aapt/AaptConfig.cpp
Normal file
@@ -0,0 +1,790 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <androidfw/ResourceTypes.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "AaptConfig.h"
|
||||
#include "AaptAssets.h"
|
||||
#include "AaptUtil.h"
|
||||
#include "ResourceFilter.h"
|
||||
|
||||
using android::String8;
|
||||
using android::Vector;
|
||||
using android::ResTable_config;
|
||||
|
||||
namespace AaptConfig {
|
||||
|
||||
static const char* kWildcardName = "any";
|
||||
|
||||
bool parse(const String8& str, ConfigDescription* out) {
|
||||
Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
|
||||
|
||||
ConfigDescription config;
|
||||
AaptLocaleValue locale;
|
||||
ssize_t index = 0;
|
||||
ssize_t localeIndex = 0;
|
||||
const ssize_t N = parts.size();
|
||||
const char* part = parts[index].string();
|
||||
|
||||
if (str.length() == 0) {
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (parseMcc(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseMnc(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
// Locale spans a few '-' separators, so we let it
|
||||
// control the index.
|
||||
localeIndex = locale.initFromDirName(parts, index);
|
||||
if (localeIndex < 0) {
|
||||
return false;
|
||||
} else if (localeIndex > index) {
|
||||
locale.writeTo(&config);
|
||||
index = localeIndex;
|
||||
if (index >= N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseLayoutDirection(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseSmallestScreenWidthDp(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseScreenWidthDp(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseScreenHeightDp(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseScreenLayoutSize(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseScreenLayoutLong(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseOrientation(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseUiModeType(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseUiModeNight(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseDensity(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseTouchscreen(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseKeysHidden(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseKeyboard(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseNavHidden(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseNavigation(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseScreenSize(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
if (parseVersion(part, &config)) {
|
||||
index++;
|
||||
if (index == N) {
|
||||
goto success;
|
||||
}
|
||||
part = parts[index].string();
|
||||
}
|
||||
|
||||
// Unrecognized.
|
||||
return false;
|
||||
|
||||
success:
|
||||
if (out != NULL) {
|
||||
applyVersionForCompatibility(&config);
|
||||
*out = config;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
|
||||
Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
|
||||
const size_t N = parts.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
ConfigDescription config;
|
||||
if (!parse(parts[i], &config)) {
|
||||
return false;
|
||||
}
|
||||
outSet->insert(config);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void applyVersionForCompatibility(ConfigDescription* config) {
|
||||
if (config == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t minSdk = 0;
|
||||
if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
|
||||
|| config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
|
||||
|| config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
|
||||
minSdk = SDK_HONEYCOMB_MR2;
|
||||
} else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
|
||||
!= ResTable_config::UI_MODE_TYPE_ANY
|
||||
|| (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
|
||||
!= ResTable_config::UI_MODE_NIGHT_ANY) {
|
||||
minSdk = SDK_FROYO;
|
||||
} else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
|
||||
!= ResTable_config::SCREENSIZE_ANY
|
||||
|| (config->screenLayout & ResTable_config::MASK_SCREENLONG)
|
||||
!= ResTable_config::SCREENLONG_ANY
|
||||
|| config->density != ResTable_config::DENSITY_DEFAULT) {
|
||||
minSdk = SDK_DONUT;
|
||||
}
|
||||
|
||||
if (minSdk > config->sdkVersion) {
|
||||
config->sdkVersion = minSdk;
|
||||
}
|
||||
}
|
||||
|
||||
bool parseMcc(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->mcc = 0;
|
||||
return true;
|
||||
}
|
||||
const char* c = name;
|
||||
if (tolower(*c) != 'm') return false;
|
||||
c++;
|
||||
if (tolower(*c) != 'c') return false;
|
||||
c++;
|
||||
if (tolower(*c) != 'c') return false;
|
||||
c++;
|
||||
|
||||
const char* val = c;
|
||||
|
||||
while (*c >= '0' && *c <= '9') {
|
||||
c++;
|
||||
}
|
||||
if (*c != 0) return false;
|
||||
if (c-val != 3) return false;
|
||||
|
||||
int d = atoi(val);
|
||||
if (d != 0) {
|
||||
if (out) out->mcc = d;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseMnc(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->mcc = 0;
|
||||
return true;
|
||||
}
|
||||
const char* c = name;
|
||||
if (tolower(*c) != 'm') return false;
|
||||
c++;
|
||||
if (tolower(*c) != 'n') return false;
|
||||
c++;
|
||||
if (tolower(*c) != 'c') return false;
|
||||
c++;
|
||||
|
||||
const char* val = c;
|
||||
|
||||
while (*c >= '0' && *c <= '9') {
|
||||
c++;
|
||||
}
|
||||
if (*c != 0) return false;
|
||||
if (c-val == 0 || c-val > 3) return false;
|
||||
|
||||
if (out) {
|
||||
out->mnc = atoi(val);
|
||||
if (out->mnc == 0) {
|
||||
out->mnc = ACONFIGURATION_MNC_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseLayoutDirection(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
|
||||
| ResTable_config::LAYOUTDIR_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "ldltr") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
|
||||
| ResTable_config::LAYOUTDIR_LTR;
|
||||
return true;
|
||||
} else if (strcmp(name, "ldrtl") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
|
||||
| ResTable_config::LAYOUTDIR_RTL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
|
||||
| ResTable_config::SCREENSIZE_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "small") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
|
||||
| ResTable_config::SCREENSIZE_SMALL;
|
||||
return true;
|
||||
} else if (strcmp(name, "normal") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
|
||||
| ResTable_config::SCREENSIZE_NORMAL;
|
||||
return true;
|
||||
} else if (strcmp(name, "large") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
|
||||
| ResTable_config::SCREENSIZE_LARGE;
|
||||
return true;
|
||||
} else if (strcmp(name, "xlarge") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
|
||||
| ResTable_config::SCREENSIZE_XLARGE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENLONG)
|
||||
| ResTable_config::SCREENLONG_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "long") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENLONG)
|
||||
| ResTable_config::SCREENLONG_YES;
|
||||
return true;
|
||||
} else if (strcmp(name, "notlong") == 0) {
|
||||
if (out) out->screenLayout =
|
||||
(out->screenLayout&~ResTable_config::MASK_SCREENLONG)
|
||||
| ResTable_config::SCREENLONG_NO;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseOrientation(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->orientation = out->ORIENTATION_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "port") == 0) {
|
||||
if (out) out->orientation = out->ORIENTATION_PORT;
|
||||
return true;
|
||||
} else if (strcmp(name, "land") == 0) {
|
||||
if (out) out->orientation = out->ORIENTATION_LAND;
|
||||
return true;
|
||||
} else if (strcmp(name, "square") == 0) {
|
||||
if (out) out->orientation = out->ORIENTATION_SQUARE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseUiModeType(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
|
||||
| ResTable_config::UI_MODE_TYPE_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "desk") == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
|
||||
| ResTable_config::UI_MODE_TYPE_DESK;
|
||||
return true;
|
||||
} else if (strcmp(name, "car") == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
|
||||
| ResTable_config::UI_MODE_TYPE_CAR;
|
||||
return true;
|
||||
} else if (strcmp(name, "television") == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
|
||||
| ResTable_config::UI_MODE_TYPE_TELEVISION;
|
||||
return true;
|
||||
} else if (strcmp(name, "appliance") == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
|
||||
| ResTable_config::UI_MODE_TYPE_APPLIANCE;
|
||||
return true;
|
||||
} else if (strcmp(name, "watch") == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
|
||||
| ResTable_config::UI_MODE_TYPE_WATCH;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseUiModeNight(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
|
||||
| ResTable_config::UI_MODE_NIGHT_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "night") == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
|
||||
| ResTable_config::UI_MODE_NIGHT_YES;
|
||||
return true;
|
||||
} else if (strcmp(name, "notnight") == 0) {
|
||||
if (out) out->uiMode =
|
||||
(out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
|
||||
| ResTable_config::UI_MODE_NIGHT_NO;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseDensity(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_DEFAULT;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "nodpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_NONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "ldpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_LOW;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "mdpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_MEDIUM;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "tvdpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_TV;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "hdpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_HIGH;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "xhdpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_XHIGH;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "xxhdpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_XXHIGH;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(name, "xxxhdpi") == 0) {
|
||||
if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
|
||||
return true;
|
||||
}
|
||||
|
||||
char* c = (char*)name;
|
||||
while (*c >= '0' && *c <= '9') {
|
||||
c++;
|
||||
}
|
||||
|
||||
// check that we have 'dpi' after the last digit.
|
||||
if (toupper(c[0]) != 'D' ||
|
||||
toupper(c[1]) != 'P' ||
|
||||
toupper(c[2]) != 'I' ||
|
||||
c[3] != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// temporarily replace the first letter with \0 to
|
||||
// use atoi.
|
||||
char tmp = c[0];
|
||||
c[0] = '\0';
|
||||
|
||||
int d = atoi(name);
|
||||
c[0] = tmp;
|
||||
|
||||
if (d != 0) {
|
||||
if (out) out->density = d;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseTouchscreen(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "notouch") == 0) {
|
||||
if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
|
||||
return true;
|
||||
} else if (strcmp(name, "stylus") == 0) {
|
||||
if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
|
||||
return true;
|
||||
} else if (strcmp(name, "finger") == 0) {
|
||||
if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseKeysHidden(const char* name, ResTable_config* out) {
|
||||
uint8_t mask = 0;
|
||||
uint8_t value = 0;
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
mask = ResTable_config::MASK_KEYSHIDDEN;
|
||||
value = ResTable_config::KEYSHIDDEN_ANY;
|
||||
} else if (strcmp(name, "keysexposed") == 0) {
|
||||
mask = ResTable_config::MASK_KEYSHIDDEN;
|
||||
value = ResTable_config::KEYSHIDDEN_NO;
|
||||
} else if (strcmp(name, "keyshidden") == 0) {
|
||||
mask = ResTable_config::MASK_KEYSHIDDEN;
|
||||
value = ResTable_config::KEYSHIDDEN_YES;
|
||||
} else if (strcmp(name, "keyssoft") == 0) {
|
||||
mask = ResTable_config::MASK_KEYSHIDDEN;
|
||||
value = ResTable_config::KEYSHIDDEN_SOFT;
|
||||
}
|
||||
|
||||
if (mask != 0) {
|
||||
if (out) out->inputFlags = (out->inputFlags&~mask) | value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseKeyboard(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->keyboard = out->KEYBOARD_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "nokeys") == 0) {
|
||||
if (out) out->keyboard = out->KEYBOARD_NOKEYS;
|
||||
return true;
|
||||
} else if (strcmp(name, "qwerty") == 0) {
|
||||
if (out) out->keyboard = out->KEYBOARD_QWERTY;
|
||||
return true;
|
||||
} else if (strcmp(name, "12key") == 0) {
|
||||
if (out) out->keyboard = out->KEYBOARD_12KEY;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseNavHidden(const char* name, ResTable_config* out) {
|
||||
uint8_t mask = 0;
|
||||
uint8_t value = 0;
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
mask = ResTable_config::MASK_NAVHIDDEN;
|
||||
value = ResTable_config::NAVHIDDEN_ANY;
|
||||
} else if (strcmp(name, "navexposed") == 0) {
|
||||
mask = ResTable_config::MASK_NAVHIDDEN;
|
||||
value = ResTable_config::NAVHIDDEN_NO;
|
||||
} else if (strcmp(name, "navhidden") == 0) {
|
||||
mask = ResTable_config::MASK_NAVHIDDEN;
|
||||
value = ResTable_config::NAVHIDDEN_YES;
|
||||
}
|
||||
|
||||
if (mask != 0) {
|
||||
if (out) out->inputFlags = (out->inputFlags&~mask) | value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseNavigation(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) out->navigation = out->NAVIGATION_ANY;
|
||||
return true;
|
||||
} else if (strcmp(name, "nonav") == 0) {
|
||||
if (out) out->navigation = out->NAVIGATION_NONAV;
|
||||
return true;
|
||||
} else if (strcmp(name, "dpad") == 0) {
|
||||
if (out) out->navigation = out->NAVIGATION_DPAD;
|
||||
return true;
|
||||
} else if (strcmp(name, "trackball") == 0) {
|
||||
if (out) out->navigation = out->NAVIGATION_TRACKBALL;
|
||||
return true;
|
||||
} else if (strcmp(name, "wheel") == 0) {
|
||||
if (out) out->navigation = out->NAVIGATION_WHEEL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseScreenSize(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) {
|
||||
out->screenWidth = out->SCREENWIDTH_ANY;
|
||||
out->screenHeight = out->SCREENHEIGHT_ANY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* x = name;
|
||||
while (*x >= '0' && *x <= '9') x++;
|
||||
if (x == name || *x != 'x') return false;
|
||||
String8 xName(name, x-name);
|
||||
x++;
|
||||
|
||||
const char* y = x;
|
||||
while (*y >= '0' && *y <= '9') y++;
|
||||
if (y == name || *y != 0) return false;
|
||||
String8 yName(x, y-x);
|
||||
|
||||
uint16_t w = (uint16_t)atoi(xName.string());
|
||||
uint16_t h = (uint16_t)atoi(yName.string());
|
||||
if (w < h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out) {
|
||||
out->screenWidth = w;
|
||||
out->screenHeight = h;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) {
|
||||
out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*name != 's') return false;
|
||||
name++;
|
||||
if (*name != 'w') return false;
|
||||
name++;
|
||||
const char* x = name;
|
||||
while (*x >= '0' && *x <= '9') x++;
|
||||
if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
|
||||
String8 xName(name, x-name);
|
||||
|
||||
if (out) {
|
||||
out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseScreenWidthDp(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) {
|
||||
out->screenWidthDp = out->SCREENWIDTH_ANY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*name != 'w') return false;
|
||||
name++;
|
||||
const char* x = name;
|
||||
while (*x >= '0' && *x <= '9') x++;
|
||||
if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
|
||||
String8 xName(name, x-name);
|
||||
|
||||
if (out) {
|
||||
out->screenWidthDp = (uint16_t)atoi(xName.string());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseScreenHeightDp(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) {
|
||||
out->screenHeightDp = out->SCREENWIDTH_ANY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*name != 'h') return false;
|
||||
name++;
|
||||
const char* x = name;
|
||||
while (*x >= '0' && *x <= '9') x++;
|
||||
if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
|
||||
String8 xName(name, x-name);
|
||||
|
||||
if (out) {
|
||||
out->screenHeightDp = (uint16_t)atoi(xName.string());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseVersion(const char* name, ResTable_config* out) {
|
||||
if (strcmp(name, kWildcardName) == 0) {
|
||||
if (out) {
|
||||
out->sdkVersion = out->SDKVERSION_ANY;
|
||||
out->minorVersion = out->MINORVERSION_ANY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*name != 'v') {
|
||||
return false;
|
||||
}
|
||||
|
||||
name++;
|
||||
const char* s = name;
|
||||
while (*s >= '0' && *s <= '9') s++;
|
||||
if (s == name || *s != 0) return false;
|
||||
String8 sdkName(name, s-name);
|
||||
|
||||
if (out) {
|
||||
out->sdkVersion = (uint16_t)atoi(sdkName.string());
|
||||
out->minorVersion = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String8 getVersion(const ResTable_config& config) {
|
||||
return String8::format("v%u", config.sdkVersion);
|
||||
}
|
||||
|
||||
bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
|
||||
return a.diff(b) == axisMask;
|
||||
}
|
||||
|
||||
} // namespace AaptConfig
|
||||
85
tools/aapt/AaptConfig.h
Normal file
85
tools/aapt/AaptConfig.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_CONFIG_H
|
||||
#define __AAPT_CONFIG_H
|
||||
|
||||
#include <set>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include "ConfigDescription.h"
|
||||
|
||||
/**
|
||||
* Utility methods for dealing with configurations.
|
||||
*/
|
||||
namespace AaptConfig {
|
||||
|
||||
/**
|
||||
* Parse a string of the form 'fr-sw600dp-land' and fill in the
|
||||
* given ResTable_config with resulting configuration parameters.
|
||||
*
|
||||
* The resulting configuration has the appropriate sdkVersion defined
|
||||
* for backwards compatibility.
|
||||
*/
|
||||
bool parse(const android::String8& str, ConfigDescription* out = NULL);
|
||||
|
||||
/**
|
||||
* Parse a comma separated list of configuration strings. Duplicate configurations
|
||||
* will be removed.
|
||||
*
|
||||
* Example input: "fr,de-land,fr-sw600dp-land"
|
||||
*/
|
||||
bool parseCommaSeparatedList(const android::String8& str, std::set<ConfigDescription>* outSet);
|
||||
|
||||
/**
|
||||
* If the configuration uses an axis that was added after
|
||||
* the original Android release, make sure the SDK version
|
||||
* is set accordingly.
|
||||
*/
|
||||
void applyVersionForCompatibility(ConfigDescription* config);
|
||||
|
||||
// Individual axis
|
||||
bool parseMcc(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseMnc(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseLayoutDirection(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseSmallestScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseOrientation(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseUiModeType(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseDensity(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseTouchscreen(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseKeysHidden(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseKeyboard(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseNavHidden(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseNavigation(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseScreenSize(const char* str, android::ResTable_config* out = NULL);
|
||||
bool parseVersion(const char* str, android::ResTable_config* out = NULL);
|
||||
|
||||
android::String8 getVersion(const android::ResTable_config& config);
|
||||
|
||||
/**
|
||||
* Returns true if the two configurations only differ by the specified axis.
|
||||
* The axis mask is a bitmask of CONFIG_* constants.
|
||||
*/
|
||||
bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask);
|
||||
|
||||
} // namespace AaptConfig
|
||||
|
||||
#endif // __AAPT_CONFIG_H
|
||||
64
tools/aapt/AaptUtil.cpp
Normal file
64
tools/aapt/AaptUtil.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 "AaptUtil.h"
|
||||
|
||||
using android::Vector;
|
||||
using android::String8;
|
||||
|
||||
namespace AaptUtil {
|
||||
|
||||
Vector<String8> split(const String8& str, const char sep) {
|
||||
Vector<String8> parts;
|
||||
const char* p = str.string();
|
||||
const char* q;
|
||||
|
||||
while (true) {
|
||||
q = strchr(p, sep);
|
||||
if (q == NULL) {
|
||||
parts.add(String8(p, strlen(p)));
|
||||
return parts;
|
||||
}
|
||||
|
||||
parts.add(String8(p, q-p));
|
||||
p = q + 1;
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
Vector<String8> splitAndLowerCase(const String8& str, const char sep) {
|
||||
Vector<String8> parts;
|
||||
const char* p = str.string();
|
||||
const char* q;
|
||||
|
||||
while (true) {
|
||||
q = strchr(p, sep);
|
||||
if (q == NULL) {
|
||||
String8 val(p, strlen(p));
|
||||
val.toLower();
|
||||
parts.add(val);
|
||||
return parts;
|
||||
}
|
||||
|
||||
String8 val(p, q-p);
|
||||
val.toLower();
|
||||
parts.add(val);
|
||||
p = q + 1;
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
} // namespace AaptUtil
|
||||
30
tools/aapt/AaptUtil.h
Normal file
30
tools/aapt/AaptUtil.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_UTIL_H
|
||||
#define __AAPT_UTIL_H
|
||||
|
||||
#include <utils/String8.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
namespace AaptUtil {
|
||||
|
||||
android::Vector<android::String8> split(const android::String8& str, const char sep);
|
||||
android::Vector<android::String8> splitAndLowerCase(const android::String8& str, const char sep);
|
||||
|
||||
} // namespace AaptUtil
|
||||
|
||||
#endif // __AAPT_UTIL_H
|
||||
@@ -1,104 +1,168 @@
|
||||
#
|
||||
# Copyright 2006 The Android Open Source Project
|
||||
#
|
||||
# Android Asset Packaging Tool
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
# This tool is prebuilt if we're doing an app-only build.
|
||||
ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
|
||||
|
||||
# ==========================================================
|
||||
# Setup some common variables for the different build
|
||||
# targets here.
|
||||
# ==========================================================
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
aapt_src_files := \
|
||||
AaptAssets.cpp \
|
||||
Command.cpp \
|
||||
CrunchCache.cpp \
|
||||
FileFinder.cpp \
|
||||
Main.cpp \
|
||||
Package.cpp \
|
||||
StringPool.cpp \
|
||||
XMLNode.cpp \
|
||||
ResourceFilter.cpp \
|
||||
ResourceIdCache.cpp \
|
||||
ResourceTable.cpp \
|
||||
Images.cpp \
|
||||
Resource.cpp \
|
||||
aaptMain := Main.cpp
|
||||
aaptSources := \
|
||||
AaptAssets.cpp \
|
||||
AaptConfig.cpp \
|
||||
AaptUtil.cpp \
|
||||
ApkBuilder.cpp \
|
||||
Command.cpp \
|
||||
CrunchCache.cpp \
|
||||
FileFinder.cpp \
|
||||
Package.cpp \
|
||||
StringPool.cpp \
|
||||
XMLNode.cpp \
|
||||
ResourceFilter.cpp \
|
||||
ResourceIdCache.cpp \
|
||||
ResourceTable.cpp \
|
||||
Images.cpp \
|
||||
Resource.cpp \
|
||||
pseudolocalize.cpp \
|
||||
SourcePos.cpp \
|
||||
WorkQueue.cpp \
|
||||
WorkQueue.cpp \
|
||||
ZipEntry.cpp \
|
||||
ZipFile.cpp \
|
||||
qsort_r_compat.c
|
||||
qsort_r_compat.c
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
aaptTests := \
|
||||
tests/AaptConfig_test.cpp \
|
||||
tests/AaptGroupEntry_test.cpp \
|
||||
tests/ResourceFilter_test.cpp
|
||||
|
||||
LOCAL_SRC_FILES := $(aapt_src_files)
|
||||
aaptCIncludes := \
|
||||
external/libpng \
|
||||
external/zlib
|
||||
|
||||
LOCAL_CFLAGS += -Wno-format-y2k
|
||||
ifeq (darwin,$(HOST_OS))
|
||||
LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
|
||||
endif
|
||||
|
||||
LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
|
||||
|
||||
LOCAL_C_INCLUDES += external/libpng
|
||||
LOCAL_C_INCLUDES += external/zlib
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libandroidfw \
|
||||
libutils \
|
||||
libcutils \
|
||||
libexpat \
|
||||
libpng \
|
||||
liblog \
|
||||
libziparchive-host
|
||||
aaptHostLdLibs :=
|
||||
aaptHostStaticLibs := \
|
||||
libandroidfw \
|
||||
libpng \
|
||||
liblog \
|
||||
libutils \
|
||||
libcutils \
|
||||
libexpat \
|
||||
libziparchive-host
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
LOCAL_LDLIBS += -lrt -ldl -lpthread
|
||||
aaptHostLdLibs += -lrt -ldl -lpthread
|
||||
endif
|
||||
|
||||
# Statically link libz for MinGW (Win SDK under Linux),
|
||||
# and dynamically link for all others.
|
||||
ifneq ($(strip $(USE_MINGW)),)
|
||||
LOCAL_STATIC_LIBRARIES += libz
|
||||
aaptHostStaticLibs += libz
|
||||
else
|
||||
LOCAL_LDLIBS += -lz
|
||||
aaptHostLdLibs += -lz
|
||||
endif
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# Build the host static library: libaapt
|
||||
# ==========================================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libaapt
|
||||
|
||||
LOCAL_SRC_FILES := $(aaptSources)
|
||||
LOCAL_C_INCLUDES += $(aaptCIncludes)
|
||||
|
||||
LOCAL_CFLAGS += -Wno-format-y2k
|
||||
LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
|
||||
ifeq (darwin,$(HOST_OS))
|
||||
LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
|
||||
endif
|
||||
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# Build the host executable: aapt
|
||||
# ==========================================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := aapt
|
||||
|
||||
LOCAL_SRC_FILES := $(aaptMain)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += \
|
||||
libaapt \
|
||||
$(aaptHostStaticLibs)
|
||||
LOCAL_LDLIBS += $(aaptHostLdLibs)
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
# aapt for running on the device
|
||||
# =========================================================
|
||||
|
||||
# ==========================================================
|
||||
# Build the host tests: libaapt_tests
|
||||
# ==========================================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libaapt_tests
|
||||
|
||||
LOCAL_SRC_FILES += $(aaptTests)
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += \
|
||||
libaapt \
|
||||
$(aaptHostStaticLibs)
|
||||
LOCAL_LDLIBS += $(aaptHostLdLibs)
|
||||
|
||||
include $(BUILD_HOST_NATIVE_TEST)
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# Build the device executable: aapt
|
||||
# ==========================================================
|
||||
ifneq ($(SDK_ONLY),true)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(aapt_src_files)
|
||||
|
||||
LOCAL_MODULE := aapt
|
||||
|
||||
LOCAL_C_INCLUDES += bionic
|
||||
LOCAL_C_INCLUDES += bionic/libstdc++/include
|
||||
LOCAL_C_INCLUDES += external/stlport/stlport
|
||||
LOCAL_C_INCLUDES += external/libpng
|
||||
LOCAL_C_INCLUDES += external/zlib
|
||||
|
||||
LOCAL_CFLAGS += -Wno-non-virtual-dtor
|
||||
LOCAL_SRC_FILES := $(aaptSources) $(aaptMain)
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(aaptCIncludes) \
|
||||
bionic \
|
||||
external/stlport/stlport
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libandroidfw \
|
||||
libutils \
|
||||
libcutils \
|
||||
libpng \
|
||||
liblog \
|
||||
libz
|
||||
libandroidfw \
|
||||
libutils \
|
||||
libcutils \
|
||||
libpng \
|
||||
liblog \
|
||||
libz
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libstlport_static \
|
||||
libexpat_static
|
||||
libstlport_static \
|
||||
libexpat_static
|
||||
|
||||
LOCAL_CPPFLAGS += -Wno-non-virtual-dtor
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
endif
|
||||
|
||||
endif # Not SDK_ONLY
|
||||
|
||||
endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
|
||||
|
||||
111
tools/aapt/ApkBuilder.cpp
Normal file
111
tools/aapt/ApkBuilder.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 "AaptAssets.h"
|
||||
#include "ApkBuilder.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter)
|
||||
: mConfigFilter(configFilter)
|
||||
, mDefaultFilter(new AndResourceFilter()) {
|
||||
// Add the default split, which is present for all APKs.
|
||||
mDefaultFilter->addFilter(mConfigFilter);
|
||||
mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true));
|
||||
}
|
||||
|
||||
status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) {
|
||||
const size_t N = mSplits.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs();
|
||||
std::set<ConfigDescription>::const_iterator iter = configs.begin();
|
||||
for (; iter != configs.end(); iter++) {
|
||||
if (splitConfigs.count(*iter) > 0) {
|
||||
// Can't have overlapping configurations.
|
||||
fprintf(stderr, "ERROR: Split configuration '%s' is already defined "
|
||||
"in another split.\n", iter->toString().string());
|
||||
return ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs);
|
||||
|
||||
// Add the inverse filter of this split filter to the base apk filter so it will
|
||||
// omit resources that belong in this split.
|
||||
mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter));
|
||||
|
||||
// Now add the apk-wide config filter to our split filter.
|
||||
sp<AndResourceFilter> filter = new AndResourceFilter();
|
||||
filter->addFilter(splitFilter);
|
||||
filter->addFilter(mConfigFilter);
|
||||
mSplits.add(new ApkSplit(configs, filter));
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) {
|
||||
const size_t N = mSplits.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (mSplits[i]->matches(file)) {
|
||||
return mSplits.editItemAt(i)->addEntry(path, file);
|
||||
}
|
||||
}
|
||||
// Entry can be dropped if it doesn't match any split. This will only happen
|
||||
// if the enry doesn't mConfigFilter.
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void ApkBuilder::print() const {
|
||||
fprintf(stderr, "APK Builder\n");
|
||||
fprintf(stderr, "-----------\n");
|
||||
const size_t N = mSplits.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
mSplits[i]->print();
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase)
|
||||
: mConfigs(configs), mFilter(filter), mIsBase(isBase) {
|
||||
std::set<ConfigDescription>::const_iterator iter = configs.begin();
|
||||
for (; iter != configs.end(); iter++) {
|
||||
if (mName.size() > 0) {
|
||||
mName.append(",");
|
||||
mDirName.append("_");
|
||||
}
|
||||
|
||||
String8 configStr = iter->toString();
|
||||
mName.append(configStr);
|
||||
mDirName.append(configStr);
|
||||
}
|
||||
}
|
||||
|
||||
status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) {
|
||||
if (!mFiles.insert(OutputEntry(path, file)).second) {
|
||||
// Duplicate file.
|
||||
return ALREADY_EXISTS;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void ApkSplit::print() const {
|
||||
fprintf(stderr, "APK Split '%s'\n", mName.string());
|
||||
|
||||
std::set<OutputEntry>::const_iterator iter = mFiles.begin();
|
||||
for (; iter != mFiles.end(); iter++) {
|
||||
fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string());
|
||||
}
|
||||
}
|
||||
113
tools/aapt/ApkBuilder.h
Normal file
113
tools/aapt/ApkBuilder.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 __APK_BUILDER_H
|
||||
#define __APK_BUILDER_H
|
||||
|
||||
#include <set>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#include "ConfigDescription.h"
|
||||
#include "OutputSet.h"
|
||||
#include "ResourceFilter.h"
|
||||
|
||||
class ApkSplit;
|
||||
class AaptFile;
|
||||
|
||||
class ApkBuilder : public android::RefBase {
|
||||
public:
|
||||
ApkBuilder(const sp<WeakResourceFilter>& configFilter);
|
||||
|
||||
/**
|
||||
* Tells the builder to generate a separate APK for resources that
|
||||
* match the configurations specified. Split APKs can not have
|
||||
* overlapping resources.
|
||||
*
|
||||
* NOTE: All splits should be set up before any files are added.
|
||||
*/
|
||||
android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs);
|
||||
|
||||
/**
|
||||
* Adds a file to be written to the final APK. It's name must not collide
|
||||
* with that of any files previously added. When a Split APK is being
|
||||
* generated, duplicates can exist as long as they are in different splits
|
||||
* (resources.arsc, AndroidManifest.xml).
|
||||
*/
|
||||
android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
|
||||
|
||||
android::Vector<sp<ApkSplit> >& getSplits() {
|
||||
return mSplits;
|
||||
}
|
||||
|
||||
void print() const;
|
||||
|
||||
private:
|
||||
android::sp<ResourceFilter> mConfigFilter;
|
||||
android::sp<AndResourceFilter> mDefaultFilter;
|
||||
android::Vector<sp<ApkSplit> > mSplits;
|
||||
};
|
||||
|
||||
class ApkSplit : public OutputSet {
|
||||
public:
|
||||
android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
|
||||
|
||||
const std::set<OutputEntry>& getEntries() const {
|
||||
return mFiles;
|
||||
}
|
||||
|
||||
const std::set<ConfigDescription>& getConfigs() const {
|
||||
return mConfigs;
|
||||
}
|
||||
|
||||
bool matches(const sp<AaptFile>& file) const {
|
||||
return mFilter->match(file->getGroupEntry().toParams());
|
||||
}
|
||||
|
||||
sp<ResourceFilter> getResourceFilter() const {
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
const android::String8& getPrintableName() const {
|
||||
return mName;
|
||||
}
|
||||
|
||||
const android::String8& getDirectorySafeName() const {
|
||||
return mDirName;
|
||||
}
|
||||
|
||||
bool isBase() const {
|
||||
return mIsBase;
|
||||
}
|
||||
|
||||
void print() const;
|
||||
|
||||
private:
|
||||
friend class ApkBuilder;
|
||||
|
||||
ApkSplit(const std::set<ConfigDescription>& configs, const android::sp<ResourceFilter>& filter, bool isBase=false);
|
||||
|
||||
std::set<ConfigDescription> mConfigs;
|
||||
const sp<ResourceFilter> mFilter;
|
||||
const bool mIsBase;
|
||||
String8 mName;
|
||||
String8 mDirName;
|
||||
std::set<OutputEntry> mFiles;
|
||||
};
|
||||
|
||||
#endif // __APK_BUILDER_H
|
||||
@@ -151,10 +151,12 @@ public:
|
||||
void setPublicOutputFile(const char* file) { mPublicOutputFile = file; }
|
||||
const char* getRClassDir() const { return mRClassDir; }
|
||||
void setRClassDir(const char* dir) { mRClassDir = dir; }
|
||||
const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
|
||||
const android::String8& getConfigurations() const { return mConfigurations; }
|
||||
void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
|
||||
const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; }
|
||||
void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } }
|
||||
const android::String8& getPreferredDensity() const { return mPreferredDensity; }
|
||||
void setPreferredDensity(const char* val) { mPreferredDensity = val; }
|
||||
void addSplitConfigurations(const char* val) { mPartialConfigurations.add(android::String8(val)); }
|
||||
const android::Vector<android::String8>& getSplitConfigurations() const { return mPartialConfigurations; }
|
||||
const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
|
||||
void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
|
||||
const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
|
||||
@@ -286,7 +288,8 @@ private:
|
||||
const char* mRClassDir;
|
||||
const char* mResourceIntermediatesDir;
|
||||
android::String8 mConfigurations;
|
||||
android::String8 mPreferredConfigurations;
|
||||
android::String8 mPreferredDensity;
|
||||
android::Vector<android::String8> mPartialConfigurations;
|
||||
android::Vector<const char*> mPackageIncludes;
|
||||
android::Vector<const char*> mJarFiles;
|
||||
android::Vector<const char*> mNoCompressExtensions;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//
|
||||
// Android Asset Packaging Tool main entry point.
|
||||
//
|
||||
#include "ApkBuilder.h"
|
||||
#include "Main.h"
|
||||
#include "Bundle.h"
|
||||
#include "ResourceFilter.h"
|
||||
@@ -2034,6 +2035,47 @@ bail:
|
||||
return (result != NO_ERROR);
|
||||
}
|
||||
|
||||
static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder) {
|
||||
const size_t numDirs = dir->getDirs().size();
|
||||
for (size_t i = 0; i < numDirs; i++) {
|
||||
status_t err = addResourcesToBuilder(dir->getDirs().valueAt(i), builder);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t numFiles = dir->getFiles().size();
|
||||
for (size_t i = 0; i < numFiles; i++) {
|
||||
sp<AaptGroup> gp = dir->getFiles().valueAt(i);
|
||||
const size_t numConfigs = gp->getFiles().size();
|
||||
for (size_t j = 0; j < numConfigs; j++) {
|
||||
status_t err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "Failed to add %s (%s) to builder.\n",
|
||||
gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
|
||||
if (split->isBase()) {
|
||||
return original;
|
||||
}
|
||||
|
||||
String8 ext(original.getPathExtension());
|
||||
if (ext == String8(".apk")) {
|
||||
return String8::format("%s_%s%s",
|
||||
original.getBasePath().string(),
|
||||
split->getDirectorySafeName().string(),
|
||||
ext.string());
|
||||
}
|
||||
|
||||
return String8::format("%s_%s", original.string(),
|
||||
split->getDirectorySafeName().string());
|
||||
}
|
||||
|
||||
/*
|
||||
* Package up an asset directory and associated application files.
|
||||
@@ -2047,17 +2089,18 @@ int doPackage(Bundle* bundle)
|
||||
int N;
|
||||
FILE* fp;
|
||||
String8 dependencyFile;
|
||||
sp<ApkBuilder> builder;
|
||||
|
||||
// -c en_XA or/and ar_XB means do pseudolocalization
|
||||
ResourceFilter filter;
|
||||
err = filter.parse(bundle->getConfigurations());
|
||||
sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
|
||||
err = configFilter->parse(bundle->getConfigurations());
|
||||
if (err != NO_ERROR) {
|
||||
goto bail;
|
||||
}
|
||||
if (filter.containsPseudo()) {
|
||||
if (configFilter->containsPseudo()) {
|
||||
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
|
||||
}
|
||||
if (filter.containsPseudoBidi()) {
|
||||
if (configFilter->containsPseudoBidi()) {
|
||||
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
|
||||
}
|
||||
|
||||
@@ -2105,9 +2148,32 @@ int doPackage(Bundle* bundle)
|
||||
assets->print(String8());
|
||||
}
|
||||
|
||||
// Create the ApkBuilder, which will collect the compiled files
|
||||
// to write to the final APK (or sets of APKs if we are building
|
||||
// a Split APK.
|
||||
builder = new ApkBuilder(configFilter);
|
||||
|
||||
// If we are generating a Split APK, find out which configurations to split on.
|
||||
if (bundle->getSplitConfigurations().size() > 0) {
|
||||
const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
|
||||
const size_t numSplits = splitStrs.size();
|
||||
for (size_t i = 0; i < numSplits; i++) {
|
||||
std::set<ConfigDescription> configs;
|
||||
if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
|
||||
fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
|
||||
goto bail;
|
||||
}
|
||||
|
||||
err = builder->createSplitForConfigs(configs);
|
||||
if (err != NO_ERROR) {
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If they asked for any fileAs that need to be compiled, do so.
|
||||
if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
|
||||
err = buildResources(bundle, assets);
|
||||
err = buildResources(bundle, assets, builder);
|
||||
if (err != 0) {
|
||||
goto bail;
|
||||
}
|
||||
@@ -2194,11 +2260,24 @@ int doPackage(Bundle* bundle)
|
||||
|
||||
// Write the apk
|
||||
if (outputAPKFile) {
|
||||
err = writeAPK(bundle, assets, String8(outputAPKFile));
|
||||
// Gather all resources and add them to the APK Builder. The builder will then
|
||||
// figure out which Split they belong in.
|
||||
err = addResourcesToBuilder(assets, builder);
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
const Vector<sp<ApkSplit> >& splits = builder->getSplits();
|
||||
const size_t numSplits = splits.size();
|
||||
for (size_t i = 0; i < numSplits; i++) {
|
||||
const sp<ApkSplit>& split = splits[i];
|
||||
String8 outputPath = buildApkName(String8(outputAPKFile), split);
|
||||
err = writeAPK(bundle, outputPath, split);
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we've been asked to generate a dependency file, we need to finish up here.
|
||||
|
||||
57
tools/aapt/ConfigDescription.h
Normal file
57
tools/aapt/ConfigDescription.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 __CONFIG_DESCRIPTION_H
|
||||
#define __CONFIG_DESCRIPTION_H
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
|
||||
/**
|
||||
* Subclass of ResTable_config that adds convenient
|
||||
* initialization and comparison methods.
|
||||
*/
|
||||
struct ConfigDescription : public android::ResTable_config {
|
||||
ConfigDescription() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
size = sizeof(android::ResTable_config);
|
||||
}
|
||||
ConfigDescription(const android::ResTable_config&o) {
|
||||
*static_cast<android::ResTable_config*>(this) = o;
|
||||
size = sizeof(android::ResTable_config);
|
||||
}
|
||||
ConfigDescription(const ConfigDescription&o) {
|
||||
*static_cast<android::ResTable_config*>(this) = o;
|
||||
}
|
||||
|
||||
ConfigDescription& operator=(const android::ResTable_config& o) {
|
||||
*static_cast<android::ResTable_config*>(this) = o;
|
||||
size = sizeof(android::ResTable_config);
|
||||
return *this;
|
||||
}
|
||||
ConfigDescription& operator=(const ConfigDescription& o) {
|
||||
*static_cast<android::ResTable_config*>(this) = o;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
|
||||
inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
|
||||
inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
|
||||
inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
|
||||
};
|
||||
|
||||
#endif // __CONFIG_DESCRIPTION_H
|
||||
@@ -70,6 +70,7 @@ void usage(void)
|
||||
" [-F apk-file] [-J R-file-dir] \\\n"
|
||||
" [--product product1,product2,...] \\\n"
|
||||
" [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
|
||||
" [--split CONFIGS [--split CONFIGS]] \\\n"
|
||||
" [raw-files-dir [raw-files-dir] ...] \\\n"
|
||||
" [--output-text-symbols DIR]\n"
|
||||
"\n"
|
||||
@@ -166,10 +167,12 @@ void usage(void)
|
||||
" generate dependency files in the same directories for R.java and resource package\n"
|
||||
" --auto-add-overlay\n"
|
||||
" Automatically add resources that are only in overlays.\n"
|
||||
" --preferred-configurations\n"
|
||||
" Like the -c option for filtering out unneeded configurations, but\n"
|
||||
" only expresses a preference. If there is no resource available with\n"
|
||||
" the preferred configuration then it will not be stripped.\n"
|
||||
" --preferred-density\n"
|
||||
" Specifies a preference for a particular density. Resources that do not\n"
|
||||
" match this density and have variants that are a closer match are removed.\n"
|
||||
" --split\n"
|
||||
" Builds a separate split APK for the configurations listed. This can\n"
|
||||
" be loaded alongside the base APK at runtime.\n"
|
||||
" --rename-manifest-package\n"
|
||||
" Rewrite the manifest so that its package name is the package name\n"
|
||||
" given here. Relative class names (for example .Foo) will be\n"
|
||||
@@ -568,15 +571,24 @@ int main(int argc, char* const argv[])
|
||||
bundle.setGenDependencies(true);
|
||||
} else if (strcmp(cp, "-utf16") == 0) {
|
||||
bundle.setWantUTF16(true);
|
||||
} else if (strcmp(cp, "-preferred-configurations") == 0) {
|
||||
} else if (strcmp(cp, "-preferred-density") == 0) {
|
||||
argc--;
|
||||
argv++;
|
||||
if (!argc) {
|
||||
fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n");
|
||||
fprintf(stderr, "ERROR: No argument supplied for '--preferred-density' option\n");
|
||||
wantUsage = true;
|
||||
goto bail;
|
||||
}
|
||||
bundle.addPreferredConfigurations(argv[0]);
|
||||
bundle.setPreferredDensity(argv[0]);
|
||||
} else if (strcmp(cp, "-split") == 0) {
|
||||
argc--;
|
||||
argv++;
|
||||
if (!argc) {
|
||||
fprintf(stderr, "ERROR: No argument supplied for '--split' option\n");
|
||||
wantUsage = true;
|
||||
goto bail;
|
||||
}
|
||||
bundle.addSplitConfigurations(argv[0]);
|
||||
} else if (strcmp(cp, "-rename-manifest-package") == 0) {
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
@@ -10,8 +10,12 @@
|
||||
#include <utils/threads.h>
|
||||
#include <utils/List.h>
|
||||
#include <utils/Errors.h>
|
||||
#include "Bundle.h"
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
#include "AaptAssets.h"
|
||||
#include "ApkBuilder.h"
|
||||
#include "Bundle.h"
|
||||
#include "ResourceFilter.h"
|
||||
#include "ZipFile.h"
|
||||
|
||||
|
||||
@@ -22,6 +26,8 @@
|
||||
#include <time.h>
|
||||
#endif /* BENCHMARK */
|
||||
|
||||
class OutputSet;
|
||||
|
||||
extern int doVersion(Bundle* bundle);
|
||||
extern int doList(Bundle* bundle);
|
||||
extern int doDump(Bundle* bundle);
|
||||
@@ -34,13 +40,13 @@ extern int doSingleCrunch(Bundle* bundle);
|
||||
extern int calcPercent(long uncompressedLen, long compressedLen);
|
||||
|
||||
extern android::status_t writeAPK(Bundle* bundle,
|
||||
const sp<AaptAssets>& assets,
|
||||
const android::String8& outputFile);
|
||||
const android::String8& outputFile,
|
||||
const android::sp<OutputSet>& outputSet);
|
||||
|
||||
extern android::status_t updatePreProcessedCache(Bundle* bundle);
|
||||
|
||||
extern android::status_t buildResources(Bundle* bundle,
|
||||
const sp<AaptAssets>& assets);
|
||||
const sp<AaptAssets>& assets, sp<ApkBuilder>& builder);
|
||||
|
||||
extern android::status_t writeResourceSymbols(Bundle* bundle,
|
||||
const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate);
|
||||
@@ -49,8 +55,6 @@ extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>&
|
||||
|
||||
extern bool isValidResourceType(const String8& type);
|
||||
|
||||
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
|
||||
|
||||
extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets);
|
||||
|
||||
int dumpResources(Bundle* bundle);
|
||||
|
||||
56
tools/aapt/OutputSet.h
Normal file
56
tools/aapt/OutputSet.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 __OUTPUT_SET_H
|
||||
#define __OUTPUT_SET_H
|
||||
|
||||
#include <set>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
class AaptFile;
|
||||
|
||||
class OutputEntry {
|
||||
public:
|
||||
OutputEntry() {}
|
||||
OutputEntry(const android::String8& path, const android::sp<const AaptFile>& file)
|
||||
: mPath(path), mFile(file) {}
|
||||
|
||||
inline const android::sp<const AaptFile>& getFile() const {
|
||||
return mFile;
|
||||
}
|
||||
|
||||
inline const android::String8& getPath() const {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
bool operator<(const OutputEntry& o) const { return getPath() < o.mPath; }
|
||||
bool operator==(const OutputEntry& o) const { return getPath() == o.mPath; }
|
||||
|
||||
private:
|
||||
android::String8 mPath;
|
||||
android::sp<const AaptFile> mFile;
|
||||
};
|
||||
|
||||
class OutputSet : public virtual android::RefBase {
|
||||
public:
|
||||
virtual const std::set<OutputEntry>& getEntries() const = 0;
|
||||
|
||||
virtual ~OutputSet() {}
|
||||
};
|
||||
|
||||
#endif // __OUTPUT_SET_H
|
||||
@@ -5,6 +5,7 @@
|
||||
//
|
||||
#include "Main.h"
|
||||
#include "AaptAssets.h"
|
||||
#include "OutputSet.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceFilter.h"
|
||||
|
||||
@@ -36,11 +37,8 @@ static const char* kNoCompressExt[] = {
|
||||
};
|
||||
|
||||
/* fwd decls, so I can write this downward */
|
||||
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
|
||||
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
|
||||
const AaptGroupEntry& ge, const ResourceFilter* filter);
|
||||
bool processFile(Bundle* bundle, ZipFile* zip,
|
||||
const sp<AaptGroup>& group, const sp<AaptFile>& file);
|
||||
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet);
|
||||
bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file);
|
||||
bool okayToCompress(Bundle* bundle, const String8& pathName);
|
||||
ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
|
||||
|
||||
@@ -51,8 +49,7 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
|
||||
* On success, "bundle->numPackages" will be the number of Zip packages
|
||||
* we created.
|
||||
*/
|
||||
status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
|
||||
const String8& outputFile)
|
||||
status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet)
|
||||
{
|
||||
#if BENCHMARK
|
||||
fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
|
||||
@@ -112,7 +109,7 @@ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
|
||||
printf("Writing all files...\n");
|
||||
}
|
||||
|
||||
count = processAssets(bundle, zip, assets);
|
||||
count = processAssets(bundle, zip, outputSet);
|
||||
if (count < 0) {
|
||||
fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
|
||||
outputFile.string());
|
||||
@@ -218,72 +215,24 @@ bail:
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t processAssets(Bundle* bundle, ZipFile* zip,
|
||||
const sp<AaptAssets>& assets)
|
||||
{
|
||||
ResourceFilter filter;
|
||||
status_t status = filter.parse(bundle->getConfigurations());
|
||||
if (status != NO_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t count = 0;
|
||||
|
||||
const size_t N = assets->getGroupEntries().size();
|
||||
for (size_t i=0; i<N; i++) {
|
||||
const AaptGroupEntry& ge = assets->getGroupEntries()[i];
|
||||
|
||||
ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
count += res;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
|
||||
const AaptGroupEntry& ge, const ResourceFilter* filter)
|
||||
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet)
|
||||
{
|
||||
ssize_t count = 0;
|
||||
|
||||
const size_t ND = dir->getDirs().size();
|
||||
size_t i;
|
||||
for (i=0; i<ND; i++) {
|
||||
const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
|
||||
|
||||
const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
|
||||
|
||||
if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
count += res;
|
||||
}
|
||||
|
||||
if (filter != NULL && !filter->match(ge.toParams())) {
|
||||
return count;
|
||||
}
|
||||
|
||||
const size_t NF = dir->getFiles().size();
|
||||
for (i=0; i<NF; i++) {
|
||||
sp<AaptGroup> gp = dir->getFiles().valueAt(i);
|
||||
ssize_t fi = gp->getFiles().indexOfKey(ge);
|
||||
if (fi >= 0) {
|
||||
sp<AaptFile> fl = gp->getFiles().valueAt(fi);
|
||||
if (!processFile(bundle, zip, gp, fl)) {
|
||||
const std::set<OutputEntry>& entries = outputSet->getEntries();
|
||||
std::set<OutputEntry>::const_iterator iter = entries.begin();
|
||||
for (; iter != entries.end(); iter++) {
|
||||
const OutputEntry& entry = *iter;
|
||||
if (entry.getFile() == NULL) {
|
||||
fprintf(stderr, "warning: null file being processed.\n");
|
||||
} else {
|
||||
String8 storagePath(entry.getPath());
|
||||
storagePath.convertToResPath();
|
||||
if (!processFile(bundle, zip, storagePath, entry.getFile())) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -294,12 +243,10 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
|
||||
* delete the existing entry before adding the new one.
|
||||
*/
|
||||
bool processFile(Bundle* bundle, ZipFile* zip,
|
||||
const sp<AaptGroup>& group, const sp<AaptFile>& file)
|
||||
String8 storageName, const sp<const AaptFile>& file)
|
||||
{
|
||||
const bool hasData = file->hasData();
|
||||
|
||||
String8 storageName(group->getPath());
|
||||
storageName.convertToResPath();
|
||||
ZipEntry* entry;
|
||||
bool fromGzip = false;
|
||||
status_t result;
|
||||
@@ -403,8 +350,8 @@ bool processFile(Bundle* bundle, ZipFile* zip,
|
||||
fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n",
|
||||
file->getPrintableSource().string());
|
||||
} else {
|
||||
fprintf(stderr, " Unable to add '%s': Zip add failed\n",
|
||||
file->getPrintableSource().string());
|
||||
fprintf(stderr, " Unable to add '%s': Zip add failed (%d)\n",
|
||||
file->getPrintableSource().string(), result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -179,24 +179,6 @@ bool isValidResourceType(const String8& type)
|
||||
|| type == "color" || type == "menu" || type == "mipmap";
|
||||
}
|
||||
|
||||
static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
|
||||
{
|
||||
sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
|
||||
sp<AaptFile> file;
|
||||
if (group != NULL) {
|
||||
file = group->getFiles().valueFor(AaptGroupEntry());
|
||||
if (file != NULL) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
if (!makeIfNecessary) {
|
||||
return NULL;
|
||||
}
|
||||
return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
|
||||
NULL, String8());
|
||||
}
|
||||
|
||||
static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
|
||||
const sp<AaptGroup>& grp)
|
||||
{
|
||||
@@ -359,23 +341,6 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass
|
||||
return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
|
||||
}
|
||||
|
||||
status_t postProcessImages(const sp<AaptAssets>& assets,
|
||||
ResourceTable* table,
|
||||
const sp<ResourceTypeSet>& set)
|
||||
{
|
||||
ResourceDirIterator it(set, String8("drawable"));
|
||||
bool hasErrors = false;
|
||||
ssize_t res;
|
||||
while ((res=it.next()) == NO_ERROR) {
|
||||
res = postProcessImage(assets, table, it.getFile());
|
||||
if (res < NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
|
||||
}
|
||||
|
||||
static void collect_files(const sp<AaptDir>& dir,
|
||||
KeyedVector<String8, sp<ResourceTypeSet> >* resources)
|
||||
{
|
||||
@@ -906,7 +871,38 @@ status_t updatePreProcessedCache(Bundle* bundle)
|
||||
return 0;
|
||||
}
|
||||
|
||||
status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split,
|
||||
sp<AaptFile>& outFile) {
|
||||
const String8 filename("AndroidManifest.xml");
|
||||
const String16 androidPrefix("android");
|
||||
const String16 androidNSUri("http://schemas.android.com/apk/res/android");
|
||||
sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri);
|
||||
|
||||
// Build the <manifest> tag
|
||||
sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
|
||||
|
||||
// Add the 'package' attribute which is set to the original package name.
|
||||
manifest->addAttribute(String16(), String16("package"), package);
|
||||
|
||||
// Add the 'split' attribute which describes the configurations included.
|
||||
String8 splitName("config_");
|
||||
splitName.append(split->getDirectorySafeName());
|
||||
manifest->addAttribute(String16(), String16("split"), String16(splitName));
|
||||
|
||||
// Build an empty <application> tag (required).
|
||||
sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
|
||||
manifest->addChild(app);
|
||||
root->addChild(manifest);
|
||||
|
||||
status_t err = root->flatten(outFile, true, true);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder)
|
||||
{
|
||||
// First, look for a package file to parse. This is required to
|
||||
// be able to generate the resource information.
|
||||
@@ -1122,12 +1118,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
if (table.hasResources()) {
|
||||
sp<AaptFile> resFile(getResourceFile(assets));
|
||||
if (resFile == NULL) {
|
||||
fprintf(stderr, "Error: unable to generate entry for resource data\n");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
err = table.assignResourceIds();
|
||||
if (err < NO_ERROR) {
|
||||
return err;
|
||||
@@ -1235,16 +1225,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
}
|
||||
|
||||
if (drawables != NULL) {
|
||||
err = postProcessImages(assets, &table, drawables);
|
||||
if (err != NO_ERROR) {
|
||||
ResourceDirIterator it(drawables, String8("drawable"));
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
err = postProcessImage(assets, &table, it.getFile());
|
||||
if (err != NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (err < NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
err = NO_ERROR;
|
||||
}
|
||||
|
||||
if (colors != NULL) {
|
||||
ResourceDirIterator it(colors, String8("color"));
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
if (err != NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
@@ -1261,12 +1259,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
String8 src = it.getFile()->getPrintableSource();
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
if (err != NO_ERROR) {
|
||||
if (err == NO_ERROR) {
|
||||
ResXMLTree block;
|
||||
block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
|
||||
checkForIds(src, block);
|
||||
} else {
|
||||
hasErrors = true;
|
||||
}
|
||||
ResXMLTree block;
|
||||
block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
|
||||
checkForIds(src, block);
|
||||
}
|
||||
|
||||
if (err < NO_ERROR) {
|
||||
@@ -1319,15 +1318,35 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
return err;
|
||||
}
|
||||
|
||||
resFile = getResourceFile(assets);
|
||||
if (resFile == NULL) {
|
||||
fprintf(stderr, "Error: unable to generate entry for resource data\n");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
Vector<sp<ApkSplit> >& splits = builder->getSplits();
|
||||
const size_t numSplits = splits.size();
|
||||
for (size_t i = 0; i < numSplits; i++) {
|
||||
sp<ApkSplit>& split = splits.editItemAt(i);
|
||||
sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),
|
||||
AaptGroupEntry(), String8());
|
||||
err = table.flatten(bundle, split->getResourceFilter(), flattenedTable);
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "Failed to generate resource table for split '%s'\n",
|
||||
split->getPrintableName().string());
|
||||
return err;
|
||||
}
|
||||
split->addEntry(String8("resources.arsc"), flattenedTable);
|
||||
|
||||
err = table.flatten(bundle, resFile);
|
||||
if (err < NO_ERROR) {
|
||||
return err;
|
||||
if (split->isBase()) {
|
||||
resFile = flattenedTable;
|
||||
finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
|
||||
} else {
|
||||
sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
|
||||
AaptGroupEntry(), String8());
|
||||
err = generateAndroidManifestForSplit(String16(assets->getPackage()), split,
|
||||
generatedManifest);
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
|
||||
split->getPrintableName().string());
|
||||
return err;
|
||||
}
|
||||
split->addEntry(String8("AndroidManifest.xml"), generatedManifest);
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle->getPublicOutputFile()) {
|
||||
@@ -1343,18 +1362,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
table.writePublicDefinitions(String16(assets->getPackage()), fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// Read resources back in,
|
||||
finalResTable.add(resFile->getData(), resFile->getSize());
|
||||
|
||||
#if 0
|
||||
NOISY(
|
||||
printf("Generated resources:\n");
|
||||
finalResTable.print();
|
||||
)
|
||||
#endif
|
||||
|
||||
if (finalResTable.getTableCount() == 0 || resFile == NULL) {
|
||||
fprintf(stderr, "No resource table was generated.\n");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Perform a basic validation of the manifest file. This time we
|
||||
// parse it with the comments intact, so that we can use them to
|
||||
// generate java docs... so we are not going to write this one
|
||||
|
||||
@@ -1,119 +1,92 @@
|
||||
//
|
||||
// Copyright 2011 The Android Open Source Project
|
||||
// Copyright 2014 The Android Open Source Project
|
||||
//
|
||||
// Build resource files from raw assets.
|
||||
//
|
||||
|
||||
#include "ResourceFilter.h"
|
||||
#include "AaptUtil.h"
|
||||
#include "AaptConfig.h"
|
||||
|
||||
status_t
|
||||
ResourceFilter::parse(const char* arg)
|
||||
WeakResourceFilter::parse(const String8& str)
|
||||
{
|
||||
if (arg == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* p = arg;
|
||||
const char* q;
|
||||
|
||||
while (true) {
|
||||
q = strchr(p, ',');
|
||||
if (q == NULL) {
|
||||
q = p + strlen(p);
|
||||
}
|
||||
|
||||
String8 part(p, q-p);
|
||||
|
||||
Vector<String8> configStrs = AaptUtil::split(str, ',');
|
||||
const size_t N = configStrs.size();
|
||||
mConfigs.clear();
|
||||
mConfigMask = 0;
|
||||
mConfigs.resize(N);
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
const String8& part = configStrs[i];
|
||||
if (part == "en_XA") {
|
||||
mContainsPseudoAccented = true;
|
||||
} else if (part == "ar_XB") {
|
||||
mContainsPseudoBidi = true;
|
||||
}
|
||||
int axis;
|
||||
AxisValue value;
|
||||
if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) {
|
||||
fprintf(stderr, "Invalid configuration: %s\n", arg);
|
||||
fprintf(stderr, " ");
|
||||
for (int i=0; i<p-arg; i++) {
|
||||
fprintf(stderr, " ");
|
||||
}
|
||||
for (int i=0; i<q-p; i++) {
|
||||
fprintf(stderr, "^");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
return 1;
|
||||
|
||||
std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i);
|
||||
|
||||
AaptLocaleValue val;
|
||||
if (val.initFromFilterString(part)) {
|
||||
// For backwards compatibility, we accept configurations that
|
||||
// only specify locale in the standard 'en_US' format.
|
||||
val.writeTo(&entry.first);
|
||||
} else if (!AaptConfig::parse(part, &entry.first)) {
|
||||
fprintf(stderr, "Invalid configuration: %s\n", part.string());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
ssize_t index = mData.indexOfKey(axis);
|
||||
if (index < 0) {
|
||||
mData.add(axis, SortedVector<AxisValue>());
|
||||
}
|
||||
SortedVector<AxisValue>& sv = mData.editValueFor(axis);
|
||||
sv.add(value);
|
||||
entry.second = mDefault.diff(entry.first);
|
||||
|
||||
// If it's a locale with a region, script or variant, we should also match an
|
||||
// unmodified locale of the same language
|
||||
if (axis == AXIS_LOCALE) {
|
||||
if (value.localeValue.region[0] || value.localeValue.script[0] ||
|
||||
value.localeValue.variant[0]) {
|
||||
AxisValue copy;
|
||||
memcpy(copy.localeValue.language, value.localeValue.language,
|
||||
sizeof(value.localeValue.language));
|
||||
sv.add(copy);
|
||||
}
|
||||
}
|
||||
p = q;
|
||||
if (!*p) break;
|
||||
p++;
|
||||
// Ignore the version
|
||||
entry.second &= ~ResTable_config::CONFIG_VERSION;
|
||||
|
||||
mConfigMask |= entry.second;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool
|
||||
ResourceFilter::isEmpty() const
|
||||
WeakResourceFilter::match(const ResTable_config& config) const
|
||||
{
|
||||
return mData.size() == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
ResourceFilter::match(int axis, const AxisValue& value) const
|
||||
{
|
||||
if (value.intValue == 0 && (value.localeValue.language[0] == 0)) {
|
||||
// they didn't specify anything so take everything
|
||||
uint32_t mask = mDefault.diff(config);
|
||||
if ((mConfigMask & mask) == 0) {
|
||||
// The two configurations don't have any common axis.
|
||||
return true;
|
||||
}
|
||||
ssize_t index = mData.indexOfKey(axis);
|
||||
if (index < 0) {
|
||||
// we didn't request anything on this axis so take everything
|
||||
return true;
|
||||
}
|
||||
const SortedVector<AxisValue>& sv = mData.valueAt(index);
|
||||
return sv.indexOf(value) >= 0;
|
||||
}
|
||||
|
||||
bool
|
||||
ResourceFilter::match(int axis, const ResTable_config& config) const
|
||||
{
|
||||
return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis));
|
||||
}
|
||||
|
||||
bool
|
||||
ResourceFilter::match(const ResTable_config& config) const
|
||||
{
|
||||
for (int i=AXIS_START; i<=AXIS_END; i++) {
|
||||
if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) {
|
||||
return false;
|
||||
const size_t N = mConfigs.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i];
|
||||
uint32_t diff = entry.first.diff(config);
|
||||
if ((diff & entry.second) == 0) {
|
||||
return true;
|
||||
} else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) {
|
||||
// If the locales differ, but the languages are the same and
|
||||
// the locale we are matching only has a language specified,
|
||||
// we match.
|
||||
if (config.language[0] && memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) {
|
||||
if (config.country[0] == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const
|
||||
{
|
||||
ssize_t index = mData.indexOfKey(axis);
|
||||
if (index < 0) {
|
||||
return NULL;
|
||||
status_t
|
||||
StrongResourceFilter::parse(const String8& str) {
|
||||
Vector<String8> configStrs = AaptUtil::split(str, ',');
|
||||
ConfigDescription config;
|
||||
mConfigs.clear();
|
||||
for (size_t i = 0; i < configStrs.size(); i++) {
|
||||
if (!AaptConfig::parse(configStrs[i], &config)) {
|
||||
fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
mConfigs.insert(config);
|
||||
}
|
||||
return &mData.valueAt(index);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -7,31 +7,137 @@
|
||||
#ifndef RESOURCE_FILTER_H
|
||||
#define RESOURCE_FILTER_H
|
||||
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#include "AaptAssets.h"
|
||||
#include "ConfigDescription.h"
|
||||
|
||||
class ResourceFilter : public virtual android::RefBase {
|
||||
public:
|
||||
virtual bool match(const android::ResTable_config& config) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements logic for parsing and handling "-c" and "--preferred-configurations"
|
||||
* options.
|
||||
*/
|
||||
class ResourceFilter
|
||||
{
|
||||
class WeakResourceFilter : public ResourceFilter {
|
||||
public:
|
||||
ResourceFilter() : mData(), mContainsPseudoAccented(false),
|
||||
mContainsPseudoBidi(false) {}
|
||||
status_t parse(const char* arg);
|
||||
bool isEmpty() const;
|
||||
bool match(int axis, const ResTable_config& config) const;
|
||||
bool match(const ResTable_config& config) const;
|
||||
const SortedVector<AxisValue>* configsForAxis(int axis) const;
|
||||
inline bool containsPseudo() const { return mContainsPseudoAccented; }
|
||||
inline bool containsPseudoBidi() const { return mContainsPseudoBidi; }
|
||||
WeakResourceFilter()
|
||||
: mContainsPseudoAccented(false)
|
||||
, mContainsPseudoBidi(false) {}
|
||||
|
||||
android::status_t parse(const android::String8& str);
|
||||
|
||||
bool match(const android::ResTable_config& config) const;
|
||||
|
||||
inline bool isEmpty() const {
|
||||
return mConfigMask == 0;
|
||||
}
|
||||
|
||||
inline bool containsPseudo() const {
|
||||
return mContainsPseudoAccented;
|
||||
}
|
||||
|
||||
inline bool containsPseudoBidi() const {
|
||||
return mContainsPseudoBidi;
|
||||
}
|
||||
|
||||
private:
|
||||
bool match(int axis, const AxisValue& value) const;
|
||||
ConfigDescription mDefault;
|
||||
uint32_t mConfigMask;
|
||||
android::Vector<std::pair<ConfigDescription, uint32_t> > mConfigs;
|
||||
|
||||
KeyedVector<int,SortedVector<AxisValue> > mData;
|
||||
bool mContainsPseudoAccented;
|
||||
bool mContainsPseudoBidi;
|
||||
};
|
||||
|
||||
/**
|
||||
* Matches resources that have at least one of the configurations
|
||||
* that this filter is looking for. In order to match a configuration,
|
||||
* the resource must have the exact same configuration.
|
||||
*
|
||||
* This filter acts as a logical OR when matching resources.
|
||||
*
|
||||
* For example, if the filter is looking for resources with
|
||||
* fr-land, de-land, or sw600dp:
|
||||
*
|
||||
* (PASS) fr-land
|
||||
* (FAIL) fr
|
||||
* (PASS) de-land
|
||||
* (FAIL) de
|
||||
* (FAIL) de-sw600dp
|
||||
* (PASS) sw600dp
|
||||
* (FAIL) sw600dp-land
|
||||
*/
|
||||
class StrongResourceFilter : public ResourceFilter {
|
||||
public:
|
||||
StrongResourceFilter() {}
|
||||
StrongResourceFilter(const std::set<ConfigDescription>& configs)
|
||||
: mConfigs(configs) {}
|
||||
|
||||
android::status_t parse(const android::String8& str);
|
||||
|
||||
bool match(const android::ResTable_config& config) const {
|
||||
std::set<ConfigDescription>::const_iterator iter = mConfigs.begin();
|
||||
for (; iter != mConfigs.end(); iter++) {
|
||||
if (iter->compare(config) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline const std::set<ConfigDescription>& getConfigs() const {
|
||||
return mConfigs;
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<ConfigDescription> mConfigs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Negates the response of the target filter.
|
||||
*/
|
||||
class InverseResourceFilter : public ResourceFilter {
|
||||
public:
|
||||
InverseResourceFilter(const android::sp<ResourceFilter>& filter)
|
||||
: mFilter(filter) {}
|
||||
|
||||
bool match(const android::ResTable_config& config) const {
|
||||
return !mFilter->match(config);
|
||||
}
|
||||
|
||||
private:
|
||||
const android::sp<ResourceFilter> mFilter;
|
||||
};
|
||||
|
||||
/**
|
||||
* A logical AND of all the added filters.
|
||||
*/
|
||||
class AndResourceFilter : public ResourceFilter {
|
||||
public:
|
||||
void addFilter(const android::sp<ResourceFilter>& filter) {
|
||||
mFilters.add(filter);
|
||||
}
|
||||
|
||||
bool match(const android::ResTable_config& config) const {
|
||||
const size_t N = mFilters.size();
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (!mFilters[i]->match(config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
android::Vector<android::sp<ResourceFilter> > mFilters;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -2080,10 +2080,10 @@ bool ResourceTable::hasResources() const {
|
||||
return mNumLocal > 0;
|
||||
}
|
||||
|
||||
sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
|
||||
sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter)
|
||||
{
|
||||
sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
|
||||
status_t err = flatten(bundle, data);
|
||||
status_t err = flatten(bundle, filter, data);
|
||||
return err == NO_ERROR ? data : NULL;
|
||||
}
|
||||
|
||||
@@ -2643,8 +2643,8 @@ ResourceTable::validateLocalizations(void)
|
||||
}
|
||||
|
||||
// Check that all requested localizations are present for this string
|
||||
if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
|
||||
const char* allConfigs = mBundle->getConfigurations();
|
||||
if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
|
||||
const char* allConfigs = mBundle->getConfigurations().string();
|
||||
const char* start = allConfigs;
|
||||
const char* comma;
|
||||
|
||||
@@ -2698,14 +2698,8 @@ ResourceTable::validateLocalizations(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest)
|
||||
{
|
||||
ResourceFilter filter;
|
||||
status_t err = filter.parse(bundle->getConfigurations());
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
const ConfigDescription nullConfig;
|
||||
|
||||
const size_t N = mOrderedPackages.size();
|
||||
@@ -2780,7 +2774,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
const size_t N = c->getEntries().size();
|
||||
for (size_t ei=0; ei<N; ei++) {
|
||||
ConfigDescription config = c->getEntries().keyAt(ei);
|
||||
if (filterable && !filter.match(config)) {
|
||||
if (filterable && !filter->match(config)) {
|
||||
continue;
|
||||
}
|
||||
sp<Entry> e = c->getEntries().valueAt(ei);
|
||||
@@ -2872,7 +2866,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
return amt;
|
||||
}
|
||||
|
||||
err = flattenLibraryTable(data, libraryPackages);
|
||||
status_t err = flattenLibraryTable(data, libraryPackages);
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "ERROR: failed to write library table\n");
|
||||
return err;
|
||||
@@ -2928,11 +2922,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
}
|
||||
const size_t CN = cl->getEntries().size();
|
||||
for (size_t ci=0; ci<CN; ci++) {
|
||||
if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
|
||||
if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
|
||||
continue;
|
||||
}
|
||||
for (size_t cj=ci+1; cj<CN; cj++) {
|
||||
if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
|
||||
if (filterable && !filter->match(cl->getEntries().keyAt(cj))) {
|
||||
continue;
|
||||
}
|
||||
typeSpecFlags[ei] |= htodl(
|
||||
@@ -2974,7 +2968,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
config.screenHeightDp,
|
||||
config.layoutDirection));
|
||||
|
||||
if (filterable && !filter.match(config)) {
|
||||
if (filterable && !filter->match(config)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3093,7 +3087,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
}
|
||||
|
||||
ssize_t strStart = dest->getSize();
|
||||
err = valueStrings.writeStringBlock(dest);
|
||||
status_t err = valueStrings.writeStringBlock(dest);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#ifndef RESOURCE_TABLE_H
|
||||
#define RESOURCE_TABLE_H
|
||||
|
||||
#include "ConfigDescription.h"
|
||||
#include "StringPool.h"
|
||||
#include "SourcePos.h"
|
||||
#include "ResourceFilter.h"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
@@ -76,37 +78,6 @@ public:
|
||||
class Type;
|
||||
class Entry;
|
||||
|
||||
struct ConfigDescription : public ResTable_config {
|
||||
ConfigDescription() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
size = sizeof(ResTable_config);
|
||||
}
|
||||
ConfigDescription(const ResTable_config&o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
size = sizeof(ResTable_config);
|
||||
}
|
||||
ConfigDescription(const ConfigDescription&o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
}
|
||||
|
||||
ConfigDescription& operator=(const ResTable_config& o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
size = sizeof(ResTable_config);
|
||||
return *this;
|
||||
}
|
||||
ConfigDescription& operator=(const ConfigDescription& o) {
|
||||
*static_cast<ResTable_config*>(this) = o;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
|
||||
inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
|
||||
inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
|
||||
inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
|
||||
inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
|
||||
inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
|
||||
};
|
||||
|
||||
ResourceTable(Bundle* bundle, const String16& assetsPackage);
|
||||
|
||||
status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
|
||||
@@ -182,7 +153,7 @@ public:
|
||||
size_t numLocalResources() const;
|
||||
bool hasResources() const;
|
||||
|
||||
sp<AaptFile> flatten(Bundle*);
|
||||
sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter);
|
||||
|
||||
static inline uint32_t makeResId(uint32_t packageId,
|
||||
uint32_t typeId,
|
||||
@@ -223,7 +194,7 @@ public:
|
||||
void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
|
||||
status_t validateLocalizations(void);
|
||||
|
||||
status_t flatten(Bundle*, const sp<AaptFile>& dest);
|
||||
status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest);
|
||||
status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs);
|
||||
|
||||
void writePublicDefinitions(const String16& package, FILE* fp);
|
||||
|
||||
78
tools/aapt/tests/AaptConfig_test.cpp
Normal file
78
tools/aapt/tests/AaptConfig_test.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <utils/String8.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "AaptConfig.h"
|
||||
#include "ConfigDescription.h"
|
||||
#include "TestHelper.h"
|
||||
|
||||
using android::String8;
|
||||
|
||||
static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) {
|
||||
if (AaptConfig::parse(String8(input), config)) {
|
||||
return ::testing::AssertionSuccess() << input << " was successfully parsed";
|
||||
}
|
||||
return ::testing::AssertionFailure() << input << " could not be parsed";
|
||||
}
|
||||
|
||||
static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) {
|
||||
return TestParse(String8(input), config);
|
||||
}
|
||||
|
||||
TEST(AaptConfigTest, ParseFailWhenQualifiersAreOutOfOrder) {
|
||||
EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
|
||||
EXPECT_FALSE(TestParse("land-en"));
|
||||
EXPECT_FALSE(TestParse("hdpi-320dpi"));
|
||||
}
|
||||
|
||||
TEST(AaptConfigTest, ParseFailWhenQualifiersAreNotMatched) {
|
||||
EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
|
||||
}
|
||||
|
||||
TEST(AaptConfigTest, ParseFailWhenQualifiersHaveTrailingDash) {
|
||||
EXPECT_FALSE(TestParse("en-sw600dp-land-"));
|
||||
}
|
||||
|
||||
TEST(AaptConfigTest, ParseBasicQualifiers) {
|
||||
ConfigDescription config;
|
||||
EXPECT_TRUE(TestParse("", &config));
|
||||
EXPECT_EQ(String8(""), config.toString());
|
||||
|
||||
EXPECT_TRUE(TestParse("fr-land", &config));
|
||||
EXPECT_EQ(String8("fr-land"), config.toString());
|
||||
|
||||
EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
|
||||
"xhdpi-keyssoft-qwerty-navexposed-nonav", &config));
|
||||
EXPECT_EQ(String8("mcc310-pl-sw720dp-normal-long-port-night-"
|
||||
"xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString());
|
||||
}
|
||||
|
||||
TEST(AaptConfigTest, ParseLocales) {
|
||||
ConfigDescription config;
|
||||
EXPECT_TRUE(TestParse("en-rUS", &config));
|
||||
EXPECT_EQ(String8("en-US"), config.toString());
|
||||
}
|
||||
|
||||
TEST(AaptConfigTest, ParseQualifierAddedInApi13) {
|
||||
ConfigDescription config;
|
||||
EXPECT_TRUE(TestParse("sw600dp", &config));
|
||||
EXPECT_EQ(String8("sw600dp-v13"), config.toString());
|
||||
|
||||
EXPECT_TRUE(TestParse("sw600dp-v8", &config));
|
||||
EXPECT_EQ(String8("sw600dp-v13"), config.toString());
|
||||
}
|
||||
54
tools/aapt/tests/AaptGroupEntry_test.cpp
Normal file
54
tools/aapt/tests/AaptGroupEntry_test.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <utils/String8.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "AaptAssets.h"
|
||||
#include "ResourceFilter.h"
|
||||
#include "TestHelper.h"
|
||||
|
||||
using android::String8;
|
||||
|
||||
static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const String8& dirName,
|
||||
String8* outType) {
|
||||
if (entry.initFromDirName(dirName, outType)) {
|
||||
return ::testing::AssertionSuccess() << dirName << " was successfully parsed";
|
||||
}
|
||||
return ::testing::AssertionFailure() << dirName << " could not be parsed";
|
||||
}
|
||||
|
||||
static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const char* input,
|
||||
String8* outType) {
|
||||
return TestParse(entry, String8(input), outType);
|
||||
}
|
||||
|
||||
TEST(AaptGroupEntryTest, ParseNoQualifier) {
|
||||
AaptGroupEntry entry;
|
||||
String8 type;
|
||||
EXPECT_TRUE(TestParse(entry, "menu", &type));
|
||||
EXPECT_EQ(String8("menu"), type);
|
||||
}
|
||||
|
||||
TEST(AaptGroupEntryTest, ParseCorrectType) {
|
||||
AaptGroupEntry entry;
|
||||
String8 type;
|
||||
EXPECT_TRUE(TestParse(entry, "anim", &type));
|
||||
EXPECT_EQ(String8("anim"), type);
|
||||
|
||||
EXPECT_TRUE(TestParse(entry, "animator", &type));
|
||||
EXPECT_EQ(String8("animator"), type);
|
||||
}
|
||||
128
tools/aapt/tests/ResourceFilter_test.cpp
Normal file
128
tools/aapt/tests/ResourceFilter_test.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <androidfw/ResourceTypes.h>
|
||||
#include <utils/String8.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "AaptConfig.h"
|
||||
#include "ResourceFilter.h"
|
||||
#include "ConfigDescription.h"
|
||||
|
||||
using android::String8;
|
||||
|
||||
// In this context, 'Axis' represents a particular field in the configuration,
|
||||
// such as language or density.
|
||||
|
||||
TEST(WeakResourceFilterTest, EmptyFilterMatchesAnything) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("")));
|
||||
|
||||
ConfigDescription config;
|
||||
config.density = 320;
|
||||
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
|
||||
config.language[0] = 'f';
|
||||
config.language[1] = 'r';
|
||||
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, MatchesConfigWithUnrelatedAxis) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
|
||||
|
||||
ConfigDescription config;
|
||||
config.density = 320;
|
||||
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxis) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
|
||||
|
||||
ConfigDescription config;
|
||||
config.language[0] = 'f';
|
||||
config.language[1] = 'r';
|
||||
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
|
||||
|
||||
ConfigDescription config;
|
||||
config.language[0] = 'f';
|
||||
config.language[1] = 'r';
|
||||
config.density = 320;
|
||||
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, DoesNotMatchConfigWithDifferentValueAxis) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
|
||||
|
||||
ConfigDescription config;
|
||||
config.language[0] = 'd';
|
||||
config.language[1] = 'e';
|
||||
|
||||
EXPECT_FALSE(filter.match(config));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("de-rDE")));
|
||||
|
||||
ConfigDescription config;
|
||||
config.language[0] = 'd';
|
||||
config.language[1] = 'e';
|
||||
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, ParsesStandardLocaleOnlyString) {
|
||||
WeakResourceFilter filter;
|
||||
EXPECT_EQ(NO_ERROR, filter.parse(String8("de_DE")));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, IgnoresVersion) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("normal-v4")));
|
||||
|
||||
ConfigDescription config;
|
||||
config.smallestScreenWidthDp = 600;
|
||||
config.version = 13;
|
||||
|
||||
// The configs don't match on any axis besides version, which should be ignored.
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
}
|
||||
|
||||
TEST(WeakResourceFilterTest, MatchesConfigWithRegion) {
|
||||
WeakResourceFilter filter;
|
||||
ASSERT_EQ(NO_ERROR, filter.parse(String8("kok,kok_IN,kok_419")));
|
||||
|
||||
ConfigDescription config;
|
||||
AaptLocaleValue val;
|
||||
ASSERT_TRUE(val.initFromFilterString(String8("kok_IN")));
|
||||
val.writeTo(&config);
|
||||
|
||||
EXPECT_TRUE(filter.match(config));
|
||||
}
|
||||
|
||||
33
tools/aapt/tests/TestHelper.h
Normal file
33
tools/aapt/tests/TestHelper.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 __TEST_HELPER_H
|
||||
#define __TEST_HELPER_H
|
||||
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* Stream operator for nicely printing String8's in gtest output.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& stream, const String8& str) {
|
||||
return stream << str.string();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user