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