blob: ff1eb532dff04866363a78009f1bbd825542e671 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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 android.view;
18
19import android.content.Context;
Mitsuru Oshima9189cab2009-06-03 11:19:12 -070020import android.content.res.CompatibilityInfo;
Mitsuru Oshima64f59342009-06-21 00:03:11 -070021import android.content.res.CompatibilityInfo.Translator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.graphics.Canvas;
23import android.graphics.PixelFormat;
24import android.graphics.PorterDuff;
25import android.graphics.Rect;
26import android.graphics.Region;
27import android.os.Handler;
28import android.os.Message;
29import android.os.RemoteException;
30import android.os.SystemClock;
31import android.os.ParcelFileDescriptor;
32import android.util.AttributeSet;
33import android.util.Config;
34import android.util.Log;
35import java.util.ArrayList;
36
37import java.util.concurrent.locks.ReentrantLock;
38import java.lang.ref.WeakReference;
39
40/**
41 * Provides a dedicated drawing surface embedded inside of a view hierarchy.
42 * You can control the format of this surface and, if you like, its size; the
43 * SurfaceView takes care of placing the surface at the correct location on the
44 * screen
45 *
46 * <p>The surface is Z ordered so that it is behind the window holding its
47 * SurfaceView; the SurfaceView punches a hole in its window to allow its
48 * surface to be displayed. The view hierarchy will take care of correctly
49 * compositing with the Surface any siblings of the SurfaceView that would
50 * normally appear on top of it. This can be used to place overlays such as
51 * buttons on top of the Surface, though note however that it can have an
52 * impact on performance since a full alpha-blended composite will be performed
53 * each time the Surface changes.
54 *
55 * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
56 * which can be retrieved by calling {@link #getHolder}.
57 *
58 * <p>The Surface will be created for you while the SurfaceView's window is
59 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
60 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
61 * Surface is created and destroyed as the window is shown and hidden.
62 *
63 * <p>One of the purposes of this class is to provide a surface in which a
64 * secondary thread can render in to the screen. If you are going to use it
65 * this way, you need to be aware of some threading semantics:
66 *
67 * <ul>
68 * <li> All SurfaceView and
69 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
70 * from the thread running the SurfaceView's window (typically the main thread
71 * of the application). They thus need to correctly synchronize with any
72 * state that is also touched by the drawing thread.
73 * <li> You must ensure that the drawing thread only touches the underlying
74 * Surface while it is valid -- between
75 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
76 * and
77 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
78 * </ul>
79 */
80public class SurfaceView extends View {
81 static private final String TAG = "SurfaceView";
82 static private final boolean DEBUG = false;
83 static private final boolean localLOGV = DEBUG ? true : Config.LOGV;
84
85 final ArrayList<SurfaceHolder.Callback> mCallbacks
86 = new ArrayList<SurfaceHolder.Callback>();
87
88 final int[] mLocation = new int[2];
89
90 final ReentrantLock mSurfaceLock = new ReentrantLock();
91 final Surface mSurface = new Surface();
92 boolean mDrawingStopped = true;
93
94 final WindowManager.LayoutParams mLayout
95 = new WindowManager.LayoutParams();
96 IWindowSession mSession;
97 MyWindow mWindow;
98 final Rect mVisibleInsets = new Rect();
99 final Rect mWinFrame = new Rect();
100 final Rect mContentInsets = new Rect();
101
102 static final int KEEP_SCREEN_ON_MSG = 1;
103 static final int GET_NEW_SURFACE_MSG = 2;
104
Dianne Hackbornc4d5d022009-05-21 17:32:42 -0700105 int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 boolean mIsCreating = false;
108
109 final Handler mHandler = new Handler() {
110 @Override
111 public void handleMessage(Message msg) {
112 switch (msg.what) {
113 case KEEP_SCREEN_ON_MSG: {
114 setKeepScreenOn(msg.arg1 != 0);
115 } break;
116 case GET_NEW_SURFACE_MSG: {
117 handleGetNewSurface();
118 } break;
119 }
120 }
121 };
122
123 boolean mRequestedVisible = false;
124 int mRequestedWidth = -1;
125 int mRequestedHeight = -1;
126 int mRequestedFormat = PixelFormat.OPAQUE;
127 int mRequestedType = -1;
128
129 boolean mHaveFrame = false;
130 boolean mDestroyReportNeeded = false;
131 boolean mNewSurfaceNeeded = false;
132 long mLastLockTime = 0;
133
134 boolean mVisible = false;
135 int mLeft = -1;
136 int mTop = -1;
137 int mWidth = -1;
138 int mHeight = -1;
139 int mFormat = -1;
140 int mType = -1;
141 final Rect mSurfaceFrame = new Rect();
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700142 private Translator mTranslator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
144 public SurfaceView(Context context) {
145 super(context);
146 setWillNotDraw(true);
147 }
148
149 public SurfaceView(Context context, AttributeSet attrs) {
150 super(context, attrs);
151 setWillNotDraw(true);
152 }
153
154 public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
155 super(context, attrs, defStyle);
156 setWillNotDraw(true);
157 }
158
159 /**
160 * Return the SurfaceHolder providing access and control over this
161 * SurfaceView's underlying surface.
162 *
163 * @return SurfaceHolder The holder of the surface.
164 */
165 public SurfaceHolder getHolder() {
166 return mSurfaceHolder;
167 }
168
169 @Override
170 protected void onAttachedToWindow() {
171 super.onAttachedToWindow();
172 mParent.requestTransparentRegion(this);
173 mSession = getWindowSession();
174 mLayout.token = getWindowToken();
175 mLayout.setTitle("SurfaceView");
176 }
177
178 @Override
179 protected void onWindowVisibilityChanged(int visibility) {
180 super.onWindowVisibilityChanged(visibility);
181 mRequestedVisible = visibility == VISIBLE;
182 updateWindow(false);
183 }
184
185 @Override
186 protected void onDetachedFromWindow() {
187 mRequestedVisible = false;
188 updateWindow(false);
189 mHaveFrame = false;
190 if (mWindow != null) {
191 try {
192 mSession.remove(mWindow);
193 } catch (RemoteException ex) {
194 }
195 mWindow = null;
196 }
197 mSession = null;
198 mLayout.token = null;
199
200 super.onDetachedFromWindow();
201 }
202
203 @Override
204 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
205 int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
206 int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
207 setMeasuredDimension(width, height);
208 }
209
210 @Override
211 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
212 super.onScrollChanged(l, t, oldl, oldt);
213 updateWindow(false);
214 }
215
216 @Override
217 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
218 super.onSizeChanged(w, h, oldw, oldh);
219 updateWindow(false);
220 }
221
222 @Override
223 public boolean gatherTransparentRegion(Region region) {
224 boolean opaque = true;
225 if ((mPrivateFlags & SKIP_DRAW) == 0) {
226 // this view draws, remove it from the transparent region
227 opaque = super.gatherTransparentRegion(region);
228 } else if (region != null) {
229 int w = getWidth();
230 int h = getHeight();
231 if (w>0 && h>0) {
232 getLocationInWindow(mLocation);
233 // otherwise, punch a hole in the whole hierarchy
234 int l = mLocation[0];
235 int t = mLocation[1];
236 region.op(l, t, l+w, t+h, Region.Op.UNION);
237 }
238 }
239 if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
240 opaque = false;
241 }
242 return opaque;
243 }
244
245 @Override
246 public void draw(Canvas canvas) {
247 // draw() is not called when SKIP_DRAW is set
248 if ((mPrivateFlags & SKIP_DRAW) == 0) {
249 // punch a whole in the view-hierarchy below us
250 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
251 }
252 super.draw(canvas);
253 }
254
255 @Override
Mitsuru Oshima001a6e522009-05-11 21:14:03 -0700256 public boolean dispatchTouchEvent(MotionEvent event) {
257 // SurfaceView uses pre-scaled size unless fixed size is requested. This hook
258 // scales the event back to the pre-scaled coordinates for such surface.
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700259 if (mRequestedWidth < 0 && mTranslator != null) {
Mitsuru Oshima001a6e522009-05-11 21:14:03 -0700260 MotionEvent scaledBack = MotionEvent.obtain(event);
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700261 scaledBack.scale(mTranslator.applicationScale);
Mitsuru Oshima001a6e522009-05-11 21:14:03 -0700262 try {
263 return super.dispatchTouchEvent(scaledBack);
264 } finally {
265 scaledBack.recycle();
266 }
267 } else {
268 return super.dispatchTouchEvent(event);
269 }
270 }
271
272 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 protected void dispatchDraw(Canvas canvas) {
274 // if SKIP_DRAW is cleared, draw() has already punched a hole
275 if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
276 // punch a whole in the view-hierarchy below us
277 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
278 }
279 // reposition ourselves where the surface is
280 mHaveFrame = true;
281 updateWindow(false);
282 super.dispatchDraw(canvas);
283 }
284
Dianne Hackbornc4d5d022009-05-21 17:32:42 -0700285 /**
286 * Hack to allow special layering of windows. The type is one of the
287 * types in WindowManager.LayoutParams. This is a hack so:
288 * @hide
289 */
290 public void setWindowType(int type) {
291 mWindowType = type;
292 }
293
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 private void updateWindow(boolean force) {
295 if (!mHaveFrame) {
296 return;
297 }
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700298 mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator;
299
300 float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301
302 int myWidth = mRequestedWidth;
303 if (myWidth <= 0) myWidth = getWidth();
304 int myHeight = mRequestedHeight;
305 if (myHeight <= 0) myHeight = getHeight();
Mitsuru Oshima001a6e522009-05-11 21:14:03 -0700306
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700307 // Use original size if the app specified the size of the view,
308 // and let the flinger to scale up.
Mitsuru Oshima1ecf5d22009-07-06 17:20:38 -0700309 if (mRequestedWidth <= 0 && mTranslator != null) {
Mitsuru Oshima9189cab2009-06-03 11:19:12 -0700310 myWidth *= appScale;
311 myHeight *= appScale;
Mitsuru Oshima001a6e522009-05-11 21:14:03 -0700312 }
313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 getLocationInWindow(mLocation);
315 final boolean creating = mWindow == null;
316 final boolean formatChanged = mFormat != mRequestedFormat;
317 final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
318 final boolean visibleChanged = mVisible != mRequestedVisible
319 || mNewSurfaceNeeded;
320 final boolean typeChanged = mType != mRequestedType;
321 if (force || creating || formatChanged || sizeChanged || visibleChanged
322 || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]) {
323
324 if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
325 + " format=" + formatChanged + " size=" + sizeChanged
326 + " visible=" + visibleChanged
327 + " left=" + (mLeft != mLocation[0])
328 + " top=" + (mTop != mLocation[1]));
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700329
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 try {
331 final boolean visible = mVisible = mRequestedVisible;
332 mLeft = mLocation[0];
333 mTop = mLocation[1];
334 mWidth = myWidth;
335 mHeight = myHeight;
336 mFormat = mRequestedFormat;
337 mType = mRequestedType;
338
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700339 // Scaling/Translate window's layout here because mLayout is not used elsewhere.
340
341 // Places the window relative
342 mLayout.x = mLeft;
343 mLayout.y = mTop;
344 mLayout.width = getWidth();
345 mLayout.height = getHeight();
346 if (mTranslator != null) {
347 mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
348 }
349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 mLayout.format = mRequestedFormat;
351 mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
352 | WindowManager.LayoutParams.FLAG_SCALED
353 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
354 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700355 | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 ;
357
358 mLayout.memoryType = mRequestedType;
359
360 if (mWindow == null) {
361 mWindow = new MyWindow(this);
Dianne Hackbornc4d5d022009-05-21 17:32:42 -0700362 mLayout.type = mWindowType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 mLayout.gravity = Gravity.LEFT|Gravity.TOP;
364 mSession.add(mWindow, mLayout,
365 mVisible ? VISIBLE : GONE, mContentInsets);
366 }
367
368 if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
369 reportSurfaceDestroyed();
370 }
371
372 mNewSurfaceNeeded = false;
373
374 mSurfaceLock.lock();
375 mDrawingStopped = !visible;
Mitsuru Oshima9189cab2009-06-03 11:19:12 -0700376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 final int relayoutResult = mSession.relayout(
Mitsuru Oshima001a6e522009-05-11 21:14:03 -0700378 mWindow, mLayout, mWidth, mHeight,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
380 mVisibleInsets, mSurface);
Mitsuru Oshima8169dae2009-04-28 18:12:09 -0700381
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 if (localLOGV) Log.i(TAG, "New surface: " + mSurface
383 + ", vis=" + visible + ", frame=" + mWinFrame);
384 mSurfaceFrame.left = 0;
385 mSurfaceFrame.top = 0;
386 mSurfaceFrame.right = mWinFrame.width();
387 mSurfaceFrame.bottom = mWinFrame.height();
388 mSurfaceLock.unlock();
389
390 try {
391 if (visible) {
392 mDestroyReportNeeded = true;
393
394 SurfaceHolder.Callback callbacks[];
395 synchronized (mCallbacks) {
396 callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
397 mCallbacks.toArray(callbacks);
Mitsuru Oshima3d914922009-05-13 22:29:15 -0700398 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399
400 if (visibleChanged) {
401 mIsCreating = true;
402 for (SurfaceHolder.Callback c : callbacks) {
403 c.surfaceCreated(mSurfaceHolder);
404 }
405 }
406 if (creating || formatChanged || sizeChanged
407 || visibleChanged) {
408 for (SurfaceHolder.Callback c : callbacks) {
409 c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
410 }
411 }
412 }
413 } finally {
414 mIsCreating = false;
415 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
416 mSession.finishDrawing(mWindow);
417 }
418 }
419 } catch (RemoteException ex) {
420 }
421 if (localLOGV) Log.v(
422 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
423 " w=" + mLayout.width + " h=" + mLayout.height +
424 ", frame=" + mSurfaceFrame);
425 }
426 }
427
428 private void reportSurfaceDestroyed() {
429 if (mDestroyReportNeeded) {
430 mDestroyReportNeeded = false;
431 SurfaceHolder.Callback callbacks[];
432 synchronized (mCallbacks) {
433 callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
434 mCallbacks.toArray(callbacks);
435 }
436 for (SurfaceHolder.Callback c : callbacks) {
437 c.surfaceDestroyed(mSurfaceHolder);
438 }
439 }
440 super.onDetachedFromWindow();
441 }
442
443 void handleGetNewSurface() {
444 mNewSurfaceNeeded = true;
445 updateWindow(false);
446 }
447
448 private static class MyWindow extends IWindow.Stub {
Mitsuru Oshima8169dae2009-04-28 18:12:09 -0700449 private final WeakReference<SurfaceView> mSurfaceView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450
451 public MyWindow(SurfaceView surfaceView) {
452 mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
453 }
454
455 public void resized(int w, int h, Rect coveredInsets,
456 Rect visibleInsets, boolean reportDraw) {
457 SurfaceView surfaceView = mSurfaceView.get();
458 if (surfaceView != null) {
459 if (localLOGV) Log.v(
460 "SurfaceView", surfaceView + " got resized: w=" +
461 w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
462 synchronized (this) {
463 if (mCurWidth != w || mCurHeight != h) {
464 mCurWidth = w;
465 mCurHeight = h;
466 }
467 if (reportDraw) {
468 try {
469 surfaceView.mSession.finishDrawing(surfaceView.mWindow);
470 } catch (RemoteException e) {
471 }
472 }
473 }
474 }
475 }
476
477 public void dispatchKey(KeyEvent event) {
478 SurfaceView surfaceView = mSurfaceView.get();
479 if (surfaceView != null) {
480 //Log.w("SurfaceView", "Unexpected key event in surface: " + event);
481 if (surfaceView.mSession != null && surfaceView.mSurface != null) {
482 try {
483 surfaceView.mSession.finishKey(surfaceView.mWindow);
484 } catch (RemoteException ex) {
485 }
486 }
487 }
488 }
489
490 public void dispatchPointer(MotionEvent event, long eventTime) {
491 Log.w("SurfaceView", "Unexpected pointer event in surface: " + event);
492 //if (mSession != null && mSurface != null) {
493 // try {
494 // //mSession.finishKey(mWindow);
495 // } catch (RemoteException ex) {
496 // }
497 //}
498 }
499
500 public void dispatchTrackball(MotionEvent event, long eventTime) {
501 Log.w("SurfaceView", "Unexpected trackball event in surface: " + event);
502 //if (mSession != null && mSurface != null) {
503 // try {
504 // //mSession.finishKey(mWindow);
505 // } catch (RemoteException ex) {
506 // }
507 //}
508 }
509
510 public void dispatchAppVisibility(boolean visible) {
511 // The point of SurfaceView is to let the app control the surface.
512 }
513
514 public void dispatchGetNewSurface() {
515 SurfaceView surfaceView = mSurfaceView.get();
516 if (surfaceView != null) {
517 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
518 surfaceView.mHandler.sendMessage(msg);
519 }
520 }
521
522 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
523 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
524 }
525
526 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
527 }
528
529 int mCurWidth = -1;
530 int mCurHeight = -1;
531 }
532
533 private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
534
535 private static final String LOG_TAG = "SurfaceHolder";
536
537 public boolean isCreating() {
538 return mIsCreating;
539 }
540
541 public void addCallback(Callback callback) {
542 synchronized (mCallbacks) {
543 // This is a linear search, but in practice we'll
544 // have only a couple callbacks, so it doesn't matter.
545 if (mCallbacks.contains(callback) == false) {
546 mCallbacks.add(callback);
547 }
548 }
549 }
550
551 public void removeCallback(Callback callback) {
552 synchronized (mCallbacks) {
553 mCallbacks.remove(callback);
554 }
555 }
556
557 public void setFixedSize(int width, int height) {
558 if (mRequestedWidth != width || mRequestedHeight != height) {
559 mRequestedWidth = width;
560 mRequestedHeight = height;
561 requestLayout();
562 }
563 }
564
565 public void setSizeFromLayout() {
566 if (mRequestedWidth != -1 || mRequestedHeight != -1) {
567 mRequestedWidth = mRequestedHeight = -1;
568 requestLayout();
569 }
570 }
571
572 public void setFormat(int format) {
573 mRequestedFormat = format;
574 if (mWindow != null) {
575 updateWindow(false);
576 }
577 }
578
579 public void setType(int type) {
580 switch (type) {
581 case SURFACE_TYPE_NORMAL:
582 case SURFACE_TYPE_HARDWARE:
583 case SURFACE_TYPE_GPU:
584 case SURFACE_TYPE_PUSH_BUFFERS:
585 mRequestedType = type;
586 if (mWindow != null) {
587 updateWindow(false);
588 }
589 break;
590 }
591 }
592
593 public void setKeepScreenOn(boolean screenOn) {
594 Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
595 msg.arg1 = screenOn ? 1 : 0;
596 mHandler.sendMessage(msg);
597 }
598
599 public Canvas lockCanvas() {
600 return internalLockCanvas(null);
601 }
602
603 public Canvas lockCanvas(Rect dirty) {
604 return internalLockCanvas(dirty);
605 }
606
607 private final Canvas internalLockCanvas(Rect dirty) {
608 if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
609 throw new BadSurfaceTypeException(
610 "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
611 }
612 mSurfaceLock.lock();
613
614 if (localLOGV) Log.i(TAG, "Locking canvas... stopped="
615 + mDrawingStopped + ", win=" + mWindow);
616
617 Canvas c = null;
618 if (!mDrawingStopped && mWindow != null) {
619 Rect frame = dirty != null ? dirty : mSurfaceFrame;
620 try {
621 c = mSurface.lockCanvas(frame);
622 } catch (Exception e) {
623 Log.e(LOG_TAG, "Exception locking surface", e);
624 }
625 }
626
627 if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
628 if (c != null) {
629 mLastLockTime = SystemClock.uptimeMillis();
630 return c;
631 }
632
633 // If the Surface is not ready to be drawn, then return null,
634 // but throttle calls to this function so it isn't called more
635 // than every 100ms.
636 long now = SystemClock.uptimeMillis();
637 long nextTime = mLastLockTime + 100;
638 if (nextTime > now) {
639 try {
640 Thread.sleep(nextTime-now);
641 } catch (InterruptedException e) {
642 }
643 now = SystemClock.uptimeMillis();
644 }
645 mLastLockTime = now;
646 mSurfaceLock.unlock();
647
648 return null;
649 }
650
651 public void unlockCanvasAndPost(Canvas canvas) {
652 mSurface.unlockCanvasAndPost(canvas);
653 mSurfaceLock.unlock();
654 }
655
656 public Surface getSurface() {
657 return mSurface;
658 }
659
660 public Rect getSurfaceFrame() {
661 return mSurfaceFrame;
662 }
663 };
664}