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