| /* |
| * 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.support.v4.graphics.drawable; |
| |
| import android.content.res.ColorStateList; |
| import android.graphics.PorterDuff; |
| import android.graphics.drawable.Drawable; |
| import android.support.v4.view.ViewCompat; |
| |
| /** |
| * Helper for accessing features in {@link android.graphics.drawable.Drawable} |
| * introduced after API level 4 in a backwards compatible fashion. |
| */ |
| public class DrawableCompat { |
| /** |
| * Interface for the full API. |
| */ |
| interface DrawableImpl { |
| void jumpToCurrentState(Drawable drawable); |
| void setAutoMirrored(Drawable drawable, boolean mirrored); |
| boolean isAutoMirrored(Drawable drawable); |
| void setHotspot(Drawable drawable, float x, float y); |
| void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom); |
| void setTint(Drawable drawable, int tint); |
| void setTintList(Drawable drawable, ColorStateList tint); |
| void setTintMode(Drawable drawable, PorterDuff.Mode tintMode); |
| Drawable wrap(Drawable drawable); |
| void setLayoutDirection(Drawable drawable, int layoutDirection); |
| int getLayoutDirection(Drawable drawable); |
| } |
| |
| /** |
| * Interface implementation that doesn't use anything about v4 APIs. |
| */ |
| static class BaseDrawableImpl implements DrawableImpl { |
| @Override |
| public void jumpToCurrentState(Drawable drawable) { |
| } |
| |
| @Override |
| public void setAutoMirrored(Drawable drawable, boolean mirrored) { |
| } |
| |
| @Override |
| public boolean isAutoMirrored(Drawable drawable) { |
| return false; |
| } |
| |
| @Override |
| public void setHotspot(Drawable drawable, float x, float y) { |
| } |
| |
| @Override |
| public void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) { |
| } |
| |
| @Override |
| public void setTint(Drawable drawable, int tint) { |
| DrawableCompatBase.setTint(drawable, tint); |
| } |
| |
| @Override |
| public void setTintList(Drawable drawable, ColorStateList tint) { |
| DrawableCompatBase.setTintList(drawable, tint); |
| } |
| |
| @Override |
| public void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) { |
| DrawableCompatBase.setTintMode(drawable, tintMode); |
| } |
| |
| @Override |
| public Drawable wrap(Drawable drawable) { |
| return DrawableCompatBase.wrapForTinting(drawable); |
| } |
| |
| @Override |
| public void setLayoutDirection(Drawable drawable, int layoutDirection) { |
| // No op for API < 23 |
| } |
| |
| @Override |
| public int getLayoutDirection(Drawable drawable) { |
| return ViewCompat.LAYOUT_DIRECTION_LTR; |
| } |
| } |
| |
| /** |
| * Interface implementation for devices with at least v11 APIs. |
| */ |
| static class HoneycombDrawableImpl extends BaseDrawableImpl { |
| @Override |
| public void jumpToCurrentState(Drawable drawable) { |
| DrawableCompatHoneycomb.jumpToCurrentState(drawable); |
| } |
| |
| @Override |
| public Drawable wrap(Drawable drawable) { |
| return DrawableCompatHoneycomb.wrapForTinting(drawable); |
| } |
| } |
| |
| static class JellybeanMr1DrawableImpl extends HoneycombDrawableImpl { |
| @Override |
| public void setLayoutDirection(Drawable drawable, int layoutDirection) { |
| DrawableCompatJellybeanMr1.setLayoutDirection(drawable, layoutDirection); |
| } |
| |
| @Override |
| public int getLayoutDirection(Drawable drawable) { |
| final int dir = DrawableCompatJellybeanMr1.getLayoutDirection(drawable); |
| return dir < 0 ? dir : ViewCompat.LAYOUT_DIRECTION_LTR; |
| } |
| } |
| |
| /** |
| * Interface implementation for devices with at least KitKat APIs. |
| */ |
| static class KitKatDrawableImpl extends JellybeanMr1DrawableImpl { |
| @Override |
| public void setAutoMirrored(Drawable drawable, boolean mirrored) { |
| DrawableCompatKitKat.setAutoMirrored(drawable, mirrored); |
| } |
| |
| @Override |
| public boolean isAutoMirrored(Drawable drawable) { |
| return DrawableCompatKitKat.isAutoMirrored(drawable); |
| } |
| |
| @Override |
| public Drawable wrap(Drawable drawable) { |
| return DrawableCompatKitKat.wrapForTinting(drawable); |
| } |
| } |
| |
| /** |
| * Interface implementation for devices with at least L APIs. |
| */ |
| static class LollipopDrawableImpl extends KitKatDrawableImpl { |
| @Override |
| public void setHotspot(Drawable drawable, float x, float y) { |
| DrawableCompatLollipop.setHotspot(drawable, x, y); |
| } |
| |
| @Override |
| public void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom) { |
| DrawableCompatLollipop.setHotspotBounds(drawable, left, top, right, bottom); |
| } |
| |
| @Override |
| public void setTint(Drawable drawable, int tint) { |
| DrawableCompatLollipop.setTint(drawable, tint); |
| } |
| |
| @Override |
| public void setTintList(Drawable drawable, ColorStateList tint) { |
| DrawableCompatLollipop.setTintList(drawable, tint); |
| } |
| |
| @Override |
| public void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) { |
| DrawableCompatLollipop.setTintMode(drawable, tintMode); |
| } |
| |
| @Override |
| public Drawable wrap(Drawable drawable) { |
| return DrawableCompatLollipop.wrapForTinting(drawable); |
| } |
| } |
| |
| /** |
| * Interface implementation for devices with at least L APIs. |
| */ |
| static class LollipopMr1DrawableImpl extends LollipopDrawableImpl { |
| @Override |
| public Drawable wrap(Drawable drawable) { |
| return DrawableCompatApi22.wrapForTinting(drawable); |
| } |
| } |
| |
| /** |
| * Interface implementation for devices with at least M APIs. |
| */ |
| static class MDrawableImpl extends LollipopMr1DrawableImpl { |
| @Override |
| public void setLayoutDirection(Drawable drawable, int layoutDirection) { |
| DrawableCompatApi23.setLayoutDirection(drawable, layoutDirection); |
| } |
| |
| @Override |
| public int getLayoutDirection(Drawable drawable) { |
| return DrawableCompatApi23.getLayoutDirection(drawable); |
| } |
| } |
| |
| /** |
| * Select the correct implementation to use for the current platform. |
| */ |
| static final DrawableImpl IMPL; |
| static { |
| final int version = android.os.Build.VERSION.SDK_INT; |
| if (version >= 23) { |
| IMPL = new MDrawableImpl(); |
| } else if (version >= 22) { |
| IMPL = new LollipopMr1DrawableImpl(); |
| } else if (version >= 21) { |
| IMPL = new LollipopDrawableImpl(); |
| } else if (version >= 19) { |
| IMPL = new KitKatDrawableImpl(); |
| } else if (version >= 17) { |
| IMPL = new JellybeanMr1DrawableImpl(); |
| } else if (version >= 11) { |
| IMPL = new HoneycombDrawableImpl(); |
| } else { |
| IMPL = new BaseDrawableImpl(); |
| } |
| } |
| |
| /** |
| * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()}. |
| * <p> |
| * If running on a pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} |
| * device this method does nothing. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| */ |
| public static void jumpToCurrentState(Drawable drawable) { |
| IMPL.jumpToCurrentState(drawable); |
| } |
| |
| /** |
| * Set whether this Drawable is automatically mirrored when its layout |
| * direction is RTL (right-to left). See |
| * {@link android.util.LayoutDirection}. |
| * <p> |
| * If running on a pre-{@link android.os.Build.VERSION_CODES#KITKAT} device |
| * this method does nothing. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| * @param mirrored Set to true if the Drawable should be mirrored, false if |
| * not. |
| */ |
| public static void setAutoMirrored(Drawable drawable, boolean mirrored) { |
| IMPL.setAutoMirrored(drawable, mirrored); |
| } |
| |
| /** |
| * Tells if this Drawable will be automatically mirrored when its layout |
| * direction is RTL right-to-left. See {@link android.util.LayoutDirection}. |
| * <p> |
| * If running on a pre-{@link android.os.Build.VERSION_CODES#KITKAT} device |
| * this method returns false. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| * @return boolean Returns true if this Drawable will be automatically |
| * mirrored. |
| */ |
| public static boolean isAutoMirrored(Drawable drawable) { |
| return IMPL.isAutoMirrored(drawable); |
| } |
| |
| /** |
| * Specifies the hotspot's location within the drawable. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| * @param x The X coordinate of the center of the hotspot |
| * @param y The Y coordinate of the center of the hotspot |
| */ |
| public static void setHotspot(Drawable drawable, float x, float y) { |
| IMPL.setHotspot(drawable, x, y); |
| } |
| |
| /** |
| * Sets the bounds to which the hotspot is constrained, if they should be |
| * different from the drawable bounds. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| */ |
| public static void setHotspotBounds(Drawable drawable, int left, int top, |
| int right, int bottom) { |
| IMPL.setHotspotBounds(drawable, left, top, right, bottom); |
| } |
| |
| /** |
| * Specifies a tint for {@code drawable}. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| * @param tint Color to use for tinting this drawable |
| */ |
| public static void setTint(Drawable drawable, int tint) { |
| IMPL.setTint(drawable, tint); |
| } |
| |
| /** |
| * Specifies a tint for {@code drawable} as a color state list. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| * @param tint Color state list to use for tinting this drawable, or null to clear the tint |
| */ |
| public static void setTintList(Drawable drawable, ColorStateList tint) { |
| IMPL.setTintList(drawable, tint); |
| } |
| |
| /** |
| * Specifies a tint blending mode for {@code drawable}. |
| * |
| * @param drawable The Drawable against which to invoke the method. |
| * @param tintMode A Porter-Duff blending mode |
| */ |
| public static void setTintMode(Drawable drawable, PorterDuff.Mode tintMode) { |
| IMPL.setTintMode(drawable, tintMode); |
| } |
| |
| /** |
| * Potentially wrap {@code drawable} so that it may be used for tinting across the |
| * different API levels, via the tinting methods in this class. |
| * <p> |
| * If you need to get hold of the original {@link android.graphics.drawable.Drawable} again, |
| * you can use the value returned from {@link #unwrap(Drawable)}. |
| * |
| * @param drawable The Drawable to process |
| * @return A drawable capable of being tinted across all API levels. |
| * |
| * @see #setTint(Drawable, int) |
| * @see #setTintList(Drawable, ColorStateList) |
| * @see #setTintMode(Drawable, PorterDuff.Mode) |
| * @see #unwrap(Drawable) |
| */ |
| public static Drawable wrap(Drawable drawable) { |
| return IMPL.wrap(drawable); |
| } |
| |
| /** |
| * Unwrap {@code drawable} if it is the result of a call to {@link #wrap(Drawable)}. If |
| * the {@code drawable} is not the result of a call to {@link #wrap(Drawable)} then |
| * {@code drawable} is returned as-is. |
| * |
| * @param drawable The drawable to unwrap |
| * @return the unwrapped {@link Drawable} or {@code drawable} if it hasn't been wrapped. |
| * |
| * @see #wrap(Drawable) |
| */ |
| public static <T extends Drawable> T unwrap(Drawable drawable) { |
| if (drawable instanceof DrawableWrapper) { |
| return (T) ((DrawableWrapper) drawable).getWrappedDrawable(); |
| } |
| return (T) drawable; |
| } |
| |
| /** |
| * Set the layout direction for this drawable. Should be a resolved |
| * layout direction, as the Drawable has no capacity to do the resolution on |
| * its own. |
| * |
| * @param layoutDirection the resolved layout direction for the drawable, |
| * either {@link ViewCompat#LAYOUT_DIRECTION_LTR} |
| * or {@link ViewCompat#LAYOUT_DIRECTION_RTL} |
| * @see #getLayoutDirection(Drawable) |
| */ |
| public static void setLayoutDirection(Drawable drawable, int layoutDirection) { |
| IMPL.setLayoutDirection(drawable, layoutDirection); |
| } |
| |
| /** |
| * Returns the resolved layout direction for this Drawable. |
| * |
| * @return One of {@link ViewCompat#LAYOUT_DIRECTION_LTR}, |
| * {@link ViewCompat#LAYOUT_DIRECTION_RTL} |
| * @see #setLayoutDirection(Drawable, int) |
| */ |
| public static int getLayoutDirection(Drawable drawable) { |
| return IMPL.getLayoutDirection(drawable); |
| } |
| } |