• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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