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