• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.KITKAT;
4 
5 import android.content.res.Resources;
6 import android.graphics.Bitmap;
7 import android.graphics.BitmapFactory;
8 import android.graphics.NinePatch;
9 import android.graphics.Rect;
10 import android.graphics.drawable.BitmapDrawable;
11 import android.graphics.drawable.Drawable;
12 import android.graphics.drawable.NinePatchDrawable;
13 import android.util.DisplayMetrics;
14 import android.util.TypedValue;
15 import java.io.InputStream;
16 import java.util.ArrayList;
17 import java.util.List;
18 import org.robolectric.annotation.Implementation;
19 import org.robolectric.annotation.Implements;
20 import org.robolectric.annotation.RealObject;
21 import org.robolectric.annotation.Resetter;
22 import org.robolectric.shadow.api.Shadow;
23 import org.robolectric.util.ReflectionHelpers;
24 import org.robolectric.util.ReflectionHelpers.ClassParameter;
25 
26 @SuppressWarnings({"UnusedDeclaration"})
27 @Implements(Drawable.class)
28 public class ShadowDrawable {
29   private static int defaultIntrinsicWidth = -1;
30   private static int defaultIntrinsicHeight = -1;
31   static final List<String> corruptStreamSources = new ArrayList<>();
32 
33   @RealObject Drawable realDrawable;
34 
35   int createdFromResId = -1;
36   InputStream createdFromInputStream;
37 
38   private int intrinsicWidth = defaultIntrinsicWidth;
39   private int intrinsicHeight = defaultIntrinsicHeight;
40   private int alpha;
41   private boolean wasInvalidated;
42 
43   @Implementation
createFromStream(InputStream is, String srcName)44   protected static Drawable createFromStream(InputStream is, String srcName) {
45     if (corruptStreamSources.contains(srcName)) {
46       return null;
47     }
48     BitmapDrawable drawable = new BitmapDrawable(ReflectionHelpers.callConstructor(Bitmap.class));
49     ShadowBitmapDrawable shadowBitmapDrawable = Shadow.extract(drawable);
50     shadowBitmapDrawable.createdFromInputStream = is;
51     shadowBitmapDrawable.drawableCreateFromStreamSource = srcName;
52     shadowBitmapDrawable.validate(); // start off not invalidated
53     return drawable;
54   }
55 
56   @Implementation // todo: this sucks, it's all just so we can detect 9-patches
createFromResourceStream( Resources res, TypedValue value, InputStream is, String srcName, BitmapFactory.Options opts)57   protected static Drawable createFromResourceStream(
58       Resources res, TypedValue value, InputStream is, String srcName, BitmapFactory.Options opts) {
59     if (is == null) {
60       return null;
61     }
62     Rect pad = new Rect();
63     if (opts == null) opts = new BitmapFactory.Options();
64     opts.inScreenDensity = DisplayMetrics.DENSITY_DEFAULT;
65 
66     Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
67     if (bm != null) {
68       boolean isNinePatch = srcName != null && srcName.contains(".9.");
69       if (isNinePatch) {
70         ReflectionHelpers.callInstanceMethod(bm, "setNinePatchChunk", ClassParameter.from(byte[].class, new byte[0]));
71       }
72       byte[] np = bm.getNinePatchChunk();
73       if (np == null || !NinePatch.isNinePatchChunk(np)) {
74         np = null;
75         pad = null;
76       }
77 
78       if (np != null) {
79         // todo: wrong
80         return new NinePatchDrawable(res, bm, np, pad, srcName);
81       }
82 
83       return new BitmapDrawable(res, bm);
84     }
85     return null;
86   }
87 
88   @Implementation
createFromPath(String pathName)89   protected static Drawable createFromPath(String pathName) {
90     BitmapDrawable drawable = new BitmapDrawable(ReflectionHelpers.callConstructor(Bitmap.class));
91     ShadowBitmapDrawable shadowBitmapDrawable = Shadow.extract(drawable);
92     shadowBitmapDrawable.drawableCreateFromPath = pathName;
93     shadowBitmapDrawable.validate(); // start off not invalidated
94     return drawable;
95   }
96 
createFromResourceId(int resourceId)97   public static Drawable createFromResourceId(int resourceId) {
98     Bitmap bitmap = ReflectionHelpers.callConstructor(Bitmap.class);
99     ShadowBitmap shadowBitmap = Shadow.extract(bitmap);
100     shadowBitmap.createdFromResId = resourceId;
101     BitmapDrawable drawable = new BitmapDrawable(bitmap);
102     ShadowBitmapDrawable shadowBitmapDrawable = Shadow.extract(drawable);
103     shadowBitmapDrawable.validate(); // start off not invalidated
104     shadowBitmapDrawable.createdFromResId = resourceId;
105     return drawable;
106   }
107 
108   @Implementation
getIntrinsicWidth()109   protected int getIntrinsicWidth() {
110     return intrinsicWidth;
111   }
112 
113   @Implementation
getIntrinsicHeight()114   protected int getIntrinsicHeight() {
115     return intrinsicHeight;
116   }
117 
addCorruptStreamSource(String src)118   public static void addCorruptStreamSource(String src) {
119     corruptStreamSources.add(src);
120   }
121 
122   @Resetter
clearCorruptStreamSources()123   public static void clearCorruptStreamSources() {
124     corruptStreamSources.clear();
125   }
126 
setDefaultIntrinsicWidth(int defaultIntrinsicWidth)127   public static void setDefaultIntrinsicWidth(int defaultIntrinsicWidth) {
128     ShadowDrawable.defaultIntrinsicWidth = defaultIntrinsicWidth;
129   }
130 
setDefaultIntrinsicHeight(int defaultIntrinsicHeight)131   public static void setDefaultIntrinsicHeight(int defaultIntrinsicHeight) {
132     ShadowDrawable.defaultIntrinsicHeight = defaultIntrinsicHeight;
133   }
134 
setIntrinsicWidth(int intrinsicWidth)135   public void setIntrinsicWidth(int intrinsicWidth) {
136     this.intrinsicWidth = intrinsicWidth;
137   }
138 
setIntrinsicHeight(int intrinsicHeight)139   public void setIntrinsicHeight(int intrinsicHeight) {
140     this.intrinsicHeight = intrinsicHeight;
141   }
142 
getInputStream()143   public InputStream getInputStream() {
144     return createdFromInputStream;
145   }
146 
147   @Implementation
setAlpha(int alpha)148   protected void setAlpha(int alpha) {
149     this.alpha = alpha;
150     Shadow.directlyOn(realDrawable, Drawable.class).setAlpha(alpha);
151   }
152 
153   @Implementation
invalidateSelf()154   protected void invalidateSelf() {
155     wasInvalidated = true;
156     Shadow.directlyOn(realDrawable, Drawable.class, "invalidateSelf");
157   }
158 
159   @Implementation(minSdk = KITKAT)
getAlpha()160   protected int getAlpha() {
161     return alpha;
162   }
163 
getCreatedFromResId()164   public int getCreatedFromResId() {
165     return createdFromResId;
166   }
167 
wasInvalidated()168   public boolean wasInvalidated() {
169     return wasInvalidated;
170   }
171 
validate()172   public void validate() {
173     wasInvalidated = false;
174   }
175 }
176