Merge "Add support for building split APKs"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f000977620
@@ -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;
|
||||
}
|
||||
@@ -1320,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()) {
|
||||
@@ -1344,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
|
||||
|
||||
@@ -2095,10 +2095,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;
|
||||
}
|
||||
|
||||
@@ -2658,8 +2658,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;
|
||||
|
||||
@@ -2713,14 +2713,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();
|
||||
@@ -2795,7 +2789,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);
|
||||
@@ -2887,7 +2881,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;
|
||||
@@ -2943,11 +2937,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(
|
||||
@@ -2989,7 +2983,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;
|
||||
}
|
||||
|
||||
@@ -3108,7 +3102,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