diff --git a/api/current.txt b/api/current.txt
index ce554898a8f55..6c4143389e476 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -291,6 +291,7 @@ package android {
field public static final int autoAdvanceViewId = 16843535; // 0x101030f
field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
field public static final int autoLink = 16842928; // 0x10100b0
+ field public static final int autoMirrored = 16843752; // 0x10103e8
field public static final int autoStart = 16843445; // 0x10102b5
field public static final deprecated int autoText = 16843114; // 0x101016a
field public static final int autoUrlDetect = 16843404; // 0x101028c
@@ -9891,6 +9892,7 @@ package android.graphics.drawable {
method public android.graphics.Shader.TileMode getTileModeY();
method public boolean hasAntiAlias();
method public boolean hasMipMap();
+ method public final boolean isAutoMirrored();
method public void setAlpha(int);
method public void setAntiAlias(boolean);
method public void setColorFilter(android.graphics.ColorFilter);
@@ -9957,6 +9959,7 @@ package android.graphics.drawable {
method public android.graphics.Region getTransparentRegion();
method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public void invalidateSelf();
+ method public boolean isAutoMirrored();
method public boolean isStateful();
method public final boolean isVisible();
method public void jumpToCurrentState();
@@ -9967,6 +9970,7 @@ package android.graphics.drawable {
method public static int resolveOpacity(int, int);
method public void scheduleSelf(java.lang.Runnable, long);
method public abstract void setAlpha(int);
+ method public void setAutoMirrored(boolean);
method public void setBounds(int, int, int, int);
method public void setBounds(android.graphics.Rect);
method public final void setCallback(android.graphics.drawable.Drawable.Callback);
@@ -25138,6 +25142,13 @@ package android.util {
method public android.util.JsonWriter value(java.lang.Number) throws java.io.IOException;
}
+ public abstract interface LayoutDirection {
+ field public static final int INHERIT = 2; // 0x2
+ field public static final int LOCALE = 3; // 0x3
+ field public static final int LTR = 0; // 0x0
+ field public static final int RTL = 1; // 0x1
+ }
+
public final class Log {
method public static int d(java.lang.String, java.lang.String);
method public static int d(java.lang.String, java.lang.String, java.lang.Throwable);
diff --git a/core/java/android/util/LayoutDirection.java b/core/java/android/util/LayoutDirection.java
new file mode 100644
index 0000000000000..e37d2f22f45ee
--- /dev/null
+++ b/core/java/android/util/LayoutDirection.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013 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.util;
+
+/**
+ * An interface for defining layout directions. A layout direction can be left-to-right (LTR)
+ * or right-to-left (RTL). It can also be inherited (from a parent) or deduced from the default
+ * language script of a locale.
+ */
+public interface LayoutDirection {
+ /**
+ * Horizontal layout direction is from Left to Right.
+ */
+ public static final int LTR = 0;
+
+ /**
+ * Horizontal layout direction is from Right to Left.
+ */
+ public static final int RTL = 1;
+
+ /**
+ * Horizontal layout direction is inherited.
+ */
+ public static final int INHERIT = 2;
+
+ /**
+ * Horizontal layout direction is deduced from the default language script for the locale.
+ */
+ public static final int LOCALE = 3;
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 299c4a230219b..7624b56dd4a43 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -51,6 +51,7 @@ import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
+import android.util.LayoutDirection;
import android.util.Log;
import android.util.LongSparseLongArray;
import android.util.Pools.SynchronizedPool;
@@ -1801,25 +1802,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* Horizontal layout direction of this view is from Left to Right.
* Use with {@link #setLayoutDirection}.
*/
- public static final int LAYOUT_DIRECTION_LTR = 0;
+ public static final int LAYOUT_DIRECTION_LTR = LayoutDirection.LTR;
/**
* Horizontal layout direction of this view is from Right to Left.
* Use with {@link #setLayoutDirection}.
*/
- public static final int LAYOUT_DIRECTION_RTL = 1;
+ public static final int LAYOUT_DIRECTION_RTL = LayoutDirection.RTL;
/**
* Horizontal layout direction of this view is inherited from its parent.
* Use with {@link #setLayoutDirection}.
*/
- public static final int LAYOUT_DIRECTION_INHERIT = 2;
+ public static final int LAYOUT_DIRECTION_INHERIT = LayoutDirection.INHERIT;
/**
* Horizontal layout direction of this view is from deduced from the default language
* script for the locale. Use with {@link #setLayoutDirection}.
*/
- public static final int LAYOUT_DIRECTION_LOCALE = 3;
+ public static final int LAYOUT_DIRECTION_LOCALE = LayoutDirection.LOCALE;
/**
* Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 33fd8ce446ac3..3e53b91bd0b05 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -732,6 +732,15 @@ public class ImageView extends View {
}
}
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ if (mDrawable != null) {
+ mDrawable.setLayoutDirection(layoutDirection);
+ }
+ }
+
private static final Matrix.ScaleToFit[] sS2FArray = {
Matrix.ScaleToFit.FILL,
Matrix.ScaleToFit.START,
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 816bb18dcd560..3181164e35285 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2042,6 +2042,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
dr.mDrawableRightInitial = right;
}
+ resetResolvedDrawables();
+ resolveDrawables();
invalidate();
requestLayout();
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2096f6635c760..67a32fd1357ed 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3905,6 +3905,10 @@
value is false. See
{@link android.graphics.drawable.Drawable#setVisible}. -->
+
+
+
+
@@ -4083,6 +4090,9 @@
+
+
@@ -4172,6 +4182,9 @@
{@link android.graphics.Bitmap#setHasMipMap(boolean)} for more information.
Default value is false. -->
+
+
@@ -4182,6 +4195,9 @@
same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
an RGB 565 screen). -->
+
+
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0eaab656cae01..80c91849103da 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2071,4 +2071,6 @@
+
+
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 8689261eef21a..5ceab36674df9 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -23,12 +23,14 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.LayoutDirection;
import android.view.Gravity;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -72,7 +74,10 @@ public class BitmapDrawable extends Drawable {
// These are scaled to match the target density.
private int mBitmapWidth;
private int mBitmapHeight;
-
+
+ // Mirroring matrix for using with Shaders
+ private Matrix mMirrorMatrix;
+
/**
* Create an empty drawable, not dealing with density.
* @deprecated Use {@link #BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap)}
@@ -398,15 +403,52 @@ public class BitmapDrawable extends Drawable {
}
}
+ @Override
+ public void setAutoMirrored(boolean mirrored) {
+ if (mBitmapState.mAutoMirrored != mirrored) {
+ mBitmapState.mAutoMirrored = mirrored;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public final boolean isAutoMirrored() {
+ return mBitmapState.mAutoMirrored;
+ }
+
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations() | mBitmapState.mChangingConfigurations;
}
-
+
+ private boolean needMirroring() {
+ return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+ }
+
+ private void updateMirrorMatrix(float dx) {
+ if (mMirrorMatrix == null) {
+ mMirrorMatrix = new Matrix();
+ }
+ mMirrorMatrix.setTranslate(dx, 0);
+ mMirrorMatrix.preScale(-1.0f, 1.0f);
+ }
+
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mApplyGravity = true;
+ Shader shader = mBitmapState.mPaint.getShader();
+ if (shader != null) {
+ if (needMirroring()) {
+ updateMirrorMatrix(bounds.right - bounds.left);
+ shader.setLocalMatrix(mMirrorMatrix);
+ } else {
+ if (mMirrorMatrix != null) {
+ mMirrorMatrix = null;
+ shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
+ }
+ }
+ }
}
@Override
@@ -430,6 +472,7 @@ public class BitmapDrawable extends Drawable {
}
Shader shader = state.mPaint.getShader();
+ final boolean needMirroring = needMirroring();
if (shader == null) {
if (mApplyGravity) {
final int layoutDirection = getLayoutDirection();
@@ -437,12 +480,31 @@ public class BitmapDrawable extends Drawable {
getBounds(), mDstRect, layoutDirection);
mApplyGravity = false;
}
+ if (needMirroring) {
+ canvas.save();
+ // Mirror the bitmap
+ canvas.translate(mDstRect.right - mDstRect.left, 0);
+ canvas.scale(-1.0f, 1.0f);
+ }
canvas.drawBitmap(bitmap, null, mDstRect, state.mPaint);
+ if (needMirroring) {
+ canvas.restore();
+ }
} else {
if (mApplyGravity) {
copyBounds(mDstRect);
mApplyGravity = false;
}
+ if (needMirroring) {
+ // Mirror the bitmap
+ updateMirrorMatrix(mDstRect.right - mDstRect.left);
+ shader.setLocalMatrix(mMirrorMatrix);
+ } else {
+ if (mMirrorMatrix != null) {
+ mMirrorMatrix = null;
+ shader.setLocalMatrix(Matrix.IDENTITY_MATRIX);
+ }
+ }
canvas.drawRect(mDstRect, state.mPaint);
}
}
@@ -505,6 +567,8 @@ public class BitmapDrawable extends Drawable {
setTargetDensity(r.getDisplayMetrics());
setMipMap(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_mipMap,
bitmap.hasMipMap()));
+ setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_autoMirrored,
+ false));
final Paint paint = mBitmapState.mPaint;
paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias,
@@ -567,6 +631,7 @@ public class BitmapDrawable extends Drawable {
Shader.TileMode mTileModeY = null;
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
boolean mRebuildShader;
+ boolean mAutoMirrored;
BitmapState(Bitmap bitmap) {
mBitmap = bitmap;
@@ -581,6 +646,7 @@ public class BitmapDrawable extends Drawable {
mTargetDensity = bitmapState.mTargetDensity;
mPaint = new Paint(bitmapState.mPaint);
mRebuildShader = bitmapState.mRebuildShader;
+ mAutoMirrored = bitmapState.mAutoMirrored;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index c8fce9eb85354..8135716d41e5b 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -571,6 +571,25 @@ public abstract class Drawable {
return mVisible;
}
+ /**
+ * Set whether this Drawable is automatically mirrored when its layout direction is RTL
+ * (right-to left). See {@link android.util.LayoutDirection}.
+ *
+ * @param mirrored Set to true if the Drawable should be mirrored, false if not.
+ */
+ public void setAutoMirrored(boolean mirrored) {
+ }
+
+ /**
+ * Tells if this Drawable will be automatically mirrored when its layout direction is RTL
+ * right-to-left. See {@link android.util.LayoutDirection}.
+ *
+ * @return boolean Returns true if this Drawable will be automatically mirrored.
+ */
+ public boolean isAutoMirrored() {
+ return false;
+ }
+
/**
* Return the opacity/transparency of this Drawable. The returned value is
* one of the abstract format constants in
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index f9cd4e261170e..e350e8d0c71c7 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -172,6 +172,19 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return mDrawableContainerState.isStateful();
}
+ @Override
+ public void setAutoMirrored(boolean mirrored) {
+ mDrawableContainerState.mAutoMirrored = mirrored;
+ if (mCurrDrawable != null) {
+ mCurrDrawable.mutate().setAutoMirrored(mDrawableContainerState.mAutoMirrored);
+ }
+ }
+
+ @Override
+ public boolean isAutoMirrored() {
+ return mDrawableContainerState.mAutoMirrored;
+ }
+
@Override
public void jumpToCurrentState() {
boolean changed = false;
@@ -334,6 +347,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
d.setLevel(getLevel());
d.setBounds(getBounds());
d.setLayoutDirection(getLayoutDirection());
+ d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
}
} else {
mCurrDrawable = null;
@@ -471,6 +485,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
int mEnterFadeDuration;
int mExitFadeDuration;
+ boolean mAutoMirrored;
+
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
@@ -490,6 +506,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mLayoutDirection = orig.mLayoutDirection;
mEnterFadeDuration = orig.mEnterFadeDuration;
mExitFadeDuration = orig.mExitFadeDuration;
+ mAutoMirrored = orig.mAutoMirrored;
// Cloning the following values may require creating futures.
mConstantPadding = orig.getConstantPadding();
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 206897bcb9067..81cc11b6bd7fb 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -119,6 +119,9 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
mOpacityOverride = a.getInt(com.android.internal.R.styleable.LayerDrawable_opacity,
PixelFormat.UNKNOWN);
+ setAutoMirrored(a.getBoolean(com.android.internal.R.styleable.LayerDrawable_autoMirrored,
+ false));
+
a.recycle();
final int innerDepth = parser.getDepth() + 1;
@@ -200,6 +203,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
st.mChildren[i] = childDrawable;
childDrawable.mId = id;
childDrawable.mDrawable = layer;
+ childDrawable.mDrawable.setAutoMirrored(isAutoMirrored());
childDrawable.mInsetL = left;
childDrawable.mInsetT = top;
childDrawable.mInsetR = right;
@@ -447,6 +451,21 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
return mLayerState.getOpacity();
}
+ @Override
+ public void setAutoMirrored(boolean mirrored) {
+ mLayerState.mAutoMirrored = mirrored;
+ final ChildDrawable[] array = mLayerState.mChildren;
+ final int N = mLayerState.mNum;
+ for (int i=0; i requires a valid 9-patch source image");
}
+ final boolean automirrored = a.getBoolean(
+ com.android.internal.R.styleable.NinePatchDrawable_autoMirrored, false);
+
setNinePatchState(new NinePatchState(new NinePatch(bitmap, bitmap.getNinePatchChunk()),
- padding, opticalInsets, dither), r);
+ padding, opticalInsets, dither, automirrored), r);
mNinePatchState.mTargetDensity = mTargetDensity;
a.recycle();
@@ -407,20 +434,23 @@ public class NinePatchDrawable extends Drawable {
final boolean mDither;
int mChangingConfigurations;
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+ boolean mAutoMirrored;
NinePatchState(NinePatch ninePatch, Rect padding) {
- this(ninePatch, padding, new Rect(), DEFAULT_DITHER);
+ this(ninePatch, padding, new Rect(), DEFAULT_DITHER, false);
}
NinePatchState(NinePatch ninePatch, Rect padding, Rect opticalInsets) {
- this(ninePatch, padding, opticalInsets, DEFAULT_DITHER);
+ this(ninePatch, padding, opticalInsets, DEFAULT_DITHER, false);
}
- NinePatchState(NinePatch ninePatch, Rect rect, Rect opticalInsets, boolean dither) {
+ NinePatchState(NinePatch ninePatch, Rect rect, Rect opticalInsets, boolean dither,
+ boolean autoMirror) {
mNinePatch = ninePatch;
mPadding = rect;
mOpticalInsets = Insets.of(opticalInsets);
mDither = dither;
+ mAutoMirrored = autoMirror;
}
// Copy constructor
@@ -434,6 +464,7 @@ public class NinePatchDrawable extends Drawable {
mDither = state.mDither;
mChangingConfigurations = state.mChangingConfigurations;
mTargetDensity = state.mTargetDensity;
+ mAutoMirrored = state.mAutoMirrored;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index e3c7bc5862886..48d66b7322695 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -132,6 +132,9 @@ public class StateListDrawable extends DrawableContainer {
setDither(a.getBoolean(com.android.internal.R.styleable.StateListDrawable_dither,
DEFAULT_DITHER));
+ setAutoMirrored(a.getBoolean(
+ com.android.internal.R.styleable.StateListDrawable_autoMirrored, false));
+
a.recycle();
int type;