1bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck/*
2bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * Copyright (C) 2014 The Android Open Source Project
3bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck *
4bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * Licensed under the Apache License, Version 2.0 (the "License");
5bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * you may not use this file except in compliance with the License.
6bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * You may obtain a copy of the License at
7bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck *
8bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck *      http://www.apache.org/licenses/LICENSE-2.0
9bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck *
10bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * Unless required by applicable law or agreed to in writing, software
11bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * distributed under the License is distributed on an "AS IS" BASIS,
12bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * See the License for the specific language governing permissions and
14bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck * limitations under the License.
15bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck */
16bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
17bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckpackage com.android.test.hwui;
18bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
19bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.app.Activity;
2010dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reckimport android.graphics.Bitmap;
21bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.graphics.Canvas;
22bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.graphics.Paint;
23bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.graphics.PorterDuff;
24bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.os.Bundle;
2510dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reckimport android.os.Environment;
26e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reckimport android.view.PixelCopy;
27bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.view.Surface;
28bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.view.SurfaceHolder;
29bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.view.SurfaceHolder.Callback;
30bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.view.SurfaceView;
3110dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reckimport android.view.View;
3210dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reckimport android.widget.Button;
33bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckimport android.widget.FrameLayout;
3410dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reckimport android.widget.LinearLayout;
3510dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reckimport android.widget.Toast;
3610dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck
3710dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reckimport java.io.FileOutputStream;
38bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
39bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reckpublic class HardwareCanvasSurfaceViewActivity extends Activity implements Callback {
40bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    private SurfaceView mSurfaceView;
41bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    private HardwareCanvasSurfaceViewActivity.RenderingThread mThread;
42bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
43bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    @Override
44bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    protected void onCreate(Bundle savedInstanceState) {
45bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        super.onCreate(savedInstanceState);
46bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
47bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        FrameLayout content = new FrameLayout(this);
48bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
49bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        mSurfaceView = new SurfaceView(this);
50bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        mSurfaceView.getHolder().addCallback(this);
51bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
5210dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        Button button = new Button(this);
5310dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        button.setText("Copy bitmap to /sdcard/surfaceview.png");
5410dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        button.setOnClickListener((View v) -> {
55e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck            final Bitmap b = Bitmap.createBitmap(
56e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                    mSurfaceView.getWidth(), mSurfaceView.getHeight(),
57e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                    Bitmap.Config.ARGB_8888);
5810dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck            PixelCopy.request(mSurfaceView, b,
59e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                    (int result) -> {
60e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                        if (result != PixelCopy.SUCCESS) {
61e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                            Toast.makeText(HardwareCanvasSurfaceViewActivity.this,
62e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                                    "Failed to copy", Toast.LENGTH_SHORT).show();
63e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                            return;
64e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                        }
65e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                        try {
66e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                            try (FileOutputStream out = new FileOutputStream(
67e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                                    Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
68e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                                b.compress(Bitmap.CompressFormat.PNG, 100, out);
69e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                            }
70e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                        } catch (Exception e) {
71e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                            // Ignore
72e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                        }
73e94cbc76d560a157c0a0d47181b4ed2a0aadbeb1John Reck                    }, mSurfaceView.getHandler());
7410dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        });
7510dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck
7610dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        LinearLayout layout = new LinearLayout(this);
7710dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        layout.setOrientation(LinearLayout.VERTICAL);
7810dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
7910dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck                LinearLayout.LayoutParams.WRAP_CONTENT);
8010dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        layout.addView(mSurfaceView, LinearLayout.LayoutParams.MATCH_PARENT,
8110dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck                LinearLayout.LayoutParams.MATCH_PARENT);
8210dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck
8310dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck        content.addView(layout, new FrameLayout.LayoutParams(
84bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                FrameLayout.LayoutParams.MATCH_PARENT,
8510dd0585c11dcedb5a271d54e645594f1d215d5cJohn Reck                FrameLayout.LayoutParams.MATCH_PARENT));
86bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        setContentView(content);
87bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    }
88bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
89bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    @Override
90bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    public void surfaceCreated(SurfaceHolder holder) {
916bc701421047bf881ee16c49b242ea19ae4cd9b9John Reck        mThread = new RenderingThread(holder);
92bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        mThread.start();
93bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    }
94bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
95bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    @Override
96bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
97bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        mThread.setSize(width, height);
98bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    }
99bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
100bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    @Override
101bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    public void surfaceDestroyed(SurfaceHolder holder) {
102bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        if (mThread != null) mThread.stopRendering();
103bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    }
104bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
105bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    private static class RenderingThread extends Thread {
1066bc701421047bf881ee16c49b242ea19ae4cd9b9John Reck        private final SurfaceHolder mSurface;
107bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        private volatile boolean mRunning = true;
108bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        private int mWidth, mHeight;
109bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
1106bc701421047bf881ee16c49b242ea19ae4cd9b9John Reck        public RenderingThread(SurfaceHolder surface) {
111bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            mSurface = surface;
112bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        }
113bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
114bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        void setSize(int width, int height) {
115bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            mWidth = width;
116bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            mHeight = height;
117bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        }
118bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
119bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        @Override
120bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        public void run() {
121bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            float x = 0.0f;
122bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            float y = 0.0f;
123bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            float speedX = 5.0f;
124bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            float speedY = 3.0f;
125bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
126bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            Paint paint = new Paint();
127bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            paint.setColor(0xff00ff00);
128bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
129bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            while (mRunning && !Thread.interrupted()) {
130bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                final Canvas canvas = mSurface.lockHardwareCanvas();
131bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                try {
132bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                    canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
133bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                    canvas.drawRect(x, y, x + 20.0f, y + 20.0f, paint);
134bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                } finally {
135bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                    mSurface.unlockCanvasAndPost(canvas);
136bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                }
137bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
138bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                if (x + 20.0f + speedX >= mWidth || x + speedX <= 0.0f) {
139bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                    speedX = -speedX;
140bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                }
141bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                if (y + 20.0f + speedY >= mHeight || y + speedY <= 0.0f) {
142bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                    speedY = -speedY;
143bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                }
144bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
145bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                x += speedX;
146bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                y += speedY;
147bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
148bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                try {
149bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                    Thread.sleep(15);
150bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                } catch (InterruptedException e) {
151bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                    // Interrupted
152bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck                }
153bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            }
154bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        }
155bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck
156bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        void stopRendering() {
157bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            interrupt();
158bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck            mRunning = false;
159bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck        }
160bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck    }
161bb2d0cc7e1d487f7021b1f9ec0c6740e41b535f2John Reck}
162