1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.core.graphics.drawable; 18 19import android.content.res.ColorStateList; 20import android.content.res.Resources; 21import android.graphics.Outline; 22import android.graphics.PorterDuff; 23import android.graphics.Rect; 24import android.graphics.drawable.Drawable; 25import android.graphics.drawable.DrawableContainer; 26import android.graphics.drawable.GradientDrawable; 27import android.graphics.drawable.InsetDrawable; 28import android.graphics.drawable.RippleDrawable; 29import android.os.Build; 30import android.util.Log; 31 32import androidx.annotation.NonNull; 33import androidx.annotation.Nullable; 34import androidx.annotation.RequiresApi; 35 36import java.lang.reflect.Method; 37 38@RequiresApi(21) 39class WrappedDrawableApi21 extends WrappedDrawableApi14 { 40 private static final String TAG = "WrappedDrawableApi21"; 41 private static Method sIsProjectedDrawableMethod; 42 43 WrappedDrawableApi21(Drawable drawable) { 44 super(drawable); 45 findAndCacheIsProjectedDrawableMethod(); 46 } 47 48 WrappedDrawableApi21(DrawableWrapperState state, Resources resources) { 49 super(state, resources); 50 findAndCacheIsProjectedDrawableMethod(); 51 } 52 53 @Override 54 public void setHotspot(float x, float y) { 55 mDrawable.setHotspot(x, y); 56 } 57 58 @Override 59 public void setHotspotBounds(int left, int top, int right, int bottom) { 60 mDrawable.setHotspotBounds(left, top, right, bottom); 61 } 62 63 @Override 64 public void getOutline(@NonNull Outline outline) { 65 mDrawable.getOutline(outline); 66 } 67 68 @NonNull 69 @Override 70 public Rect getDirtyBounds() { 71 return mDrawable.getDirtyBounds(); 72 } 73 74 @Override 75 public void setTintList(ColorStateList tint) { 76 if (isCompatTintEnabled()) { 77 super.setTintList(tint); 78 } else { 79 mDrawable.setTintList(tint); 80 } 81 } 82 83 @Override 84 public void setTint(int tintColor) { 85 if (isCompatTintEnabled()) { 86 super.setTint(tintColor); 87 } else { 88 mDrawable.setTint(tintColor); 89 } 90 } 91 92 @Override 93 public void setTintMode(PorterDuff.Mode tintMode) { 94 if (isCompatTintEnabled()) { 95 super.setTintMode(tintMode); 96 } else { 97 mDrawable.setTintMode(tintMode); 98 } 99 } 100 101 @Override 102 public boolean setState(@NonNull int[] stateSet) { 103 if (super.setState(stateSet)) { 104 // Manually invalidate because the framework doesn't currently force an invalidation 105 // on a state change 106 invalidateSelf(); 107 return true; 108 } 109 return false; 110 } 111 112 @Override 113 protected boolean isCompatTintEnabled() { 114 if (Build.VERSION.SDK_INT == 21) { 115 final Drawable drawable = mDrawable; 116 return drawable instanceof GradientDrawable 117 || drawable instanceof DrawableContainer 118 || drawable instanceof InsetDrawable 119 || drawable instanceof RippleDrawable; 120 } 121 return false; 122 } 123 124 /** 125 * This method is overriding hidden framework method in {@link Drawable}. It is used by the 126 * system and thus it should not be removed. 127 */ 128 public boolean isProjected() { 129 if (mDrawable != null && sIsProjectedDrawableMethod != null) { 130 try { 131 return (Boolean) sIsProjectedDrawableMethod.invoke(mDrawable); 132 } catch (Exception ex) { 133 Log.w(TAG, "Error calling Drawable#isProjected() method", ex); 134 } 135 } 136 137 return false; 138 } 139 140 @NonNull 141 @Override 142 DrawableWrapperState mutateConstantState() { 143 return new DrawableWrapperStateLollipop(mState, null); 144 } 145 146 private static class DrawableWrapperStateLollipop extends DrawableWrapperState { 147 DrawableWrapperStateLollipop(@Nullable DrawableWrapperState orig, 148 @Nullable Resources res) { 149 super(orig, res); 150 } 151 152 @NonNull 153 @Override 154 public Drawable newDrawable(@Nullable Resources res) { 155 return new WrappedDrawableApi21(this, res); 156 } 157 } 158 159 private void findAndCacheIsProjectedDrawableMethod() { 160 if (sIsProjectedDrawableMethod == null) { 161 try { 162 sIsProjectedDrawableMethod = Drawable.class.getDeclaredMethod("isProjected"); 163 } catch (Exception ex) { 164 Log.w(TAG, "Failed to retrieve Drawable#isProjected() method", ex); 165 } 166 } 167 } 168} 169