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