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