Home | History | Annotate | Download | only in shadows
      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
     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
     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
     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 
     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
    109   protected int getIntrinsicWidth() {
    110     return intrinsicWidth;
    111   }
    112 
    113   @Implementation
    114   protected int getIntrinsicHeight() {
    115     return intrinsicHeight;
    116   }
    117 
    118   public static void addCorruptStreamSource(String src) {
    119     corruptStreamSources.add(src);
    120   }
    121 
    122   @Resetter
    123   public static void clearCorruptStreamSources() {
    124     corruptStreamSources.clear();
    125   }
    126 
    127   public static void setDefaultIntrinsicWidth(int defaultIntrinsicWidth) {
    128     ShadowDrawable.defaultIntrinsicWidth = defaultIntrinsicWidth;
    129   }
    130 
    131   public static void setDefaultIntrinsicHeight(int defaultIntrinsicHeight) {
    132     ShadowDrawable.defaultIntrinsicHeight = defaultIntrinsicHeight;
    133   }
    134 
    135   public void setIntrinsicWidth(int intrinsicWidth) {
    136     this.intrinsicWidth = intrinsicWidth;
    137   }
    138 
    139   public void setIntrinsicHeight(int intrinsicHeight) {
    140     this.intrinsicHeight = intrinsicHeight;
    141   }
    142 
    143   public InputStream getInputStream() {
    144     return createdFromInputStream;
    145   }
    146 
    147   @Implementation
    148   protected void setAlpha(int alpha) {
    149     this.alpha = alpha;
    150     Shadow.directlyOn(realDrawable, Drawable.class).setAlpha(alpha);
    151   }
    152 
    153   @Implementation
    154   protected void invalidateSelf() {
    155     wasInvalidated = true;
    156     Shadow.directlyOn(realDrawable, Drawable.class, "invalidateSelf");
    157   }
    158 
    159   @Implementation(minSdk = KITKAT)
    160   protected int getAlpha() {
    161     return alpha;
    162   }
    163 
    164   public int getCreatedFromResId() {
    165     return createdFromResId;
    166   }
    167 
    168   public boolean wasInvalidated() {
    169     return wasInvalidated;
    170   }
    171 
    172   public void validate() {
    173     wasInvalidated = false;
    174   }
    175 }
    176