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