SurfaceView.java revision 95c42974f719d1fac90fc0438eac778e9795681f
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import com.android.internal.view.BaseIWindow;
20
21import android.content.Context;
22import android.content.res.Configuration;
23import android.content.res.CompatibilityInfo.Translator;
24import android.graphics.Canvas;
25import android.graphics.PixelFormat;
26import android.graphics.PorterDuff;
27import android.graphics.Rect;
28import android.graphics.Region;
29import android.os.Handler;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.SystemClock;
33import android.os.ParcelFileDescriptor;
34import android.util.AttributeSet;
35import android.util.Log;
36
37import java.lang.ref.WeakReference;
38import java.util.ArrayList;
39import 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 */
86public 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 Configuration mConfiguration = new Configuration();
109
110    static final int KEEP_SCREEN_ON_MSG = 1;
111    static final int GET_NEW_SURFACE_MSG = 2;
112    static final int UPDATE_WINDOW_MSG = 3;
113
114    int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
115
116    boolean mIsCreating = false;
117
118    final Handler mHandler = new Handler() {
119        @Override
120        public void handleMessage(Message msg) {
121            switch (msg.what) {
122                case KEEP_SCREEN_ON_MSG: {
123                    setKeepScreenOn(msg.arg1 != 0);
124                } break;
125                case GET_NEW_SURFACE_MSG: {
126                    handleGetNewSurface();
127                } break;
128                case UPDATE_WINDOW_MSG: {
129                    updateWindow(false, false);
130                } break;
131            }
132        }
133    };
134
135    final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
136            = new ViewTreeObserver.OnScrollChangedListener() {
137                    @Override
138                    public void onScrollChanged() {
139                        updateWindow(false, false);
140                    }
141            };
142
143    boolean mRequestedVisible = false;
144    boolean mWindowVisibility = false;
145    boolean mViewVisibility = false;
146    int mRequestedWidth = -1;
147    int mRequestedHeight = -1;
148    /* Set SurfaceView's format to 565 by default to maintain backward
149     * compatibility with applications assuming this format.
150     */
151    int mRequestedFormat = PixelFormat.RGB_565;
152
153    boolean mHaveFrame = false;
154    boolean mSurfaceCreated = false;
155    long mLastLockTime = 0;
156
157    boolean mVisible = false;
158    int mLeft = -1;
159    int mTop = -1;
160    int mWidth = -1;
161    int mHeight = -1;
162    int mFormat = -1;
163    final Rect mSurfaceFrame = new Rect();
164    int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
165    boolean mUpdateWindowNeeded;
166    boolean mReportDrawNeeded;
167    private Translator mTranslator;
168
169    private final ViewTreeObserver.OnPreDrawListener mDrawListener =
170            new ViewTreeObserver.OnPreDrawListener() {
171                @Override
172                public boolean onPreDraw() {
173                    // reposition ourselves where the surface is
174                    mHaveFrame = getWidth() > 0 && getHeight() > 0;
175                    updateWindow(false, false);
176                    return true;
177                }
178            };
179    private boolean mGlobalListenersAdded;
180
181    public SurfaceView(Context context) {
182        super(context);
183        init();
184    }
185
186    public SurfaceView(Context context, AttributeSet attrs) {
187        super(context, attrs);
188        init();
189    }
190
191    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
192        super(context, attrs, defStyle);
193        init();
194    }
195
196    private void init() {
197        setWillNotDraw(true);
198    }
199
200    /**
201     * Return the SurfaceHolder providing access and control over this
202     * SurfaceView's underlying surface.
203     *
204     * @return SurfaceHolder The holder of the surface.
205     */
206    public SurfaceHolder getHolder() {
207        return mSurfaceHolder;
208    }
209
210    @Override
211    protected void onAttachedToWindow() {
212        super.onAttachedToWindow();
213        mParent.requestTransparentRegion(this);
214        mSession = getWindowSession();
215        mLayout.token = getWindowToken();
216        mLayout.setTitle("SurfaceView");
217        mViewVisibility = getVisibility() == VISIBLE;
218
219        if (!mGlobalListenersAdded) {
220            ViewTreeObserver observer = getViewTreeObserver();
221            observer.addOnScrollChangedListener(mScrollChangedListener);
222            observer.addOnPreDrawListener(mDrawListener);
223            mGlobalListenersAdded = true;
224        }
225    }
226
227    @Override
228    protected void onWindowVisibilityChanged(int visibility) {
229        super.onWindowVisibilityChanged(visibility);
230        mWindowVisibility = visibility == VISIBLE;
231        mRequestedVisible = mWindowVisibility && mViewVisibility;
232        updateWindow(false, false);
233    }
234
235    @Override
236    public void setVisibility(int visibility) {
237        super.setVisibility(visibility);
238        mViewVisibility = visibility == VISIBLE;
239        boolean newRequestedVisible = mWindowVisibility && mViewVisibility;
240        if (newRequestedVisible != mRequestedVisible) {
241            // our base class (View) invalidates the layout only when
242            // we go from/to the GONE state. However, SurfaceView needs
243            // to request a re-layout when the visibility changes at all.
244            // This is needed because the transparent region is computed
245            // as part of the layout phase, and it changes (obviously) when
246            // the visibility changes.
247            requestLayout();
248        }
249        mRequestedVisible = newRequestedVisible;
250        updateWindow(false, false);
251    }
252
253    @Override
254    protected void onDetachedFromWindow() {
255        if (mGlobalListenersAdded) {
256            ViewTreeObserver observer = getViewTreeObserver();
257            observer.removeOnScrollChangedListener(mScrollChangedListener);
258            observer.removeOnPreDrawListener(mDrawListener);
259            mGlobalListenersAdded = false;
260        }
261
262        mRequestedVisible = false;
263        updateWindow(false, false);
264        mHaveFrame = false;
265        if (mWindow != null) {
266            try {
267                mSession.remove(mWindow);
268            } catch (RemoteException ex) {
269                // Not much we can do here...
270            }
271            mWindow = null;
272        }
273        mSession = null;
274        mLayout.token = null;
275
276        super.onDetachedFromWindow();
277    }
278
279    @Override
280    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
281        int width = mRequestedWidth >= 0
282                ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
283                : getDefaultSize(0, widthMeasureSpec);
284        int height = mRequestedHeight >= 0
285                ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
286                : getDefaultSize(0, heightMeasureSpec);
287        setMeasuredDimension(width, height);
288    }
289
290    /** @hide */
291    @Override
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
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 & PFLAG_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
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 & PFLAG_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
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 & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
342                // punch a whole in the view-hierarchy below us
343                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
344            }
345        }
346        super.dispatchDraw(canvas);
347    }
348
349    /**
350     * Control whether the surface view's surface is placed on top of another
351     * regular surface view in the window (but still behind the window itself).
352     * This is typically used to place overlays on top of an underlying media
353     * surface view.
354     *
355     * <p>Note that this must be set before the surface view's containing
356     * window is attached to the window manager.
357     *
358     * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
359     */
360    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
361        mWindowType = isMediaOverlay
362                ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
363                : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
364    }
365
366    /**
367     * Control whether the surface view's surface is placed on top of its
368     * window.  Normally it is placed behind the window, to allow it to
369     * (for the most part) appear to composite with the views in the
370     * hierarchy.  By setting this, you cause it to be placed above the
371     * window.  This means that none of the contents of the window this
372     * SurfaceView is in will be visible on top of its surface.
373     *
374     * <p>Note that this must be set before the surface view's containing
375     * window is attached to the window manager.
376     *
377     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
378     */
379    public void setZOrderOnTop(boolean onTop) {
380        if (onTop) {
381            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
382            // ensures the surface is placed below the IME
383            mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
384        } else {
385            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
386            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
387        }
388    }
389
390    /**
391     * Control whether the surface view's content should be treated as secure,
392     * preventing it from appearing in screenshots or from being viewed on
393     * non-secure displays.
394     *
395     * <p>Note that this must be set before the surface view's containing
396     * window is attached to the window manager.
397     *
398     * <p>See {@link android.view.Display#FLAG_SECURE} for details.
399     *
400     * @param isSecure True if the surface view is secure.
401     */
402    public void setSecure(boolean isSecure) {
403        if (isSecure) {
404            mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
405        } else {
406            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
407        }
408    }
409
410    /**
411     * Hack to allow special layering of windows.  The type is one of the
412     * types in WindowManager.LayoutParams.  This is a hack so:
413     * @hide
414     */
415    public void setWindowType(int type) {
416        mWindowType = type;
417    }
418
419    private void updateWindow(boolean force, boolean redrawNeeded) {
420        if (!mHaveFrame) {
421            return;
422        }
423        ViewRootImpl viewRoot = getViewRootImpl();
424        if (viewRoot != null) {
425            mTranslator = viewRoot.mTranslator;
426        }
427
428        if (mTranslator != null) {
429            mSurface.setCompatibilityTranslator(mTranslator);
430        }
431
432        int myWidth = mRequestedWidth;
433        if (myWidth <= 0) myWidth = getWidth();
434        int myHeight = mRequestedHeight;
435        if (myHeight <= 0) myHeight = getHeight();
436
437        getLocationInWindow(mLocation);
438        final boolean creating = mWindow == null;
439        final boolean formatChanged = mFormat != mRequestedFormat;
440        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
441        final boolean visibleChanged = mVisible != mRequestedVisible;
442
443        if (force || creating || formatChanged || sizeChanged || visibleChanged
444            || mLeft != mLocation[0] || mTop != mLocation[1]
445            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
446
447            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
448                    + " format=" + formatChanged + " size=" + sizeChanged
449                    + " visible=" + visibleChanged
450                    + " left=" + (mLeft != mLocation[0])
451                    + " top=" + (mTop != mLocation[1]));
452
453            try {
454                final boolean visible = mVisible = mRequestedVisible;
455                mLeft = mLocation[0];
456                mTop = mLocation[1];
457                mWidth = myWidth;
458                mHeight = myHeight;
459                mFormat = mRequestedFormat;
460
461                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
462
463                // Places the window relative
464                mLayout.x = mLeft;
465                mLayout.y = mTop;
466                mLayout.width = getWidth();
467                mLayout.height = getHeight();
468                if (mTranslator != null) {
469                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
470                }
471
472                mLayout.format = mRequestedFormat;
473                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
474                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
475                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
476                              | WindowManager.LayoutParams.FLAG_SCALED
477                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
478                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
479                              ;
480                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
481                    mLayout.privateFlags |=
482                            WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
483                }
484                mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
485
486                if (mWindow == null) {
487                    Display display = getDisplay();
488                    mWindow = new MyWindow(this);
489                    mLayout.type = mWindowType;
490                    mLayout.gravity = Gravity.START|Gravity.TOP;
491                    mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
492                            mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets);
493                }
494
495                boolean realSizeChanged;
496                boolean reportDrawNeeded;
497
498                int relayoutResult;
499
500                mSurfaceLock.lock();
501                try {
502                    mUpdateWindowNeeded = false;
503                    reportDrawNeeded = mReportDrawNeeded;
504                    mReportDrawNeeded = false;
505                    mDrawingStopped = !visible;
506
507                    if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
508
509                    relayoutResult = mSession.relayout(
510                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
511                            visible ? VISIBLE : GONE,
512                            WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
513                            mWinFrame, mOverscanInsets, mContentInsets,
514                            mVisibleInsets, mConfiguration, mNewSurface);
515                    if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
516                        mReportDrawNeeded = true;
517                    }
518
519                    if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
520                            + ", vis=" + visible + ", frame=" + mWinFrame);
521
522                    mSurfaceFrame.left = 0;
523                    mSurfaceFrame.top = 0;
524                    if (mTranslator == null) {
525                        mSurfaceFrame.right = mWinFrame.width();
526                        mSurfaceFrame.bottom = mWinFrame.height();
527                    } else {
528                        float appInvertedScale = mTranslator.applicationInvertedScale;
529                        mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
530                        mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
531                    }
532
533                    final int surfaceWidth = mSurfaceFrame.right;
534                    final int surfaceHeight = mSurfaceFrame.bottom;
535                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
536                            || mLastSurfaceHeight != surfaceHeight;
537                    mLastSurfaceWidth = surfaceWidth;
538                    mLastSurfaceHeight = surfaceHeight;
539                } finally {
540                    mSurfaceLock.unlock();
541                }
542
543                try {
544                    redrawNeeded |= creating | reportDrawNeeded;
545
546                    SurfaceHolder.Callback callbacks[] = null;
547
548                    final boolean surfaceChanged = (relayoutResult
549                            & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
550                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
551                        mSurfaceCreated = false;
552                        if (mSurface.isValid()) {
553                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
554                            callbacks = getSurfaceCallbacks();
555                            for (SurfaceHolder.Callback c : callbacks) {
556                                c.surfaceDestroyed(mSurfaceHolder);
557                            }
558                        }
559                    }
560
561                    mSurface.transferFrom(mNewSurface);
562
563                    if (visible && mSurface.isValid()) {
564                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
565                            mSurfaceCreated = true;
566                            mIsCreating = true;
567                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
568                            if (callbacks == null) {
569                                callbacks = getSurfaceCallbacks();
570                            }
571                            for (SurfaceHolder.Callback c : callbacks) {
572                                c.surfaceCreated(mSurfaceHolder);
573                            }
574                        }
575                        if (creating || formatChanged || sizeChanged
576                                || visibleChanged || realSizeChanged) {
577                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
578                                    + " w=" + myWidth + " h=" + myHeight);
579                            if (callbacks == null) {
580                                callbacks = getSurfaceCallbacks();
581                            }
582                            for (SurfaceHolder.Callback c : callbacks) {
583                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
584                            }
585                        }
586                        if (redrawNeeded) {
587                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
588                            if (callbacks == null) {
589                                callbacks = getSurfaceCallbacks();
590                            }
591                            for (SurfaceHolder.Callback c : callbacks) {
592                                if (c instanceof SurfaceHolder.Callback2) {
593                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
594                                            mSurfaceHolder);
595                                }
596                            }
597                        }
598                    }
599                } finally {
600                    mIsCreating = false;
601                    if (redrawNeeded) {
602                        if (DEBUG) Log.i(TAG, "finishedDrawing");
603                        mSession.finishDrawing(mWindow);
604                    }
605                    mSession.performDeferredDestroy(mWindow);
606                }
607            } catch (RemoteException ex) {
608            }
609            if (DEBUG) Log.v(
610                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
611                " w=" + mLayout.width + " h=" + mLayout.height +
612                ", frame=" + mSurfaceFrame);
613        }
614    }
615
616    private SurfaceHolder.Callback[] getSurfaceCallbacks() {
617        SurfaceHolder.Callback callbacks[];
618        synchronized (mCallbacks) {
619            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
620            mCallbacks.toArray(callbacks);
621        }
622        return callbacks;
623    }
624
625    void handleGetNewSurface() {
626        updateWindow(false, false);
627    }
628
629    /**
630     * Check to see if the surface has fixed size dimensions or if the surface's
631     * dimensions are dimensions are dependent on its current layout.
632     *
633     * @return true if the surface has dimensions that are fixed in size
634     * @hide
635     */
636    public boolean isFixedSize() {
637        return (mRequestedWidth != -1 || mRequestedHeight != -1);
638    }
639
640    private static class MyWindow extends BaseIWindow {
641        private final WeakReference<SurfaceView> mSurfaceView;
642
643        public MyWindow(SurfaceView surfaceView) {
644            mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
645        }
646
647        @Override
648        public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
649                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
650            SurfaceView surfaceView = mSurfaceView.get();
651            if (surfaceView != null) {
652                if (DEBUG) Log.v(
653                        "SurfaceView", surfaceView + " got resized: w=" + frame.width()
654                        + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
655                surfaceView.mSurfaceLock.lock();
656                try {
657                    if (reportDraw) {
658                        surfaceView.mUpdateWindowNeeded = true;
659                        surfaceView.mReportDrawNeeded = true;
660                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
661                    } else if (surfaceView.mWinFrame.width() != frame.width()
662                            || surfaceView.mWinFrame.height() != frame.height()) {
663                        surfaceView.mUpdateWindowNeeded = true;
664                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
665                    }
666                } finally {
667                    surfaceView.mSurfaceLock.unlock();
668                }
669            }
670        }
671
672        @Override
673        public void dispatchAppVisibility(boolean visible) {
674            // The point of SurfaceView is to let the app control the surface.
675        }
676
677        @Override
678        public void dispatchGetNewSurface() {
679            SurfaceView surfaceView = mSurfaceView.get();
680            if (surfaceView != null) {
681                Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
682                surfaceView.mHandler.sendMessage(msg);
683            }
684        }
685
686        @Override
687        public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
688            Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
689        }
690
691        @Override
692        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
693        }
694
695        int mCurWidth = -1;
696        int mCurHeight = -1;
697    }
698
699    private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
700
701        private static final String LOG_TAG = "SurfaceHolder";
702
703        @Override
704        public boolean isCreating() {
705            return mIsCreating;
706        }
707
708        @Override
709        public void addCallback(Callback callback) {
710            synchronized (mCallbacks) {
711                // This is a linear search, but in practice we'll
712                // have only a couple callbacks, so it doesn't matter.
713                if (mCallbacks.contains(callback) == false) {
714                    mCallbacks.add(callback);
715                }
716            }
717        }
718
719        @Override
720        public void removeCallback(Callback callback) {
721            synchronized (mCallbacks) {
722                mCallbacks.remove(callback);
723            }
724        }
725
726        @Override
727        public void setFixedSize(int width, int height) {
728            if (mRequestedWidth != width || mRequestedHeight != height) {
729                mRequestedWidth = width;
730                mRequestedHeight = height;
731                requestLayout();
732            }
733        }
734
735        @Override
736        public void setSizeFromLayout() {
737            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
738                mRequestedWidth = mRequestedHeight = -1;
739                requestLayout();
740            }
741        }
742
743        @Override
744        public void setFormat(int format) {
745
746            // for backward compatibility reason, OPAQUE always
747            // means 565 for SurfaceView
748            if (format == PixelFormat.OPAQUE)
749                format = PixelFormat.RGB_565;
750
751            mRequestedFormat = format;
752            if (mWindow != null) {
753                updateWindow(false, false);
754            }
755        }
756
757        /**
758         * @deprecated setType is now ignored.
759         */
760        @Override
761        @Deprecated
762        public void setType(int type) { }
763
764        @Override
765        public void setKeepScreenOn(boolean screenOn) {
766            Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
767            msg.arg1 = screenOn ? 1 : 0;
768            mHandler.sendMessage(msg);
769        }
770
771        /**
772         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
773         *
774         * After drawing into the provided {@link Canvas}, the caller must
775         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
776         *
777         * The caller must redraw the entire surface.
778         * @return A canvas for drawing into the surface.
779         */
780        @Override
781        public Canvas lockCanvas() {
782            return internalLockCanvas(null);
783        }
784
785        /**
786         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
787         *
788         * After drawing into the provided {@link Canvas}, the caller must
789         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
790         *
791         * @param inOutDirty A rectangle that represents the dirty region that the caller wants
792         * to redraw.  This function may choose to expand the dirty rectangle if for example
793         * the surface has been resized or if the previous contents of the surface were
794         * not available.  The caller must redraw the entire dirty region as represented
795         * by the contents of the inOutDirty rectangle upon return from this function.
796         * The caller may also pass <code>null</code> instead, in the case where the
797         * entire surface should be redrawn.
798         * @return A canvas for drawing into the surface.
799         */
800        @Override
801        public Canvas lockCanvas(Rect inOutDirty) {
802            return internalLockCanvas(inOutDirty);
803        }
804
805        private final Canvas internalLockCanvas(Rect dirty) {
806            mSurfaceLock.lock();
807
808            if (DEBUG) Log.i(TAG, "Locking canvas... stopped="
809                    + mDrawingStopped + ", win=" + mWindow);
810
811            Canvas c = null;
812            if (!mDrawingStopped && mWindow != null) {
813                try {
814                    c = mSurface.lockCanvas(dirty);
815                } catch (Exception e) {
816                    Log.e(LOG_TAG, "Exception locking surface", e);
817                }
818            }
819
820            if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
821            if (c != null) {
822                mLastLockTime = SystemClock.uptimeMillis();
823                return c;
824            }
825
826            // If the Surface is not ready to be drawn, then return null,
827            // but throttle calls to this function so it isn't called more
828            // than every 100ms.
829            long now = SystemClock.uptimeMillis();
830            long nextTime = mLastLockTime + 100;
831            if (nextTime > now) {
832                try {
833                    Thread.sleep(nextTime-now);
834                } catch (InterruptedException e) {
835                }
836                now = SystemClock.uptimeMillis();
837            }
838            mLastLockTime = now;
839            mSurfaceLock.unlock();
840
841            return null;
842        }
843
844        /**
845         * Posts the new contents of the {@link Canvas} to the surface and
846         * releases the {@link Canvas}.
847         *
848         * @param canvas The canvas previously obtained from {@link #lockCanvas}.
849         */
850        @Override
851        public void unlockCanvasAndPost(Canvas canvas) {
852            mSurface.unlockCanvasAndPost(canvas);
853            mSurfaceLock.unlock();
854        }
855
856        @Override
857        public Surface getSurface() {
858            return mSurface;
859        }
860
861        @Override
862        public Rect getSurfaceFrame() {
863            return mSurfaceFrame;
864        }
865    };
866}
867