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