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