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