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