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