Merge "Fix clipping and outline for top quick settings header."
diff --git a/api/current.txt b/api/current.txt
index 380d641..40e3ad2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1047,6 +1047,7 @@
field public static final int spinnerStyle = 16842881; // 0x1010081
field public static final int spinnersShown = 16843595; // 0x101034b
field public static final int splitMotionEvents = 16843503; // 0x10102ef
+ field public static final int splitTrack = 16843858; // 0x1010452
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
field public static final int sspPattern = 16843749; // 0x10103e5
@@ -13340,6 +13341,45 @@
method public void stop();
}
+ public final class AudioAttributes {
+ method public int getContentType();
+ method public int getFlags();
+ method public java.util.Set<java.lang.String> getTags();
+ method public int getUsage();
+ field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+ field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+ field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+ field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+ field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+ field public static final int USAGE_ALARM = 4; // 0x4
+ field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+ field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+ field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+ field public static final int USAGE_GAME = 14; // 0xe
+ field public static final int USAGE_MEDIA = 1; // 0x1
+ field public static final int USAGE_NOTIFICATION = 5; // 0x5
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+ field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+ field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6
+ field public static final int USAGE_UNKNOWN = 0; // 0x0
+ field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+ field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+ }
+
+ public static class AudioAttributes.Builder {
+ ctor public AudioAttributes.Builder();
+ ctor public AudioAttributes.Builder(android.media.AudioAttributes);
+ method public android.media.AudioAttributes.Builder addTag(java.lang.String);
+ method public android.media.AudioAttributes build();
+ method public android.media.AudioAttributes.Builder setContentType(int);
+ method public android.media.AudioAttributes.Builder setFlags(int);
+ method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
+ method public android.media.AudioAttributes.Builder setUsage(int);
+ }
+
public class AudioFormat {
ctor public AudioFormat();
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -34347,9 +34387,11 @@
ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int);
ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int, int);
method public int getKeyProgressIncrement();
+ method public boolean getSplitTrack();
method public android.graphics.drawable.Drawable getThumb();
method public int getThumbOffset();
method public void setKeyProgressIncrement(int);
+ method public void setSplitTrack(boolean);
method public void setThumb(android.graphics.drawable.Drawable);
method public void setThumbOffset(int);
}
@@ -36154,6 +36196,7 @@
ctor public Switch(android.content.Context, android.util.AttributeSet);
ctor public Switch(android.content.Context, android.util.AttributeSet, int);
ctor public Switch(android.content.Context, android.util.AttributeSet, int, int);
+ method public boolean getSplitTrack();
method public int getSwitchMinWidth();
method public int getSwitchPadding();
method public java.lang.CharSequence getTextOff();
@@ -36162,6 +36205,7 @@
method public int getThumbTextPadding();
method public android.graphics.drawable.Drawable getTrackDrawable();
method public void onMeasure(int, int);
+ method public void setSplitTrack(boolean);
method public void setSwitchMinWidth(int);
method public void setSwitchPadding(int);
method public void setSwitchTextAppearance(android.content.Context, int);
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
index 804f8ee..862d59e6 100644
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ b/core/java/android/net/BaseNetworkStateTracker.java
@@ -45,6 +45,7 @@
protected NetworkInfo mNetworkInfo;
protected LinkProperties mLinkProperties;
protected LinkCapabilities mLinkCapabilities;
+ protected Network mNetwork = new Network(ConnectivityManager.INVALID_NET_ID);
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
@@ -201,4 +202,14 @@
public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
// nothing to do
}
+
+ @Override
+ public void setNetId(int netId) {
+ mNetwork = new Network(netId);
+ }
+
+ @Override
+ public Network getNetwork() {
+ return mNetwork;
+ }
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 30d7043..3e00250 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -408,6 +408,11 @@
*/
public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
+ /**
+ * @hide
+ */
+ public final static int INVALID_NET_ID = 0;
+
private final IConnectivityManager mService;
private final String mPackageName;
diff --git a/core/java/android/net/Network.aidl b/core/java/android/net/Network.aidl
new file mode 100644
index 0000000..73ba1af
--- /dev/null
+++ b/core/java/android/net/Network.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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 android.net;
+
+parcelable Network;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
new file mode 100644
index 0000000..f82bc22
--- /dev/null
+++ b/core/java/android/net/Network.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+
+/**
+ * Identifies the Network.
+ * @hide
+ */
+public class Network implements Parcelable {
+
+ public final int netId;
+
+ public Network(int netId) {
+ this.netId = netId;
+ }
+
+ public Network(Network that) {
+ this.netId = that.netId;
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(netId);
+ }
+
+ public static final Creator<Network> CREATOR =
+ new Creator<Network>() {
+ public Network createFromParcel(Parcel in) {
+ int netId = in.readInt();
+
+ return new Network(netId);
+ }
+
+ public Network[] newArray(int size) {
+ return new Network[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index c49b1d1..29b57a5 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -250,4 +250,14 @@
*/
public void stopSampling(SamplingDataTracker.SamplingSnapshot s);
+ /*
+ * Record the current netId
+ */
+ public void setNetId(int netId);
+
+ /*
+ * ?
+ */
+ public Network getNetwork();
+
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index f5ff185..9e03f95 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -455,4 +455,14 @@
* Check whether the mobile radio is currently active.
*/
boolean isNetworkActive();
+
+ /**
+ * setup a new network
+ */
+ void createNetwork(int netId, String iface);
+
+ /**
+ * remove a network
+ */
+ void removeNetwork(int netId);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 565ea13..d9a4f57 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -743,7 +743,7 @@
*
* @param view The view whose scroll state is being reported
*
- * @param scrollState The current scroll state. One of
+ * @param scrollState The current scroll state. One of
* {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.
*/
public void onScrollStateChanged(AbsListView view, int scrollState);
@@ -3305,18 +3305,22 @@
private void scrollIfNeeded(int x, int y, MotionEvent vtev) {
int rawDeltaY = y - mMotionY;
+ int scrollOffsetCorrection = 0;
+ int scrollConsumedCorrection = 0;
+ if (mLastY == Integer.MIN_VALUE) {
+ rawDeltaY -= mMotionCorrection;
+ }
if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
rawDeltaY -= mScrollConsumed[1];
- mMotionCorrection -= mScrollOffset[1];
- if (mLastY != Integer.MIN_VALUE) {
- mLastY -= mScrollOffset[1] + mScrollConsumed[1];
- }
+ scrollOffsetCorrection -= mScrollOffset[1];
+ scrollConsumedCorrection -= mScrollConsumed[1];
if (vtev != null) {
vtev.offsetLocation(0, mScrollOffset[1]);
}
}
- final int deltaY = rawDeltaY - mMotionCorrection;
- int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+ final int deltaY = rawDeltaY;
+ int incrementalDeltaY =
+ mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;
int lastYCorrection = 0;
if (mTouchMode == TOUCH_MODE_SCROLL) {
@@ -3378,7 +3382,6 @@
(motionViewRealTop - motionViewPrevTop);
if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
mScrollOffset)) {
- mMotionCorrection -= mScrollOffset[1];
lastYCorrection -= mScrollOffset[1];
if (vtev != null) {
vtev.offsetLocation(0, mScrollOffset[1]);
@@ -3421,9 +3424,9 @@
}
}
}
- mMotionY = y;
+ mMotionY = y + scrollOffsetCorrection;
}
- mLastY = y + lastYCorrection;
+ mLastY = y + lastYCorrection + scrollOffsetCorrection;
}
} else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
if (y != mLastY) {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 225cd6d..4f2d9c6 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -19,7 +19,9 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -32,9 +34,12 @@
import com.android.internal.R;
public abstract class AbsSeekBar extends ProgressBar {
+ private final Rect mTempRect = new Rect();
+
private Drawable mThumb;
private int mThumbOffset;
-
+ private boolean mSplitTrack;
+
/**
* On touch, this offset plus the scaled value from the position of the
* touch will form the progress value. Usually 0.
@@ -51,10 +56,10 @@
* progress.
*/
private int mKeyProgressIncrement = 1;
-
+
private static final int NO_ALPHA = 0xFF;
private float mDisabledAlpha;
-
+
private int mScaledTouchSlop;
private float mTouchDownX;
private boolean mIsDragging;
@@ -76,12 +81,16 @@
TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes);
- Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
- setThumb(thumb); // will guess mThumbOffset if thumb != null...
- // ...but allow layout to override this
- int thumbOffset = a.getDimensionPixelOffset(
+
+ final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
+ setThumb(thumb);
+
+ // Guess thumb offset if thumb != null, but allow layout to override.
+ final int thumbOffset = a.getDimensionPixelOffset(
com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
setThumbOffset(thumbOffset);
+
+ mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false);
a.recycle();
a = context.obtainStyledAttributes(attrs,
@@ -97,7 +106,7 @@
* <p>
* If the thumb is a valid drawable (i.e. not null), half its width will be
* used as the new thumb offset (@see #setThumbOffset(int)).
- *
+ *
* @param thumb Drawable representing the thumb
*/
public void setThumb(Drawable thumb) {
@@ -132,7 +141,7 @@
mThumb = thumb;
invalidate();
if (needUpdate) {
- updateThumbPos(getWidth(), getHeight());
+ updateThumbAndTrackPos(getWidth(), getHeight());
if (thumb != null && thumb.isStateful()) {
// Note that if the states are different this won't work.
// For now, let's consider that an app bug.
@@ -162,7 +171,7 @@
/**
* Sets the thumb offset that allows the thumb to extend out of the range of
* the track.
- *
+ *
* @param thumbOffset The offset amount in pixels.
*/
public void setThumbOffset(int thumbOffset) {
@@ -171,8 +180,27 @@
}
/**
+ * Specifies whether the track should be split by the thumb. When true,
+ * the thumb's optical bounds will be clipped out of the track drawable,
+ * then the thumb will be drawn into the resulting gap.
+ *
+ * @param splitTrack Whether the track should be split by the thumb
+ */
+ public void setSplitTrack(boolean splitTrack) {
+ mSplitTrack = splitTrack;
+ invalidate();
+ }
+
+ /**
+ * Returns whether the track should be split by the thumb.
+ */
+ public boolean getSplitTrack() {
+ return mSplitTrack;
+ }
+
+ /**
* Sets the amount of progress changed via the arrow keys.
- *
+ *
* @param increment The amount to increment or decrement when the user
* presses the arrow keys.
*/
@@ -184,14 +212,14 @@
* Returns the amount of progress changed via the arrow keys.
* <p>
* By default, this will be a value that is derived from the max progress.
- *
+ *
* @return The amount to increment or decrement when the user presses the
* arrow keys. This will be positive.
*/
public int getKeyProgressIncrement() {
return mKeyProgressIncrement;
}
-
+
@Override
public synchronized void setMax(int max) {
super.setMax(max);
@@ -217,79 +245,85 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
-
- Drawable progressDrawable = getProgressDrawable();
+
+ final Drawable progressDrawable = getProgressDrawable();
if (progressDrawable != null) {
progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
}
-
- if (mThumb != null && mThumb.isStateful()) {
- int[] state = getDrawableState();
- mThumb.setState(state);
+
+ final Drawable thumb = mThumb;
+ if (thumb != null && thumb.isStateful()) {
+ thumb.setState(getDrawableState());
}
}
-
+
@Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
- Drawable thumb = mThumb;
+
+ final Drawable thumb = mThumb;
if (thumb != null) {
setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- /*
- * Since we draw translated, the drawable's bounds that it signals
- * for invalidation won't be the actual bounds we want invalidated,
- * so just invalidate this whole view.
- */
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
invalidate();
}
}
-
-
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- updateThumbPos(w, h);
+
+ updateThumbAndTrackPos(w, h);
}
- private void updateThumbPos(int w, int h) {
- Drawable d = getCurrentDrawable();
- Drawable thumb = mThumb;
- int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+ private void updateThumbAndTrackPos(int w, int h) {
+ final Drawable track = getCurrentDrawable();
+ final Drawable thumb = mThumb;
+
// The max height does not incorporate padding, whereas the height
- // parameter does
- int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
-
- int max = getMax();
- float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-
+ // parameter does.
+ final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
+ final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+
+ // Apply offset to whichever item is taller.
+ final int trackOffset;
+ final int thumbOffset;
if (thumbHeight > trackHeight) {
- if (thumb != null) {
- setThumbPos(w, thumb, scale, 0);
- }
- int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
- if (d != null) {
- // Canvas will be translated by the padding, so 0,0 is where we start drawing
- d.setBounds(0, gapForCenteringTrack,
- w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack
- - mPaddingTop);
- }
+ trackOffset = (thumbHeight - trackHeight) / 2;
+ thumbOffset = 0;
} else {
- if (d != null) {
- // Canvas will be translated by the padding, so 0,0 is where we start drawing
- d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
- - mPaddingTop);
- }
- int gap = (trackHeight - thumbHeight) / 2;
- if (thumb != null) {
- setThumbPos(w, thumb, scale, gap);
- }
+ trackOffset = 0;
+ thumbOffset = (trackHeight - thumbHeight) / 2;
+ }
+
+ if (track != null) {
+ track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft,
+ h - mPaddingBottom - trackOffset - mPaddingTop);
+ }
+
+ if (thumb != null) {
+ setThumbPos(w, thumb, getScale(), thumbOffset);
}
}
+ private float getScale() {
+ final int max = getMax();
+ return max > 0 ? getProgress() / (float) max : 0;
+ }
+
/**
- * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and
+ * Updates the thumb drawable bounds.
+ *
+ * @param w Width of the view, including padding
+ * @param thumb Drawable used for the thumb
+ * @param scale Current progress between 0 and 1
+ * @param offset Vertical offset for centering. If set to
+ * {@link Integer#MIN_VALUE}, the current offset will be used.
*/
- private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
+ private void setThumbPos(int w, Drawable thumb, float scale, int offset) {
int available = w - mPaddingLeft - mPaddingRight;
final int thumbWidth = thumb.getIntrinsicWidth();
final int thumbHeight = thumb.getIntrinsicHeight();
@@ -301,13 +335,13 @@
final int thumbPos = (int) (scale * available + 0.5f);
final int top, bottom;
- if (gap == Integer.MIN_VALUE) {
+ if (offset == Integer.MIN_VALUE) {
final Rect oldBounds = thumb.getBounds();
top = oldBounds.top;
bottom = oldBounds.bottom;
} else {
- top = gap;
- bottom = gap + thumbHeight;
+ top = offset;
+ bottom = offset + thumbHeight;
}
final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos;
@@ -342,6 +376,33 @@
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
+ drawThumb(canvas);
+ }
+
+ @Override
+ void drawTrack(Canvas canvas) {
+ final Drawable thumbDrawable = mThumb;
+ if (thumbDrawable != null && mSplitTrack) {
+ final Insets insets = thumbDrawable.getOpticalInsets();
+ final Rect tempRect = mTempRect;
+ thumbDrawable.copyBounds(tempRect);
+ tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop);
+ tempRect.left += insets.left;
+ tempRect.right -= insets.right;
+
+ final int saveCount = canvas.save();
+ canvas.clipRect(tempRect, Op.DIFFERENCE);
+ super.drawTrack(canvas);
+ canvas.restoreToCount(saveCount);
+ } else {
+ super.drawTrack(canvas);
+ }
+ }
+
+ /**
+ * Draw the thumb.
+ */
+ void drawThumb(Canvas canvas) {
if (mThumb != null) {
canvas.save();
// Translate the padding. For the x, we need to allow the thumb to
@@ -366,17 +427,17 @@
}
dw += mPaddingLeft + mPaddingRight;
dh += mPaddingTop + mPaddingBottom;
-
+
setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
resolveSizeAndState(dh, heightMeasureSpec, 0));
}
-
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mIsUserSeekable || !isEnabled()) {
return false;
}
-
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isInScrollingContainer()) {
@@ -391,7 +452,7 @@
attemptClaimDrag();
}
break;
-
+
case MotionEvent.ACTION_MOVE:
if (mIsDragging) {
trackTouchEvent(event);
@@ -408,7 +469,7 @@
}
}
break;
-
+
case MotionEvent.ACTION_UP:
if (mIsDragging) {
trackTouchEvent(event);
@@ -426,7 +487,7 @@
// value has not apparently changed)
invalidate();
break;
-
+
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
onStopTrackingTouch();
@@ -493,7 +554,7 @@
mParent.requestDisallowInterceptTouchEvent(true);
}
}
-
+
/**
* This is called when the user has started touching this widget.
*/
@@ -526,7 +587,7 @@
setProgress(progress - mKeyProgressIncrement, true);
onKeyChange();
return true;
-
+
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (progress >= getMax()) break;
setProgress(progress + mKeyProgressIncrement, true);
@@ -595,17 +656,13 @@
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
- int max = getMax();
- float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-
- Drawable thumb = mThumb;
+ final Drawable thumb = mThumb;
if (thumb != null) {
- setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- /*
- * Since we draw translated, the drawable's bounds that it signals
- * for invalidation won't be the actual bounds we want invalidated,
- * so just invalidate this whole view.
- */
+ setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE);
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
invalidate();
}
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index f7e81b8..0c3715d 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1066,21 +1066,30 @@
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
- Drawable d = mCurrentDrawable;
+ drawTrack(canvas);
+ }
+
+ /**
+ * Draws the progress bar track.
+ */
+ void drawTrack(Canvas canvas) {
+ final Drawable d = mCurrentDrawable;
if (d != null) {
// Translate canvas so a indeterminate circular progress bar with padding
// rotates properly in its animation
- canvas.save();
+ final int saveCount = canvas.save();
+
if(isLayoutRtl() && mMirrorForRtl) {
canvas.translate(getWidth() - mPaddingRight, mPaddingTop);
canvas.scale(-1.0f, 1.0f);
} else {
canvas.translate(mPaddingLeft, mPaddingTop);
}
- long time = getDrawingTime();
+
+ final long time = getDrawingTime();
if (mHasAnimation) {
mAnimation.getTransformation(time, mTransformation);
- float scale = mTransformation.getAlpha();
+ final float scale = mTransformation.getAlpha();
try {
mInDrawing = true;
d.setLevel((int) (scale * MAX_LEVEL));
@@ -1089,8 +1098,10 @@
}
postInvalidateOnAnimation();
}
+
d.draw(canvas);
- canvas.restore();
+ canvas.restoreToCount(saveCount);
+
if (mShouldStartAnimationDrawable && d instanceof Animatable) {
((Animatable) d).start();
mShouldStartAnimationDrawable = false;
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 08af4de..438e164 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -22,9 +22,11 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
@@ -85,6 +87,7 @@
private int mThumbTextPadding;
private int mSwitchMinWidth;
private int mSwitchPadding;
+ private boolean mSplitTrack;
private CharSequence mTextOn;
private CharSequence mTextOff;
@@ -174,13 +177,13 @@
super(context, attrs, defStyleAttr, defStyleRes);
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
- Resources res = getResources();
+
+ final Resources res = getResources();
mTextPaint.density = res.getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);
-
mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
@@ -191,15 +194,16 @@
com.android.internal.R.styleable.Switch_switchMinWidth, 0);
mSwitchPadding = a.getDimensionPixelSize(
com.android.internal.R.styleable.Switch_switchPadding, 0);
+ mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
- int appearance = a.getResourceId(
+ final int appearance = a.getResourceId(
com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
if (appearance != 0) {
setSwitchTextAppearance(context, appearance);
}
a.recycle();
- ViewConfiguration config = ViewConfiguration.get(context);
+ final ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
@@ -469,6 +473,29 @@
}
/**
+ * Specifies whether the track should be split by the thumb. When true,
+ * the thumb's optical bounds will be clipped out of the track drawable,
+ * then the thumb will be drawn into the resulting gap.
+ *
+ * @param splitTrack Whether the track should be split by the thumb
+ *
+ * @attr ref android.R.styleable#Switch_splitTrack
+ */
+ public void setSplitTrack(boolean splitTrack) {
+ mSplitTrack = splitTrack;
+ invalidate();
+ }
+
+ /**
+ * Returns whether the track should be split by the thumb.
+ *
+ * @attr ref android.R.styleable#Switch_splitTrack
+ */
+ public boolean getSplitTrack() {
+ return mSplitTrack;
+ }
+
+ /**
* Returns the text displayed when the button is in the checked state.
*
* @attr ref android.R.styleable#Switch_textOn
@@ -518,13 +545,15 @@
mTrackDrawable.getPadding(mTempRect);
- final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
+ final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+ + mThumbTextPadding * 2;
+ mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
+
final int switchWidth = Math.max(mSwitchMinWidth,
- maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
+ 2 * mThumbWidth + mTempRect.left + mTempRect.right);
final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(),
mThumbDrawable.getIntrinsicHeight());
- mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
mSwitchWidth = switchWidth;
mSwitchHeight = switchHeight;
@@ -777,7 +806,7 @@
final Drawable trackDrawable = mTrackDrawable;
final Drawable thumbDrawable = mThumbDrawable;
- // Draw the switch
+ // Layout the track.
final int switchLeft = mSwitchLeft;
final int switchTop = mSwitchTop;
final int switchRight = mSwitchRight;
@@ -793,9 +822,10 @@
// Relies on mTempRect, MUST be called first!
final int thumbPos = getThumbOffset();
+ // Layout the thumb.
thumbDrawable.getPadding(tempRect);
- int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
- int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
+ final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
+ final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
final Drawable background = getBackground();
@@ -805,20 +835,32 @@
super.onDraw(canvas);
- trackDrawable.draw(canvas);
+ if (mSplitTrack) {
+ final Insets insets = thumbDrawable.getOpticalInsets();
+ thumbDrawable.copyBounds(tempRect);
+ tempRect.left += insets.left;
+ tempRect.right -= insets.right;
+
+ final int saveCount = canvas.save();
+ canvas.clipRect(tempRect, Op.DIFFERENCE);
+ trackDrawable.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ } else {
+ trackDrawable.draw(canvas);
+ }
final int saveCount = canvas.save();
canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
thumbDrawable.draw(canvas);
- final int drawableState[] = getDrawableState();
- if (mTextColors != null) {
- mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
- }
- mTextPaint.drawableState = drawableState;
-
final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
if (switchText != null) {
+ final int drawableState[] = getDrawableState();
+ if (mTextColors != null) {
+ mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
+ }
+ mTextPaint.drawableState = drawableState;
+
final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2;
final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
canvas.translate(left, top);
@@ -889,12 +931,16 @@
protected void drawableStateChanged() {
super.drawableStateChanged();
- int[] myDrawableState = getDrawableState();
+ final int[] myDrawableState = getDrawableState();
- // Set the state of the Drawable
- // Drawable may be null when checked state is set from XML, from super constructor
- if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);
- if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);
+ if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) {
+ // Handle changes to thumb width and height.
+ requestLayout();
+ }
+
+ if (mTrackDrawable != null) {
+ mTrackDrawable.setState(myDrawableState);
+ }
invalidate();
}
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 52281d9..34f62ba 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -450,6 +450,7 @@
public void disconnect() {
if ((mConnection != null) && (mSrcContext != null)) {
mSrcContext.unbindService(mConnection);
+ mConnection = null;
}
try {
// Send the DISCONNECTED, although it may not be received
@@ -463,10 +464,12 @@
// Tell source we're disconnected.
if (mSrcHandler != null) {
replyDisconnected(STATUS_SUCCESSFUL);
+ mSrcHandler = null;
}
// Unlink only when bindService isn't used
if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
+ mDeathMonitor = null;
}
}
diff --git a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
index f1023ea..5a99528 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
index 15ceeee..79de664 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
index 90b1498..73e8f1c 100644
--- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
index b535758..ff6affe 100644
--- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
index 1833704..e40cba8 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
index e64d3f2..437a3e3 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
index ffd6c39..8949b52 100644
--- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
index 15faff0..d727683 100644
--- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
index ad72f06..729e0bf 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
index 7aceed1..d018a7c 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
index 309b528..a7a972c 100644
--- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
index 139795e..dd8910b 100644
--- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
index c11b0ae..a2b5716 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
index cde797e..caabc2c 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
index 9e234af..8d79a13 100644
--- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
index b371eab..e0e4ef9 100644
--- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
index d172b05..f82fe7a 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
@@ -16,22 +16,26 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false">
- <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+ <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item>
<layer-list>
<item android:id="@id/background">
- <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+ <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item android:id="@id/secondaryProgress">
- <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </scale>
</item>
<item android:id="@id/progress">
- <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </scale>
</item>
</layer-list>
</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7a6832e..cedb92d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3343,6 +3343,8 @@
<attr name="thumb" format="reference" />
<!-- An offset for the thumb that allows it to extend out of the range of the track. -->
<attr name="thumbOffset" format="dimension" />
+ <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+ <attr name="splitTrack" format="boolean" />
</declare-styleable>
<declare-styleable name="StackView">
@@ -6393,6 +6395,8 @@
<attr name="switchMinWidth" format="dimension" />
<!-- Minimum space between the switch and caption text -->
<attr name="switchPadding" format="dimension" />
+ <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+ <attr name="splitTrack" />
</declare-styleable>
<declare-styleable name="Pointer">
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ecb22ae..dc5efea 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2169,6 +2169,7 @@
<public type="attr" name="toId" />
<public type="attr" name="fromId" />
<public type="attr" name="reversible" />
+ <public type="attr" name="splitTrack" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 88a2a9f..e693673 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -455,10 +455,10 @@
<style name="Widget.Quantum.CompoundButton.Switch">
<item name="track">@drawable/switch_track_quantum</item>
<item name="thumb">@drawable/switch_inner_quantum</item>
+ <item name="splitTrack">true</item>
<item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item>
<item name="textOn"></item>
<item name="textOff"></item>
- <item name="thumbTextPadding">12dip</item>
<item name="switchMinWidth">72dip</item>
<item name="switchPadding">16dip</item>
<item name="background">?attr/selectableItemBackground</item>
@@ -572,10 +572,8 @@
<item name="indeterminateOnly">false</item>
<item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
<item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
- <item name="minHeight">13dip</item>
- <item name="maxHeight">13dip</item>
<item name="thumb">@drawable/scrubber_control_selector_quantum</item>
- <item name="thumbOffset">16dip</item>
+ <item name="splitTrack">true</item>
<item name="focusable">true</item>
<item name="paddingStart">16dip</item>
<item name="paddingEnd">16dip</item>
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 098753b..6aad5fb 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2452,15 +2452,19 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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) {
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
new file mode 100644
index 0000000..bb23a36
--- /dev/null
+++ b/media/java/android/media/AudioAttributes.java
@@ -0,0 +1,444 @@
+/*
+ * 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 android.media;
+
+import android.annotation.IntDef;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A class to encapsulate a collection of attributes describing information about an audio
+ * player or recorder.
+ */
+public final class AudioAttributes {
+ private final static String TAG = "AudioAttributes";
+
+ /**
+ * Content type value to use when the content type is unknown, or other than the ones defined.
+ */
+ public final static int CONTENT_TYPE_UNKNOWN = 0;
+ /**
+ * Content type value to use when the content type is speech.
+ */
+ public final static int CONTENT_TYPE_SPEECH = 1;
+ /**
+ * Content type value to use when the content type is music.
+ */
+ public final static int CONTENT_TYPE_MUSIC = 2;
+ /**
+ * Content type value to use when the content type is a soundtrack, typically accompanying
+ * a movie or TV program.
+ */
+ public final static int CONTENT_TYPE_MOVIE = 3;
+ /**
+ * Content type value to use when the content type is a sound used to accompany a user
+ * action, such as a beep or sound effect expressing a key click, or event, such as the
+ * type of a sound for a bonus being received in a game. These sounds are mostly synthesized
+ * or short Foley sounds.
+ */
+ public final static int CONTENT_TYPE_SONIFICATION = 4;
+
+ /**
+ * Usage value to use when the usage is unknown.
+ */
+ public final static int USAGE_UNKNOWN = 0;
+ /**
+ * Usage value to use when the usage is media, such as music, or movie
+ * soundtracks.
+ */
+ public final static int USAGE_MEDIA = 1;
+ /**
+ * Usage value to use when the usage is voice communications, such as telephony
+ * or VoIP.
+ */
+ public final static int USAGE_VOICE_COMMUNICATION = 2;
+ /**
+ * Usage value to use when the usage is in-call signalling, such as with
+ * a "busy" beep, or DTMF tones.
+ */
+ public final static int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3;
+ /**
+ * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+ */
+ public final static int USAGE_ALARM = 4;
+ /**
+ * Usage value to use when the usage is notification. See other
+ * notification usages for more specialized uses.
+ */
+ public final static int USAGE_NOTIFICATION = 5;
+ /**
+ * Usage value to use when the usage is telephony ringtone.
+ */
+ public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6;
+ /**
+ * Usage value to use when the usage is a request to enter/end a
+ * communication, such as a VoIP communication or video-conference.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7;
+ /**
+ * Usage value to use when the usage is notification for an "instant"
+ * communication such as a chat, or SMS.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8;
+ /**
+ * Usage value to use when the usage is notification for a
+ * non-immediate type of communication such as e-mail.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9;
+ /**
+ * Usage value to use when the usage is to attract the user's attention,
+ * such as a reminder or low battery warning.
+ */
+ public final static int USAGE_NOTIFICATION_EVENT = 10;
+ /**
+ * Usage value to use when the usage is for accessibility, such as with
+ * a screen reader.
+ */
+ public final static int USAGE_ASSISTANCE_ACCESSIBILITY = 11;
+ /**
+ * Usage value to use when the usage is driving or navigation directions.
+ */
+ public final static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12;
+ /**
+ * Usage value to use when the usage is sonification, such as with user
+ * interface sounds.
+ */
+ public final static int USAGE_ASSISTANCE_SONIFICATION = 13;
+ /**
+ * Usage value to use when the usage is for game audio.
+ */
+ public final static int USAGE_GAME = 14;
+
+ /**
+ * Flag defining a behavior where the audibility of the sound will be ensured by the system.
+ */
+ public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
+ /**
+ * @hide
+ * Flag defining a behavior where the playback of the sound is ensured without
+ * degradation only when going to a secure sink.
+ */
+ // FIXME not guaranteed yet
+ // TODO add OR to getFlags() when supported and in public API
+ public final static int FLAG_SECURE = 0x1 << 1;
+ /**
+ * @hide
+ * Flag to enable when the stream is associated with SCO usage.
+ * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO
+ */
+ public final static int FLAG_SCO = 0x1 << 2;
+
+
+ private int mUsage = USAGE_UNKNOWN;
+ private int mContentType = CONTENT_TYPE_UNKNOWN;
+ private int mFlags = 0x0;
+ private HashSet<String> mTags;
+
+ private AudioAttributes() {
+ }
+
+ /**
+ * Return the content type.
+ * @return one of the values that can be set in {@link Builder#setContentType(int)}
+ */
+ public int getContentType() {
+ return mContentType;
+ }
+
+ /**
+ * Return the usage.
+ * @return one of the values that can be set in {@link Builder#setUsage(int)}
+ */
+ public int getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * Return the flags.
+ * @return a combined mask of all flags
+ */
+ public int getFlags() {
+ // only return the flags that are public
+ return (mFlags & (FLAG_AUDIBILITY_ENFORCED));
+ }
+
+ /**
+ * @hide
+ * Return all the flags, even the non-public ones.
+ * Internal use only
+ * @return a combined mask of all flags
+ */
+ public int getAllFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Return the set of tags.
+ * @return a read-only set of all tags stored as strings.
+ */
+ public Set<String> getTags() {
+ return Collections.unmodifiableSet(mTags);
+ }
+
+ /**
+ * Builder class for {@link AudioAttributes} objects.
+ */
+ public static class Builder {
+ private int mUsage = USAGE_UNKNOWN;
+ private int mContentType = CONTENT_TYPE_UNKNOWN;
+ private int mFlags = 0x0;
+ private HashSet<String> mTags = new HashSet<String>();
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given AudioAttributes
+ * @param aa the AudioAttributes object whose data will be reused in the new Builder.
+ */
+ @SuppressWarnings("unchecked") // for cloning of mTags
+ public Builder(AudioAttributes aa) {
+ mUsage = aa.mUsage;
+ mContentType = aa.mContentType;
+ mFlags = aa.mFlags;
+ mTags = (HashSet<String>) aa.mTags.clone();
+ }
+
+ /**
+ * Combines all of the attributes that have been set and return a new
+ * {@link AudioAttributes} object.
+ * @return a new {@link AudioAttributes} object
+ */
+ @SuppressWarnings("unchecked") // for cloning of mTags
+ public AudioAttributes build() {
+ AudioAttributes aa = new AudioAttributes();
+ aa.mContentType = mContentType;
+ aa.mUsage = mUsage;
+ aa.mFlags = mFlags;
+ aa.mTags = (HashSet<String>) mTags.clone();
+ return aa;
+ }
+
+ /**
+ * Sets the attribute describing what is the intended use of the the audio signal,
+ * such as alarm or ringtone.
+ * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN},
+ * {@link AudioAttributes#USAGE_MEDIA},
+ * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION},
+ * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING},
+ * {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_TELEPHONY_RINGTONE},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_EVENT},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION},
+ * {@link AudioAttributes#USAGE_GAME}.
+ * @return the same Builder instance.
+ */
+ public Builder setUsage(@AttributeUsage int usage) {
+ switch (usage) {
+ case USAGE_UNKNOWN:
+ case USAGE_MEDIA:
+ case USAGE_VOICE_COMMUNICATION:
+ case USAGE_VOICE_COMMUNICATION_SIGNALLING:
+ case USAGE_ALARM:
+ case USAGE_NOTIFICATION:
+ case USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+ case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ case USAGE_NOTIFICATION_EVENT:
+ case USAGE_ASSISTANCE_ACCESSIBILITY:
+ case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+ case USAGE_ASSISTANCE_SONIFICATION:
+ case USAGE_GAME:
+ mUsage = usage;
+ break;
+ default:
+ mUsage = USAGE_UNKNOWN;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the attribute describing the content type of the audio signal, such as speech,
+ * or music.
+ * @param contentType the content type values, one of
+ * {@link AudioAttributes#CONTENT_TYPE_MOVIE},
+ * {@link AudioAttributes#CONTENT_TYPE_MUSIC},
+ * {@link AudioAttributes#CONTENT_TYPE_SONIFICATION},
+ * {@link AudioAttributes#CONTENT_TYPE_SPEECH},
+ * {@link AudioAttributes#CONTENT_TYPE_UNKNOWN}.
+ * @return the same Builder instance.
+ */
+ public Builder setContentType(@AttributeContentType int contentType) {
+ switch (contentType) {
+ case CONTENT_TYPE_UNKNOWN:
+ case CONTENT_TYPE_MOVIE:
+ case CONTENT_TYPE_MUSIC:
+ case CONTENT_TYPE_SONIFICATION:
+ case CONTENT_TYPE_SPEECH:
+ mContentType = contentType;
+ break;
+ default:
+ mUsage = CONTENT_TYPE_UNKNOWN;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the combination of flags.
+ * @param flags the {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED} flag.
+ * @return the same Builder instance.
+ */
+ public Builder setFlags(int flags) {
+ flags &= (AudioAttributes.FLAG_AUDIBILITY_ENFORCED | AudioAttributes.FLAG_SCO
+ | AudioAttributes.FLAG_SECURE);
+ mFlags |= flags;
+ return this;
+ }
+
+ /**
+ * Add a custom tag stored as a string
+ * @param tag
+ * @return the same Builder instance.
+ */
+ public Builder addTag(String tag) {
+ mTags.add(tag);
+ return this;
+ }
+
+ /**
+ * Adds attributes inferred from the legacy stream types.
+ * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL},
+ * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},
+ * {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM},
+ * or {@link AudioManager#STREAM_NOTIFICATION}.
+ * @return the same Builder instance.
+ */
+ public Builder setLegacyStreamType(int streamType) {
+ return setInternalLegacyStreamType(streamType);
+ }
+
+ /**
+ * @hide
+ * For internal framework use only, enables building from hidden stream types.
+ * @param streamType
+ * @return the same Builder instance.
+ */
+ public Builder setInternalLegacyStreamType(int streamType) {
+ switch(streamType) {
+ case AudioSystem.STREAM_VOICE_CALL:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_VOICE_COMMUNICATION;
+ break;
+ case AudioSystem.STREAM_SYSTEM_ENFORCED:
+ mFlags |= FLAG_AUDIBILITY_ENFORCED;
+ // intended fall through, attributes in common with STREAM_SYSTEM
+ case AudioSystem.STREAM_SYSTEM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_ASSISTANCE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_RING:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+ break;
+ case AudioSystem.STREAM_MUSIC:
+ mContentType = CONTENT_TYPE_MUSIC;
+ mUsage = USAGE_MEDIA;
+ break;
+ case AudioSystem.STREAM_ALARM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_ALARM;
+ break;
+ case AudioSystem.STREAM_NOTIFICATION:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_NOTIFICATION;
+ break;
+ case AudioSystem.STREAM_BLUETOOTH_SCO:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_VOICE_COMMUNICATION;
+ mFlags |= FLAG_SCO;
+ break;
+ case AudioSystem.STREAM_DTMF:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_VOICE_COMMUNICATION_SIGNALLING;
+ break;
+ case AudioSystem.STREAM_TTS:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_ASSISTANCE_ACCESSIBILITY;
+ break;
+ default:
+ Log.e(TAG, "Invalid stream type " + streamType + " in for AudioAttributes");
+ }
+ return this;
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString () {
+ return new String("AudioAttributes:"
+ + " usage=" + mUsage
+ + " content=" + mContentType
+ + " flags=0x" + Integer.toHexString(mFlags)
+ + " tags=" + mTags);
+ }
+
+ /** @hide */
+ @IntDef({
+ USAGE_UNKNOWN,
+ USAGE_MEDIA,
+ USAGE_VOICE_COMMUNICATION,
+ USAGE_VOICE_COMMUNICATION_SIGNALLING,
+ USAGE_ALARM,
+ USAGE_NOTIFICATION,
+ USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+ USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+ USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+ USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+ USAGE_NOTIFICATION_EVENT,
+ USAGE_ASSISTANCE_ACCESSIBILITY,
+ USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ USAGE_ASSISTANCE_SONIFICATION,
+ USAGE_GAME
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttributeUsage {}
+
+ /** @hide */
+ @IntDef({
+ CONTENT_TYPE_UNKNOWN,
+ CONTENT_TYPE_SPEECH,
+ CONTENT_TYPE_MUSIC,
+ CONTENT_TYPE_MOVIE,
+ CONTENT_TYPE_SONIFICATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttributeContentType {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 7306c83..bd9de82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -82,7 +82,7 @@
mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
MAX_ITEMS_IN_TOP_STACK,
mTopStackPeekSize,
- mTopStackTotalSize,
+ mTopStackTotalSize - mTopStackPeekSize,
0.5f);
mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
MAX_ITEMS_IN_BOTTOM_STACK,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dfffa8a..4ea33db 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -65,6 +65,7 @@
import android.net.LinkProperties.CompareResult;
import android.net.LinkQualityInfo;
import android.net.MobileDataStateTracker;
+import android.net.Network;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -165,6 +166,8 @@
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
+import static android.net.ConnectivityManager.INVALID_NET_ID;
+
/**
* @hide
*/
@@ -442,6 +445,10 @@
TelephonyManager mTelephonyManager;
+ private final static int MIN_NET_ID = 10; // some reserved marks
+ private final static int MAX_NET_ID = 65535;
+ private int mNextNetId = MIN_NET_ID;
+
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
@@ -706,6 +713,12 @@
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
+ private synchronized int nextNetId() {
+ int netId = mNextNetId;
+ if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+ return netId;
+ }
+
/**
* Factory that creates {@link NetworkStateTracker} instances using given
* {@link NetworkConfig}.
@@ -1984,6 +1997,7 @@
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+ int thisNetId = mNetTrackers[prevNetType].getNetwork().netId;
// Remove idletimer previously setup in {@code handleConnect}
if (mNetConfigs[prevNetType].isDefault()) {
@@ -2069,6 +2083,13 @@
sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(),
getConnectivityChangeDelay());
}
+ try {
+ mNetd.removeNetwork(thisNetId);
+ } catch (Exception e) {
+ loge("Exception removing network: " + e);
+ } finally {
+ mNetTrackers[prevNetType].setNetId(INVALID_NET_ID);
+ }
}
private void tryFailover(int prevNetType) {
@@ -2336,17 +2357,23 @@
if (mNetConfigs[newNetType].isDefault()) {
if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
- // tear down the other
- NetworkStateTracker otherNet =
- mNetTrackers[mActiveDefaultNetwork];
- if (DBG) {
- log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
- " teardown");
- }
- if (!teardown(otherNet)) {
- loge("Network declined teardown request");
- teardown(thisNet);
- return;
+ String teardownPolicy = SystemProperties.get("net.teardownPolicy");
+ if (TextUtils.equals(teardownPolicy, "keep") == false) {
+ // tear down the other
+ NetworkStateTracker otherNet =
+ mNetTrackers[mActiveDefaultNetwork];
+ if (DBG) {
+ log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
+ " teardown");
+ }
+ if (!teardown(otherNet)) {
+ loge("Network declined teardown request");
+ teardown(thisNet);
+ return;
+ }
+ } else {
+ //TODO - remove
+ loge("network teardown skipped due to net.teardownPolicy setting");
}
} else {
// don't accept this one
@@ -2358,6 +2385,15 @@
return;
}
}
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+ mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
setupDataActivityTracking(newNetType);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in a second
@@ -2380,6 +2416,16 @@
// Don't do this - if we never sign in stay, grey
//reportNetworkCondition(mActiveDefaultNetwork, 100);
updateNetworkSettings(thisNet);
+ } else {
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+ mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
}
thisNet.setTeardownRequested(false);
updateMtuSizeSettings(thisNet);
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 0d1e122..96f9ab0 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -50,6 +50,8 @@
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
private static final boolean LOGD = false;
+ private final static boolean VDBG = false;
+
private final String TAG;
private String mSocket;
@@ -409,7 +411,7 @@
loge("timed-out waiting for response to " + logCmd);
throw new NativeDaemonFailureException(logCmd, event);
}
- log("RMV <- {" + event + "}");
+ if (VDBG) log("RMV <- {" + event + "}");
events.add(event);
} while (event.isClassContinue());
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7ce45f7..b9c86dc 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2029,4 +2029,24 @@
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
+
+ public void createNetwork(int netId, String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "create", netId, iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ public void removeNetwork(int netId) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "destroy", netId);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 4f0c9b5..512ebc6 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -171,6 +171,7 @@
return;
}
writer.println("Current scorer: " + currentScorer);
+ writer.flush();
for (INetworkScoreCache scoreCache : getScoreCaches()) {
try {
diff --git a/tests/Split/Android.mk b/tests/Split/Android.mk
new file mode 100644
index 0000000..7884d4d
--- /dev/null
+++ b/tests/Split/Android.mk
@@ -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)
diff --git a/tests/Split/AndroidManifest.xml b/tests/Split/AndroidManifest.xml
new file mode 100644
index 0000000..a4956a7
--- /dev/null
+++ b/tests/Split/AndroidManifest.xml
@@ -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>
diff --git a/tests/Split/assets/blah.txt b/tests/Split/assets/blah.txt
new file mode 100644
index 0000000..1b37e40
--- /dev/null
+++ b/tests/Split/assets/blah.txt
@@ -0,0 +1 @@
+This is some useful info.
diff --git a/tests/Split/assets/statement.xml b/tests/Split/assets/statement.xml
new file mode 100644
index 0000000..91750d1
--- /dev/null
+++ b/tests/Split/assets/statement.xml
@@ -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>
diff --git a/tests/Split/res/layout-fr-sw600dp/main.xml b/tests/Split/res/layout-fr-sw600dp/main.xml
new file mode 100644
index 0000000..2461c8c
--- /dev/null
+++ b/tests/Split/res/layout-fr-sw600dp/main.xml
@@ -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>
diff --git a/tests/Split/res/layout/main.xml b/tests/Split/res/layout/main.xml
new file mode 100644
index 0000000..36992a2
--- /dev/null
+++ b/tests/Split/res/layout/main.xml
@@ -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"/>
diff --git a/tests/Split/res/values-de/values.xml b/tests/Split/res/values-de/values.xml
new file mode 100644
index 0000000..26d0507
--- /dev/null
+++ b/tests/Split/res/values-de/values.xml
@@ -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>
diff --git a/tests/Split/res/values-fr/values.xml b/tests/Split/res/values-fr/values.xml
new file mode 100644
index 0000000..16532da
--- /dev/null
+++ b/tests/Split/res/values-fr/values.xml
@@ -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>
diff --git a/tests/Split/res/values-sw600dp/values.xml b/tests/Split/res/values-sw600dp/values.xml
new file mode 100644
index 0000000..a8329bb
--- /dev/null
+++ b/tests/Split/res/values-sw600dp/values.xml
@@ -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>
diff --git a/tests/Split/res/values/values.xml b/tests/Split/res/values/values.xml
new file mode 100644
index 0000000..68edc77
--- /dev/null
+++ b/tests/Split/res/values/values.xml
@@ -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>
diff --git a/tests/Split/src/java/com/android/example/split/ActivityMain.java b/tests/Split/src/java/com/android/example/split/ActivityMain.java
new file mode 100644
index 0000000..a15fb3c
--- /dev/null
+++ b/tests/Split/src/java/com/android/example/split/ActivityMain.java
@@ -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);
+ }
+}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index e0dab78..12d5389 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -3,8 +3,10 @@
//
#include "AaptAssets.h"
-#include "ResourceFilter.h"
+#include "AaptConfig.h"
+#include "AaptUtil.h"
#include "Main.h"
+#include "ResourceFilter.h"
#include <utils/misc.h>
#include <utils/SortedVector.h>
@@ -14,7 +16,6 @@
#include <errno.h>
static const char* kDefaultLocale = "default";
-static const char* kWildcardName = "any";
static const char* kAssetDir = "assets";
static const char* kResourceDir = "res";
static const char* kValuesDir = "values";
@@ -149,24 +150,6 @@
// =========================================================================
// =========================================================================
-/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
- Vector<String8>* parts, const char separator) {
- const char *p = chars;
- const char *q;
- while (NULL != (q = strchr(p, separator))) {
- String8 val(p, q - p);
- val.toLower();
- parts->add(val);
- p = q+1;
- }
-
- if (p < chars + strlen(chars)) {
- String8 val(p);
- val.toLower();
- parts->add(val);
- }
-}
-
/* static */
inline bool isAlpha(const String8& string) {
const size_t length = string.length();
@@ -230,8 +213,7 @@
bool AaptLocaleValue::initFromFilterString(const String8& str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
- Vector<String8> parts;
- splitAndLowerCase(str.string(), &parts, '_');
+ Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
const int numTags = parts.size();
bool valid = false;
@@ -301,8 +283,7 @@
if (part[0] == 'b' && part[1] == '+') {
// This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
// except that the separator is "+" and not "-".
- Vector<String8> subtags;
- AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
+ Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
subtags.removeItemsAt(0);
if (subtags.size() == 1) {
setLanguage(subtags[0]);
@@ -438,1349 +419,46 @@
}
}
-
-/* static */ bool
-AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
-{
- ResTable_config config;
- memset(&config, 0, sizeof(ResTable_config));
-
- // IMSI - MCC
- if (getMccName(part.string(), &config)) {
- *axis = AXIS_MCC;
- value->intValue = config.mcc;
- return true;
- }
-
- // IMSI - MNC
- if (getMncName(part.string(), &config)) {
- *axis = AXIS_MNC;
- value->intValue = config.mnc;
- return true;
- }
-
- // locale - language
- if (value->localeValue.initFromFilterString(part)) {
- *axis = AXIS_LOCALE;
- return true;
- }
-
- // layout direction
- if (getLayoutDirectionName(part.string(), &config)) {
- *axis = AXIS_LAYOUTDIR;
- value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
- return true;
- }
-
- // smallest screen dp width
- if (getSmallestScreenWidthDpName(part.string(), &config)) {
- *axis = AXIS_SMALLESTSCREENWIDTHDP;
- value->intValue = config.smallestScreenWidthDp;
- return true;
- }
-
- // screen dp width
- if (getScreenWidthDpName(part.string(), &config)) {
- *axis = AXIS_SCREENWIDTHDP;
- value->intValue = config.screenWidthDp;
- return true;
- }
-
- // screen dp height
- if (getScreenHeightDpName(part.string(), &config)) {
- *axis = AXIS_SCREENHEIGHTDP;
- value->intValue = config.screenHeightDp;
- return true;
- }
-
- // screen layout size
- if (getScreenLayoutSizeName(part.string(), &config)) {
- *axis = AXIS_SCREENLAYOUTSIZE;
- value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
- return true;
- }
-
- // screen layout long
- if (getScreenLayoutLongName(part.string(), &config)) {
- *axis = AXIS_SCREENLAYOUTLONG;
- value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
- return true;
- }
-
- // orientation
- if (getOrientationName(part.string(), &config)) {
- *axis = AXIS_ORIENTATION;
- value->intValue = config.orientation;
- return true;
- }
-
- // ui mode type
- if (getUiModeTypeName(part.string(), &config)) {
- *axis = AXIS_UIMODETYPE;
- value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- return true;
- }
-
- // ui mode night
- if (getUiModeNightName(part.string(), &config)) {
- *axis = AXIS_UIMODENIGHT;
- value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- return true;
- }
-
- // density
- if (getDensityName(part.string(), &config)) {
- *axis = AXIS_DENSITY;
- value->intValue = config.density;
- return true;
- }
-
- // touchscreen
- if (getTouchscreenName(part.string(), &config)) {
- *axis = AXIS_TOUCHSCREEN;
- value->intValue = config.touchscreen;
- return true;
- }
-
- // keyboard hidden
- if (getKeysHiddenName(part.string(), &config)) {
- *axis = AXIS_KEYSHIDDEN;
- value->intValue = config.inputFlags;
- return true;
- }
-
- // keyboard
- if (getKeyboardName(part.string(), &config)) {
- *axis = AXIS_KEYBOARD;
- value->intValue = config.keyboard;
- return true;
- }
-
- // navigation hidden
- if (getNavHiddenName(part.string(), &config)) {
- *axis = AXIS_NAVHIDDEN;
- value->intValue = config.inputFlags;
- return 0;
- }
-
- // navigation
- if (getNavigationName(part.string(), &config)) {
- *axis = AXIS_NAVIGATION;
- value->intValue = config.navigation;
- return true;
- }
-
- // screen size
- if (getScreenSizeName(part.string(), &config)) {
- *axis = AXIS_SCREENSIZE;
- value->intValue = config.screenSize;
- return true;
- }
-
- // version
- if (getVersionName(part.string(), &config)) {
- *axis = AXIS_VERSION;
- value->intValue = config.version;
- return true;
- }
-
- return false;
-}
-
-AxisValue
-AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
-{
- AxisValue value;
- switch (axis) {
- case AXIS_MCC:
- value.intValue = config.mcc;
- break;
- case AXIS_MNC:
- value.intValue = config.mnc;
- break;
- case AXIS_LOCALE:
- value.localeValue.initFromResTable(config);
- break;
- case AXIS_LAYOUTDIR:
- value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
- break;
- case AXIS_SCREENLAYOUTSIZE:
- value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
- break;
- case AXIS_ORIENTATION:
- value.intValue = config.orientation;
- break;
- case AXIS_UIMODETYPE:
- value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- break;
- case AXIS_UIMODENIGHT:
- value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- break;
- case AXIS_DENSITY:
- value.intValue = config.density;
- break;
- case AXIS_TOUCHSCREEN:
- value.intValue = config.touchscreen;
- break;
- case AXIS_KEYSHIDDEN:
- value.intValue = config.inputFlags;
- break;
- case AXIS_KEYBOARD:
- value.intValue = config.keyboard;
- break;
- case AXIS_NAVIGATION:
- value.intValue = config.navigation;
- break;
- case AXIS_SCREENSIZE:
- value.intValue = config.screenSize;
- break;
- case AXIS_SMALLESTSCREENWIDTHDP:
- value.intValue = config.smallestScreenWidthDp;
- break;
- case AXIS_SCREENWIDTHDP:
- value.intValue = config.screenWidthDp;
- break;
- case AXIS_SCREENHEIGHTDP:
- value.intValue = config.screenHeightDp;
- break;
- case AXIS_VERSION:
- value.intValue = config.version;
- break;
- }
-
- return value;
-}
-
-bool
-AaptGroupEntry::configSameExcept(const ResTable_config& config,
- const ResTable_config& otherConfig, int axis)
-{
- for (int i=AXIS_START; i<=AXIS_END; i++) {
- if (i == axis) {
- continue;
- }
- if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
- return false;
- }
- }
- return true;
-}
-
bool
AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
{
- mParamsChanged = true;
+ const char* q = strchr(dir, '-');
+ size_t typeLen;
+ if (q != NULL) {
+ typeLen = q - dir;
+ } else {
+ typeLen = strlen(dir);
+ }
- Vector<String8> parts;
- AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
-
- String8 mcc, mnc, layoutsize, layoutlong, orient, den;
- String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
- String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
-
- AaptLocaleValue locale;
- int numLocaleComponents = 0;
-
- const int N = parts.size();
- int index = 0;
- String8 part = parts[index];
-
- // resource type
- if (!isValidResourceType(part)) {
+ String8 type(dir, typeLen);
+ if (!isValidResourceType(type)) {
return false;
}
- *resType = part;
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
-
- // imsi - mcc
- if (getMccName(part.string())) {
- mcc = part;
-
- index++;
- if (index == N) {
- goto success;
+ if (q != NULL) {
+ if (!AaptConfig::parse(String8(q + 1), &mParams)) {
+ return false;
}
- part = parts[index];
- } else {
- //printf("not mcc: %s\n", part.string());
}
- // imsi - mnc
- if (getMncName(part.string())) {
- mnc = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not mnc: %s\n", part.string());
- }
-
- index = locale.initFromDirName(parts, index);
- if (index == -1) {
- return false;
- }
- if (index >= N){
- goto success;
- }
-
- part = parts[index];
- if (getLayoutDirectionName(part.string())) {
- layoutDir = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not layout direction: %s\n", part.string());
- }
-
- if (getSmallestScreenWidthDpName(part.string())) {
- smallestwidthdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not smallest screen width dp: %s\n", part.string());
- }
-
- if (getScreenWidthDpName(part.string())) {
- widthdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen width dp: %s\n", part.string());
- }
-
- if (getScreenHeightDpName(part.string())) {
- heightdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen height dp: %s\n", part.string());
- }
-
- if (getScreenLayoutSizeName(part.string())) {
- layoutsize = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen layout size: %s\n", part.string());
- }
-
- if (getScreenLayoutLongName(part.string())) {
- layoutlong = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen layout long: %s\n", part.string());
- }
-
- // orientation
- if (getOrientationName(part.string())) {
- orient = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not orientation: %s\n", part.string());
- }
-
- // ui mode type
- if (getUiModeTypeName(part.string())) {
- uiModeType = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not ui mode type: %s\n", part.string());
- }
-
- // ui mode night
- if (getUiModeNightName(part.string())) {
- uiModeNight = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not ui mode night: %s\n", part.string());
- }
-
- // density
- if (getDensityName(part.string())) {
- den = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not density: %s\n", part.string());
- }
-
- // touchscreen
- if (getTouchscreenName(part.string())) {
- touch = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not touchscreen: %s\n", part.string());
- }
-
- // keyboard hidden
- if (getKeysHiddenName(part.string())) {
- keysHidden = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not keysHidden: %s\n", part.string());
- }
-
- // keyboard
- if (getKeyboardName(part.string())) {
- key = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not keyboard: %s\n", part.string());
- }
-
- // navigation hidden
- if (getNavHiddenName(part.string())) {
- navHidden = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not navHidden: %s\n", part.string());
- }
-
- if (getNavigationName(part.string())) {
- nav = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not navigation: %s\n", part.string());
- }
-
- if (getScreenSizeName(part.string())) {
- size = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen size: %s\n", part.string());
- }
-
- if (getVersionName(part.string())) {
- vers = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not version: %s\n", part.string());
- }
-
- // if there are extra parts, it doesn't match
- return false;
-
-success:
- this->mcc = mcc;
- this->mnc = mnc;
- this->locale = locale;
- this->screenLayoutSize = layoutsize;
- this->screenLayoutLong = layoutlong;
- this->smallestScreenWidthDp = smallestwidthdp;
- this->screenWidthDp = widthdp;
- this->screenHeightDp = heightdp;
- this->orientation = orient;
- this->uiModeType = uiModeType;
- this->uiModeNight = uiModeNight;
- this->density = den;
- this->touchscreen = touch;
- this->keysHidden = keysHidden;
- this->keyboard = key;
- this->navHidden = navHidden;
- this->navigation = nav;
- this->screenSize = size;
- this->layoutDirection = layoutDir;
- this->version = vers;
-
- // what is this anyway?
- this->vendor = "";
-
+ *resType = type;
return true;
}
String8
-AaptGroupEntry::toString() const
-{
- String8 s = this->mcc;
- s += ",";
- s += this->mnc;
- s += ",";
- s += locale.toDirName();
- s += ",";
- s += layoutDirection;
- s += ",";
- s += smallestScreenWidthDp;
- s += ",";
- s += screenWidthDp;
- s += ",";
- s += screenHeightDp;
- s += ",";
- s += screenLayoutSize;
- s += ",";
- s += screenLayoutLong;
- s += ",";
- s += this->orientation;
- s += ",";
- s += uiModeType;
- s += ",";
- s += uiModeNight;
- s += ",";
- s += density;
- s += ",";
- s += touchscreen;
- s += ",";
- s += keysHidden;
- s += ",";
- s += keyboard;
- s += ",";
- s += navHidden;
- s += ",";
- s += navigation;
- s += ",";
- s += screenSize;
- s += ",";
- s += version;
- return s;
-}
-
-String8
AaptGroupEntry::toDirName(const String8& resType) const
{
String8 s = resType;
- if (this->mcc != "") {
+ String8 params = mParams.toString();
+ if (params.length() > 0) {
if (s.length() > 0) {
s += "-";
}
- s += mcc;
+ s += params;
}
- if (this->mnc != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += mnc;
- }
-
- const String8 localeComponent = locale.toDirName();
- if (localeComponent != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += localeComponent;
- }
-
- if (this->layoutDirection != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += layoutDirection;
- }
- if (this->smallestScreenWidthDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += smallestScreenWidthDp;
- }
- if (this->screenWidthDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenWidthDp;
- }
- if (this->screenHeightDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenHeightDp;
- }
- if (this->screenLayoutSize != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenLayoutSize;
- }
- if (this->screenLayoutLong != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenLayoutLong;
- }
- if (this->orientation != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += orientation;
- }
- if (this->uiModeType != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += uiModeType;
- }
- if (this->uiModeNight != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += uiModeNight;
- }
- if (this->density != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += density;
- }
- if (this->touchscreen != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += touchscreen;
- }
- if (this->keysHidden != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += keysHidden;
- }
- if (this->keyboard != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += keyboard;
- }
- if (this->navHidden != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += navHidden;
- }
- if (this->navigation != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += navigation;
- }
- if (this->screenSize != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenSize;
- }
- if (this->version != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += version;
- }
-
return s;
}
-bool AaptGroupEntry::getMccName(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 AaptGroupEntry::getMncName(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 AaptGroupEntry::getLayoutDirectionName(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 AaptGroupEntry::getScreenLayoutSizeName(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 AaptGroupEntry::getScreenLayoutLongName(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 AaptGroupEntry::getOrientationName(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 AaptGroupEntry::getUiModeTypeName(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 AaptGroupEntry::getUiModeNightName(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 AaptGroupEntry::getDensityName(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 AaptGroupEntry::getTouchscreenName(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 AaptGroupEntry::getKeysHiddenName(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 AaptGroupEntry::getKeyboardName(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 AaptGroupEntry::getNavHiddenName(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 AaptGroupEntry::getNavigationName(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 AaptGroupEntry::getScreenSizeName(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 AaptGroupEntry::getSmallestScreenWidthDpName(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 AaptGroupEntry::getScreenWidthDpName(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 AaptGroupEntry::getScreenHeightDpName(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 AaptGroupEntry::getVersionName(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;
-}
-
-int AaptGroupEntry::compare(const AaptGroupEntry& o) const
-{
- int v = mcc.compare(o.mcc);
- if (v == 0) v = mnc.compare(o.mnc);
- if (v == 0) v = locale.compare(o.locale);
- if (v == 0) v = layoutDirection.compare(o.layoutDirection);
- if (v == 0) v = vendor.compare(o.vendor);
- if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
- if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
- if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
- if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
- if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
- if (v == 0) v = orientation.compare(o.orientation);
- if (v == 0) v = uiModeType.compare(o.uiModeType);
- if (v == 0) v = uiModeNight.compare(o.uiModeNight);
- if (v == 0) v = density.compare(o.density);
- if (v == 0) v = touchscreen.compare(o.touchscreen);
- if (v == 0) v = keysHidden.compare(o.keysHidden);
- if (v == 0) v = keyboard.compare(o.keyboard);
- if (v == 0) v = navHidden.compare(o.navHidden);
- if (v == 0) v = navigation.compare(o.navigation);
- if (v == 0) v = screenSize.compare(o.screenSize);
- if (v == 0) v = version.compare(o.version);
- return v;
-}
-
-const ResTable_config AaptGroupEntry::toParams() const
-{
- if (!mParamsChanged) {
- return mParams;
- }
-
- mParamsChanged = false;
- ResTable_config& params = mParams;
- memset(¶ms, 0, sizeof(ResTable_config));
- getMccName(mcc.string(), ¶ms);
- getMncName(mnc.string(), ¶ms);
- locale.writeTo(¶ms);
- getLayoutDirectionName(layoutDirection.string(), ¶ms);
- getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
- getScreenWidthDpName(screenWidthDp.string(), ¶ms);
- getScreenHeightDpName(screenHeightDp.string(), ¶ms);
- getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms);
- getScreenLayoutLongName(screenLayoutLong.string(), ¶ms);
- getOrientationName(orientation.string(), ¶ms);
- getUiModeTypeName(uiModeType.string(), ¶ms);
- getUiModeNightName(uiModeNight.string(), ¶ms);
- getDensityName(density.string(), ¶ms);
- getTouchscreenName(touchscreen.string(), ¶ms);
- getKeysHiddenName(keysHidden.string(), ¶ms);
- getKeyboardName(keyboard.string(), ¶ms);
- getNavHiddenName(navHidden.string(), ¶ms);
- getNavigationName(navigation.string(), ¶ms);
- getScreenSizeName(screenSize.string(), ¶ms);
- getVersionName(version.string(), ¶ms);
-
- // Fix up version number based on specified parameters.
- int minSdk = 0;
- if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
- minSdk = SDK_HONEYCOMB_MR2;
- } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
- != ResTable_config::UI_MODE_TYPE_ANY
- || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
- != ResTable_config::UI_MODE_NIGHT_ANY) {
- minSdk = SDK_FROYO;
- } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
- != ResTable_config::SCREENSIZE_ANY
- || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
- != ResTable_config::SCREENLONG_ANY
- || params.density != ResTable_config::DENSITY_DEFAULT) {
- minSdk = SDK_DONUT;
- }
-
- if (minSdk > params.sdkVersion) {
- params.sdkVersion = minSdk;
- }
-
- return params;
-}
// =========================================================================
// =========================================================================
@@ -2229,9 +907,7 @@
: AaptDir(String8(), String8()),
mHavePrivateSymbols(false),
mChanged(false), mHaveIncludedAssets(false),
- mRes(NULL)
-{
-}
+ mRes(NULL) {}
const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
if (mChanged) {
@@ -2506,7 +1182,7 @@
String8 resType;
bool b = group.initFromDirName(entry->d_name, &resType);
if (!b) {
- fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
+ fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
entry->d_name);
err = -1;
continue;
@@ -2654,30 +1330,35 @@
status_t AaptAssets::filter(Bundle* bundle)
{
- ResourceFilter reqFilter;
+ WeakResourceFilter reqFilter;
status_t err = reqFilter.parse(bundle->getConfigurations());
if (err != NO_ERROR) {
return err;
}
- ResourceFilter prefFilter;
- err = prefFilter.parse(bundle->getPreferredConfigurations());
- if (err != NO_ERROR) {
- return err;
+ uint32_t preferredDensity = 0;
+ if (bundle->getPreferredDensity().size() > 0) {
+ ResTable_config preferredConfig;
+ if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
+ fprintf(stderr, "Error parsing preferred density: %s\n",
+ bundle->getPreferredDensity().string());
+ return UNKNOWN_ERROR;
+ }
+ preferredDensity = preferredConfig.density;
}
- if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
+ if (reqFilter.isEmpty() && preferredDensity == 0) {
return NO_ERROR;
}
if (bundle->getVerbose()) {
if (!reqFilter.isEmpty()) {
printf("Applying required filter: %s\n",
- bundle->getConfigurations());
+ bundle->getConfigurations().string());
}
- if (!prefFilter.isEmpty()) {
- printf("Applying preferred filter: %s\n",
- bundle->getPreferredConfigurations());
+ if (preferredDensity > 0) {
+ printf("Applying preferred density filter: %s\n",
+ bundle->getPreferredDensity().string());
}
}
@@ -2734,89 +1415,71 @@
}
// Quick check: no preferred filters, nothing more to do.
- if (prefFilter.isEmpty()) {
+ if (preferredDensity == 0) {
continue;
}
// Get the preferred density if there is one. We do not match exactly for density.
// If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
// pick xhdpi.
- uint32_t preferredDensity = 0;
- const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
- if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
- preferredDensity = (*preferredConfigs)[0].intValue;
- }
+ for (size_t k=0; k<grp->getFiles().size(); k++) {
+ sp<AaptFile> file = grp->getFiles().valueAt(k);
+ if (k == 0 && grp->getFiles().size() == 1) {
+ // If this is the only file left, we need to keep it.
+ // Otherwise the resource IDs we are using will be inconsistent
+ // with what we get when not stripping. Sucky, but at least
+ // for now we can rely on the back-end doing another filtering
+ // pass to take this out and leave us with this resource name
+ // containing no entries.
+ continue;
+ }
+ if (file->getPath().getPathExtension() == ".xml") {
+ // We can't remove .xml files at this point, because when
+ // we parse them they may add identifier resources, so
+ // removing them can cause our resource identifiers to
+ // become inconsistent.
+ continue;
+ }
+ const ResTable_config& config(file->getGroupEntry().toParams());
+ if (config.density != 0 && config.density != preferredDensity) {
+ // This is a resource we would prefer not to have. Check
+ // to see if have a similar variation that we would like
+ // to have and, if so, we can drop it.
+ uint32_t bestDensity = config.density;
- // Now deal with preferred configurations.
- for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
- for (size_t k=0; k<grp->getFiles().size(); k++) {
- sp<AaptFile> file = grp->getFiles().valueAt(k);
- if (k == 0 && grp->getFiles().size() == 1) {
- // If this is the only file left, we need to keep it.
- // Otherwise the resource IDs we are using will be inconsistent
- // with what we get when not stripping. Sucky, but at least
- // for now we can rely on the back-end doing another filtering
- // pass to take this out and leave us with this resource name
- // containing no entries.
- continue;
- }
- if (file->getPath().getPathExtension() == ".xml") {
- // We can't remove .xml files at this point, because when
- // we parse them they may add identifier resources, so
- // removing them can cause our resource identifiers to
- // become inconsistent.
- continue;
- }
- const ResTable_config& config(file->getGroupEntry().toParams());
- if (!prefFilter.match(axis, config)) {
- // This is a resource we would prefer not to have. Check
- // to see if have a similar variation that we would like
- // to have and, if so, we can drop it.
-
- uint32_t bestDensity = config.density;
-
- for (size_t m=0; m<grp->getFiles().size(); m++) {
- if (m == k) continue;
- sp<AaptFile> mfile = grp->getFiles().valueAt(m);
- const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
- if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
- if (axis == AXIS_DENSITY && preferredDensity > 0) {
- // See if there is a better density resource
- if (mconfig.density < bestDensity &&
- mconfig.density > preferredDensity &&
- bestDensity > preferredDensity) {
- // This density is between our best density and
- // the preferred density, therefore it is better.
- bestDensity = mconfig.density;
- } else if (mconfig.density > bestDensity &&
- bestDensity < preferredDensity) {
- // This density is better than our best density and
- // our best density was smaller than our preferred
- // density, so it is better.
- bestDensity = mconfig.density;
- }
- } else if (prefFilter.match(axis, mconfig)) {
- if (bundle->getVerbose()) {
- printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
- }
- grp->removeFile(k);
- k--;
- break;
- }
- }
+ for (size_t m=0; m<grp->getFiles().size(); m++) {
+ if (m == k) {
+ continue;
}
- if (axis == AXIS_DENSITY && preferredDensity > 0 &&
- bestDensity != config.density) {
- if (bundle->getVerbose()) {
- printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
+ sp<AaptFile> mfile = grp->getFiles().valueAt(m);
+ const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
+ if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
+ // See if there is a better density resource
+ if (mconfig.density < bestDensity &&
+ mconfig.density > preferredDensity &&
+ bestDensity > preferredDensity) {
+ // This density is between our best density and
+ // the preferred density, therefore it is better.
+ bestDensity = mconfig.density;
+ } else if (mconfig.density > bestDensity &&
+ bestDensity < preferredDensity) {
+ // This density is better than our best density and
+ // our best density was smaller than our preferred
+ // density, so it is better.
+ bestDensity = mconfig.density;
}
- grp->removeFile(k);
- k--;
}
}
+
+ if (bestDensity != config.density) {
+ if (bundle->getVerbose()) {
+ printf("Pruning unneeded resource: %s\n",
+ file->getPrintableSource().string());
+ }
+ grp->removeFile(k);
+ k--;
+ }
}
}
}
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 82dda5f..0c2576a 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -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 @@
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 @@
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 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);
+ inline const ConfigDescription& toParams() const { return mParams; }
- 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 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 @@
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)
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
new file mode 100644
index 0000000..69a9c7f
--- /dev/null
+++ b/tools/aapt/AaptConfig.cpp
@@ -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
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
new file mode 100644
index 0000000..2963539
--- /dev/null
+++ b/tools/aapt/AaptConfig.h
@@ -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
diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp
new file mode 100644
index 0000000..293e144
--- /dev/null
+++ b/tools/aapt/AaptUtil.cpp
@@ -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
diff --git a/tools/aapt/AaptUtil.h b/tools/aapt/AaptUtil.h
new file mode 100644
index 0000000..47a704a
--- /dev/null
+++ b/tools/aapt/AaptUtil.h
@@ -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
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 806f8ff..700afa1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -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
diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp
new file mode 100644
index 0000000..12f6040
--- /dev/null
+++ b/tools/aapt/ApkBuilder.cpp
@@ -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());
+ }
+}
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
new file mode 100644
index 0000000..a4b7d4a
--- /dev/null
+++ b/tools/aapt/ApkBuilder.h
@@ -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
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index ebe1bed..ceb52a0 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -151,10 +151,12 @@
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 @@
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;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0af1ce1..0360200 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -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 @@
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 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 @@
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 @@
// 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.
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
new file mode 100644
index 0000000..779c423
--- /dev/null
+++ b/tools/aapt/ConfigDescription.h
@@ -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
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 1cf4783..5a60014 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -70,6 +70,7 @@
" [-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 @@
" 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 @@
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++;
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index a6b39ac..34c4496 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -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 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 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);
diff --git a/tools/aapt/OutputSet.h b/tools/aapt/OutputSet.h
new file mode 100644
index 0000000..ea9ef70
--- /dev/null
+++ b/tools/aapt/OutputSet.h
@@ -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
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 872d95c..dc16e35 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -5,6 +5,7 @@
//
#include "Main.h"
#include "AaptAssets.h"
+#include "OutputSet.h"
#include "ResourceTable.h"
#include "ResourceFilter.h"
@@ -36,11 +37,8 @@
};
/* 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 @@
* 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 @@
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 @@
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 @@
* 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 @@
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;
}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 1348be3..e599643 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -179,24 +179,6 @@
|| 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 @@
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 @@
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 @@
// --------------------------------------------------------------------
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 @@
}
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 @@
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 @@
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
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8ca852e..de8b4fc 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -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;
}
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index c57770e..f459584 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -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
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 6eab65b..efbba40 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2095,10 +2095,10 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
}
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 @@
config.screenHeightDp,
config.layoutDirection));
- if (filterable && !filter.match(config)) {
+ if (filterable && !filter->match(config)) {
continue;
}
@@ -3108,7 +3102,7 @@
}
ssize_t strStart = dest->getSize();
- err = valueStrings.writeStringBlock(dest);
+ status_t err = valueStrings.writeStringBlock(dest);
if (err != NO_ERROR) {
return err;
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index ec8fd175e..a73993c 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -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 @@
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 @@
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 @@
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);
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
new file mode 100644
index 0000000..e795d818
--- /dev/null
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -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());
+}
diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp
new file mode 100644
index 0000000..7348a08
--- /dev/null
+++ b/tools/aapt/tests/AaptGroupEntry_test.cpp
@@ -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);
+}
diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp
new file mode 100644
index 0000000..30697bb
--- /dev/null
+++ b/tools/aapt/tests/ResourceFilter_test.cpp
@@ -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));
+}
+
diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h
new file mode 100644
index 0000000..79174832
--- /dev/null
+++ b/tools/aapt/tests/TestHelper.h
@@ -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