Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import android.graphics.Bitmap;
      4 import android.graphics.Canvas;
      5 import android.graphics.ColorFilter;
      6 import android.graphics.ColorMatrix;
      7 import android.graphics.ColorMatrixColorFilter;
      8 import android.graphics.Matrix;
      9 import android.graphics.Paint;
     10 import android.graphics.Path;
     11 import android.graphics.Rect;
     12 import android.graphics.RectF;
     13 import java.util.ArrayList;
     14 import java.util.List;
     15 import org.robolectric.annotation.Implementation;
     16 import org.robolectric.annotation.Implements;
     17 import org.robolectric.shadow.api.Shadow;
     18 import org.robolectric.util.Join;
     19 import org.robolectric.util.ReflectionHelpers;
     20 
     21 /**
     22  * Broken. This implementation is very specific to the application for which it was developed.
     23  * Todo: Reimplement. Consider using the same strategy of collecting a history of draw events
     24  * and providing methods for writing queries based on type, number, and order of events.
     25  */
     26 @SuppressWarnings({"UnusedDeclaration"})
     27 @Implements(Canvas.class)
     28 public class ShadowCanvas {
     29   private List<PathPaintHistoryEvent> pathPaintEvents = new ArrayList<>();
     30   private List<CirclePaintHistoryEvent> circlePaintEvents = new ArrayList<>();
     31   private List<ArcPaintHistoryEvent> arcPaintEvents = new ArrayList<>();
     32   private List<RectPaintHistoryEvent> rectPaintEvents = new ArrayList<>();
     33   private List<LinePaintHistoryEvent> linePaintEvents = new ArrayList<>();
     34   private List<OvalPaintHistoryEvent> ovalPaintEvents = new ArrayList<>();
     35   private List<TextHistoryEvent> drawnTextEventHistory = new ArrayList<>();
     36   private Paint drawnPaint;
     37   private Bitmap targetBitmap = ReflectionHelpers.callConstructor(Bitmap.class);
     38   private float translateX;
     39   private float translateY;
     40   private float scaleX = 1;
     41   private float scaleY = 1;
     42   private int height;
     43   private int width;
     44 
     45   /**
     46    * Returns a textual representation of the appearance of the object.
     47    *
     48    * @param canvas the canvas to visualize
     49    * @return The textual representation of the appearance of the object.
     50    */
     51   public static String visualize(Canvas canvas) {
     52     ShadowCanvas shadowCanvas = Shadow.extract(canvas);
     53     return shadowCanvas.getDescription();
     54   }
     55 
     56   @Implementation
     57   protected void __constructor__(Bitmap bitmap) {
     58     this.targetBitmap = bitmap;
     59   }
     60 
     61   public void appendDescription(String s) {
     62     ShadowBitmap shadowBitmap = Shadow.extract(targetBitmap);
     63     shadowBitmap.appendDescription(s);
     64   }
     65 
     66   public String getDescription() {
     67     ShadowBitmap shadowBitmap = Shadow.extract(targetBitmap);
     68     return shadowBitmap.getDescription();
     69   }
     70 
     71   @Implementation
     72   protected void setBitmap(Bitmap bitmap) {
     73     targetBitmap = bitmap;
     74   }
     75 
     76   @Implementation
     77   protected void drawText(String text, float x, float y, Paint paint) {
     78     drawnTextEventHistory.add(new TextHistoryEvent(x, y, paint, text));
     79   }
     80 
     81   @Implementation
     82   protected void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
     83     drawnTextEventHistory.add(
     84         new TextHistoryEvent(x, y, paint, text.subSequence(start, end).toString()));
     85   }
     86 
     87   @Implementation
     88   protected void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
     89     drawnTextEventHistory.add(new TextHistoryEvent(x, y, paint, new String(text, index, count)));
     90   }
     91 
     92   @Implementation
     93   protected void drawText(String text, int start, int end, float x, float y, Paint paint) {
     94     drawnTextEventHistory.add(new TextHistoryEvent(x, y, paint, text.substring(start, end)));
     95   }
     96 
     97   @Implementation
     98   protected void translate(float x, float y) {
     99     this.translateX = x;
    100     this.translateY = y;
    101   }
    102 
    103   @Implementation
    104   protected void scale(float sx, float sy) {
    105     this.scaleX = sx;
    106     this.scaleY = sy;
    107   }
    108 
    109   @Implementation
    110   protected void scale(float sx, float sy, float px, float py) {
    111     this.scaleX = sx;
    112     this.scaleY = sy;
    113   }
    114 
    115   @Implementation
    116   protected void drawPaint(Paint paint) {
    117     drawnPaint = paint;
    118   }
    119 
    120   @Implementation
    121   protected void drawColor(int color) {
    122     appendDescription("draw color " + color);
    123   }
    124 
    125   @Implementation
    126   protected void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
    127     describeBitmap(bitmap, paint);
    128 
    129     int x = (int) (left + translateX);
    130     int y = (int) (top + translateY);
    131     if (x != 0 || y != 0) {
    132       appendDescription(" at (" + x + "," + y + ")");
    133   }
    134 
    135     if (scaleX != 1 && scaleY != 1) {
    136       appendDescription(" scaled by (" + scaleX + "," + scaleY + ")");
    137     }
    138   }
    139 
    140   @Implementation
    141   protected void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
    142     describeBitmap(bitmap, paint);
    143 
    144     StringBuilder descriptionBuilder = new StringBuilder();
    145     if (dst != null) {
    146       descriptionBuilder.append(" at (").append(dst.left).append(",").append(dst.top)
    147           .append(") with height=").append(dst.height()).append(" and width=").append(dst.width());
    148     }
    149 
    150     if (src != null) {
    151       descriptionBuilder.append( " taken from ").append(src.toString());
    152     }
    153     appendDescription(descriptionBuilder.toString());
    154   }
    155 
    156   @Implementation
    157   protected void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
    158     describeBitmap(bitmap, paint);
    159 
    160     StringBuilder descriptionBuilder = new StringBuilder();
    161     if (dst != null) {
    162       descriptionBuilder.append(" at (").append(dst.left).append(",").append(dst.top)
    163           .append(") with height=").append(dst.height()).append(" and width=").append(dst.width());
    164     }
    165 
    166     if (src != null) {
    167       descriptionBuilder.append( " taken from ").append(src.toString());
    168     }
    169     appendDescription(descriptionBuilder.toString());
    170   }
    171 
    172   @Implementation
    173   protected void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
    174     describeBitmap(bitmap, paint);
    175 
    176     ShadowMatrix shadowMatrix = Shadow.extract(matrix);
    177     appendDescription(" transformed by " + shadowMatrix.getDescription());
    178   }
    179 
    180   @Implementation
    181   protected void drawPath(Path path, Paint paint) {
    182     pathPaintEvents.add(new PathPaintHistoryEvent(new Path(path), new Paint(paint)));
    183 
    184     separateLines();
    185     ShadowPath shadowPath = Shadow.extract(path);
    186     appendDescription("Path " + shadowPath.getPoints().toString());
    187   }
    188 
    189   @Implementation
    190   protected void drawCircle(float cx, float cy, float radius, Paint paint) {
    191     circlePaintEvents.add(new CirclePaintHistoryEvent(cx, cy, radius, paint));
    192   }
    193 
    194   @Implementation
    195   protected void drawArc(
    196       RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) {
    197     arcPaintEvents.add(new ArcPaintHistoryEvent(oval, startAngle, sweepAngle, useCenter, paint));
    198   }
    199 
    200   @Implementation
    201   protected void drawRect(float left, float top, float right, float bottom, Paint paint) {
    202     rectPaintEvents.add(new RectPaintHistoryEvent(left, top, right, bottom, paint));
    203   }
    204 
    205   @Implementation
    206   protected void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
    207     linePaintEvents.add(new LinePaintHistoryEvent(startX, startY, stopX, stopY, paint));
    208   }
    209 
    210   @Implementation
    211   protected void drawOval(RectF oval, Paint paint) {
    212     ovalPaintEvents.add(new OvalPaintHistoryEvent(oval, paint));
    213   }
    214 
    215   @Implementation
    216   protected int save() {
    217     return 1;
    218   }
    219 
    220   @Implementation
    221   protected void restore() {}
    222 
    223   private void describeBitmap(Bitmap bitmap, Paint paint) {
    224     separateLines();
    225 
    226     ShadowBitmap shadowBitmap = Shadow.extract(bitmap);
    227     appendDescription(shadowBitmap.getDescription());
    228 
    229     if (paint != null) {
    230       ColorFilter colorFilter = paint.getColorFilter();
    231       if (colorFilter != null) {
    232         if (colorFilter instanceof ColorMatrixColorFilter) {
    233           ColorMatrixColorFilter colorMatrixColorFilter = (ColorMatrixColorFilter) colorFilter;
    234           ShadowColorMatrixColorFilter shadowColorMatrixColorFilter =
    235               Shadow.extract(colorMatrixColorFilter);
    236           ColorMatrix colorMatrix = shadowColorMatrixColorFilter.getMatrix();
    237           appendDescription(" with ColorMatrixColorFilter<" + formatColorMatric(colorMatrix) + ">");
    238         } else {
    239           appendDescription(" with " + colorFilter);
    240         }
    241       }
    242     }
    243   }
    244 
    245   private String formatColorMatric(ColorMatrix colorMatrix) {
    246     List<String> floats = new ArrayList<>();
    247     for (float f : colorMatrix.getArray()) {
    248       String format = String.format("%.2f", f);
    249       format = format.replace(".00", "");
    250       floats.add(format);
    251     }
    252     return Join.join(",", floats);
    253   }
    254 
    255   private void separateLines() {
    256     if (getDescription().length() != 0) {
    257       appendDescription("\n");
    258     }
    259   }
    260 
    261   public int getPathPaintHistoryCount() {
    262     return pathPaintEvents.size();
    263   }
    264 
    265   public int getCirclePaintHistoryCount() {
    266     return circlePaintEvents.size();
    267   }
    268 
    269   public int getArcPaintHistoryCount() {
    270     return arcPaintEvents.size();
    271   }
    272 
    273   public boolean hasDrawnPath() {
    274     return getPathPaintHistoryCount() > 0;
    275   }
    276 
    277   public boolean hasDrawnCircle() {
    278     return circlePaintEvents.size() > 0;
    279   }
    280 
    281   public Paint getDrawnPathPaint(int i) {
    282     return pathPaintEvents.get(i).pathPaint;
    283   }
    284 
    285   public Path getDrawnPath(int i) {
    286     return pathPaintEvents.get(i).drawnPath;
    287   }
    288 
    289   public CirclePaintHistoryEvent getDrawnCircle(int i) {
    290     return circlePaintEvents.get(i);
    291   }
    292 
    293   public ArcPaintHistoryEvent getDrawnArc(int i) {
    294     return arcPaintEvents.get(i);
    295   }
    296 
    297   public void resetCanvasHistory() {
    298     drawnTextEventHistory.clear();
    299     pathPaintEvents.clear();
    300     circlePaintEvents.clear();
    301     rectPaintEvents.clear();
    302     linePaintEvents.clear();
    303     ovalPaintEvents.clear();
    304     ShadowBitmap shadowBitmap = Shadow.extract(targetBitmap);
    305     shadowBitmap.setDescription("");
    306   }
    307 
    308   public Paint getDrawnPaint() {
    309     return drawnPaint;
    310   }
    311 
    312   public void setHeight(int height) {
    313     this.height = height;
    314   }
    315 
    316   public void setWidth(int width) {
    317     this.width = width;
    318   }
    319 
    320   @Implementation
    321   protected int getWidth() {
    322     return width;
    323   }
    324 
    325   @Implementation
    326   protected int getHeight() {
    327     return height;
    328   }
    329 
    330   public TextHistoryEvent getDrawnTextEvent(int i) {
    331     return drawnTextEventHistory.get(i);
    332   }
    333 
    334   public int getTextHistoryCount() {
    335     return drawnTextEventHistory.size();
    336   }
    337 
    338   public RectPaintHistoryEvent getDrawnRect(int i) {
    339     return rectPaintEvents.get(i);
    340   }
    341 
    342   public RectPaintHistoryEvent getLastDrawnRect() {
    343     return rectPaintEvents.get(rectPaintEvents.size() - 1);
    344   }
    345 
    346   public int getRectPaintHistoryCount() {
    347     return rectPaintEvents.size();
    348   }
    349 
    350   public LinePaintHistoryEvent getDrawnLine(int i) {
    351     return linePaintEvents.get(i);
    352   }
    353 
    354   public int getLinePaintHistoryCount() {
    355     return linePaintEvents.size();
    356   }
    357 
    358   public int getOvalPaintHistoryCount() {
    359     return ovalPaintEvents.size();
    360   }
    361 
    362   public OvalPaintHistoryEvent getDrawnOval(int i) {
    363     return ovalPaintEvents.get(i);
    364   }
    365 
    366   public static class LinePaintHistoryEvent {
    367     public Paint paint;
    368     public float startX;
    369     public float startY;
    370     public float stopX;
    371     public float stopY;
    372 
    373     private LinePaintHistoryEvent(
    374         float startX, float startY, float stopX, float stopY, Paint paint) {
    375       this.paint = new Paint(paint);
    376       this.paint.setColor(paint.getColor());
    377       this.paint.setStrokeWidth(paint.getStrokeWidth());
    378       this.startX = startX;
    379       this.startY = startY;
    380       this.stopX = stopX;
    381       this.stopY = stopY;
    382     }
    383   }
    384 
    385   public static class OvalPaintHistoryEvent {
    386     public final RectF oval;
    387     public final Paint paint;
    388 
    389     private OvalPaintHistoryEvent(RectF oval, Paint paint) {
    390       this.oval = new RectF(oval);
    391       this.paint = new Paint(paint);
    392       this.paint.setColor(paint.getColor());
    393       this.paint.setStrokeWidth(paint.getStrokeWidth());
    394     }
    395   }
    396 
    397   public static class RectPaintHistoryEvent {
    398     public final Paint paint;
    399     public final RectF rect;
    400     public final float left;
    401     public final float top;
    402     public final float right;
    403     public final float bottom;
    404 
    405     private RectPaintHistoryEvent(
    406         float left, float top, float right, float bottom, Paint paint){
    407       this.rect = new RectF(left, top, right, bottom);
    408       this.paint = new Paint(paint);
    409       this.paint.setColor(paint.getColor());
    410       this.paint.setStrokeWidth(paint.getStrokeWidth());
    411       this.paint.setTextSize(paint.getTextSize());
    412       this.paint.setStyle(paint.getStyle());
    413       this.left = left;
    414       this.top = top;
    415       this.right = right;
    416       this.bottom = bottom;
    417     }
    418   }
    419 
    420   private static class PathPaintHistoryEvent {
    421     private final Path drawnPath;
    422     private final Paint pathPaint;
    423 
    424     PathPaintHistoryEvent(Path drawnPath, Paint pathPaint) {
    425       this.drawnPath = drawnPath;
    426       this.pathPaint = pathPaint;
    427     }
    428   }
    429 
    430   public static class CirclePaintHistoryEvent {
    431     public final float centerX;
    432     public final float centerY;
    433     public final float radius;
    434     public final Paint paint;
    435 
    436     private CirclePaintHistoryEvent(float centerX, float centerY, float radius, Paint paint) {
    437       this.centerX = centerX;
    438       this.centerY = centerY;
    439       this.radius = radius;
    440       this.paint = paint;
    441     }
    442   }
    443 
    444   public static class ArcPaintHistoryEvent {
    445     public final RectF oval;
    446     public final float startAngle;
    447     public final float sweepAngle;
    448     public final boolean useCenter;
    449     public final Paint paint;
    450 
    451     public ArcPaintHistoryEvent(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
    452                                 Paint paint) {
    453       this.oval = oval;
    454       this.startAngle = startAngle;
    455       this.sweepAngle = sweepAngle;
    456       this.useCenter = useCenter;
    457       this.paint = paint;
    458     }
    459   }
    460 
    461   public static class TextHistoryEvent {
    462     public final float x;
    463     public final float y;
    464     public final Paint paint;
    465     public final String text;
    466 
    467     private TextHistoryEvent(float x, float y, Paint paint, String text) {
    468       this.x = x;
    469       this.y = y;
    470       this.paint = paint;
    471       this.text = text;
    472     }
    473   }
    474 }
    475