• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    */
visualize(Canvas canvas)51   public static String visualize(Canvas canvas) {
52     ShadowCanvas shadowCanvas = Shadow.extract(canvas);
53     return shadowCanvas.getDescription();
54   }
55 
56   @Implementation
__constructor__(Bitmap bitmap)57   protected void __constructor__(Bitmap bitmap) {
58     this.targetBitmap = bitmap;
59   }
60 
appendDescription(String s)61   public void appendDescription(String s) {
62     ShadowBitmap shadowBitmap = Shadow.extract(targetBitmap);
63     shadowBitmap.appendDescription(s);
64   }
65 
getDescription()66   public String getDescription() {
67     ShadowBitmap shadowBitmap = Shadow.extract(targetBitmap);
68     return shadowBitmap.getDescription();
69   }
70 
71   @Implementation
setBitmap(Bitmap bitmap)72   protected void setBitmap(Bitmap bitmap) {
73     targetBitmap = bitmap;
74   }
75 
76   @Implementation
drawText(String text, float x, float y, Paint paint)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
drawText(CharSequence text, int start, int end, float x, float y, Paint paint)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
drawText(char[] text, int index, int count, float x, float y, Paint paint)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
drawText(String text, int start, int end, float x, float y, Paint paint)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
translate(float x, float y)98   protected void translate(float x, float y) {
99     this.translateX = x;
100     this.translateY = y;
101   }
102 
103   @Implementation
scale(float sx, float sy)104   protected void scale(float sx, float sy) {
105     this.scaleX = sx;
106     this.scaleY = sy;
107   }
108 
109   @Implementation
scale(float sx, float sy, float px, float py)110   protected void scale(float sx, float sy, float px, float py) {
111     this.scaleX = sx;
112     this.scaleY = sy;
113   }
114 
115   @Implementation
drawPaint(Paint paint)116   protected void drawPaint(Paint paint) {
117     drawnPaint = paint;
118   }
119 
120   @Implementation
drawColor(int color)121   protected void drawColor(int color) {
122     appendDescription("draw color " + color);
123   }
124 
125   @Implementation
drawBitmap(Bitmap bitmap, float left, float top, Paint paint)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
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)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
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)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
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)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
drawPath(Path path, Paint paint)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
drawCircle(float cx, float cy, float radius, Paint paint)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
drawArc( RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)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
drawRect(float left, float top, float right, float bottom, Paint paint)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
drawLine(float startX, float startY, float stopX, float stopY, Paint paint)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
drawOval(RectF oval, Paint paint)211   protected void drawOval(RectF oval, Paint paint) {
212     ovalPaintEvents.add(new OvalPaintHistoryEvent(oval, paint));
213   }
214 
215   @Implementation
save()216   protected int save() {
217     return 1;
218   }
219 
220   @Implementation
restore()221   protected void restore() {}
222 
describeBitmap(Bitmap bitmap, Paint paint)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 
formatColorMatric(ColorMatrix colorMatrix)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 
separateLines()255   private void separateLines() {
256     if (getDescription().length() != 0) {
257       appendDescription("\n");
258     }
259   }
260 
getPathPaintHistoryCount()261   public int getPathPaintHistoryCount() {
262     return pathPaintEvents.size();
263   }
264 
getCirclePaintHistoryCount()265   public int getCirclePaintHistoryCount() {
266     return circlePaintEvents.size();
267   }
268 
getArcPaintHistoryCount()269   public int getArcPaintHistoryCount() {
270     return arcPaintEvents.size();
271   }
272 
hasDrawnPath()273   public boolean hasDrawnPath() {
274     return getPathPaintHistoryCount() > 0;
275   }
276 
hasDrawnCircle()277   public boolean hasDrawnCircle() {
278     return circlePaintEvents.size() > 0;
279   }
280 
getDrawnPathPaint(int i)281   public Paint getDrawnPathPaint(int i) {
282     return pathPaintEvents.get(i).pathPaint;
283   }
284 
getDrawnPath(int i)285   public Path getDrawnPath(int i) {
286     return pathPaintEvents.get(i).drawnPath;
287   }
288 
getDrawnCircle(int i)289   public CirclePaintHistoryEvent getDrawnCircle(int i) {
290     return circlePaintEvents.get(i);
291   }
292 
getDrawnArc(int i)293   public ArcPaintHistoryEvent getDrawnArc(int i) {
294     return arcPaintEvents.get(i);
295   }
296 
resetCanvasHistory()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 
getDrawnPaint()308   public Paint getDrawnPaint() {
309     return drawnPaint;
310   }
311 
setHeight(int height)312   public void setHeight(int height) {
313     this.height = height;
314   }
315 
setWidth(int width)316   public void setWidth(int width) {
317     this.width = width;
318   }
319 
320   @Implementation
getWidth()321   protected int getWidth() {
322     return width;
323   }
324 
325   @Implementation
getHeight()326   protected int getHeight() {
327     return height;
328   }
329 
getDrawnTextEvent(int i)330   public TextHistoryEvent getDrawnTextEvent(int i) {
331     return drawnTextEventHistory.get(i);
332   }
333 
getTextHistoryCount()334   public int getTextHistoryCount() {
335     return drawnTextEventHistory.size();
336   }
337 
getDrawnRect(int i)338   public RectPaintHistoryEvent getDrawnRect(int i) {
339     return rectPaintEvents.get(i);
340   }
341 
getLastDrawnRect()342   public RectPaintHistoryEvent getLastDrawnRect() {
343     return rectPaintEvents.get(rectPaintEvents.size() - 1);
344   }
345 
getRectPaintHistoryCount()346   public int getRectPaintHistoryCount() {
347     return rectPaintEvents.size();
348   }
349 
getDrawnLine(int i)350   public LinePaintHistoryEvent getDrawnLine(int i) {
351     return linePaintEvents.get(i);
352   }
353 
getLinePaintHistoryCount()354   public int getLinePaintHistoryCount() {
355     return linePaintEvents.size();
356   }
357 
getOvalPaintHistoryCount()358   public int getOvalPaintHistoryCount() {
359     return ovalPaintEvents.size();
360   }
361 
getDrawnOval(int i)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 
LinePaintHistoryEvent( float startX, float startY, float stopX, float stopY, Paint paint)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 
OvalPaintHistoryEvent(RectF oval, Paint paint)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 
RectPaintHistoryEvent( float left, float top, float right, float bottom, Paint paint)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 
PathPaintHistoryEvent(Path drawnPath, Paint pathPaint)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 
CirclePaintHistoryEvent(float centerX, float centerY, float radius, Paint paint)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 
ArcPaintHistoryEvent(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)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 
TextHistoryEvent(float x, float y, Paint paint, String text)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