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