SurfaceView.java revision 5be8de3420ba4c9d816b98e29bdec11715f6b626
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.Config;
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 : Config.LOGV;
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        ViewRoot viewRoot = (ViewRoot) getRootView().getParent();
431        if (viewRoot != null) {
432            mTranslator = viewRoot.mTranslator;
433        }
434
435        if (mTranslator != null) {
436            mSurface.setCompatibilityTranslator(mTranslator);
437        }
438
439        int myWidth = mRequestedWidth;
440        if (myWidth <= 0) myWidth = getWidth();
441        int myHeight = mRequestedHeight;
442        if (myHeight <= 0) myHeight = getHeight();
443
444        getLocationInWindow(mLocation);
445        final boolean creating = mWindow == null;
446        final boolean formatChanged = mFormat != mRequestedFormat;
447        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
448        final boolean visibleChanged = mVisible != mRequestedVisible
449                || mNewSurfaceNeeded;
450
451        if (force || creating || formatChanged || sizeChanged || visibleChanged
452            || mLeft != mLocation[0] || mTop != mLocation[1]
453            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
454
455            if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
456                    + " format=" + formatChanged + " size=" + sizeChanged
457                    + " visible=" + visibleChanged
458                    + " left=" + (mLeft != mLocation[0])
459                    + " top=" + (mTop != mLocation[1]));
460
461            try {
462                final boolean visible = mVisible = mRequestedVisible;
463                mLeft = mLocation[0];
464                mTop = mLocation[1];
465                mWidth = myWidth;
466                mHeight = myHeight;
467                mFormat = mRequestedFormat;
468
469                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
470
471                // Places the window relative
472                mLayout.x = mLeft;
473                mLayout.y = mTop;
474                mLayout.width = getWidth();
475                mLayout.height = getHeight();
476                if (mTranslator != null) {
477                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
478                }
479
480                mLayout.format = mRequestedFormat;
481                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
482                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
483                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
484                              | WindowManager.LayoutParams.FLAG_SCALED
485                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
486                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
487                              ;
488                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
489                    mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
490                }
491
492                if (mWindow == null) {
493                    mWindow = new MyWindow(this);
494                    mLayout.type = mWindowType;
495                    mLayout.gravity = Gravity.LEFT|Gravity.TOP;
496                    mSession.addWithoutInputChannel(mWindow, mLayout,
497                            mVisible ? VISIBLE : GONE, mContentInsets);
498                }
499
500                if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
501                    reportSurfaceDestroyed();
502                }
503
504                mNewSurfaceNeeded = false;
505
506                boolean realSizeChanged;
507                boolean reportDrawNeeded;
508
509                mSurfaceLock.lock();
510                try {
511                    mUpdateWindowNeeded = false;
512                    reportDrawNeeded = mReportDrawNeeded;
513                    mReportDrawNeeded = false;
514                    mDrawingStopped = !visible;
515
516                    final int relayoutResult = mSession.relayout(
517                        mWindow, mLayout, mWidth, mHeight,
518                            visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
519                            mVisibleInsets, mConfiguration, mSurface);
520                    if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
521                        mReportDrawNeeded = true;
522                    }
523
524                    if (localLOGV) Log.i(TAG, "New surface: " + mSurface
525                            + ", vis=" + visible + ", frame=" + mWinFrame);
526
527                    mSurfaceFrame.left = 0;
528                    mSurfaceFrame.top = 0;
529                    if (mTranslator == null) {
530                        mSurfaceFrame.right = mWinFrame.width();
531                        mSurfaceFrame.bottom = mWinFrame.height();
532                    } else {
533                        float appInvertedScale = mTranslator.applicationInvertedScale;
534                        mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
535                        mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
536                    }
537
538                    final int surfaceWidth = mSurfaceFrame.right;
539                    final int surfaceHeight = mSurfaceFrame.bottom;
540                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
541                            || mLastSurfaceHeight != surfaceHeight;
542                    mLastSurfaceWidth = surfaceWidth;
543                    mLastSurfaceHeight = surfaceHeight;
544                } finally {
545                    mSurfaceLock.unlock();
546                }
547
548                try {
549                    redrawNeeded |= creating | reportDrawNeeded;
550
551                    if (visible) {
552                        mDestroyReportNeeded = true;
553
554                        SurfaceHolder.Callback callbacks[];
555                        synchronized (mCallbacks) {
556                            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
557                            mCallbacks.toArray(callbacks);
558                        }
559
560                        if (visibleChanged) {
561                            mIsCreating = true;
562                            for (SurfaceHolder.Callback c : callbacks) {
563                                c.surfaceCreated(mSurfaceHolder);
564                            }
565                        }
566                        if (creating || formatChanged || sizeChanged
567                                || visibleChanged || realSizeChanged) {
568                            for (SurfaceHolder.Callback c : callbacks) {
569                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
570                            }
571                        }
572                        if (redrawNeeded) {
573                            for (SurfaceHolder.Callback c : callbacks) {
574                                if (c instanceof SurfaceHolder.Callback2) {
575                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
576                                            mSurfaceHolder);
577                                }
578                            }
579                        }
580                    } else {
581                        mSurface.release();
582                    }
583                } finally {
584                    mIsCreating = false;
585                    if (redrawNeeded) {
586                        mSession.finishDrawing(mWindow);
587                    }
588                }
589            } catch (RemoteException ex) {
590            }
591            if (localLOGV) Log.v(
592                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
593                " w=" + mLayout.width + " h=" + mLayout.height +
594                ", frame=" + mSurfaceFrame);
595        }
596    }
597
598    private void reportSurfaceDestroyed() {
599        if (mDestroyReportNeeded) {
600            mDestroyReportNeeded = false;
601            SurfaceHolder.Callback callbacks[];
602            synchronized (mCallbacks) {
603                callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
604                mCallbacks.toArray(callbacks);
605            }
606            for (SurfaceHolder.Callback c : callbacks) {
607                c.surfaceDestroyed(mSurfaceHolder);
608            }
609        }
610        super.onDetachedFromWindow();
611    }
612
613    void handleGetNewSurface() {
614        mNewSurfaceNeeded = true;
615        updateWindow(false, false);
616    }
617
618    /**
619     * Check to see if the surface has fixed size dimensions or if the surface's
620     * dimensions are dimensions are dependent on its current layout.
621     *
622     * @return true if the surface has dimensions that are fixed in size
623     * @hide
624     */
625    public boolean isFixedSize() {
626        return (mRequestedWidth != -1 || mRequestedHeight != -1);
627    }
628
629    private static class MyWindow extends BaseIWindow {
630        private final WeakReference<SurfaceView> mSurfaceView;
631
632        public MyWindow(SurfaceView surfaceView) {
633            mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
634        }
635
636        public void resized(int w, int h, Rect coveredInsets,
637                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
638            SurfaceView surfaceView = mSurfaceView.get();
639            if (surfaceView != null) {
640                if (localLOGV) Log.v(
641                        "SurfaceView", surfaceView + " got resized: w=" +
642                                w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
643                surfaceView.mSurfaceLock.lock();
644                try {
645                    if (reportDraw) {
646                        surfaceView.mUpdateWindowNeeded = true;
647                        surfaceView.mReportDrawNeeded = true;
648                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
649                    } else if (surfaceView.mWinFrame.width() != w
650                            || surfaceView.mWinFrame.height() != h) {
651                        surfaceView.mUpdateWindowNeeded = true;
652                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
653                    }
654                } finally {
655                    surfaceView.mSurfaceLock.unlock();
656                }
657            }
658        }
659
660        public void dispatchAppVisibility(boolean visible) {
661            // The point of SurfaceView is to let the app control the surface.
662        }
663
664        public void dispatchGetNewSurface() {
665            SurfaceView surfaceView = mSurfaceView.get();
666            if (surfaceView != null) {
667                Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
668                surfaceView.mHandler.sendMessage(msg);
669            }
670        }
671
672        public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
673            Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
674        }
675
676        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
677        }
678
679        int mCurWidth = -1;
680        int mCurHeight = -1;
681    }
682
683    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
684
685        private static final String LOG_TAG = "SurfaceHolder";
686
687        public boolean isCreating() {
688            return mIsCreating;
689        }
690
691        public void addCallback(Callback callback) {
692            synchronized (mCallbacks) {
693                // This is a linear search, but in practice we'll
694                // have only a couple callbacks, so it doesn't matter.
695                if (mCallbacks.contains(callback) == false) {
696                    mCallbacks.add(callback);
697                }
698            }
699        }
700
701        public void removeCallback(Callback callback) {
702            synchronized (mCallbacks) {
703                mCallbacks.remove(callback);
704            }
705        }
706
707        public void setFixedSize(int width, int height) {
708            if (mRequestedWidth != width || mRequestedHeight != height) {
709                mRequestedWidth = width;
710                mRequestedHeight = height;
711                requestLayout();
712            }
713        }
714
715        public void setSizeFromLayout() {
716            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
717                mRequestedWidth = mRequestedHeight = -1;
718                requestLayout();
719            }
720        }
721
722        public void setFormat(int format) {
723
724            // for backward compatibility reason, OPAQUE always
725            // means 565 for SurfaceView
726            if (format == PixelFormat.OPAQUE)
727                format = PixelFormat.RGB_565;
728
729            mRequestedFormat = format;
730            if (mWindow != null) {
731                updateWindow(false, false);
732            }
733        }
734
735        /**
736         * @deprecated setType is now ignored.
737         */
738        @Deprecated
739        public void setType(int type) { }
740
741        public void setKeepScreenOn(boolean screenOn) {
742            Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
743            msg.arg1 = screenOn ? 1 : 0;
744            mHandler.sendMessage(msg);
745        }
746
747        public Canvas lockCanvas() {
748            return internalLockCanvas(null);
749        }
750
751        public Canvas lockCanvas(Rect dirty) {
752            return internalLockCanvas(dirty);
753        }
754
755        private final Canvas internalLockCanvas(Rect dirty) {
756            mSurfaceLock.lock();
757
758            if (localLOGV) Log.i(TAG, "Locking canvas... stopped="
759                    + mDrawingStopped + ", win=" + mWindow);
760
761            Canvas c = null;
762            if (!mDrawingStopped && mWindow != null) {
763                if (dirty == null) {
764                    if (mTmpDirty == null) {
765                        mTmpDirty = new Rect();
766                    }
767                    mTmpDirty.set(mSurfaceFrame);
768                    dirty = mTmpDirty;
769                }
770
771                try {
772                    c = mSurface.lockCanvas(dirty);
773                } catch (Exception e) {
774                    Log.e(LOG_TAG, "Exception locking surface", e);
775                }
776            }
777
778            if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
779            if (c != null) {
780                mLastLockTime = SystemClock.uptimeMillis();
781                return c;
782            }
783
784            // If the Surface is not ready to be drawn, then return null,
785            // but throttle calls to this function so it isn't called more
786            // than every 100ms.
787            long now = SystemClock.uptimeMillis();
788            long nextTime = mLastLockTime + 100;
789            if (nextTime > now) {
790                try {
791                    Thread.sleep(nextTime-now);
792                } catch (InterruptedException e) {
793                }
794                now = SystemClock.uptimeMillis();
795            }
796            mLastLockTime = now;
797            mSurfaceLock.unlock();
798
799            return null;
800        }
801
802        public void unlockCanvasAndPost(Canvas canvas) {
803            mSurface.unlockCanvasAndPost(canvas);
804            mSurfaceLock.unlock();
805        }
806
807        public Surface getSurface() {
808            return mSurface;
809        }
810
811        public Rect getSurfaceFrame() {
812            return mSurfaceFrame;
813        }
814    };
815}
816