blob: 5d0f523a5f9c5dcf95f448b7d8638cf125198142 [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001/*
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
Dianne Hackborn72c82ab2009-08-11 21:13:54 -070019import com.android.internal.view.BaseIWindow;
20
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070021import android.content.Context;
Dianne Hackborne36d6e22010-02-17 19:46:25 -080022import android.content.res.Configuration;
Mitsuru Oshima64f59342009-06-21 00:03:11 -070023import android.content.res.CompatibilityInfo.Translator;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070024import android.graphics.Canvas;
25import android.graphics.PixelFormat;
26import android.graphics.PorterDuff;
27import android.graphics.Rect;
28import android.graphics.Region;
29import android.os.Handler;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.SystemClock;
33import android.os.ParcelFileDescriptor;
34import android.util.AttributeSet;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070035import android.util.Log;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070036
Jon Larimer9bdf5762009-01-02 18:55:15 -050037import java.lang.ref.WeakReference;
38import java.util.ArrayList;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070039import java.util.concurrent.locks.ReentrantLock;
40
41/**
42 * Provides a dedicated drawing surface embedded inside of a view hierarchy.
43 * You can control the format of this surface and, if you like, its size; the
44 * SurfaceView takes care of placing the surface at the correct location on the
45 * screen
46 *
47 * <p>The surface is Z ordered so that it is behind the window holding its
48 * SurfaceView; the SurfaceView punches a hole in its window to allow its
Jesse Hallc9f345f2012-09-26 11:55:16 -070049 * surface to be displayed. The view hierarchy will take care of correctly
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070050 * compositing with the Surface any siblings of the SurfaceView that would
Jesse Hallc9f345f2012-09-26 11:55:16 -070051 * normally appear on top of it. This can be used to place overlays such as
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070052 * buttons on top of the Surface, though note however that it can have an
53 * impact on performance since a full alpha-blended composite will be performed
54 * each time the Surface changes.
55 *
Jesse Hallc9f345f2012-09-26 11:55:16 -070056 * <p> The transparent region that makes the surface visible is based on the
57 * layout positions in the view hierarchy. If the post-layout transform
58 * properties are used to draw a sibling view on top of the SurfaceView, the
59 * view may not be properly composited with the surface.
60 *
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070061 * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
62 * which can be retrieved by calling {@link #getHolder}.
63 *
64 * <p>The Surface will be created for you while the SurfaceView's window is
65 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
66 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
67 * Surface is created and destroyed as the window is shown and hidden.
68 *
69 * <p>One of the purposes of this class is to provide a surface in which a
Jesse Hallc9f345f2012-09-26 11:55:16 -070070 * secondary thread can render into the screen. If you are going to use it
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070071 * this way, you need to be aware of some threading semantics:
72 *
73 * <ul>
74 * <li> All SurfaceView and
75 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
76 * from the thread running the SurfaceView's window (typically the main thread
Jesse Hallc9f345f2012-09-26 11:55:16 -070077 * of the application). They thus need to correctly synchronize with any
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070078 * state that is also touched by the drawing thread.
79 * <li> You must ensure that the drawing thread only touches the underlying
80 * Surface while it is valid -- between
81 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
82 * and
83 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
84 * </ul>
85 */
86public class SurfaceView extends View {
87 static private final String TAG = "SurfaceView";
88 static private final boolean DEBUG = false;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070089
90 final ArrayList<SurfaceHolder.Callback> mCallbacks
91 = new ArrayList<SurfaceHolder.Callback>();
92
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080093 final int[] mLocation = new int[2];
94
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070095 final ReentrantLock mSurfaceLock = new ReentrantLock();
Dianne Hackborn61566cc2011-12-02 23:31:52 -080096 final Surface mSurface = new Surface(); // Current surface in use
97 final Surface mNewSurface = new Surface(); // New surface we are switching to
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070098 boolean mDrawingStopped = true;
99
100 final WindowManager.LayoutParams mLayout
101 = new WindowManager.LayoutParams();
102 IWindowSession mSession;
103 MyWindow mWindow;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800104 final Rect mVisibleInsets = new Rect();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700105 final Rect mWinFrame = new Rect();
Dianne Hackbornc4aad012013-02-22 15:05:25 -0800106 final Rect mOverscanInsets = new Rect();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800107 final Rect mContentInsets = new Rect();
Dianne Hackborn694f79b2010-03-17 19:44:59 -0700108 final Configuration mConfiguration = new Configuration();
109
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700110 static final int KEEP_SCREEN_ON_MSG = 1;
111 static final int GET_NEW_SURFACE_MSG = 2;
Dianne Hackborn726426e2010-03-31 22:04:36 -0700112 static final int UPDATE_WINDOW_MSG = 3;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700113
Dianne Hackbornc4d5d022009-05-21 17:32:42 -0700114 int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
115
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700116 boolean mIsCreating = false;
117
118 final Handler mHandler = new Handler() {
119 @Override
120 public void handleMessage(Message msg) {
121 switch (msg.what) {
122 case KEEP_SCREEN_ON_MSG: {
123 setKeepScreenOn(msg.arg1 != 0);
124 } break;
125 case GET_NEW_SURFACE_MSG: {
126 handleGetNewSurface();
127 } break;
Dianne Hackborn726426e2010-03-31 22:04:36 -0700128 case UPDATE_WINDOW_MSG: {
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700129 updateWindow(false, false);
Dianne Hackborn726426e2010-03-31 22:04:36 -0700130 } break;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700131 }
132 }
133 };
134
Dianne Hackborne2af5c82010-03-18 15:44:34 -0700135 final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
136 = new ViewTreeObserver.OnScrollChangedListener() {
137 public void onScrollChanged() {
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700138 updateWindow(false, false);
Dianne Hackborne2af5c82010-03-18 15:44:34 -0700139 }
140 };
141
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700142 boolean mRequestedVisible = false;
Mathias Agopian6b7f1a62009-09-09 18:32:34 -0700143 boolean mWindowVisibility = false;
144 boolean mViewVisibility = false;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700145 int mRequestedWidth = -1;
146 int mRequestedHeight = -1;
Mathias Agopiand6ddcb72010-05-24 19:00:08 -0700147 /* Set SurfaceView's format to 565 by default to maintain backward
148 * compatibility with applications assuming this format.
149 */
150 int mRequestedFormat = PixelFormat.RGB_565;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700151
152 boolean mHaveFrame = false;
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800153 boolean mSurfaceCreated = false;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700154 long mLastLockTime = 0;
155
156 boolean mVisible = false;
157 int mLeft = -1;
158 int mTop = -1;
159 int mWidth = -1;
160 int mHeight = -1;
161 int mFormat = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700162 final Rect mSurfaceFrame = new Rect();
Jeff Brown30bc34f2011-01-25 12:56:56 -0800163 Rect mTmpDirty;
Dianne Hackborn726426e2010-03-31 22:04:36 -0700164 int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
165 boolean mUpdateWindowNeeded;
166 boolean mReportDrawNeeded;
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700167 private Translator mTranslator;
Romain Guyf2499fa2011-01-26 18:31:23 -0800168
169 private final ViewTreeObserver.OnPreDrawListener mDrawListener =
170 new ViewTreeObserver.OnPreDrawListener() {
171 @Override
172 public boolean onPreDraw() {
173 // reposition ourselves where the surface is
Romain Guy0c756222011-01-26 18:45:28 -0800174 mHaveFrame = getWidth() > 0 && getHeight() > 0;
Romain Guyf2499fa2011-01-26 18:31:23 -0800175 updateWindow(false, false);
176 return true;
177 }
178 };
Romain Guy01d5edc2011-01-28 11:28:53 -0800179 private boolean mGlobalListenersAdded;
Romain Guyf2499fa2011-01-26 18:31:23 -0800180
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700181 public SurfaceView(Context context) {
182 super(context);
Mathias Agopiand6ddcb72010-05-24 19:00:08 -0700183 init();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700184 }
185
186 public SurfaceView(Context context, AttributeSet attrs) {
187 super(context, attrs);
Mathias Agopiand6ddcb72010-05-24 19:00:08 -0700188 init();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700189 }
190
191 public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
192 super(context, attrs, defStyle);
Mathias Agopiand6ddcb72010-05-24 19:00:08 -0700193 init();
194 }
195
196 private void init() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700197 setWillNotDraw(true);
198 }
199
200 /**
201 * Return the SurfaceHolder providing access and control over this
202 * SurfaceView's underlying surface.
203 *
204 * @return SurfaceHolder The holder of the surface.
205 */
206 public SurfaceHolder getHolder() {
207 return mSurfaceHolder;
208 }
209
210 @Override
211 protected void onAttachedToWindow() {
212 super.onAttachedToWindow();
213 mParent.requestTransparentRegion(this);
214 mSession = getWindowSession();
215 mLayout.token = getWindowToken();
216 mLayout.setTitle("SurfaceView");
Mathias Agopian6b7f1a62009-09-09 18:32:34 -0700217 mViewVisibility = getVisibility() == VISIBLE;
Romain Guy01d5edc2011-01-28 11:28:53 -0800218
219 if (!mGlobalListenersAdded) {
220 ViewTreeObserver observer = getViewTreeObserver();
221 observer.addOnScrollChangedListener(mScrollChangedListener);
222 observer.addOnPreDrawListener(mDrawListener);
223 mGlobalListenersAdded = true;
224 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700225 }
226
227 @Override
228 protected void onWindowVisibilityChanged(int visibility) {
229 super.onWindowVisibilityChanged(visibility);
Mathias Agopian6b7f1a62009-09-09 18:32:34 -0700230 mWindowVisibility = visibility == VISIBLE;
231 mRequestedVisible = mWindowVisibility && mViewVisibility;
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700232 updateWindow(false, false);
Mathias Agopian6b7f1a62009-09-09 18:32:34 -0700233 }
234
235 @Override
236 public void setVisibility(int visibility) {
237 super.setVisibility(visibility);
238 mViewVisibility = visibility == VISIBLE;
Mathias Agopiancbeb3322012-05-12 19:57:07 -0700239 boolean newRequestedVisible = mWindowVisibility && mViewVisibility;
240 if (newRequestedVisible != mRequestedVisible) {
241 // our base class (View) invalidates the layout only when
242 // we go from/to the GONE state. However, SurfaceView needs
243 // to request a re-layout when the visibility changes at all.
244 // This is needed because the transparent region is computed
245 // as part of the layout phase, and it changes (obviously) when
246 // the visibility changes.
247 requestLayout();
248 }
249 mRequestedVisible = newRequestedVisible;
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700250 updateWindow(false, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700251 }
Romain Guyafc3e112010-06-07 17:04:33 -0700252
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700253 @Override
254 protected void onDetachedFromWindow() {
Romain Guy01d5edc2011-01-28 11:28:53 -0800255 if (mGlobalListenersAdded) {
256 ViewTreeObserver observer = getViewTreeObserver();
257 observer.removeOnScrollChangedListener(mScrollChangedListener);
258 observer.removeOnPreDrawListener(mDrawListener);
259 mGlobalListenersAdded = false;
260 }
261
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700262 mRequestedVisible = false;
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700263 updateWindow(false, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700264 mHaveFrame = false;
265 if (mWindow != null) {
266 try {
267 mSession.remove(mWindow);
268 } catch (RemoteException ex) {
Romain Guy01d5edc2011-01-28 11:28:53 -0800269 // Not much we can do here...
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700270 }
271 mWindow = null;
272 }
273 mSession = null;
274 mLayout.token = null;
275
276 super.onDetachedFromWindow();
277 }
278
279 @Override
280 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Dianne Hackborn189ee182010-12-02 21:48:53 -0800281 int width = mRequestedWidth >= 0
282 ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
283 : getDefaultSize(0, widthMeasureSpec);
284 int height = mRequestedHeight >= 0
285 ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
286 : getDefaultSize(0, heightMeasureSpec);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700287 setMeasuredDimension(width, height);
288 }
289
Mathias Agopianef115302010-10-04 20:15:08 -0700290 /** @hide */
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700291 @Override
Mathias Agopian995bb9d2010-10-04 17:13:15 -0700292 protected boolean setFrame(int left, int top, int right, int bottom) {
293 boolean result = super.setFrame(left, top, right, bottom);
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700294 updateWindow(false, false);
Mathias Agopian995bb9d2010-10-04 17:13:15 -0700295 return result;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700296 }
297
298 @Override
299 public boolean gatherTransparentRegion(Region region) {
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700300 if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
301 return super.gatherTransparentRegion(region);
302 }
303
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700304 boolean opaque = true;
Dianne Hackborn4702a852012-08-17 15:18:29 -0700305 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700306 // this view draws, remove it from the transparent region
307 opaque = super.gatherTransparentRegion(region);
308 } else if (region != null) {
309 int w = getWidth();
310 int h = getHeight();
311 if (w>0 && h>0) {
312 getLocationInWindow(mLocation);
313 // otherwise, punch a hole in the whole hierarchy
314 int l = mLocation[0];
315 int t = mLocation[1];
316 region.op(l, t, l+w, t+h, Region.Op.UNION);
317 }
318 }
319 if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
320 opaque = false;
321 }
322 return opaque;
323 }
324
325 @Override
326 public void draw(Canvas canvas) {
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700327 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
328 // draw() is not called when SKIP_DRAW is set
Dianne Hackborn4702a852012-08-17 15:18:29 -0700329 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700330 // punch a whole in the view-hierarchy below us
331 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
332 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700333 }
334 super.draw(canvas);
335 }
336
337 @Override
338 protected void dispatchDraw(Canvas canvas) {
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700339 if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
340 // if SKIP_DRAW is cleared, draw() has already punched a hole
Dianne Hackborn4702a852012-08-17 15:18:29 -0700341 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700342 // punch a whole in the view-hierarchy below us
343 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
344 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700345 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700346 super.dispatchDraw(canvas);
347 }
348
Dianne Hackbornc4d5d022009-05-21 17:32:42 -0700349 /**
Dianne Hackborn29e4a3c2009-09-30 22:35:40 -0700350 * Control whether the surface view's surface is placed on top of another
351 * regular surface view in the window (but still behind the window itself).
352 * This is typically used to place overlays on top of an underlying media
353 * surface view.
354 *
355 * <p>Note that this must be set before the surface view's containing
356 * window is attached to the window manager.
357 *
358 * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
359 */
360 public void setZOrderMediaOverlay(boolean isMediaOverlay) {
361 mWindowType = isMediaOverlay
362 ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
363 : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
364 }
365
366 /**
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700367 * Control whether the surface view's surface is placed on top of its
368 * window. Normally it is placed behind the window, to allow it to
369 * (for the most part) appear to composite with the views in the
370 * hierarchy. By setting this, you cause it to be placed above the
371 * window. This means that none of the contents of the window this
372 * SurfaceView is in will be visible on top of its surface.
373 *
374 * <p>Note that this must be set before the surface view's containing
375 * window is attached to the window manager.
Dianne Hackborn29e4a3c2009-09-30 22:35:40 -0700376 *
377 * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700378 */
Dianne Hackborn29e4a3c2009-09-30 22:35:40 -0700379 public void setZOrderOnTop(boolean onTop) {
Derek Sollenbergerecde72f2010-03-01 13:44:42 -0500380 if (onTop) {
381 mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
382 // ensures the surface is placed below the IME
383 mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
384 } else {
385 mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
386 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
387 }
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700388 }
Jeff Brownf0681b32012-10-23 17:35:57 -0700389
390 /**
391 * Control whether the surface view's content should be treated as secure,
392 * preventing it from appearing in screenshots or from being viewed on
393 * non-secure displays.
394 *
395 * <p>Note that this must be set before the surface view's containing
396 * window is attached to the window manager.
397 *
398 * <p>See {@link android.view.Display#FLAG_SECURE} for details.
399 *
400 * @param isSecure True if the surface view is secure.
401 */
402 public void setSecure(boolean isSecure) {
403 if (isSecure) {
404 mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
405 } else {
406 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
407 }
408 }
409
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700410 /**
Dianne Hackbornc4d5d022009-05-21 17:32:42 -0700411 * Hack to allow special layering of windows. The type is one of the
412 * types in WindowManager.LayoutParams. This is a hack so:
413 * @hide
414 */
415 public void setWindowType(int type) {
416 mWindowType = type;
417 }
Mitsuru Oshima34bf2ee2009-07-17 09:57:28 -0700418
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700419 private void updateWindow(boolean force, boolean redrawNeeded) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700420 if (!mHaveFrame) {
421 return;
422 }
Jeff Browna175a5b2012-02-15 19:18:31 -0800423 ViewRootImpl viewRoot = getViewRootImpl();
Joe Onorato168173a2009-08-12 21:40:29 -0700424 if (viewRoot != null) {
425 mTranslator = viewRoot.mTranslator;
426 }
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700427
Dianne Hackborn5be8de32011-05-24 18:11:57 -0700428 if (mTranslator != null) {
429 mSurface.setCompatibilityTranslator(mTranslator);
Mitsuru Oshima38ed7d772009-07-21 14:39:34 -0700430 }
431
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700432 int myWidth = mRequestedWidth;
433 if (myWidth <= 0) myWidth = getWidth();
434 int myHeight = mRequestedHeight;
435 if (myHeight <= 0) myHeight = getHeight();
Mitsuru Oshima001a6e522009-05-11 21:14:03 -0700436
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700437 getLocationInWindow(mLocation);
438 final boolean creating = mWindow == null;
439 final boolean formatChanged = mFormat != mRequestedFormat;
440 final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800441 final boolean visibleChanged = mVisible != mRequestedVisible;
Mathias Agopiand2112302010-12-07 19:38:17 -0800442
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700443 if (force || creating || formatChanged || sizeChanged || visibleChanged
Mathias Agopiand2112302010-12-07 19:38:17 -0800444 || mLeft != mLocation[0] || mTop != mLocation[1]
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700445 || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700446
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800447 if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700448 + " format=" + formatChanged + " size=" + sizeChanged
449 + " visible=" + visibleChanged
450 + " left=" + (mLeft != mLocation[0])
451 + " top=" + (mTop != mLocation[1]));
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700452
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700453 try {
454 final boolean visible = mVisible = mRequestedVisible;
455 mLeft = mLocation[0];
456 mTop = mLocation[1];
457 mWidth = myWidth;
458 mHeight = myHeight;
459 mFormat = mRequestedFormat;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700460
Mitsuru Oshima64f59342009-06-21 00:03:11 -0700461 // Scaling/Translate window's layout here because mLayout is not used elsewhere.
462
463 // Places the window relative
464 mLayout.x = mLeft;
465 mLayout.y = mTop;
466 mLayout.width = getWidth();
467 mLayout.height = getHeight();
468 if (mTranslator != null) {
469 mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
470 }
471
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700472 mLayout.format = mRequestedFormat;
Dianne Hackborn1cd403e2009-09-14 22:29:14 -0700473 mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
474 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
475 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700476 | WindowManager.LayoutParams.FLAG_SCALED
477 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
478 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
479 ;
Mitsuru Oshima841f13c2009-07-17 17:23:31 -0700480 if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
481 mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
482 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700483
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700484 if (mWindow == null) {
Jeff Brown8d0243a2012-09-24 15:01:47 -0700485 Display display = getDisplay();
Jon Larimer9bdf5762009-01-02 18:55:15 -0500486 mWindow = new MyWindow(this);
Dianne Hackbornc4d5d022009-05-21 17:32:42 -0700487 mLayout.type = mWindowType;
Fabrice Di Meglioaac0d4e2012-07-19 19:21:26 -0700488 mLayout.gravity = Gravity.START|Gravity.TOP;
Craig Mautner6881a102012-07-27 13:04:51 -0700489 mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
Jeff Brown8d0243a2012-09-24 15:01:47 -0700490 mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700491 }
492
Dianne Hackborn726426e2010-03-31 22:04:36 -0700493 boolean realSizeChanged;
494 boolean reportDrawNeeded;
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800495
496 int relayoutResult;
497
Dianne Hackborn726426e2010-03-31 22:04:36 -0700498 mSurfaceLock.lock();
499 try {
500 mUpdateWindowNeeded = false;
501 reportDrawNeeded = mReportDrawNeeded;
502 mReportDrawNeeded = false;
503 mDrawingStopped = !visible;
504
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800505 if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
506
507 relayoutResult = mSession.relayout(
Dianne Hackborn9a230e02011-10-06 11:51:27 -0700508 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800509 visible ? VISIBLE : GONE,
Jeff Brown98365d72012-08-19 20:30:52 -0700510 WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
Dianne Hackbornc4aad012013-02-22 15:05:25 -0800511 mWinFrame, mOverscanInsets, mContentInsets,
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800512 mVisibleInsets, mConfiguration, mNewSurface);
Jeff Brown98365d72012-08-19 20:30:52 -0700513 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
Dianne Hackborn726426e2010-03-31 22:04:36 -0700514 mReportDrawNeeded = true;
515 }
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800516
517 if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
Dianne Hackborn726426e2010-03-31 22:04:36 -0700518 + ", vis=" + visible + ", frame=" + mWinFrame);
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800519
Dianne Hackborn726426e2010-03-31 22:04:36 -0700520 mSurfaceFrame.left = 0;
521 mSurfaceFrame.top = 0;
522 if (mTranslator == null) {
523 mSurfaceFrame.right = mWinFrame.width();
524 mSurfaceFrame.bottom = mWinFrame.height();
525 } else {
526 float appInvertedScale = mTranslator.applicationInvertedScale;
527 mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
528 mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
529 }
530
531 final int surfaceWidth = mSurfaceFrame.right;
532 final int surfaceHeight = mSurfaceFrame.bottom;
533 realSizeChanged = mLastSurfaceWidth != surfaceWidth
534 || mLastSurfaceHeight != surfaceHeight;
535 mLastSurfaceWidth = surfaceWidth;
536 mLastSurfaceHeight = surfaceHeight;
537 } finally {
538 mSurfaceLock.unlock();
Mitsuru Oshima589cebe2009-07-22 20:38:58 -0700539 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700540
541 try {
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700542 redrawNeeded |= creating | reportDrawNeeded;
543
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800544 SurfaceHolder.Callback callbacks[] = null;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700545
Jeff Brown98365d72012-08-19 20:30:52 -0700546 final boolean surfaceChanged = (relayoutResult
547 & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800548 if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
549 mSurfaceCreated = false;
550 if (mSurface.isValid()) {
551 if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
552 callbacks = getSurfaceCallbacks();
553 for (SurfaceHolder.Callback c : callbacks) {
554 c.surfaceDestroyed(mSurfaceHolder);
555 }
Mitsuru Oshima3d914922009-05-13 22:29:15 -0700556 }
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800557 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700558
Dianne Hackborn61566cc2011-12-02 23:31:52 -0800559 mSurface.transferFrom(mNewSurface);
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800560
Andreas Röhlf750b8c2012-07-02 13:06:26 +0200561 if (visible && mSurface.isValid()) {
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800562 if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
563 mSurfaceCreated = true;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700564 mIsCreating = true;
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800565 if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
566 if (callbacks == null) {
567 callbacks = getSurfaceCallbacks();
568 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700569 for (SurfaceHolder.Callback c : callbacks) {
570 c.surfaceCreated(mSurfaceHolder);
571 }
572 }
573 if (creating || formatChanged || sizeChanged
Dianne Hackborn726426e2010-03-31 22:04:36 -0700574 || visibleChanged || realSizeChanged) {
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800575 if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
576 + " w=" + myWidth + " h=" + myHeight);
577 if (callbacks == null) {
578 callbacks = getSurfaceCallbacks();
579 }
Dianne Hackborn251fd432010-07-14 16:56:31 -0700580 for (SurfaceHolder.Callback c : callbacks) {
581 c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
582 }
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700583 }
584 if (redrawNeeded) {
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800585 if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
586 if (callbacks == null) {
587 callbacks = getSurfaceCallbacks();
588 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700589 for (SurfaceHolder.Callback c : callbacks) {
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700590 if (c instanceof SurfaceHolder.Callback2) {
591 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
592 mSurfaceHolder);
593 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700594 }
595 }
596 }
597 } finally {
598 mIsCreating = false;
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700599 if (redrawNeeded) {
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800600 if (DEBUG) Log.i(TAG, "finishedDrawing");
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700601 mSession.finishDrawing(mWindow);
602 }
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800603 mSession.performDeferredDestroy(mWindow);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700604 }
605 } catch (RemoteException ex) {
606 }
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800607 if (DEBUG) Log.v(
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700608 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
609 " w=" + mLayout.width + " h=" + mLayout.height +
610 ", frame=" + mSurfaceFrame);
611 }
612 }
613
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800614 private SurfaceHolder.Callback[] getSurfaceCallbacks() {
615 SurfaceHolder.Callback callbacks[];
616 synchronized (mCallbacks) {
617 callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
618 mCallbacks.toArray(callbacks);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700619 }
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800620 return callbacks;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700621 }
622
623 void handleGetNewSurface() {
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700624 updateWindow(false, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700625 }
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800626
Derek Sollenberger7179b812010-03-22 13:41:20 -0400627 /**
628 * Check to see if the surface has fixed size dimensions or if the surface's
629 * dimensions are dimensions are dependent on its current layout.
630 *
631 * @return true if the surface has dimensions that are fixed in size
632 * @hide
633 */
634 public boolean isFixedSize() {
635 return (mRequestedWidth != -1 || mRequestedHeight != -1);
636 }
637
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700638 private static class MyWindow extends BaseIWindow {
Mitsuru Oshima8169dae2009-04-28 18:12:09 -0700639 private final WeakReference<SurfaceView> mSurfaceView;
Jon Larimer9bdf5762009-01-02 18:55:15 -0500640
641 public MyWindow(SurfaceView surfaceView) {
642 mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
643 }
644
Craig Mautner656e3af2012-09-06 15:04:22 -0700645 @Override
Dianne Hackbornc4aad012013-02-22 15:05:25 -0800646 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Dianne Hackborne36d6e22010-02-17 19:46:25 -0800647 Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
Jon Larimer9bdf5762009-01-02 18:55:15 -0500648 SurfaceView surfaceView = mSurfaceView.get();
649 if (surfaceView != null) {
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800650 if (DEBUG) Log.v(
Craig Mautner656e3af2012-09-06 15:04:22 -0700651 "SurfaceView", surfaceView + " got resized: w=" + frame.width()
652 + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
Dianne Hackborn726426e2010-03-31 22:04:36 -0700653 surfaceView.mSurfaceLock.lock();
654 try {
Jon Larimer9bdf5762009-01-02 18:55:15 -0500655 if (reportDraw) {
Dianne Hackborn726426e2010-03-31 22:04:36 -0700656 surfaceView.mUpdateWindowNeeded = true;
657 surfaceView.mReportDrawNeeded = true;
658 surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
Craig Mautner656e3af2012-09-06 15:04:22 -0700659 } else if (surfaceView.mWinFrame.width() != frame.width()
660 || surfaceView.mWinFrame.height() != frame.height()) {
Dianne Hackborn726426e2010-03-31 22:04:36 -0700661 surfaceView.mUpdateWindowNeeded = true;
662 surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700663 }
Dianne Hackborn726426e2010-03-31 22:04:36 -0700664 } finally {
665 surfaceView.mSurfaceLock.unlock();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700666 }
667 }
668 }
669
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700670 public void dispatchAppVisibility(boolean visible) {
671 // The point of SurfaceView is to let the app control the surface.
672 }
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800673
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700674 public void dispatchGetNewSurface() {
Jon Larimer9bdf5762009-01-02 18:55:15 -0500675 SurfaceView surfaceView = mSurfaceView.get();
676 if (surfaceView != null) {
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800677 Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
678 surfaceView.mHandler.sendMessage(msg);
Jon Larimer9bdf5762009-01-02 18:55:15 -0500679 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700680 }
681
682 public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
683 Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
684 }
685
686 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
687 }
688
689 int mCurWidth = -1;
690 int mCurHeight = -1;
691 }
692
693 private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
694
695 private static final String LOG_TAG = "SurfaceHolder";
696
697 public boolean isCreating() {
698 return mIsCreating;
699 }
700
701 public void addCallback(Callback callback) {
702 synchronized (mCallbacks) {
703 // This is a linear search, but in practice we'll
704 // have only a couple callbacks, so it doesn't matter.
705 if (mCallbacks.contains(callback) == false) {
706 mCallbacks.add(callback);
707 }
708 }
709 }
710
711 public void removeCallback(Callback callback) {
712 synchronized (mCallbacks) {
713 mCallbacks.remove(callback);
714 }
715 }
716
717 public void setFixedSize(int width, int height) {
718 if (mRequestedWidth != width || mRequestedHeight != height) {
719 mRequestedWidth = width;
720 mRequestedHeight = height;
721 requestLayout();
722 }
723 }
724
725 public void setSizeFromLayout() {
726 if (mRequestedWidth != -1 || mRequestedHeight != -1) {
727 mRequestedWidth = mRequestedHeight = -1;
728 requestLayout();
729 }
730 }
731
732 public void setFormat(int format) {
Mathias Agopian2d468c52010-06-14 21:50:48 -0700733
734 // for backward compatibility reason, OPAQUE always
735 // means 565 for SurfaceView
736 if (format == PixelFormat.OPAQUE)
737 format = PixelFormat.RGB_565;
738
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700739 mRequestedFormat = format;
740 if (mWindow != null) {
Dianne Hackbornd76b67c2010-07-13 17:48:30 -0700741 updateWindow(false, false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700742 }
743 }
744
Mathias Agopiand2112302010-12-07 19:38:17 -0800745 /**
746 * @deprecated setType is now ignored.
747 */
748 @Deprecated
749 public void setType(int type) { }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700750
751 public void setKeepScreenOn(boolean screenOn) {
752 Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
753 msg.arg1 = screenOn ? 1 : 0;
754 mHandler.sendMessage(msg);
755 }
756
757 public Canvas lockCanvas() {
758 return internalLockCanvas(null);
759 }
760
761 public Canvas lockCanvas(Rect dirty) {
762 return internalLockCanvas(dirty);
763 }
764
765 private final Canvas internalLockCanvas(Rect dirty) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700766 mSurfaceLock.lock();
767
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800768 if (DEBUG) Log.i(TAG, "Locking canvas... stopped="
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700769 + mDrawingStopped + ", win=" + mWindow);
770
771 Canvas c = null;
772 if (!mDrawingStopped && mWindow != null) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800773 if (dirty == null) {
774 if (mTmpDirty == null) {
775 mTmpDirty = new Rect();
776 }
777 mTmpDirty.set(mSurfaceFrame);
778 dirty = mTmpDirty;
779 }
780
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700781 try {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800782 c = mSurface.lockCanvas(dirty);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700783 } catch (Exception e) {
784 Log.e(LOG_TAG, "Exception locking surface", e);
785 }
786 }
787
Dianne Hackborn6d05fd32011-11-19 14:36:15 -0800788 if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700789 if (c != null) {
790 mLastLockTime = SystemClock.uptimeMillis();
791 return c;
792 }
793
794 // If the Surface is not ready to be drawn, then return null,
795 // but throttle calls to this function so it isn't called more
796 // than every 100ms.
797 long now = SystemClock.uptimeMillis();
798 long nextTime = mLastLockTime + 100;
799 if (nextTime > now) {
800 try {
801 Thread.sleep(nextTime-now);
802 } catch (InterruptedException e) {
803 }
804 now = SystemClock.uptimeMillis();
805 }
806 mLastLockTime = now;
807 mSurfaceLock.unlock();
808
809 return null;
810 }
811
812 public void unlockCanvasAndPost(Canvas canvas) {
813 mSurface.unlockCanvasAndPost(canvas);
814 mSurfaceLock.unlock();
815 }
816
817 public Surface getSurface() {
818 return mSurface;
819 }
820
821 public Rect getSurfaceFrame() {
822 return mSurfaceFrame;
823 }
824 };
825}