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