1package com.example.android.apis.graphics;
2
3import android.app.Activity;
4import android.graphics.Canvas;
5import android.graphics.Paint;
6import android.os.Bundle;
7import android.util.Log;
8import android.view.KeyEvent;
9import android.view.MotionEvent;
10import android.view.SurfaceHolder;
11
12/**
13 * Demonstrates how to take over the Surface from a window to do direct
14 * drawing to it (without going through the view hierarchy).
15 */
16public class WindowSurface extends Activity implements SurfaceHolder.Callback2 {
17    DrawingThread mDrawingThread;
18
19    @Override
20    protected void onCreate(Bundle savedInstanceState) {
21        super.onCreate(savedInstanceState);
22
23        // Tell the activity's window that we want to do our own drawing
24        // to its surface.  This prevents the view hierarchy from drawing to
25        // it, though we can still add views to capture input if desired.
26        getWindow().takeSurface(this);
27
28        // This is the thread that will be drawing to our surface.
29        mDrawingThread = new DrawingThread();
30        mDrawingThread.start();
31    }
32
33    @Override
34    protected void onPause() {
35        super.onPause();
36
37        // Make sure the drawing thread is not running while we are paused.
38        synchronized (mDrawingThread) {
39            mDrawingThread.mRunning = false;
40            mDrawingThread.notify();
41        }
42    }
43
44    @Override
45    protected void onResume() {
46        super.onResume();
47
48        // Let the drawing thread resume running.
49        synchronized (mDrawingThread) {
50            mDrawingThread.mRunning = true;
51            mDrawingThread.notify();
52        }
53    }
54
55    @Override
56    protected void onDestroy() {
57        super.onDestroy();
58
59        // Make sure the drawing thread goes away.
60        synchronized (mDrawingThread) {
61            mDrawingThread.mQuit = true;
62            mDrawingThread.notify();
63        }
64    }
65
66    public void surfaceCreated(SurfaceHolder holder) {
67        // Tell the drawing thread that a surface is available.
68        synchronized (mDrawingThread) {
69            mDrawingThread.mSurface = holder;
70            mDrawingThread.notify();
71        }
72    }
73
74    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
75        // Don't need to do anything here; the drawing thread will pick up
76        // new sizes from the canvas.
77    }
78
79    public void surfaceRedrawNeeded(SurfaceHolder holder) {
80    }
81
82    public void surfaceDestroyed(SurfaceHolder holder) {
83        // We need to tell the drawing thread to stop, and block until
84        // it has done so.
85        synchronized (mDrawingThread) {
86            mDrawingThread.mSurface = holder;
87            mDrawingThread.notify();
88            while (mDrawingThread.mActive) {
89                try {
90                    mDrawingThread.wait();
91                } catch (InterruptedException e) {
92                    e.printStackTrace();
93                }
94            }
95        }
96    }
97
98    // Tracking of a single point that is moving on the screen.
99    static final class MovingPoint {
100        float x, y, dx, dy;
101
102        void init(int width, int height, float minStep) {
103            x = (float)((width-1)*Math.random());
104            y = (float)((height-1)*Math.random());
105            dx = (float)(Math.random()*minStep*2) + 1;
106            dy = (float)(Math.random()*minStep*2) + 1;
107        }
108
109        float adjDelta(float cur, float minStep, float maxStep) {
110            cur += (Math.random()*minStep) - (minStep/2);
111            if (cur < 0 && cur > -minStep) cur = -minStep;
112            if (cur >= 0 && cur < minStep) cur = minStep;
113            if (cur > maxStep) cur = maxStep;
114            if (cur < -maxStep) cur = -maxStep;
115            return cur;
116        }
117
118        void step(int width, int height, float minStep, float maxStep) {
119            x += dx;
120            if (x <= 0 || x >= (width-1)) {
121                if (x <= 0) x = 0;
122                else if (x >= (width-1)) x = width-1;
123                dx = adjDelta(-dx, minStep, maxStep);
124            }
125            y += dy;
126            if (y <= 0 || y >= (height-1)) {
127                if (y <= 0) y = 0;
128                else if (y >= (height-1)) y = height-1;
129                dy = adjDelta(-dy, minStep, maxStep);
130            }
131        }
132    }
133
134    /**
135     * This is a thread that will be running a loop, drawing into the
136     * window's surface.
137     */
138    class DrawingThread extends Thread {
139        // These are protected by the Thread's lock.
140        SurfaceHolder mSurface;
141        boolean mRunning;
142        boolean mActive;
143        boolean mQuit;
144
145        // Internal state.
146        int mLineWidth;
147        float mMinStep;
148        float mMaxStep;
149
150        boolean mInitialized;
151        final MovingPoint mPoint1 = new MovingPoint();
152        final MovingPoint mPoint2 = new MovingPoint();
153
154        static final int NUM_OLD = 100;
155        int mNumOld = 0;
156        final float[] mOld = new float[NUM_OLD*4];
157        final int[] mOldColor = new int[NUM_OLD];
158        int mBrightLine = 0;
159
160        // X is red, Y is blue.
161        final MovingPoint mColor = new MovingPoint();
162
163        final Paint mBackground = new Paint();
164        final Paint mForeground = new Paint();
165
166        int makeGreen(int index) {
167            int dist = Math.abs(mBrightLine-index);
168            if (dist > 10) return 0;
169            return (255-(dist*(255/10))) << 8;
170        }
171
172        @Override
173        public void run() {
174            mLineWidth = (int)(getResources().getDisplayMetrics().density * 1.5);
175            if (mLineWidth < 1) mLineWidth = 1;
176            mMinStep = mLineWidth * 2;
177            mMaxStep = mMinStep * 3;
178
179            mBackground.setColor(0xff000000);
180            mForeground.setColor(0xff00ffff);
181            mForeground.setAntiAlias(false);
182            mForeground.setStrokeWidth(mLineWidth);
183
184            while (true) {
185                // Synchronize with activity: block until the activity is ready
186                // and we have a surface; report whether we are active or inactive
187                // at this point; exit thread when asked to quit.
188                synchronized (this) {
189                    while (mSurface == null || !mRunning) {
190                        if (mActive) {
191                            mActive = false;
192                            notify();
193                        }
194                        if (mQuit) {
195                            return;
196                        }
197                        try {
198                            wait();
199                        } catch (InterruptedException e) {
200                        }
201                    }
202
203                    if (!mActive) {
204                        mActive = true;
205                        notify();
206                    }
207
208                    // Lock the canvas for drawing.
209                    Canvas canvas = mSurface.lockCanvas();
210                    if (canvas == null) {
211                        Log.i("WindowSurface", "Failure locking canvas");
212                        continue;
213                    }
214
215                    // Update graphics.
216                    if (!mInitialized) {
217                        mInitialized = true;
218                        mPoint1.init(canvas.getWidth(), canvas.getHeight(), mMinStep);
219                        mPoint2.init(canvas.getWidth(), canvas.getHeight(), mMinStep);
220                        mColor.init(127, 127, 1);
221                    } else {
222                        mPoint1.step(canvas.getWidth(), canvas.getHeight(),
223                                mMinStep, mMaxStep);
224                        mPoint2.step(canvas.getWidth(), canvas.getHeight(),
225                                mMinStep, mMaxStep);
226                        mColor.step(127, 127, 1, 3);
227                    }
228                    mBrightLine+=2;
229                    if (mBrightLine > (NUM_OLD*2)) {
230                        mBrightLine = -2;
231                    }
232
233                    // Clear background.
234                    canvas.drawColor(mBackground.getColor());
235
236                    // Draw old lines.
237                    for (int i=mNumOld-1; i>=0; i--) {
238                        mForeground.setColor(mOldColor[i] | makeGreen(i));
239                        mForeground.setAlpha(((NUM_OLD-i) * 255) / NUM_OLD);
240                        int p = i*4;
241                        canvas.drawLine(mOld[p], mOld[p+1], mOld[p+2], mOld[p+3], mForeground);
242                    }
243
244                    // Draw new line.
245                    int red = (int)mColor.x + 128;
246                    if (red > 255) red = 255;
247                    int blue = (int)mColor.y + 128;
248                    if (blue > 255) blue = 255;
249                    int color = 0xff000000 | (red<<16) | blue;
250                    mForeground.setColor(color | makeGreen(-2));
251                    canvas.drawLine(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y, mForeground);
252
253                    // Add in the new line.
254                    if (mNumOld > 1) {
255                        System.arraycopy(mOld, 0, mOld, 4, (mNumOld-1)*4);
256                        System.arraycopy(mOldColor, 0, mOldColor, 1, mNumOld-1);
257                    }
258                    if (mNumOld < NUM_OLD) mNumOld++;
259                    mOld[0] = mPoint1.x;
260                    mOld[1] = mPoint1.y;
261                    mOld[2] = mPoint2.x;
262                    mOld[3] = mPoint2.y;
263                    mOldColor[0] = color;
264
265                    // All done!
266                    mSurface.unlockCanvasAndPost(canvas);
267                }
268            }
269        }
270    }
271}
272