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