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