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