1package com.xtremelabs.robolectric.shadows;
2
3import android.graphics.*;
4import com.xtremelabs.robolectric.internal.Implementation;
5import com.xtremelabs.robolectric.internal.Implements;
6
7import java.util.ArrayList;
8import java.util.List;
9
10import static com.xtremelabs.robolectric.Robolectric.newInstanceOf;
11import static com.xtremelabs.robolectric.Robolectric.shadowOf;
12
13/**
14 * Shadows the {@code android.graphics.Canvas} class.
15 * <p/>
16 * Broken.
17 * This implementation is very specific to the application for which it was developed.
18 * Todo: Reimplement. Consider using the same strategy of collecting a history of draw events and providing methods for writing queries based on type, number, and order of events.
19 */
20@SuppressWarnings({"UnusedDeclaration"})
21@Implements(Canvas.class)
22public class ShadowCanvas {
23    private List<PathPaintHistoryEvent> pathPaintEvents = new ArrayList<PathPaintHistoryEvent>();
24    private List<CirclePaintHistoryEvent> circlePaintEvents = new ArrayList<CirclePaintHistoryEvent>();
25    private Paint drawnPaint;
26    private Bitmap targetBitmap = newInstanceOf(Bitmap.class);
27    private float translateX;
28    private float translateY;
29    private float scaleX = 1;
30    private float scaleY = 1;
31
32    public void __constructor__(Bitmap bitmap) {
33        this.targetBitmap = bitmap;
34    }
35
36    public void appendDescription(String s) {
37        shadowOf(targetBitmap).appendDescription(s);
38    }
39
40    public String getDescription() {
41        return shadowOf(targetBitmap).getDescription();
42    }
43
44    @Implementation
45    public void translate(float x, float y) {
46        this.translateX = x;
47        this.translateY = y;
48    }
49
50    @Implementation
51    public void scale(float sx, float sy) {
52        this.scaleX = sx;
53        this.scaleY = sy;
54    }
55
56    @Implementation
57    public void scale(float sx, float sy, float px, float py) {
58        this.scaleX = sx;
59        this.scaleY = sy;
60    }
61
62    @Implementation
63    public void drawPaint(Paint paint) {
64        drawnPaint = paint;
65    }
66
67    @Implementation
68    public void drawColor(int color) {
69        appendDescription("draw color " + color);
70    }
71
72    @Implementation
73    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
74        describeBitmap(bitmap, paint);
75
76        int x = (int) (left + translateX);
77        int y = (int) (top + translateY);
78        if (x != 0 && y != 0) {
79            appendDescription(" at (" + x + "," + y + ")");
80        }
81
82        if (scaleX != 1 && scaleY != 1) {
83            appendDescription(" scaled by (" + scaleX + "," + scaleY + ")");
84        }
85    }
86
87    @Implementation
88    public void drawPath(Path path, Paint paint) {
89        pathPaintEvents.add(new PathPaintHistoryEvent(path, paint));
90
91        separateLines();
92        appendDescription("Path " + shadowOf(path).getPoints().toString());
93    }
94
95    private void describeBitmap(Bitmap bitmap, Paint paint) {
96        separateLines();
97
98        appendDescription(shadowOf(bitmap).getDescription());
99
100        if (paint != null) {
101            ColorFilter colorFilter = paint.getColorFilter();
102            if (colorFilter != null) {
103                appendDescription(" with " + colorFilter);
104            }
105        }
106    }
107
108    private void separateLines() {
109        if (getDescription().length() != 0) {
110            appendDescription("\n");
111        }
112    }
113
114    @Implementation
115    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
116        describeBitmap(bitmap, paint);
117
118        appendDescription(" transformed by matrix");
119    }
120
121    public int getPathPaintHistoryCount() {
122        return pathPaintEvents.size();
123    }
124
125    public int getCirclePaintHistoryCount() {
126        return circlePaintEvents.size();
127    }
128
129    public boolean hasDrawnPath() {
130        return getPathPaintHistoryCount() > 0;
131    }
132
133    public boolean hasDrawnCircle() {
134        return circlePaintEvents.size() > 0;
135    }
136
137    public Paint getDrawnPathPaint(int i) {
138        return pathPaintEvents.get(i).pathPaint;
139    }
140
141    public Path getDrawnPath(int i) {
142        return pathPaintEvents.get(i).drawnPath;
143    }
144
145    public CirclePaintHistoryEvent getDrawnCircle(int i) {
146        return circlePaintEvents.get(i);
147    }
148
149    public void resetCanvasHistory() {
150        pathPaintEvents.clear();
151        circlePaintEvents.clear();
152    }
153
154    public Paint getDrawnPaint() {
155        return drawnPaint;
156    }
157
158    private static class PathPaintHistoryEvent {
159        private Path drawnPath;
160        private Paint pathPaint;
161
162        PathPaintHistoryEvent(Path drawnPath, Paint pathPaint) {
163            this.drawnPath = drawnPath;
164            this.pathPaint = pathPaint;
165        }
166    }
167
168    public static class CirclePaintHistoryEvent {
169        public Paint paint;
170        public float centerX;
171        public float centerY;
172        public float radius;
173
174        private CirclePaintHistoryEvent(float centerX, float centerY, float radius, Paint paint) {
175            this.paint = paint;
176            this.centerX = centerX;
177            this.centerY = centerY;
178            this.radius = radius;
179        }
180    }
181}
182