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