/* * 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); } }