blob: 7713f5da36edaa8ea15f0ca265cf33ea81e81154 [file] [log] [blame]
Skuhneea7a7fb2015-08-28 07:10:31 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.test.hwui;
18
19import android.app.Activity;
20import android.graphics.Canvas;
21import android.graphics.ColorFilter;
22import android.graphics.Paint;
23import android.graphics.PixelFormat;
24import android.graphics.Rect;
25import android.graphics.drawable.Drawable;
26import android.os.Bundle;
27import android.view.DisplayListCanvas;
John Reck51aaf902015-12-02 15:08:07 -080028import android.view.ThreadedRenderer;
Skuhneea7a7fb2015-08-28 07:10:31 -070029import android.view.RenderNode;
Skuhneea7a7fb2015-08-28 07:10:31 -070030import android.view.View;
31import android.view.View.OnClickListener;
32import android.widget.AbsoluteLayout;
33import android.widget.AbsoluteLayout.LayoutParams;
34
35public class MultiProducerActivity extends Activity implements OnClickListener {
36 private static final int DURATION = 800;
37 private View mBackgroundTarget = null;
38 private View mFrameTarget = null;
39 private View mContent = null;
40 // The width & height of our "output drawing".
41 private final int WIDTH = 900;
42 private final int HEIGHT = 600;
43 // A border width around the drawing.
44 private static final int BORDER_WIDTH = 20;
45 // The Gap between the content and the frame which should get filled on the right and bottom
46 // side by the backdrop.
47 final int CONTENT_GAP = 100;
48
49 // For debug purposes - disable drawing of frame / background.
50 private final boolean USE_FRAME = true;
51 private final boolean USE_BACK = true;
52
53 @Override
54 protected void onCreate(Bundle savedInstanceState) {
55 super.onCreate(savedInstanceState);
56 // To make things simple - we do a quick and dirty absolute layout.
57 final AbsoluteLayout layout = new AbsoluteLayout(this);
58
59 // Create the outer frame
60 if (USE_FRAME) {
61 mFrameTarget = new View(this);
62 LayoutParams frameLP = new LayoutParams(WIDTH, HEIGHT, 0, 0);
63 layout.addView(mFrameTarget, frameLP);
64 }
65
66 // Create the background which fills the gap between content and frame.
67 if (USE_BACK) {
68 mBackgroundTarget = new View(this);
69 LayoutParams backgroundLP = new LayoutParams(
70 WIDTH - 2 * BORDER_WIDTH, HEIGHT - 2 * BORDER_WIDTH,
71 BORDER_WIDTH, BORDER_WIDTH);
72 layout.addView(mBackgroundTarget, backgroundLP);
73 }
74
75 // Create the content
76 // Note: We reduce the size by CONTENT_GAP pixels on right and bottom, so that they get
77 // drawn by the backdrop.
78 mContent = new View(this);
79 mContent.setBackground(new ColorPulse(0xFFF44336, 0xFF9C27B0, null));
80 mContent.setOnClickListener(this);
81 LayoutParams contentLP = new LayoutParams(WIDTH - 2 * BORDER_WIDTH - CONTENT_GAP,
82 HEIGHT - 2 * BORDER_WIDTH - CONTENT_GAP, BORDER_WIDTH, BORDER_WIDTH);
83 layout.addView(mContent, contentLP);
84
85 setContentView(layout);
86 }
87
88 @Override
89 protected void onStart() {
90 super.onStart();
91 View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
92 if (view != null) {
93 view.post(mSetup);
94 }
95 }
96
97 @Override
98 protected void onStop() {
99 super.onStop();
100 View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
101 if (view != null) {
102 view.removeCallbacks(mSetup);
103 }
104 if (mBgRenderer != null) {
105 mBgRenderer.destroy();
106 mBgRenderer = null;
107 }
108 }
109
110 @Override
111 public void onClick(View view) {
112 sBlockThread.run();
113 }
114
115 private Runnable mSetup = new Runnable() {
116 @Override
117 public void run() {
118 View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
119 if (view == null) {
120 view.postDelayed(mSetup, 50);
121 }
Stan Iliev45faba52016-06-28 13:33:15 -0400122 ThreadedRenderer renderer = view.getThreadedRenderer();
Skuhneea7a7fb2015-08-28 07:10:31 -0700123 if (renderer == null || view.getWidth() == 0) {
124 view.postDelayed(mSetup, 50);
125 }
126 ThreadedRenderer threaded = (ThreadedRenderer) renderer;
127
128 mBgRenderer = new FakeFrame(threaded,mFrameTarget, mBackgroundTarget);
129 mBgRenderer.start();
130 }
131 };
132
133 private FakeFrame mBgRenderer;
134 private class FakeFrame extends Thread {
135 ThreadedRenderer mRenderer;
136 volatile boolean mRunning = true;
137 View mTargetFrame;
138 View mTargetBack;
139 Drawable mFrameContent;
140 Drawable mBackContent;
141 // The Z value where to place this.
142 int mZFrame;
143 int mZBack;
144 String mRenderNodeName;
145
146 FakeFrame(ThreadedRenderer renderer, View targetFrame, View targetBack) {
147 mRenderer = renderer;
148 mTargetFrame = targetFrame;
149
150 mTargetBack = targetBack;
151 mFrameContent = new ColorPulse(0xFF101010, 0xFF707070, new Rect(0, 0, WIDTH, HEIGHT));
152 mBackContent = new ColorPulse(0xFF909090, 0xFFe0e0e0, null);
153 }
154
155 @Override
156 public void run() {
157 Rect currentFrameBounds = new Rect();
158 Rect currentBackBounds = new Rect();
159 Rect newBounds = new Rect();
160 int[] surfaceOrigin = new int[2];
161 RenderNode nodeFrame = null;
162 RenderNode nodeBack = null;
163
164 // Since we are overriding the window painting logic we need to at least fill the
165 // surface with some window content (otherwise the world will go black).
166 try {
167 Thread.sleep(200);
168 } catch (InterruptedException e) {
169 }
170
171 if (mTargetBack != null) {
172 nodeBack = RenderNode.create("FakeBackdrop", null);
173 nodeBack.setClipToBounds(true);
174 mRenderer.addRenderNode(nodeBack, true);
175 }
176
177 if (mTargetFrame != null) {
178 nodeFrame = RenderNode.create("FakeFrame", null);
179 nodeFrame.setClipToBounds(true);
180 mRenderer.addRenderNode(nodeFrame, false);
181 }
182
183 while (mRunning) {
184 // Get the surface position to draw to within our surface.
185 surfaceOrigin[0] = 0;
186 surfaceOrigin[1] = 0;
187 // This call should be done while the rendernode's displaylist is produced.
188 // For simplicity of this test we do this before we kick off the draw.
189 mContent.getLocationInSurface(surfaceOrigin);
Skuhneb8160872015-09-22 09:51:39 -0700190 mRenderer.setContentDrawBounds(surfaceOrigin[0], surfaceOrigin[1],
Skuhneea7a7fb2015-08-28 07:10:31 -0700191 surfaceOrigin[0] + mContent.getWidth(),
192 surfaceOrigin[1] + mContent.getHeight());
193 // Determine new position for frame.
194 if (nodeFrame != null) {
195 surfaceOrigin[0] = 0;
196 surfaceOrigin[1] = 0;
197 mTargetFrame.getLocationInSurface(surfaceOrigin);
198 newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
199 surfaceOrigin[0] + mTargetFrame.getWidth(),
200 surfaceOrigin[1] + mTargetFrame.getHeight());
201 if (!currentFrameBounds.equals(newBounds)) {
202 currentFrameBounds.set(newBounds);
203 nodeFrame.setLeftTopRightBottom(currentFrameBounds.left,
204 currentFrameBounds.top,
205 currentFrameBounds.right, currentFrameBounds.bottom);
206 }
207
208 // Draw frame
209 DisplayListCanvas canvas = nodeFrame.start(currentFrameBounds.width(),
210 currentFrameBounds.height());
211 mFrameContent.draw(canvas);
212 nodeFrame.end(canvas);
213 }
214
215 // Determine new position for backdrop
216 if (nodeBack != null) {
217 surfaceOrigin[0] = 0;
218 surfaceOrigin[1] = 0;
219 mTargetBack.getLocationInSurface(surfaceOrigin);
220 newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
221 surfaceOrigin[0] + mTargetBack.getWidth(),
222 surfaceOrigin[1] + mTargetBack.getHeight());
223 if (!currentBackBounds.equals(newBounds)) {
224 currentBackBounds.set(newBounds);
225 nodeBack.setLeftTopRightBottom(currentBackBounds.left,
226 currentBackBounds.top,
227 currentBackBounds.right, currentBackBounds.bottom);
228 }
229
230 // Draw Backdrop
231 DisplayListCanvas canvas = nodeBack.start(currentBackBounds.width(),
232 currentBackBounds.height());
233 mBackContent.draw(canvas);
234 nodeBack.end(canvas);
235 }
236
237 // we need to only render one guy - the rest will happen automatically (I think).
238 if (nodeFrame != null) {
239 mRenderer.drawRenderNode(nodeFrame);
240 }
241 if (nodeBack != null) {
242 mRenderer.drawRenderNode(nodeBack);
243 }
244 try {
245 Thread.sleep(5);
246 } catch (InterruptedException e) {}
247 }
248 if (nodeFrame != null) {
249 mRenderer.removeRenderNode(nodeFrame);
250 }
251 if (nodeBack != null) {
252 mRenderer.removeRenderNode(nodeBack);
253 }
254 }
255
256 public void destroy() {
257 mRunning = false;
258 try {
259 join();
260 } catch (InterruptedException e) {}
261 }
262 }
263
264 private final static Runnable sBlockThread = new Runnable() {
265 @Override
266 public void run() {
267 try {
268 Thread.sleep(DURATION);
269 } catch (InterruptedException e) {
270 }
271 }
272 };
273
274 static class ColorPulse extends Drawable {
275
276 private int mColorStart;
277 private int mColorEnd;
278 private int mStep;
279 private Rect mRect;
280 private Paint mPaint = new Paint();
281
282 public ColorPulse(int color1, int color2, Rect rect) {
283 mColorStart = color1;
284 mColorEnd = color2;
285 if (rect != null) {
286 mRect = new Rect(rect.left + BORDER_WIDTH / 2, rect.top + BORDER_WIDTH / 2,
287 rect.right - BORDER_WIDTH / 2, rect.bottom - BORDER_WIDTH / 2);
288 }
289 }
290
291 static int evaluate(float fraction, int startInt, int endInt) {
292 int startA = (startInt >> 24) & 0xff;
293 int startR = (startInt >> 16) & 0xff;
294 int startG = (startInt >> 8) & 0xff;
295 int startB = startInt & 0xff;
296
297 int endA = (endInt >> 24) & 0xff;
298 int endR = (endInt >> 16) & 0xff;
299 int endG = (endInt >> 8) & 0xff;
300 int endB = endInt & 0xff;
301
302 return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
303 (int)((startR + (int)(fraction * (endR - startR))) << 16) |
304 (int)((startG + (int)(fraction * (endG - startG))) << 8) |
305 (int)((startB + (int)(fraction * (endB - startB))));
306 }
307
308 @Override
309 public void draw(Canvas canvas) {
310 float frac = mStep / 50.0f;
311 int color = evaluate(frac, mColorStart, mColorEnd);
312 if (mRect != null && !mRect.isEmpty()) {
313 mPaint.setStyle(Paint.Style.STROKE);
314 mPaint.setStrokeWidth(BORDER_WIDTH);
315 mPaint.setColor(color);
316 canvas.drawRect(mRect, mPaint);
317 } else {
318 canvas.drawColor(color);
319 }
320
321 mStep++;
322 if (mStep >= 50) {
323 mStep = 0;
324 int tmp = mColorStart;
325 mColorStart = mColorEnd;
326 mColorEnd = tmp;
327 }
328 invalidateSelf();
329 }
330
331 @Override
332 public void setAlpha(int alpha) {
333 }
334
335 @Override
336 public void setColorFilter(ColorFilter colorFilter) {
337 }
338
339 @Override
340 public int getOpacity() {
341 return mRect == null || mRect.isEmpty() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
342 }
343
344 }
345}
346