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