SurfaceView.java revision cbeb33249d5beec7903f18269c991a5515c5d981
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>Access to the underlying surface is provided via the SurfaceHolder interface,
57 * which can be retrieved by calling {@link #getHolder}.
58 *
59 * <p>The Surface will be created for you while the SurfaceView's window is
60 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
61 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
62 * Surface is created and destroyed as the window is shown and hidden.
63 *
64 * <p>One of the purposes of this class is to provide a surface in which a
65 * secondary thread can render into the screen.  If you are going to use it
66 * this way, you need to be aware of some threading semantics:
67 *
68 * <ul>
69 * <li> All SurfaceView and
70 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
71 * from the thread running the SurfaceView's window (typically the main thread
72 * of the application).  They thus need to correctly synchronize with any
73 * state that is also touched by the drawing thread.
74 * <li> You must ensure that the drawing thread only touches the underlying
75 * Surface while it is valid -- between
76 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
77 * and
78 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
79 * </ul>
80 */
81public class SurfaceView extends View {
82    static private final String TAG = "SurfaceView";
83    static private final boolean DEBUG = false;
84
85    final ArrayList<SurfaceHolder.Callback> mCallbacks
86            = new ArrayList<SurfaceHolder.Callback>();
87
88    final int[] mLocation = new int[2];
89
90    final ReentrantLock mSurfaceLock = new ReentrantLock();
91    final Surface mSurface = new Surface();       // Current surface in use
92    final Surface mNewSurface = new Surface();    // New surface we are switching to
93    boolean mDrawingStopped = true;
94
95    final WindowManager.LayoutParams mLayout
96            = new WindowManager.LayoutParams();
97    IWindowSession mSession;
98    MyWindow mWindow;
99    final Rect mVisibleInsets = new Rect();
100    final Rect mWinFrame = new Rect();
101    final Rect mSystemInsets = new Rect();
102    final Rect mContentInsets = new Rect();
103    final Configuration mConfiguration = new Configuration();
104
105    static final int KEEP_SCREEN_ON_MSG = 1;
106    static final int GET_NEW_SURFACE_MSG = 2;
107    static final int UPDATE_WINDOW_MSG = 3;
108
109    int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
110
111    boolean mIsCreating = false;
112
113    final Handler mHandler = new Handler() {
114        @Override
115        public void handleMessage(Message msg) {
116            switch (msg.what) {
117                case KEEP_SCREEN_ON_MSG: {
118                    setKeepScreenOn(msg.arg1 != 0);
119                } break;
120                case GET_NEW_SURFACE_MSG: {
121                    handleGetNewSurface();
122                } break;
123                case UPDATE_WINDOW_MSG: {
124                    updateWindow(false, false);
125                } break;
126            }
127        }
128    };
129
130    final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
131            = new ViewTreeObserver.OnScrollChangedListener() {
132                    public void onScrollChanged() {
133                        updateWindow(false, false);
134                    }
135            };
136
137    boolean mRequestedVisible = false;
138    boolean mWindowVisibility = false;
139    boolean mViewVisibility = false;
140    int mRequestedWidth = -1;
141    int mRequestedHeight = -1;
142    /* Set SurfaceView's format to 565 by default to maintain backward
143     * compatibility with applications assuming this format.
144     */
145    int mRequestedFormat = PixelFormat.RGB_565;
146
147    boolean mHaveFrame = false;
148    boolean mSurfaceCreated = false;
149    long mLastLockTime = 0;
150
151    boolean mVisible = false;
152    int mLeft = -1;
153    int mTop = -1;
154    int mWidth = -1;
155    int mHeight = -1;
156    int mFormat = -1;
157    final Rect mSurfaceFrame = new Rect();
158    Rect mTmpDirty;
159    int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
160    boolean mUpdateWindowNeeded;
161    boolean mReportDrawNeeded;
162    private Translator mTranslator;
163
164    private final ViewTreeObserver.OnPreDrawListener mDrawListener =
165            new ViewTreeObserver.OnPreDrawListener() {
166                @Override
167                public boolean onPreDraw() {
168                    // reposition ourselves where the surface is
169                    mHaveFrame = getWidth() > 0 && getHeight() > 0;
170                    updateWindow(false, false);
171                    return true;
172                }
173            };
174    private boolean mGlobalListenersAdded;
175
176    public SurfaceView(Context context) {
177        super(context);
178        init();
179    }
180
181    public SurfaceView(Context context, AttributeSet attrs) {
182        super(context, attrs);
183        init();
184    }
185
186    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
187        super(context, attrs, defStyle);
188        init();
189    }
190
191    private void init() {
192        setWillNotDraw(true);
193    }
194
195    /**
196     * Return the SurfaceHolder providing access and control over this
197     * SurfaceView's underlying surface.
198     *
199     * @return SurfaceHolder The holder of the surface.
200     */
201    public SurfaceHolder getHolder() {
202        return mSurfaceHolder;
203    }
204
205    @Override
206    protected void onAttachedToWindow() {
207        super.onAttachedToWindow();
208        mParent.requestTransparentRegion(this);
209        mSession = getWindowSession();
210        mLayout.token = getWindowToken();
211        mLayout.setTitle("SurfaceView");
212        mViewVisibility = getVisibility() == VISIBLE;
213
214        if (!mGlobalListenersAdded) {
215            ViewTreeObserver observer = getViewTreeObserver();
216            observer.addOnScrollChangedListener(mScrollChangedListener);
217            observer.addOnPreDrawListener(mDrawListener);
218            mGlobalListenersAdded = true;
219        }
220    }
221
222    @Override
223    protected void onWindowVisibilityChanged(int visibility) {
224        super.onWindowVisibilityChanged(visibility);
225        mWindowVisibility = visibility == VISIBLE;
226        mRequestedVisible = mWindowVisibility && mViewVisibility;
227        updateWindow(false, false);
228    }
229
230    @Override
231    public void setVisibility(int visibility) {
232        super.setVisibility(visibility);
233        mViewVisibility = visibility == VISIBLE;
234        boolean newRequestedVisible = mWindowVisibility && mViewVisibility;
235        if (newRequestedVisible != mRequestedVisible) {
236            // our base class (View) invalidates the layout only when
237            // we go from/to the GONE state. However, SurfaceView needs
238            // to request a re-layout when the visibility changes at all.
239            // This is needed because the transparent region is computed
240            // as part of the layout phase, and it changes (obviously) when
241            // the visibility changes.
242            requestLayout();
243        }
244        mRequestedVisible = newRequestedVisible;
245        updateWindow(false, false);
246    }
247
248    @Override
249    protected void onDetachedFromWindow() {
250        if (mGlobalListenersAdded) {
251            ViewTreeObserver observer = getViewTreeObserver();
252            observer.removeOnScrollChangedListener(mScrollChangedListener);
253            observer.removeOnPreDrawListener(mDrawListener);
254            mGlobalListenersAdded = false;
255        }
256
257        mRequestedVisible = false;
258        updateWindow(false, false);
259        mHaveFrame = false;
260        if (mWindow != null) {
261            try {
262                mSession.remove(mWindow);
263            } catch (RemoteException ex) {
264                // Not much we can do here...
265            }
266            mWindow = null;
267        }
268        mSession = null;
269        mLayout.token = null;
270
271        super.onDetachedFromWindow();
272    }
273
274    @Override
275    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
276        int width = mRequestedWidth >= 0
277                ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
278                : getDefaultSize(0, widthMeasureSpec);
279        int height = mRequestedHeight >= 0
280                ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
281                : getDefaultSize(0, heightMeasureSpec);
282        setMeasuredDimension(width, height);
283    }
284
285    /** @hide */
286    @Override
287    protected boolean setFrame(int left, int top, int right, int bottom) {
288        boolean result = super.setFrame(left, top, right, bottom);
289        updateWindow(false, false);
290        return result;
291    }
292
293    @Override
294    public boolean gatherTransparentRegion(Region region) {
295        if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
296            return super.gatherTransparentRegion(region);
297        }
298
299        boolean opaque = true;
300        if ((mPrivateFlags & SKIP_DRAW) == 0) {
301            // this view draws, remove it from the transparent region
302            opaque = super.gatherTransparentRegion(region);
303        } else if (region != null) {
304            int w = getWidth();
305            int h = getHeight();
306            if (w>0 && h>0) {
307                getLocationInWindow(mLocation);
308                // otherwise, punch a hole in the whole hierarchy
309                int l = mLocation[0];
310                int t = mLocation[1];
311                region.op(l, t, l+w, t+h, Region.Op.UNION);
312            }
313        }
314        if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
315            opaque = false;
316        }
317        return opaque;
318    }
319
320    @Override
321    public void draw(Canvas canvas) {
322        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
323            // draw() is not called when SKIP_DRAW is set
324            if ((mPrivateFlags & SKIP_DRAW) == 0) {
325                // punch a whole in the view-hierarchy below us
326                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
327            }
328        }
329        super.draw(canvas);
330    }
331
332    @Override
333    protected void dispatchDraw(Canvas canvas) {
334        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
335            // if SKIP_DRAW is cleared, draw() has already punched a hole
336            if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
337                // punch a whole in the view-hierarchy below us
338                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
339            }
340        }
341        super.dispatchDraw(canvas);
342    }
343
344    /**
345     * Control whether the surface view's surface is placed on top of another
346     * regular surface view in the window (but still behind the window itself).
347     * This is typically used to place overlays on top of an underlying media
348     * surface view.
349     *
350     * <p>Note that this must be set before the surface view's containing
351     * window is attached to the window manager.
352     *
353     * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
354     */
355    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
356        mWindowType = isMediaOverlay
357                ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
358                : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
359    }
360
361    /**
362     * Control whether the surface view's surface is placed on top of its
363     * window.  Normally it is placed behind the window, to allow it to
364     * (for the most part) appear to composite with the views in the
365     * hierarchy.  By setting this, you cause it to be placed above the
366     * window.  This means that none of the contents of the window this
367     * SurfaceView is in will be visible on top of its surface.
368     *
369     * <p>Note that this must be set before the surface view's containing
370     * window is attached to the window manager.
371     *
372     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
373     */
374    public void setZOrderOnTop(boolean onTop) {
375        if (onTop) {
376            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
377            // ensures the surface is placed below the IME
378            mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
379        } else {
380            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
381            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
382        }
383    }
384
385    /**
386     * Hack to allow special layering of windows.  The type is one of the
387     * types in WindowManager.LayoutParams.  This is a hack so:
388     * @hide
389     */
390    public void setWindowType(int type) {
391        mWindowType = type;
392    }
393
394    private void updateWindow(boolean force, boolean redrawNeeded) {
395        if (!mHaveFrame) {
396            return;
397        }
398        ViewRootImpl viewRoot = getViewRootImpl();
399        if (viewRoot != null) {
400            mTranslator = viewRoot.mTranslator;
401        }
402
403        if (mTranslator != null) {
404            mSurface.setCompatibilityTranslator(mTranslator);
405        }
406
407        int myWidth = mRequestedWidth;
408        if (myWidth <= 0) myWidth = getWidth();
409        int myHeight = mRequestedHeight;
410        if (myHeight <= 0) myHeight = getHeight();
411
412        getLocationInWindow(mLocation);
413        final boolean creating = mWindow == null;
414        final boolean formatChanged = mFormat != mRequestedFormat;
415        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
416        final boolean visibleChanged = mVisible != mRequestedVisible;
417
418        if (force || creating || formatChanged || sizeChanged || visibleChanged
419            || mLeft != mLocation[0] || mTop != mLocation[1]
420            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
421
422            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
423                    + " format=" + formatChanged + " size=" + sizeChanged
424                    + " visible=" + visibleChanged
425                    + " left=" + (mLeft != mLocation[0])
426                    + " top=" + (mTop != mLocation[1]));
427
428            try {
429                final boolean visible = mVisible = mRequestedVisible;
430                mLeft = mLocation[0];
431                mTop = mLocation[1];
432                mWidth = myWidth;
433                mHeight = myHeight;
434                mFormat = mRequestedFormat;
435
436                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
437
438                // Places the window relative
439                mLayout.x = mLeft;
440                mLayout.y = mTop;
441                mLayout.width = getWidth();
442                mLayout.height = getHeight();
443                if (mTranslator != null) {
444                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
445                }
446
447                mLayout.format = mRequestedFormat;
448                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
449                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
450                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
451                              | WindowManager.LayoutParams.FLAG_SCALED
452                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
453                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
454                              ;
455                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
456                    mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
457                }
458
459                if (mWindow == null) {
460                    mWindow = new MyWindow(this);
461                    mLayout.type = mWindowType;
462                    mLayout.gravity = Gravity.LEFT|Gravity.TOP;
463                    mSession.addWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
464                            mVisible ? VISIBLE : GONE, mContentInsets);
465                }
466
467                boolean realSizeChanged;
468                boolean reportDrawNeeded;
469
470                int relayoutResult;
471
472                mSurfaceLock.lock();
473                try {
474                    mUpdateWindowNeeded = false;
475                    reportDrawNeeded = mReportDrawNeeded;
476                    mReportDrawNeeded = false;
477                    mDrawingStopped = !visible;
478
479                    if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
480
481                    relayoutResult = mSession.relayout(
482                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
483                            visible ? VISIBLE : GONE,
484                            WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY,
485                            mWinFrame, mSystemInsets, mContentInsets,
486                            mVisibleInsets, mConfiguration, mNewSurface);
487                    if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
488                        mReportDrawNeeded = true;
489                    }
490
491                    if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
492                            + ", vis=" + visible + ", frame=" + mWinFrame);
493
494                    mSurfaceFrame.left = 0;
495                    mSurfaceFrame.top = 0;
496                    if (mTranslator == null) {
497                        mSurfaceFrame.right = mWinFrame.width();
498                        mSurfaceFrame.bottom = mWinFrame.height();
499                    } else {
500                        float appInvertedScale = mTranslator.applicationInvertedScale;
501                        mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
502                        mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
503                    }
504
505                    final int surfaceWidth = mSurfaceFrame.right;
506                    final int surfaceHeight = mSurfaceFrame.bottom;
507                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
508                            || mLastSurfaceHeight != surfaceHeight;
509                    mLastSurfaceWidth = surfaceWidth;
510                    mLastSurfaceHeight = surfaceHeight;
511                } finally {
512                    mSurfaceLock.unlock();
513                }
514
515                try {
516                    redrawNeeded |= creating | reportDrawNeeded;
517
518                    SurfaceHolder.Callback callbacks[] = null;
519
520                    final boolean surfaceChanged =
521                            (relayoutResult&WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED) != 0;
522                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
523                        mSurfaceCreated = false;
524                        if (mSurface.isValid()) {
525                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
526                            callbacks = getSurfaceCallbacks();
527                            for (SurfaceHolder.Callback c : callbacks) {
528                                c.surfaceDestroyed(mSurfaceHolder);
529                            }
530                        }
531                    }
532
533                    mSurface.transferFrom(mNewSurface);
534
535                    if (visible) {
536                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
537                            mSurfaceCreated = true;
538                            mIsCreating = true;
539                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
540                            if (callbacks == null) {
541                                callbacks = getSurfaceCallbacks();
542                            }
543                            for (SurfaceHolder.Callback c : callbacks) {
544                                c.surfaceCreated(mSurfaceHolder);
545                            }
546                        }
547                        if (creating || formatChanged || sizeChanged
548                                || visibleChanged || realSizeChanged) {
549                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
550                                    + " w=" + myWidth + " h=" + myHeight);
551                            if (callbacks == null) {
552                                callbacks = getSurfaceCallbacks();
553                            }
554                            for (SurfaceHolder.Callback c : callbacks) {
555                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
556                            }
557                        }
558                        if (redrawNeeded) {
559                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
560                            if (callbacks == null) {
561                                callbacks = getSurfaceCallbacks();
562                            }
563                            for (SurfaceHolder.Callback c : callbacks) {
564                                if (c instanceof SurfaceHolder.Callback2) {
565                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
566                                            mSurfaceHolder);
567                                }
568                            }
569                        }
570                    }
571                } finally {
572                    mIsCreating = false;
573                    if (redrawNeeded) {
574                        if (DEBUG) Log.i(TAG, "finishedDrawing");
575                        mSession.finishDrawing(mWindow);
576                    }
577                    mSession.performDeferredDestroy(mWindow);
578                }
579            } catch (RemoteException ex) {
580            }
581            if (DEBUG) Log.v(
582                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
583                " w=" + mLayout.width + " h=" + mLayout.height +
584                ", frame=" + mSurfaceFrame);
585        }
586    }
587
588    private SurfaceHolder.Callback[] getSurfaceCallbacks() {
589        SurfaceHolder.Callback callbacks[];
590        synchronized (mCallbacks) {
591            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
592            mCallbacks.toArray(callbacks);
593        }
594        return callbacks;
595    }
596
597    void handleGetNewSurface() {
598        updateWindow(false, false);
599    }
600
601    /**
602     * Check to see if the surface has fixed size dimensions or if the surface's
603     * dimensions are dimensions are dependent on its current layout.
604     *
605     * @return true if the surface has dimensions that are fixed in size
606     * @hide
607     */
608    public boolean isFixedSize() {
609        return (mRequestedWidth != -1 || mRequestedHeight != -1);
610    }
611
612    private static class MyWindow extends BaseIWindow {
613        private final WeakReference<SurfaceView> mSurfaceView;
614
615        public MyWindow(SurfaceView surfaceView) {
616            mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
617        }
618
619        public void resized(int w, int h, Rect systemInsets, Rect contentInsets,
620                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
621            SurfaceView surfaceView = mSurfaceView.get();
622            if (surfaceView != null) {
623                if (DEBUG) Log.v(
624                        "SurfaceView", surfaceView + " got resized: w=" +
625                                w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
626                surfaceView.mSurfaceLock.lock();
627                try {
628                    if (reportDraw) {
629                        surfaceView.mUpdateWindowNeeded = true;
630                        surfaceView.mReportDrawNeeded = true;
631                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
632                    } else if (surfaceView.mWinFrame.width() != w
633                            || surfaceView.mWinFrame.height() != h) {
634                        surfaceView.mUpdateWindowNeeded = true;
635                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
636                    }
637                } finally {
638                    surfaceView.mSurfaceLock.unlock();
639                }
640            }
641        }
642
643        public void dispatchAppVisibility(boolean visible) {
644            // The point of SurfaceView is to let the app control the surface.
645        }
646
647        public void dispatchGetNewSurface() {
648            SurfaceView surfaceView = mSurfaceView.get();
649            if (surfaceView != null) {
650                Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
651                surfaceView.mHandler.sendMessage(msg);
652            }
653        }
654
655        public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
656            Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
657        }
658
659        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
660        }
661
662        int mCurWidth = -1;
663        int mCurHeight = -1;
664    }
665
666    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
667
668        private static final String LOG_TAG = "SurfaceHolder";
669
670        public boolean isCreating() {
671            return mIsCreating;
672        }
673
674        public void addCallback(Callback callback) {
675            synchronized (mCallbacks) {
676                // This is a linear search, but in practice we'll
677                // have only a couple callbacks, so it doesn't matter.
678                if (mCallbacks.contains(callback) == false) {
679                    mCallbacks.add(callback);
680                }
681            }
682        }
683
684        public void removeCallback(Callback callback) {
685            synchronized (mCallbacks) {
686                mCallbacks.remove(callback);
687            }
688        }
689
690        public void setFixedSize(int width, int height) {
691            if (mRequestedWidth != width || mRequestedHeight != height) {
692                mRequestedWidth = width;
693                mRequestedHeight = height;
694                requestLayout();
695            }
696        }
697
698        public void setSizeFromLayout() {
699            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
700                mRequestedWidth = mRequestedHeight = -1;
701                requestLayout();
702            }
703        }
704
705        public void setFormat(int format) {
706
707            // for backward compatibility reason, OPAQUE always
708            // means 565 for SurfaceView
709            if (format == PixelFormat.OPAQUE)
710                format = PixelFormat.RGB_565;
711
712            mRequestedFormat = format;
713            if (mWindow != null) {
714                updateWindow(false, false);
715            }
716        }
717
718        /**
719         * @deprecated setType is now ignored.
720         */
721        @Deprecated
722        public void setType(int type) { }
723
724        public void setKeepScreenOn(boolean screenOn) {
725            Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
726            msg.arg1 = screenOn ? 1 : 0;
727            mHandler.sendMessage(msg);
728        }
729
730        public Canvas lockCanvas() {
731            return internalLockCanvas(null);
732        }
733
734        public Canvas lockCanvas(Rect dirty) {
735            return internalLockCanvas(dirty);
736        }
737
738        private final Canvas internalLockCanvas(Rect dirty) {
739            mSurfaceLock.lock();
740
741            if (DEBUG) Log.i(TAG, "Locking canvas... stopped="
742                    + mDrawingStopped + ", win=" + mWindow);
743
744            Canvas c = null;
745            if (!mDrawingStopped && mWindow != null) {
746                if (dirty == null) {
747                    if (mTmpDirty == null) {
748                        mTmpDirty = new Rect();
749                    }
750                    mTmpDirty.set(mSurfaceFrame);
751                    dirty = mTmpDirty;
752                }
753
754                try {
755                    c = mSurface.lockCanvas(dirty);
756                } catch (Exception e) {
757                    Log.e(LOG_TAG, "Exception locking surface", e);
758                }
759            }
760
761            if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
762            if (c != null) {
763                mLastLockTime = SystemClock.uptimeMillis();
764                return c;
765            }
766
767            // If the Surface is not ready to be drawn, then return null,
768            // but throttle calls to this function so it isn't called more
769            // than every 100ms.
770            long now = SystemClock.uptimeMillis();
771            long nextTime = mLastLockTime + 100;
772            if (nextTime > now) {
773                try {
774                    Thread.sleep(nextTime-now);
775                } catch (InterruptedException e) {
776                }
777                now = SystemClock.uptimeMillis();
778            }
779            mLastLockTime = now;
780            mSurfaceLock.unlock();
781
782            return null;
783        }
784
785        public void unlockCanvasAndPost(Canvas canvas) {
786            mSurface.unlockCanvasAndPost(canvas);
787            mSurfaceLock.unlock();
788        }
789
790        public Surface getSurface() {
791            return mSurface;
792        }
793
794        public Rect getSurfaceFrame() {
795            return mSurfaceFrame;
796        }
797    };
798}
799