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 SurfaceView(Context context)182 public SurfaceView(Context context) { 183 super(context); 184 init(); 185 } 186 SurfaceView(Context context, AttributeSet attrs)187 public SurfaceView(Context context, AttributeSet attrs) { 188 super(context, attrs); 189 init(); 190 } 191 SurfaceView(Context context, AttributeSet attrs, int defStyleAttr)192 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 193 super(context, attrs, defStyleAttr); 194 init(); 195 } 196 SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)197 public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 198 super(context, attrs, defStyleAttr, defStyleRes); 199 init(); 200 } 201 init()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 */ getHolder()212 public SurfaceHolder getHolder() { 213 return mSurfaceHolder; 214 } 215 216 @Override onAttachedToWindow()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 onWindowVisibilityChanged(int visibility)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 setVisibility(int visibility)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 onDetachedFromWindow()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 onMeasure(int widthMeasureSpec, int heightMeasureSpec)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 setFrame(int left, int top, int right, int bottom)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 gatherTransparentRegion(Region region)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 draw(Canvas canvas)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 dispatchDraw(Canvas canvas)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 */ setZOrderMediaOverlay(boolean isMediaOverlay)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 */ setZOrderOnTop(boolean onTop)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 */ setSecure(boolean isSecure)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 */ setWindowType(int type)421 public void setWindowType(int type) { 422 mWindowType = type; 423 } 424 425 /** @hide */ updateWindow(boolean force, boolean redrawNeeded)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 getSurfaceCallbacks()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 handleGetNewSurface()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 */ isFixedSize()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 MyWindow(SurfaceView surfaceView)651 public MyWindow(SurfaceView surfaceView) { 652 mSurfaceView = new WeakReference<SurfaceView>(surfaceView); 653 } 654 655 @Override resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig)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 dispatchAppVisibility(boolean visible)682 public void dispatchAppVisibility(boolean visible) { 683 // The point of SurfaceView is to let the app control the surface. 684 } 685 686 @Override dispatchGetNewSurface()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 windowFocusChanged(boolean hasFocus, boolean touchEnabled)696 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { 697 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); 698 } 699 700 @Override executeCommand(String command, String parameters, ParcelFileDescriptor out)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