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