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