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