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