SurfaceView.java revision 7179b8133d4a1d8e5f26cbe3da6aa978094e75c9
1/* 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 com.android.internal.view.BaseIWindow; 20 21import android.content.Context; 22import android.content.res.Configuration; 23import android.content.res.Resources; 24import android.content.res.CompatibilityInfo.Translator; 25import android.graphics.Canvas; 26import android.graphics.PixelFormat; 27import android.graphics.PorterDuff; 28import android.graphics.Rect; 29import android.graphics.Region; 30import android.os.Handler; 31import android.os.Message; 32import android.os.RemoteException; 33import android.os.SystemClock; 34import android.os.ParcelFileDescriptor; 35import android.util.AttributeSet; 36import android.util.Config; 37import android.util.Log; 38 39import java.lang.ref.WeakReference; 40import java.util.ArrayList; 41import java.util.concurrent.locks.ReentrantLock; 42 43/** 44 * Provides a dedicated drawing surface embedded inside of a view hierarchy. 45 * You can control the format of this surface and, if you like, its size; the 46 * SurfaceView takes care of placing the surface at the correct location on the 47 * screen 48 * 49 * <p>The surface is Z ordered so that it is behind the window holding its 50 * SurfaceView; the SurfaceView punches a hole in its window to allow its 51 * surface to be displayed. The view hierarchy will take care of correctly 52 * compositing with the Surface any siblings of the SurfaceView that would 53 * normally appear on top of it. This can be used to place overlays such as 54 * buttons on top of the Surface, though note however that it can have an 55 * impact on performance since a full alpha-blended composite will be performed 56 * each time the Surface changes. 57 * 58 * <p>Access to the underlying surface is provided via the SurfaceHolder interface, 59 * which can be retrieved by calling {@link #getHolder}. 60 * 61 * <p>The Surface will be created for you while the SurfaceView's window is 62 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated} 63 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the 64 * Surface is created and destroyed as the window is shown and hidden. 65 * 66 * <p>One of the purposes of this class is to provide a surface in which a 67 * secondary thread can render in to the screen. If you are going to use it 68 * this way, you need to be aware of some threading semantics: 69 * 70 * <ul> 71 * <li> All SurfaceView and 72 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called 73 * from the thread running the SurfaceView's window (typically the main thread 74 * of the application). They thus need to correctly synchronize with any 75 * state that is also touched by the drawing thread. 76 * <li> You must ensure that the drawing thread only touches the underlying 77 * Surface while it is valid -- between 78 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()} 79 * and 80 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}. 81 * </ul> 82 */ 83public class SurfaceView extends View { 84 static private final String TAG = "SurfaceView"; 85 static private final boolean DEBUG = false; 86 static private final boolean localLOGV = DEBUG ? true : Config.LOGV; 87 88 final ArrayList<SurfaceHolder.Callback> mCallbacks 89 = new ArrayList<SurfaceHolder.Callback>(); 90 91 final int[] mLocation = new int[2]; 92 93 final ReentrantLock mSurfaceLock = new ReentrantLock(); 94 final Surface mSurface = new Surface(); 95 boolean mDrawingStopped = true; 96 97 final WindowManager.LayoutParams mLayout 98 = new WindowManager.LayoutParams(); 99 IWindowSession mSession; 100 MyWindow mWindow; 101 final Rect mVisibleInsets = new Rect(); 102 final Rect mWinFrame = new Rect(); 103 final Rect mContentInsets = new Rect(); 104 final Configuration mConfiguration = new Configuration(); 105 106 static final int KEEP_SCREEN_ON_MSG = 1; 107 static final int GET_NEW_SURFACE_MSG = 2; 108 109 int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 110 111 boolean mIsCreating = false; 112 113 final Handler mHandler = new Handler() { 114 @Override 115 public void handleMessage(Message msg) { 116 switch (msg.what) { 117 case KEEP_SCREEN_ON_MSG: { 118 setKeepScreenOn(msg.arg1 != 0); 119 } break; 120 case GET_NEW_SURFACE_MSG: { 121 handleGetNewSurface(); 122 } break; 123 } 124 } 125 }; 126 127 final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener 128 = new ViewTreeObserver.OnScrollChangedListener() { 129 public void onScrollChanged() { 130 updateWindow(false); 131 } 132 }; 133 134 boolean mRequestedVisible = false; 135 boolean mWindowVisibility = false; 136 boolean mViewVisibility = false; 137 int mRequestedWidth = -1; 138 int mRequestedHeight = -1; 139 int mRequestedFormat = PixelFormat.OPAQUE; 140 int mRequestedType = -1; 141 142 boolean mHaveFrame = false; 143 boolean mDestroyReportNeeded = false; 144 boolean mNewSurfaceNeeded = false; 145 long mLastLockTime = 0; 146 147 boolean mVisible = false; 148 int mLeft = -1; 149 int mTop = -1; 150 int mWidth = -1; 151 int mHeight = -1; 152 int mFormat = -1; 153 int mType = -1; 154 final Rect mSurfaceFrame = new Rect(); 155 private Translator mTranslator; 156 157 public SurfaceView(Context context) { 158 super(context); 159 setWillNotDraw(true); 160 } 161 162 public SurfaceView(Context context, AttributeSet attrs) { 163 super(context, attrs); 164 setWillNotDraw(true); 165 } 166 167 public SurfaceView(Context context, AttributeSet attrs, int defStyle) { 168 super(context, attrs, defStyle); 169 setWillNotDraw(true); 170 } 171 172 /** 173 * Return the SurfaceHolder providing access and control over this 174 * SurfaceView's underlying surface. 175 * 176 * @return SurfaceHolder The holder of the surface. 177 */ 178 public SurfaceHolder getHolder() { 179 return mSurfaceHolder; 180 } 181 182 @Override 183 protected void onAttachedToWindow() { 184 super.onAttachedToWindow(); 185 mParent.requestTransparentRegion(this); 186 mSession = getWindowSession(); 187 mLayout.token = getWindowToken(); 188 mLayout.setTitle("SurfaceView"); 189 mViewVisibility = getVisibility() == VISIBLE; 190 getViewTreeObserver().addOnScrollChangedListener(mScrollChangedListener); 191 } 192 193 @Override 194 protected void onWindowVisibilityChanged(int visibility) { 195 super.onWindowVisibilityChanged(visibility); 196 mWindowVisibility = visibility == VISIBLE; 197 mRequestedVisible = mWindowVisibility && mViewVisibility; 198 updateWindow(false); 199 } 200 201 @Override 202 public void setVisibility(int visibility) { 203 super.setVisibility(visibility); 204 mViewVisibility = visibility == VISIBLE; 205 mRequestedVisible = mWindowVisibility && mViewVisibility; 206 updateWindow(false); 207 } 208 209 @Override 210 protected void onDetachedFromWindow() { 211 getViewTreeObserver().removeOnScrollChangedListener(mScrollChangedListener); 212 mRequestedVisible = false; 213 updateWindow(false); 214 mHaveFrame = false; 215 if (mWindow != null) { 216 try { 217 mSession.remove(mWindow); 218 } catch (RemoteException ex) { 219 } 220 mWindow = null; 221 } 222 mSession = null; 223 mLayout.token = null; 224 225 super.onDetachedFromWindow(); 226 } 227 228 @Override 229 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 230 int width = getDefaultSize(mRequestedWidth, widthMeasureSpec); 231 int height = getDefaultSize(mRequestedHeight, heightMeasureSpec); 232 setMeasuredDimension(width, height); 233 } 234 235 @Override 236 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 237 super.onSizeChanged(w, h, oldw, oldh); 238 updateWindow(false); 239 } 240 241 @Override 242 public boolean gatherTransparentRegion(Region region) { 243 if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 244 return super.gatherTransparentRegion(region); 245 } 246 247 boolean opaque = true; 248 if ((mPrivateFlags & SKIP_DRAW) == 0) { 249 // this view draws, remove it from the transparent region 250 opaque = super.gatherTransparentRegion(region); 251 } else if (region != null) { 252 int w = getWidth(); 253 int h = getHeight(); 254 if (w>0 && h>0) { 255 getLocationInWindow(mLocation); 256 // otherwise, punch a hole in the whole hierarchy 257 int l = mLocation[0]; 258 int t = mLocation[1]; 259 region.op(l, t, l+w, t+h, Region.Op.UNION); 260 } 261 } 262 if (PixelFormat.formatHasAlpha(mRequestedFormat)) { 263 opaque = false; 264 } 265 return opaque; 266 } 267 268 @Override 269 public void draw(Canvas canvas) { 270 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 271 // draw() is not called when SKIP_DRAW is set 272 if ((mPrivateFlags & SKIP_DRAW) == 0) { 273 // punch a whole in the view-hierarchy below us 274 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 275 } 276 } 277 super.draw(canvas); 278 } 279 280 @Override 281 protected void dispatchDraw(Canvas canvas) { 282 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { 283 // if SKIP_DRAW is cleared, draw() has already punched a hole 284 if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { 285 // punch a whole in the view-hierarchy below us 286 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 287 } 288 } 289 // reposition ourselves where the surface is 290 mHaveFrame = true; 291 updateWindow(false); 292 super.dispatchDraw(canvas); 293 } 294 295 /** 296 * Control whether the surface view's surface is placed on top of another 297 * regular surface view in the window (but still behind the window itself). 298 * This is typically used to place overlays on top of an underlying media 299 * surface view. 300 * 301 * <p>Note that this must be set before the surface view's containing 302 * window is attached to the window manager. 303 * 304 * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}. 305 */ 306 public void setZOrderMediaOverlay(boolean isMediaOverlay) { 307 mWindowType = isMediaOverlay 308 ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY 309 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 310 } 311 312 /** 313 * Control whether the surface view's surface is placed on top of its 314 * window. Normally it is placed behind the window, to allow it to 315 * (for the most part) appear to composite with the views in the 316 * hierarchy. By setting this, you cause it to be placed above the 317 * window. This means that none of the contents of the window this 318 * SurfaceView is in will be visible on top of its surface. 319 * 320 * <p>Note that this must be set before the surface view's containing 321 * window is attached to the window manager. 322 * 323 * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}. 324 */ 325 public void setZOrderOnTop(boolean onTop) { 326 if (onTop) { 327 mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 328 // ensures the surface is placed below the IME 329 mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 330 } else { 331 mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 332 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 333 } 334 } 335 336 /** 337 * Hack to allow special layering of windows. The type is one of the 338 * types in WindowManager.LayoutParams. This is a hack so: 339 * @hide 340 */ 341 public void setWindowType(int type) { 342 mWindowType = type; 343 } 344 345 private void updateWindow(boolean force) { 346 if (!mHaveFrame) { 347 return; 348 } 349 ViewRoot viewRoot = (ViewRoot) getRootView().getParent(); 350 if (viewRoot != null) { 351 mTranslator = viewRoot.mTranslator; 352 } 353 354 Resources res = getContext().getResources(); 355 if (mTranslator != null || !res.getCompatibilityInfo().supportsScreen()) { 356 mSurface.setCompatibleDisplayMetrics(res.getDisplayMetrics(), mTranslator); 357 } 358 359 int myWidth = mRequestedWidth; 360 if (myWidth <= 0) myWidth = getWidth(); 361 int myHeight = mRequestedHeight; 362 if (myHeight <= 0) myHeight = getHeight(); 363 364 getLocationInWindow(mLocation); 365 final boolean creating = mWindow == null; 366 final boolean formatChanged = mFormat != mRequestedFormat; 367 final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 368 final boolean visibleChanged = mVisible != mRequestedVisible 369 || mNewSurfaceNeeded; 370 final boolean typeChanged = mType != mRequestedType; 371 if (force || creating || formatChanged || sizeChanged || visibleChanged 372 || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]) { 373 374 if (localLOGV) Log.i(TAG, "Changes: creating=" + creating 375 + " format=" + formatChanged + " size=" + sizeChanged 376 + " visible=" + visibleChanged 377 + " left=" + (mLeft != mLocation[0]) 378 + " top=" + (mTop != mLocation[1])); 379 380 try { 381 final boolean visible = mVisible = mRequestedVisible; 382 mLeft = mLocation[0]; 383 mTop = mLocation[1]; 384 mWidth = myWidth; 385 mHeight = myHeight; 386 mFormat = mRequestedFormat; 387 mType = mRequestedType; 388 389 // Scaling/Translate window's layout here because mLayout is not used elsewhere. 390 391 // Places the window relative 392 mLayout.x = mLeft; 393 mLayout.y = mTop; 394 mLayout.width = getWidth(); 395 mLayout.height = getHeight(); 396 if (mTranslator != null) { 397 mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout); 398 } 399 400 mLayout.format = mRequestedFormat; 401 mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 402 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 403 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 404 | WindowManager.LayoutParams.FLAG_SCALED 405 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 406 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 407 ; 408 if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) { 409 mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; 410 } 411 412 mLayout.memoryType = mRequestedType; 413 414 if (mWindow == null) { 415 mWindow = new MyWindow(this); 416 mLayout.type = mWindowType; 417 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 418 mSession.add(mWindow, mLayout, 419 mVisible ? VISIBLE : GONE, mContentInsets); 420 } 421 422 if (visibleChanged && (!visible || mNewSurfaceNeeded)) { 423 reportSurfaceDestroyed(); 424 } 425 426 mNewSurfaceNeeded = false; 427 428 mSurfaceLock.lock(); 429 mDrawingStopped = !visible; 430 431 final int relayoutResult = mSession.relayout( 432 mWindow, mLayout, mWidth, mHeight, 433 visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, 434 mVisibleInsets, mConfiguration, mSurface); 435 436 if (localLOGV) Log.i(TAG, "New surface: " + mSurface 437 + ", vis=" + visible + ", frame=" + mWinFrame); 438 439 mSurfaceFrame.left = 0; 440 mSurfaceFrame.top = 0; 441 if (mTranslator == null) { 442 mSurfaceFrame.right = mWinFrame.width(); 443 mSurfaceFrame.bottom = mWinFrame.height(); 444 } else { 445 float appInvertedScale = mTranslator.applicationInvertedScale; 446 mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f); 447 mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f); 448 } 449 mSurfaceLock.unlock(); 450 451 try { 452 if (visible) { 453 mDestroyReportNeeded = true; 454 455 SurfaceHolder.Callback callbacks[]; 456 synchronized (mCallbacks) { 457 callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; 458 mCallbacks.toArray(callbacks); 459 } 460 461 if (visibleChanged) { 462 mIsCreating = true; 463 for (SurfaceHolder.Callback c : callbacks) { 464 c.surfaceCreated(mSurfaceHolder); 465 } 466 } 467 if (creating || formatChanged || sizeChanged 468 || visibleChanged) { 469 for (SurfaceHolder.Callback c : callbacks) { 470 c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight); 471 } 472 } 473 } 474 } finally { 475 mIsCreating = false; 476 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 477 mSession.finishDrawing(mWindow); 478 } 479 } 480 } catch (RemoteException ex) { 481 } 482 if (localLOGV) Log.v( 483 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 484 " w=" + mLayout.width + " h=" + mLayout.height + 485 ", frame=" + mSurfaceFrame); 486 } 487 } 488 489 private void reportSurfaceDestroyed() { 490 if (mDestroyReportNeeded) { 491 mDestroyReportNeeded = false; 492 SurfaceHolder.Callback callbacks[]; 493 synchronized (mCallbacks) { 494 callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; 495 mCallbacks.toArray(callbacks); 496 } 497 for (SurfaceHolder.Callback c : callbacks) { 498 c.surfaceDestroyed(mSurfaceHolder); 499 } 500 } 501 super.onDetachedFromWindow(); 502 } 503 504 void handleGetNewSurface() { 505 mNewSurfaceNeeded = true; 506 updateWindow(false); 507 } 508 509 /** 510 * Check to see if the surface has fixed size dimensions or if the surface's 511 * dimensions are dimensions are dependent on its current layout. 512 * 513 * @return true if the surface has dimensions that are fixed in size 514 * @hide 515 */ 516 public boolean isFixedSize() { 517 return (mRequestedWidth != -1 || mRequestedHeight != -1); 518 } 519 520 private static class MyWindow extends BaseIWindow { 521 private final WeakReference<SurfaceView> mSurfaceView; 522 523 public MyWindow(SurfaceView surfaceView) { 524 mSurfaceView = new WeakReference<SurfaceView>(surfaceView); 525 } 526 527 public void resized(int w, int h, Rect coveredInsets, 528 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 529 SurfaceView surfaceView = mSurfaceView.get(); 530 if (surfaceView != null) { 531 if (localLOGV) Log.v( 532 "SurfaceView", surfaceView + " got resized: w=" + 533 w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight); 534 synchronized (this) { 535 if (mCurWidth != w || mCurHeight != h) { 536 mCurWidth = w; 537 mCurHeight = h; 538 } 539 if (reportDraw) { 540 try { 541 surfaceView.mSession.finishDrawing(surfaceView.mWindow); 542 } catch (RemoteException e) { 543 } 544 } 545 } 546 } 547 } 548 549 public void dispatchKey(KeyEvent event) { 550 SurfaceView surfaceView = mSurfaceView.get(); 551 if (surfaceView != null) { 552 //Log.w("SurfaceView", "Unexpected key event in surface: " + event); 553 if (surfaceView.mSession != null && surfaceView.mSurface != null) { 554 try { 555 surfaceView.mSession.finishKey(surfaceView.mWindow); 556 } catch (RemoteException ex) { 557 } 558 } 559 } 560 } 561 562 public void dispatchPointer(MotionEvent event, long eventTime, 563 boolean callWhenDone) { 564 Log.w("SurfaceView", "Unexpected pointer event in surface: " + event); 565 //if (mSession != null && mSurface != null) { 566 // try { 567 // //mSession.finishKey(mWindow); 568 // } catch (RemoteException ex) { 569 // } 570 //} 571 } 572 573 public void dispatchTrackball(MotionEvent event, long eventTime, 574 boolean callWhenDone) { 575 Log.w("SurfaceView", "Unexpected trackball event in surface: " + event); 576 //if (mSession != null && mSurface != null) { 577 // try { 578 // //mSession.finishKey(mWindow); 579 // } catch (RemoteException ex) { 580 // } 581 //} 582 } 583 584 public void dispatchAppVisibility(boolean visible) { 585 // The point of SurfaceView is to let the app control the surface. 586 } 587 588 public void dispatchGetNewSurface() { 589 SurfaceView surfaceView = mSurfaceView.get(); 590 if (surfaceView != null) { 591 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG); 592 surfaceView.mHandler.sendMessage(msg); 593 } 594 } 595 596 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { 597 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); 598 } 599 600 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 601 } 602 603 int mCurWidth = -1; 604 int mCurHeight = -1; 605 } 606 607 private SurfaceHolder mSurfaceHolder = new SurfaceHolder() { 608 609 private static final String LOG_TAG = "SurfaceHolder"; 610 private int mSaveCount; 611 612 public boolean isCreating() { 613 return mIsCreating; 614 } 615 616 public void addCallback(Callback callback) { 617 synchronized (mCallbacks) { 618 // This is a linear search, but in practice we'll 619 // have only a couple callbacks, so it doesn't matter. 620 if (mCallbacks.contains(callback) == false) { 621 mCallbacks.add(callback); 622 } 623 } 624 } 625 626 public void removeCallback(Callback callback) { 627 synchronized (mCallbacks) { 628 mCallbacks.remove(callback); 629 } 630 } 631 632 public void setFixedSize(int width, int height) { 633 if (mRequestedWidth != width || mRequestedHeight != height) { 634 mRequestedWidth = width; 635 mRequestedHeight = height; 636 requestLayout(); 637 } 638 } 639 640 public void setSizeFromLayout() { 641 if (mRequestedWidth != -1 || mRequestedHeight != -1) { 642 mRequestedWidth = mRequestedHeight = -1; 643 requestLayout(); 644 } 645 } 646 647 public void setFormat(int format) { 648 mRequestedFormat = format; 649 if (mWindow != null) { 650 updateWindow(false); 651 } 652 } 653 654 public void setType(int type) { 655 switch (type) { 656 case SURFACE_TYPE_HARDWARE: 657 case SURFACE_TYPE_GPU: 658 // these are deprecated, treat as "NORMAL" 659 type = SURFACE_TYPE_NORMAL; 660 break; 661 } 662 switch (type) { 663 case SURFACE_TYPE_NORMAL: 664 case SURFACE_TYPE_PUSH_BUFFERS: 665 mRequestedType = type; 666 if (mWindow != null) { 667 updateWindow(false); 668 } 669 break; 670 } 671 } 672 673 public void setKeepScreenOn(boolean screenOn) { 674 Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG); 675 msg.arg1 = screenOn ? 1 : 0; 676 mHandler.sendMessage(msg); 677 } 678 679 public Canvas lockCanvas() { 680 return internalLockCanvas(null); 681 } 682 683 public Canvas lockCanvas(Rect dirty) { 684 return internalLockCanvas(dirty); 685 } 686 687 private final Canvas internalLockCanvas(Rect dirty) { 688 if (mType == SURFACE_TYPE_PUSH_BUFFERS) { 689 throw new BadSurfaceTypeException( 690 "Surface type is SURFACE_TYPE_PUSH_BUFFERS"); 691 } 692 mSurfaceLock.lock(); 693 694 if (localLOGV) Log.i(TAG, "Locking canvas... stopped=" 695 + mDrawingStopped + ", win=" + mWindow); 696 697 Canvas c = null; 698 if (!mDrawingStopped && mWindow != null) { 699 Rect frame = dirty != null ? dirty : mSurfaceFrame; 700 try { 701 c = mSurface.lockCanvas(frame); 702 } catch (Exception e) { 703 Log.e(LOG_TAG, "Exception locking surface", e); 704 } 705 } 706 707 if (localLOGV) Log.i(TAG, "Returned canvas: " + c); 708 if (c != null) { 709 mLastLockTime = SystemClock.uptimeMillis(); 710 return c; 711 } 712 713 // If the Surface is not ready to be drawn, then return null, 714 // but throttle calls to this function so it isn't called more 715 // than every 100ms. 716 long now = SystemClock.uptimeMillis(); 717 long nextTime = mLastLockTime + 100; 718 if (nextTime > now) { 719 try { 720 Thread.sleep(nextTime-now); 721 } catch (InterruptedException e) { 722 } 723 now = SystemClock.uptimeMillis(); 724 } 725 mLastLockTime = now; 726 mSurfaceLock.unlock(); 727 728 return null; 729 } 730 731 public void unlockCanvasAndPost(Canvas canvas) { 732 mSurface.unlockCanvasAndPost(canvas); 733 mSurfaceLock.unlock(); 734 } 735 736 public Surface getSurface() { 737 return mSurface; 738 } 739 740 public Rect getSurfaceFrame() { 741 return mSurfaceFrame; 742 } 743 }; 744} 745