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