SurfaceView.java revision fa10423fa00f3495e451016acba9b6848eb995c9
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 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
182    public SurfaceView(Context context) {
183        super(context);
184        init();
185    }
186
187    public SurfaceView(Context context, AttributeSet attrs) {
188        super(context, attrs);
189        init();
190    }
191
192    public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
193        super(context, attrs, defStyleAttr);
194        init();
195    }
196
197    public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
198        super(context, attrs, defStyleAttr, defStyleRes);
199        init();
200    }
201
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     */
212    public SurfaceHolder getHolder() {
213        return mSurfaceHolder;
214    }
215
216    @Override
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
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
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    /** @hide */
260    @Override
261    protected void onDetachedFromWindowInternal() {
262        if (mGlobalListenersAdded) {
263            ViewTreeObserver observer = getViewTreeObserver();
264            observer.removeOnScrollChangedListener(mScrollChangedListener);
265            observer.removeOnPreDrawListener(mDrawListener);
266            mGlobalListenersAdded = false;
267        }
268
269        mRequestedVisible = false;
270        updateWindow(false, false);
271        mHaveFrame = false;
272        if (mWindow != null) {
273            try {
274                mSession.remove(mWindow);
275            } catch (RemoteException ex) {
276                // Not much we can do here...
277            }
278            mWindow = null;
279        }
280        mSession = null;
281        mLayout.token = null;
282
283        super.onDetachedFromWindowInternal();
284    }
285
286    @Override
287    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
288        int width = mRequestedWidth >= 0
289                ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
290                : getDefaultSize(0, widthMeasureSpec);
291        int height = mRequestedHeight >= 0
292                ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
293                : getDefaultSize(0, heightMeasureSpec);
294        setMeasuredDimension(width, height);
295    }
296
297    /** @hide */
298    @Override
299    protected boolean setFrame(int left, int top, int right, int bottom) {
300        boolean result = super.setFrame(left, top, right, bottom);
301        updateWindow(false, false);
302        return result;
303    }
304
305    @Override
306    public boolean gatherTransparentRegion(Region region) {
307        if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
308            return super.gatherTransparentRegion(region);
309        }
310
311        boolean opaque = true;
312        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
313            // this view draws, remove it from the transparent region
314            opaque = super.gatherTransparentRegion(region);
315        } else if (region != null) {
316            int w = getWidth();
317            int h = getHeight();
318            if (w>0 && h>0) {
319                getLocationInWindow(mLocation);
320                // otherwise, punch a hole in the whole hierarchy
321                int l = mLocation[0];
322                int t = mLocation[1];
323                region.op(l, t, l+w, t+h, Region.Op.UNION);
324            }
325        }
326        if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
327            opaque = false;
328        }
329        return opaque;
330    }
331
332    @Override
333    public void draw(Canvas canvas) {
334        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
335            // draw() is not called when SKIP_DRAW is set
336            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
337                // punch a whole in the view-hierarchy below us
338                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
339            }
340        }
341        super.draw(canvas);
342    }
343
344    @Override
345    protected void dispatchDraw(Canvas canvas) {
346        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
347            // if SKIP_DRAW is cleared, draw() has already punched a hole
348            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
349                // punch a whole in the view-hierarchy below us
350                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
351            }
352        }
353        super.dispatchDraw(canvas);
354    }
355
356    /**
357     * Control whether the surface view's surface is placed on top of another
358     * regular surface view in the window (but still behind the window itself).
359     * This is typically used to place overlays on top of an underlying media
360     * surface view.
361     *
362     * <p>Note that this must be set before the surface view's containing
363     * window is attached to the window manager.
364     *
365     * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
366     */
367    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
368        mWindowType = isMediaOverlay
369                ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
370                : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
371    }
372
373    /**
374     * Control whether the surface view's surface is placed on top of its
375     * window.  Normally it is placed behind the window, to allow it to
376     * (for the most part) appear to composite with the views in the
377     * hierarchy.  By setting this, you cause it to be placed above the
378     * window.  This means that none of the contents of the window this
379     * SurfaceView is in will be visible on top of its surface.
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 #setZOrderMediaOverlay}.
385     */
386    public void setZOrderOnTop(boolean onTop) {
387        if (onTop) {
388            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
389            // ensures the surface is placed below the IME
390            mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
391        } else {
392            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
393            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
394        }
395    }
396
397    /**
398     * Control whether the surface view's content should be treated as secure,
399     * preventing it from appearing in screenshots or from being viewed on
400     * non-secure displays.
401     *
402     * <p>Note that this must be set before the surface view's containing
403     * window is attached to the window manager.
404     *
405     * <p>See {@link android.view.Display#FLAG_SECURE} for details.
406     *
407     * @param isSecure True if the surface view is secure.
408     */
409    public void setSecure(boolean isSecure) {
410        if (isSecure) {
411            mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
412        } else {
413            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
414        }
415    }
416
417    /**
418     * Hack to allow special layering of windows.  The type is one of the
419     * types in WindowManager.LayoutParams.  This is a hack so:
420     * @hide
421     */
422    public void setWindowType(int type) {
423        mWindowType = type;
424    }
425
426    /** @hide */
427    protected void updateWindow(boolean force, boolean redrawNeeded) {
428        if (!mHaveFrame) {
429            return;
430        }
431        ViewRootImpl viewRoot = getViewRootImpl();
432        if (viewRoot != null) {
433            mTranslator = viewRoot.mTranslator;
434        }
435
436        if (mTranslator != null) {
437            mSurface.setCompatibilityTranslator(mTranslator);
438        }
439
440        int myWidth = mRequestedWidth;
441        if (myWidth <= 0) myWidth = getWidth();
442        int myHeight = mRequestedHeight;
443        if (myHeight <= 0) myHeight = getHeight();
444
445        getLocationInWindow(mLocation);
446        final boolean creating = mWindow == null;
447        final boolean formatChanged = mFormat != mRequestedFormat;
448        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
449        final boolean visibleChanged = mVisible != mRequestedVisible;
450
451        if (force || creating || formatChanged || sizeChanged || visibleChanged
452            || mLeft != mLocation[0] || mTop != mLocation[1]
453            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
454
455            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
456                    + " format=" + formatChanged + " size=" + sizeChanged
457                    + " visible=" + visibleChanged
458                    + " left=" + (mLeft != mLocation[0])
459                    + " top=" + (mTop != mLocation[1]));
460
461            try {
462                final boolean visible = mVisible = mRequestedVisible;
463                mLeft = mLocation[0];
464                mTop = mLocation[1];
465                mWidth = myWidth;
466                mHeight = myHeight;
467                mFormat = mRequestedFormat;
468
469                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
470
471                // Places the window relative
472                mLayout.x = mLeft;
473                mLayout.y = mTop;
474                mLayout.width = getWidth();
475                mLayout.height = getHeight();
476                if (mTranslator != null) {
477                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
478                }
479
480                mLayout.format = mRequestedFormat;
481                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
482                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
483                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
484                              | WindowManager.LayoutParams.FLAG_SCALED
485                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
486                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
487                              ;
488                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
489                    mLayout.privateFlags |=
490                            WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
491                }
492                mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
493
494                if (mWindow == null) {
495                    Display display = getDisplay();
496                    mWindow = new MyWindow(this);
497                    mLayout.type = mWindowType;
498                    mLayout.gravity = Gravity.START|Gravity.TOP;
499                    mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
500                            mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets);
501                }
502
503                boolean realSizeChanged;
504                boolean reportDrawNeeded;
505
506                int relayoutResult;
507
508                mSurfaceLock.lock();
509                try {
510                    mUpdateWindowNeeded = false;
511                    reportDrawNeeded = mReportDrawNeeded;
512                    mReportDrawNeeded = false;
513                    mDrawingStopped = !visible;
514
515                    if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
516
517                    relayoutResult = mSession.relayout(
518                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
519                            visible ? VISIBLE : GONE,
520                            WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
521                            mWinFrame, mOverscanInsets, mContentInsets,
522                            mVisibleInsets, mStableInsets, mConfiguration, mNewSurface);
523                    if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
524                        mReportDrawNeeded = true;
525                    }
526
527                    if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
528                            + ", vis=" + visible + ", frame=" + mWinFrame);
529
530                    mSurfaceFrame.left = 0;
531                    mSurfaceFrame.top = 0;
532                    if (mTranslator == null) {
533                        mSurfaceFrame.right = mWinFrame.width();
534                        mSurfaceFrame.bottom = mWinFrame.height();
535                    } else {
536                        float appInvertedScale = mTranslator.applicationInvertedScale;
537                        mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
538                        mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
539                    }
540
541                    final int surfaceWidth = mSurfaceFrame.right;
542                    final int surfaceHeight = mSurfaceFrame.bottom;
543                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
544                            || mLastSurfaceHeight != surfaceHeight;
545                    mLastSurfaceWidth = surfaceWidth;
546                    mLastSurfaceHeight = surfaceHeight;
547                } finally {
548                    mSurfaceLock.unlock();
549                }
550
551                try {
552                    redrawNeeded |= creating | reportDrawNeeded;
553
554                    SurfaceHolder.Callback callbacks[] = null;
555
556                    final boolean surfaceChanged = (relayoutResult
557                            & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
558                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
559                        mSurfaceCreated = false;
560                        if (mSurface.isValid()) {
561                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
562                            callbacks = getSurfaceCallbacks();
563                            for (SurfaceHolder.Callback c : callbacks) {
564                                c.surfaceDestroyed(mSurfaceHolder);
565                            }
566                        }
567                    }
568
569                    mSurface.transferFrom(mNewSurface);
570
571                    if (visible && mSurface.isValid()) {
572                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
573                            mSurfaceCreated = true;
574                            mIsCreating = true;
575                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
576                            if (callbacks == null) {
577                                callbacks = getSurfaceCallbacks();
578                            }
579                            for (SurfaceHolder.Callback c : callbacks) {
580                                c.surfaceCreated(mSurfaceHolder);
581                            }
582                        }
583                        if (creating || formatChanged || sizeChanged
584                                || visibleChanged || realSizeChanged) {
585                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
586                                    + " w=" + myWidth + " h=" + myHeight);
587                            if (callbacks == null) {
588                                callbacks = getSurfaceCallbacks();
589                            }
590                            for (SurfaceHolder.Callback c : callbacks) {
591                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
592                            }
593                        }
594                        if (redrawNeeded) {
595                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
596                            if (callbacks == null) {
597                                callbacks = getSurfaceCallbacks();
598                            }
599                            for (SurfaceHolder.Callback c : callbacks) {
600                                if (c instanceof SurfaceHolder.Callback2) {
601                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
602                                            mSurfaceHolder);
603                                }
604                            }
605                        }
606                    }
607                } finally {
608                    mIsCreating = false;
609                    if (redrawNeeded) {
610                        if (DEBUG) Log.i(TAG, "finishedDrawing");
611                        mSession.finishDrawing(mWindow);
612                    }
613                    mSession.performDeferredDestroy(mWindow);
614                }
615            } catch (RemoteException ex) {
616            }
617            if (DEBUG) Log.v(
618                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
619                " w=" + mLayout.width + " h=" + mLayout.height +
620                ", frame=" + mSurfaceFrame);
621        }
622    }
623
624    private SurfaceHolder.Callback[] getSurfaceCallbacks() {
625        SurfaceHolder.Callback callbacks[];
626        synchronized (mCallbacks) {
627            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
628            mCallbacks.toArray(callbacks);
629        }
630        return callbacks;
631    }
632
633    void handleGetNewSurface() {
634        updateWindow(false, false);
635    }
636
637    /**
638     * Check to see if the surface has fixed size dimensions or if the surface's
639     * dimensions are dimensions are dependent on its current layout.
640     *
641     * @return true if the surface has dimensions that are fixed in size
642     * @hide
643     */
644    public boolean isFixedSize() {
645        return (mRequestedWidth != -1 || mRequestedHeight != -1);
646    }
647
648    private static class MyWindow extends BaseIWindow {
649        private final WeakReference<SurfaceView> mSurfaceView;
650
651        public MyWindow(SurfaceView surfaceView) {
652            mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
653        }
654
655        @Override
656        public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
657                Rect visibleInsets, Rect stableInsets, boolean reportDraw,
658                Configuration newConfig) {
659            SurfaceView surfaceView = mSurfaceView.get();
660            if (surfaceView != null) {
661                if (DEBUG) Log.v(
662                        "SurfaceView", surfaceView + " got resized: w=" + frame.width()
663                        + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
664                surfaceView.mSurfaceLock.lock();
665                try {
666                    if (reportDraw) {
667                        surfaceView.mUpdateWindowNeeded = true;
668                        surfaceView.mReportDrawNeeded = true;
669                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
670                    } else if (surfaceView.mWinFrame.width() != frame.width()
671                            || surfaceView.mWinFrame.height() != frame.height()) {
672                        surfaceView.mUpdateWindowNeeded = true;
673                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
674                    }
675                } finally {
676                    surfaceView.mSurfaceLock.unlock();
677                }
678            }
679        }
680
681        @Override
682        public void dispatchAppVisibility(boolean visible) {
683            // The point of SurfaceView is to let the app control the surface.
684        }
685
686        @Override
687        public void dispatchGetNewSurface() {
688            SurfaceView surfaceView = mSurfaceView.get();
689            if (surfaceView != null) {
690                Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
691                surfaceView.mHandler.sendMessage(msg);
692            }
693        }
694
695        @Override
696        public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
697            Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
698        }
699
700        @Override
701        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
702        }
703
704        int mCurWidth = -1;
705        int mCurHeight = -1;
706    }
707
708    private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
709
710        private static final String LOG_TAG = "SurfaceHolder";
711
712        @Override
713        public boolean isCreating() {
714            return mIsCreating;
715        }
716
717        @Override
718        public void addCallback(Callback callback) {
719            synchronized (mCallbacks) {
720                // This is a linear search, but in practice we'll
721                // have only a couple callbacks, so it doesn't matter.
722                if (mCallbacks.contains(callback) == false) {
723                    mCallbacks.add(callback);
724                }
725            }
726        }
727
728        @Override
729        public void removeCallback(Callback callback) {
730            synchronized (mCallbacks) {
731                mCallbacks.remove(callback);
732            }
733        }
734
735        @Override
736        public void setFixedSize(int width, int height) {
737            if (mRequestedWidth != width || mRequestedHeight != height) {
738                mRequestedWidth = width;
739                mRequestedHeight = height;
740                requestLayout();
741            }
742        }
743
744        @Override
745        public void setSizeFromLayout() {
746            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
747                mRequestedWidth = mRequestedHeight = -1;
748                requestLayout();
749            }
750        }
751
752        @Override
753        public void setFormat(int format) {
754
755            // for backward compatibility reason, OPAQUE always
756            // means 565 for SurfaceView
757            if (format == PixelFormat.OPAQUE)
758                format = PixelFormat.RGB_565;
759
760            mRequestedFormat = format;
761            if (mWindow != null) {
762                updateWindow(false, false);
763            }
764        }
765
766        /**
767         * @deprecated setType is now ignored.
768         */
769        @Override
770        @Deprecated
771        public void setType(int type) { }
772
773        @Override
774        public void setKeepScreenOn(boolean screenOn) {
775            Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
776            msg.arg1 = screenOn ? 1 : 0;
777            mHandler.sendMessage(msg);
778        }
779
780        /**
781         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
782         *
783         * After drawing into the provided {@link Canvas}, the caller must
784         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
785         *
786         * The caller must redraw the entire surface.
787         * @return A canvas for drawing into the surface.
788         */
789        @Override
790        public Canvas lockCanvas() {
791            return internalLockCanvas(null);
792        }
793
794        /**
795         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
796         *
797         * After drawing into the provided {@link Canvas}, the caller must
798         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
799         *
800         * @param inOutDirty A rectangle that represents the dirty region that the caller wants
801         * to redraw.  This function may choose to expand the dirty rectangle if for example
802         * the surface has been resized or if the previous contents of the surface were
803         * not available.  The caller must redraw the entire dirty region as represented
804         * by the contents of the inOutDirty rectangle upon return from this function.
805         * The caller may also pass <code>null</code> instead, in the case where the
806         * entire surface should be redrawn.
807         * @return A canvas for drawing into the surface.
808         */
809        @Override
810        public Canvas lockCanvas(Rect inOutDirty) {
811            return internalLockCanvas(inOutDirty);
812        }
813
814        private final Canvas internalLockCanvas(Rect dirty) {
815            mSurfaceLock.lock();
816
817            if (DEBUG) Log.i(TAG, "Locking canvas... stopped="
818                    + mDrawingStopped + ", win=" + mWindow);
819
820            Canvas c = null;
821            if (!mDrawingStopped && mWindow != null) {
822                try {
823                    c = mSurface.lockCanvas(dirty);
824                } catch (Exception e) {
825                    Log.e(LOG_TAG, "Exception locking surface", e);
826                }
827            }
828
829            if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
830            if (c != null) {
831                mLastLockTime = SystemClock.uptimeMillis();
832                return c;
833            }
834
835            // If the Surface is not ready to be drawn, then return null,
836            // but throttle calls to this function so it isn't called more
837            // than every 100ms.
838            long now = SystemClock.uptimeMillis();
839            long nextTime = mLastLockTime + 100;
840            if (nextTime > now) {
841                try {
842                    Thread.sleep(nextTime-now);
843                } catch (InterruptedException e) {
844                }
845                now = SystemClock.uptimeMillis();
846            }
847            mLastLockTime = now;
848            mSurfaceLock.unlock();
849
850            return null;
851        }
852
853        /**
854         * Posts the new contents of the {@link Canvas} to the surface and
855         * releases the {@link Canvas}.
856         *
857         * @param canvas The canvas previously obtained from {@link #lockCanvas}.
858         */
859        @Override
860        public void unlockCanvasAndPost(Canvas canvas) {
861            mSurface.unlockCanvasAndPost(canvas);
862            mSurfaceLock.unlock();
863        }
864
865        @Override
866        public Surface getSurface() {
867            return mSurface;
868        }
869
870        @Override
871        public Rect getSurfaceFrame() {
872            return mSurfaceFrame;
873        }
874    };
875}
876