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 } 501 502 boolean realSizeChanged; 503 boolean reportDrawNeeded; 504 505 int relayoutResult; 506 507 mSurfaceLock.lock(); 508 try { 509 mUpdateWindowNeeded = false; 510 reportDrawNeeded = mReportDrawNeeded; 511 mReportDrawNeeded = false; 512 mDrawingStopped = !visible; 513 514 if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface); 515 516 relayoutResult = mSession.relayout( 517 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 518 visible ? VISIBLE : GONE, 519 WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY, 520 mWinFrame, mOverscanInsets, mContentInsets, 521 mVisibleInsets, mStableInsets, mConfiguration, mNewSurface); 522 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 523 mReportDrawNeeded = true; 524 } 525 526 if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface 527 + ", vis=" + visible + ", frame=" + mWinFrame); 528 529 mSurfaceFrame.left = 0; 530 mSurfaceFrame.top = 0; 531 if (mTranslator == null) { 532 mSurfaceFrame.right = mWinFrame.width(); 533 mSurfaceFrame.bottom = mWinFrame.height(); 534 } else { 535 float appInvertedScale = mTranslator.applicationInvertedScale; 536 mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f); 537 mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f); 538 } 539 540 final int surfaceWidth = mSurfaceFrame.right; 541 final int surfaceHeight = mSurfaceFrame.bottom; 542 realSizeChanged = mLastSurfaceWidth != surfaceWidth 543 || mLastSurfaceHeight != surfaceHeight; 544 mLastSurfaceWidth = surfaceWidth; 545 mLastSurfaceHeight = surfaceHeight; 546 } finally { 547 mSurfaceLock.unlock(); 548 } 549 550 try { 551 redrawNeeded |= creating | reportDrawNeeded; 552 553 SurfaceHolder.Callback callbacks[] = null; 554 555 final boolean surfaceChanged = (relayoutResult 556 & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0; 557 if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { 558 mSurfaceCreated = false; 559 if (mSurface.isValid()) { 560 if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed"); 561 callbacks = getSurfaceCallbacks(); 562 for (SurfaceHolder.Callback c : callbacks) { 563 c.surfaceDestroyed(mSurfaceHolder); 564 } 565 } 566 } 567 568 mSurface.transferFrom(mNewSurface); 569 570 if (visible && mSurface.isValid()) { 571 if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { 572 mSurfaceCreated = true; 573 mIsCreating = true; 574 if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated"); 575 if (callbacks == null) { 576 callbacks = getSurfaceCallbacks(); 577 } 578 for (SurfaceHolder.Callback c : callbacks) { 579 c.surfaceCreated(mSurfaceHolder); 580 } 581 } 582 if (creating || formatChanged || sizeChanged 583 || visibleChanged || realSizeChanged) { 584 if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat 585 + " w=" + myWidth + " h=" + myHeight); 586 if (callbacks == null) { 587 callbacks = getSurfaceCallbacks(); 588 } 589 for (SurfaceHolder.Callback c : callbacks) { 590 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); 591 } 592 } 593 if (redrawNeeded) { 594 if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded"); 595 if (callbacks == null) { 596 callbacks = getSurfaceCallbacks(); 597 } 598 for (SurfaceHolder.Callback c : callbacks) { 599 if (c instanceof SurfaceHolder.Callback2) { 600 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 601 mSurfaceHolder); 602 } 603 } 604 } 605 } 606 } finally { 607 mIsCreating = false; 608 if (redrawNeeded) { 609 if (DEBUG) Log.i(TAG, "finishedDrawing"); 610 mSession.finishDrawing(mWindow); 611 } 612 mSession.performDeferredDestroy(mWindow); 613 } 614 } catch (RemoteException ex) { 615 } 616 if (DEBUG) Log.v( 617 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 618 " w=" + mLayout.width + " h=" + mLayout.height + 619 ", frame=" + mSurfaceFrame); 620 } 621 } 622 getSurfaceCallbacks()623 private SurfaceHolder.Callback[] getSurfaceCallbacks() { 624 SurfaceHolder.Callback callbacks[]; 625 synchronized (mCallbacks) { 626 callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; 627 mCallbacks.toArray(callbacks); 628 } 629 return callbacks; 630 } 631 handleGetNewSurface()632 void handleGetNewSurface() { 633 updateWindow(false, false); 634 } 635 636 /** 637 * Check to see if the surface has fixed size dimensions or if the surface's 638 * dimensions are dimensions are dependent on its current layout. 639 * 640 * @return true if the surface has dimensions that are fixed in size 641 * @hide 642 */ isFixedSize()643 public boolean isFixedSize() { 644 return (mRequestedWidth != -1 || mRequestedHeight != -1); 645 } 646 647 private static class MyWindow extends BaseIWindow { 648 private final WeakReference<SurfaceView> mSurfaceView; 649 MyWindow(SurfaceView surfaceView)650 public MyWindow(SurfaceView surfaceView) { 651 mSurfaceView = new WeakReference<SurfaceView>(surfaceView); 652 } 653 654 @Override resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig)655 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 656 Rect visibleInsets, Rect stableInsets, boolean reportDraw, 657 Configuration newConfig) { 658 SurfaceView surfaceView = mSurfaceView.get(); 659 if (surfaceView != null) { 660 if (DEBUG) Log.v( 661 "SurfaceView", surfaceView + " got resized: w=" + frame.width() 662 + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight); 663 surfaceView.mSurfaceLock.lock(); 664 try { 665 if (reportDraw) { 666 surfaceView.mUpdateWindowNeeded = true; 667 surfaceView.mReportDrawNeeded = true; 668 surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); 669 } else if (surfaceView.mWinFrame.width() != frame.width() 670 || surfaceView.mWinFrame.height() != frame.height()) { 671 surfaceView.mUpdateWindowNeeded = true; 672 surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG); 673 } 674 } finally { 675 surfaceView.mSurfaceLock.unlock(); 676 } 677 } 678 } 679 680 @Override dispatchAppVisibility(boolean visible)681 public void dispatchAppVisibility(boolean visible) { 682 // The point of SurfaceView is to let the app control the surface. 683 } 684 685 @Override dispatchGetNewSurface()686 public void dispatchGetNewSurface() { 687 SurfaceView surfaceView = mSurfaceView.get(); 688 if (surfaceView != null) { 689 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG); 690 surfaceView.mHandler.sendMessage(msg); 691 } 692 } 693 694 @Override windowFocusChanged(boolean hasFocus, boolean touchEnabled)695 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { 696 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled); 697 } 698 699 @Override executeCommand(String command, String parameters, ParcelFileDescriptor out)700 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { 701 } 702 703 int mCurWidth = -1; 704 int mCurHeight = -1; 705 } 706 707 private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { 708 709 private static final String LOG_TAG = "SurfaceHolder"; 710 711 @Override 712 public boolean isCreating() { 713 return mIsCreating; 714 } 715 716 @Override 717 public void addCallback(Callback callback) { 718 synchronized (mCallbacks) { 719 // This is a linear search, but in practice we'll 720 // have only a couple callbacks, so it doesn't matter. 721 if (mCallbacks.contains(callback) == false) { 722 mCallbacks.add(callback); 723 } 724 } 725 } 726 727 @Override 728 public void removeCallback(Callback callback) { 729 synchronized (mCallbacks) { 730 mCallbacks.remove(callback); 731 } 732 } 733 734 @Override 735 public void setFixedSize(int width, int height) { 736 if (mRequestedWidth != width || mRequestedHeight != height) { 737 mRequestedWidth = width; 738 mRequestedHeight = height; 739 requestLayout(); 740 } 741 } 742 743 @Override 744 public void setSizeFromLayout() { 745 if (mRequestedWidth != -1 || mRequestedHeight != -1) { 746 mRequestedWidth = mRequestedHeight = -1; 747 requestLayout(); 748 } 749 } 750 751 @Override 752 public void setFormat(int format) { 753 754 // for backward compatibility reason, OPAQUE always 755 // means 565 for SurfaceView 756 if (format == PixelFormat.OPAQUE) 757 format = PixelFormat.RGB_565; 758 759 mRequestedFormat = format; 760 if (mWindow != null) { 761 updateWindow(false, false); 762 } 763 } 764 765 /** 766 * @deprecated setType is now ignored. 767 */ 768 @Override 769 @Deprecated 770 public void setType(int type) { } 771 772 @Override 773 public void setKeepScreenOn(boolean screenOn) { 774 Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG); 775 msg.arg1 = screenOn ? 1 : 0; 776 mHandler.sendMessage(msg); 777 } 778 779 /** 780 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface 781 * 782 * After drawing into the provided {@link Canvas}, the caller must 783 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 784 * 785 * The caller must redraw the entire surface. 786 * @return A canvas for drawing into the surface. 787 */ 788 @Override 789 public Canvas lockCanvas() { 790 return internalLockCanvas(null); 791 } 792 793 /** 794 * Gets a {@link Canvas} for drawing into the SurfaceView's Surface 795 * 796 * After drawing into the provided {@link Canvas}, the caller must 797 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 798 * 799 * @param inOutDirty A rectangle that represents the dirty region that the caller wants 800 * to redraw. This function may choose to expand the dirty rectangle if for example 801 * the surface has been resized or if the previous contents of the surface were 802 * not available. The caller must redraw the entire dirty region as represented 803 * by the contents of the inOutDirty rectangle upon return from this function. 804 * The caller may also pass <code>null</code> instead, in the case where the 805 * entire surface should be redrawn. 806 * @return A canvas for drawing into the surface. 807 */ 808 @Override 809 public Canvas lockCanvas(Rect inOutDirty) { 810 return internalLockCanvas(inOutDirty); 811 } 812 813 private final Canvas internalLockCanvas(Rect dirty) { 814 mSurfaceLock.lock(); 815 816 if (DEBUG) Log.i(TAG, "Locking canvas... stopped=" 817 + mDrawingStopped + ", win=" + mWindow); 818 819 Canvas c = null; 820 if (!mDrawingStopped && mWindow != null) { 821 try { 822 c = mSurface.lockCanvas(dirty); 823 } catch (Exception e) { 824 Log.e(LOG_TAG, "Exception locking surface", e); 825 } 826 } 827 828 if (DEBUG) Log.i(TAG, "Returned canvas: " + c); 829 if (c != null) { 830 mLastLockTime = SystemClock.uptimeMillis(); 831 return c; 832 } 833 834 // If the Surface is not ready to be drawn, then return null, 835 // but throttle calls to this function so it isn't called more 836 // than every 100ms. 837 long now = SystemClock.uptimeMillis(); 838 long nextTime = mLastLockTime + 100; 839 if (nextTime > now) { 840 try { 841 Thread.sleep(nextTime-now); 842 } catch (InterruptedException e) { 843 } 844 now = SystemClock.uptimeMillis(); 845 } 846 mLastLockTime = now; 847 mSurfaceLock.unlock(); 848 849 return null; 850 } 851 852 /** 853 * Posts the new contents of the {@link Canvas} to the surface and 854 * releases the {@link Canvas}. 855 * 856 * @param canvas The canvas previously obtained from {@link #lockCanvas}. 857 */ 858 @Override 859 public void unlockCanvasAndPost(Canvas canvas) { 860 mSurface.unlockCanvasAndPost(canvas); 861 mSurfaceLock.unlock(); 862 } 863 864 @Override 865 public Surface getSurface() { 866 return mSurface; 867 } 868 869 @Override 870 public Rect getSurfaceFrame() { 871 return mSurfaceFrame; 872 } 873 }; 874 } 875