auto import from //depot/cupcake/@135843
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
new file mode 100644
index 0000000..e928998
--- /dev/null
+++ b/core/java/android/view/SurfaceView.java
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.ParcelFileDescriptor;
+import android.util.AttributeSet;
+import android.util.Config;
+import android.util.Log;
+import java.util.ArrayList;
+
+import java.util.concurrent.locks.ReentrantLock;
+import java.lang.ref.WeakReference;
+
+/**
+ * Provides a dedicated drawing surface embedded inside of a view hierarchy.
+ * You can control the format of this surface and, if you like, its size; the
+ * SurfaceView takes care of placing the surface at the correct location on the
+ * screen
+ *
+ * <p>The surface is Z ordered so that it is behind the window holding its
+ * SurfaceView; the SurfaceView punches a hole in its window to allow its
+ * surface to be displayed. The view hierarchy will take care of correctly
+ * compositing with the Surface any siblings of the SurfaceView that would
+ * normally appear on top of it. This can be used to place overlays such as
+ * buttons on top of the Surface, though note however that it can have an
+ * impact on performance since a full alpha-blended composite will be performed
+ * each time the Surface changes.
+ *
+ * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
+ * which can be retrieved by calling {@link #getHolder}.
+ *
+ * <p>The Surface will be created for you while the SurfaceView's window is
+ * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
+ * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
+ * Surface is created and destroyed as the window is shown and hidden.
+ *
+ * <p>One of the purposes of this class is to provide a surface in which a
+ * secondary thread can render in to the screen. If you are going to use it
+ * this way, you need to be aware of some threading semantics:
+ *
+ * <ul>
+ * <li> All SurfaceView and
+ * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
+ * from the thread running the SurfaceView's window (typically the main thread
+ * of the application). They thus need to correctly synchronize with any
+ * state that is also touched by the drawing thread.
+ * <li> You must ensure that the drawing thread only touches the underlying
+ * Surface while it is valid -- between
+ * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
+ * and
+ * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
+ * </ul>
+ */
+public class SurfaceView extends View {
+ static private final String TAG = "SurfaceView";
+ static private final boolean DEBUG = false;
+ static private final boolean localLOGV = DEBUG ? true : Config.LOGV;
+
+ final ArrayList<SurfaceHolder.Callback> mCallbacks
+ = new ArrayList<SurfaceHolder.Callback>();
+
+ final int[] mLocation = new int[2];
+
+ final ReentrantLock mSurfaceLock = new ReentrantLock();
+ final Surface mSurface = new Surface();
+ boolean mDrawingStopped = true;
+
+ final WindowManager.LayoutParams mLayout
+ = new WindowManager.LayoutParams();
+ IWindowSession mSession;
+ MyWindow mWindow;
+ final Rect mVisibleInsets = new Rect();
+ final Rect mWinFrame = new Rect();
+ final Rect mContentInsets = new Rect();
+
+ static final int KEEP_SCREEN_ON_MSG = 1;
+ static final int GET_NEW_SURFACE_MSG = 2;
+
+ boolean mIsCreating = false;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case KEEP_SCREEN_ON_MSG: {
+ setKeepScreenOn(msg.arg1 != 0);
+ } break;
+ case GET_NEW_SURFACE_MSG: {
+ handleGetNewSurface();
+ } break;
+ }
+ }
+ };
+
+ boolean mRequestedVisible = false;
+ int mRequestedWidth = -1;
+ int mRequestedHeight = -1;
+ int mRequestedFormat = PixelFormat.OPAQUE;
+ int mRequestedType = -1;
+
+ boolean mHaveFrame = false;
+ boolean mDestroyReportNeeded = false;
+ boolean mNewSurfaceNeeded = false;
+ long mLastLockTime = 0;
+
+ boolean mVisible = false;
+ int mLeft = -1;
+ int mTop = -1;
+ int mWidth = -1;
+ int mHeight = -1;
+ int mFormat = -1;
+ int mType = -1;
+ final Rect mSurfaceFrame = new Rect();
+
+ public SurfaceView(Context context) {
+ super(context);
+ setWillNotDraw(true);
+ }
+
+ public SurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(true);
+ }
+
+ public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setWillNotDraw(true);
+ }
+
+ /**
+ * Return the SurfaceHolder providing access and control over this
+ * SurfaceView's underlying surface.
+ *
+ * @return SurfaceHolder The holder of the surface.
+ */
+ public SurfaceHolder getHolder() {
+ return mSurfaceHolder;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mParent.requestTransparentRegion(this);
+ mSession = getWindowSession();
+ mLayout.token = getWindowToken();
+ mLayout.setTitle("SurfaceView");
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ mRequestedVisible = visibility == VISIBLE;
+ updateWindow(false);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ mRequestedVisible = false;
+ updateWindow(false);
+ mHaveFrame = false;
+ if (mWindow != null) {
+ try {
+ mSession.remove(mWindow);
+ } catch (RemoteException ex) {
+ }
+ mWindow = null;
+ }
+ mSession = null;
+ mLayout.token = null;
+
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
+ int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ updateWindow(false);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ updateWindow(false);
+ }
+
+ @Override
+ public boolean gatherTransparentRegion(Region region) {
+ boolean opaque = true;
+ if ((mPrivateFlags & SKIP_DRAW) == 0) {
+ // this view draws, remove it from the transparent region
+ opaque = super.gatherTransparentRegion(region);
+ } else if (region != null) {
+ int w = getWidth();
+ int h = getHeight();
+ if (w>0 && h>0) {
+ getLocationInWindow(mLocation);
+ // otherwise, punch a hole in the whole hierarchy
+ int l = mLocation[0];
+ int t = mLocation[1];
+ region.op(l, t, l+w, t+h, Region.Op.UNION);
+ }
+ }
+ if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
+ opaque = false;
+ }
+ return opaque;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ // draw() is not called when SKIP_DRAW is set
+ if ((mPrivateFlags & SKIP_DRAW) == 0) {
+ // punch a whole in the view-hierarchy below us
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
+ super.draw(canvas);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ // if SKIP_DRAW is cleared, draw() has already punched a hole
+ if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+ // punch a whole in the view-hierarchy below us
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
+ // reposition ourselves where the surface is
+ mHaveFrame = true;
+ updateWindow(false);
+ super.dispatchDraw(canvas);
+ }
+
+ private void updateWindow(boolean force) {
+ if (!mHaveFrame) {
+ return;
+ }
+
+ int myWidth = mRequestedWidth;
+ if (myWidth <= 0) myWidth = getWidth();
+ int myHeight = mRequestedHeight;
+ if (myHeight <= 0) myHeight = getHeight();
+
+ getLocationInWindow(mLocation);
+ final boolean creating = mWindow == null;
+ final boolean formatChanged = mFormat != mRequestedFormat;
+ final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
+ final boolean visibleChanged = mVisible != mRequestedVisible
+ || mNewSurfaceNeeded;
+ final boolean typeChanged = mType != mRequestedType;
+ if (force || creating || formatChanged || sizeChanged || visibleChanged
+ || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]) {
+
+ if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
+ + " format=" + formatChanged + " size=" + sizeChanged
+ + " visible=" + visibleChanged
+ + " left=" + (mLeft != mLocation[0])
+ + " top=" + (mTop != mLocation[1]));
+
+ try {
+ final boolean visible = mVisible = mRequestedVisible;
+ mLeft = mLocation[0];
+ mTop = mLocation[1];
+ mWidth = myWidth;
+ mHeight = myHeight;
+ mFormat = mRequestedFormat;
+ mType = mRequestedType;
+
+ mLayout.x = mLeft;
+ mLayout.y = mTop;
+ mLayout.width = getWidth();
+ mLayout.height = getHeight();
+ mLayout.format = mRequestedFormat;
+ mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_SCALED
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ ;
+
+ mLayout.memoryType = mRequestedType;
+
+ if (mWindow == null) {
+ mWindow = new MyWindow(this);
+ mLayout.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+ mLayout.gravity = Gravity.LEFT|Gravity.TOP;
+ mSession.add(mWindow, mLayout,
+ mVisible ? VISIBLE : GONE, mContentInsets);
+ }
+
+ if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
+ reportSurfaceDestroyed();
+ }
+
+ mNewSurfaceNeeded = false;
+
+ mSurfaceLock.lock();
+ mDrawingStopped = !visible;
+ final int relayoutResult = mSession.relayout(
+ mWindow, mLayout, mWidth, mHeight,
+ visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
+ mVisibleInsets, mSurface);
+ if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+ + ", vis=" + visible + ", frame=" + mWinFrame);
+ mSurfaceFrame.left = 0;
+ mSurfaceFrame.top = 0;
+ mSurfaceFrame.right = mWinFrame.width();
+ mSurfaceFrame.bottom = mWinFrame.height();
+ mSurfaceLock.unlock();
+
+ try {
+ if (visible) {
+ mDestroyReportNeeded = true;
+
+ SurfaceHolder.Callback callbacks[];
+ synchronized (mCallbacks) {
+ callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+ mCallbacks.toArray(callbacks);
+ }
+
+ if (visibleChanged) {
+ mIsCreating = true;
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceCreated(mSurfaceHolder);
+ }
+ }
+ if (creating || formatChanged || sizeChanged
+ || visibleChanged) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
+ }
+ }
+ }
+ } finally {
+ mIsCreating = false;
+ if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+ mSession.finishDrawing(mWindow);
+ }
+ }
+ } catch (RemoteException ex) {
+ }
+ if (localLOGV) Log.v(
+ TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
+ " w=" + mLayout.width + " h=" + mLayout.height +
+ ", frame=" + mSurfaceFrame);
+ }
+ }
+
+ private void reportSurfaceDestroyed() {
+ if (mDestroyReportNeeded) {
+ mDestroyReportNeeded = false;
+ SurfaceHolder.Callback callbacks[];
+ synchronized (mCallbacks) {
+ callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+ mCallbacks.toArray(callbacks);
+ }
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ }
+ super.onDetachedFromWindow();
+ }
+
+ void handleGetNewSurface() {
+ mNewSurfaceNeeded = true;
+ updateWindow(false);
+ }
+
+ private static class MyWindow extends IWindow.Stub {
+ private WeakReference<SurfaceView> mSurfaceView;
+
+ public MyWindow(SurfaceView surfaceView) {
+ mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
+ }
+
+ public void resized(int w, int h, Rect coveredInsets,
+ Rect visibleInsets, boolean reportDraw) {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ if (localLOGV) Log.v(
+ "SurfaceView", surfaceView + " got resized: w=" +
+ w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
+ synchronized (this) {
+ if (mCurWidth != w || mCurHeight != h) {
+ mCurWidth = w;
+ mCurHeight = h;
+ }
+ if (reportDraw) {
+ try {
+ surfaceView.mSession.finishDrawing(surfaceView.mWindow);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+ }
+
+ public void dispatchKey(KeyEvent event) {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ //Log.w("SurfaceView", "Unexpected key event in surface: " + event);
+ if (surfaceView.mSession != null && surfaceView.mSurface != null) {
+ try {
+ surfaceView.mSession.finishKey(surfaceView.mWindow);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ }
+
+ public void dispatchPointer(MotionEvent event, long eventTime) {
+ Log.w("SurfaceView", "Unexpected pointer event in surface: " + event);
+ //if (mSession != null && mSurface != null) {
+ // try {
+ // //mSession.finishKey(mWindow);
+ // } catch (RemoteException ex) {
+ // }
+ //}
+ }
+
+ public void dispatchTrackball(MotionEvent event, long eventTime) {
+ Log.w("SurfaceView", "Unexpected trackball event in surface: " + event);
+ //if (mSession != null && mSurface != null) {
+ // try {
+ // //mSession.finishKey(mWindow);
+ // } catch (RemoteException ex) {
+ // }
+ //}
+ }
+
+ public void dispatchAppVisibility(boolean visible) {
+ // The point of SurfaceView is to let the app control the surface.
+ }
+
+ public void dispatchGetNewSurface() {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
+ surfaceView.mHandler.sendMessage(msg);
+ }
+ }
+
+ public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+ Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
+ }
+
+ public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+ }
+
+ int mCurWidth = -1;
+ int mCurHeight = -1;
+ }
+
+ private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+
+ private static final String LOG_TAG = "SurfaceHolder";
+
+ public boolean isCreating() {
+ return mIsCreating;
+ }
+
+ public void addCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ // This is a linear search, but in practice we'll
+ // have only a couple callbacks, so it doesn't matter.
+ if (mCallbacks.contains(callback) == false) {
+ mCallbacks.add(callback);
+ }
+ }
+ }
+
+ public void removeCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ public void setFixedSize(int width, int height) {
+ if (mRequestedWidth != width || mRequestedHeight != height) {
+ mRequestedWidth = width;
+ mRequestedHeight = height;
+ requestLayout();
+ }
+ }
+
+ public void setSizeFromLayout() {
+ if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+ mRequestedWidth = mRequestedHeight = -1;
+ requestLayout();
+ }
+ }
+
+ public void setFormat(int format) {
+ mRequestedFormat = format;
+ if (mWindow != null) {
+ updateWindow(false);
+ }
+ }
+
+ public void setType(int type) {
+ switch (type) {
+ case SURFACE_TYPE_NORMAL:
+ case SURFACE_TYPE_HARDWARE:
+ case SURFACE_TYPE_GPU:
+ case SURFACE_TYPE_PUSH_BUFFERS:
+ mRequestedType = type;
+ if (mWindow != null) {
+ updateWindow(false);
+ }
+ break;
+ }
+ }
+
+ public void setKeepScreenOn(boolean screenOn) {
+ Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
+ msg.arg1 = screenOn ? 1 : 0;
+ mHandler.sendMessage(msg);
+ }
+
+ public Canvas lockCanvas() {
+ return internalLockCanvas(null);
+ }
+
+ public Canvas lockCanvas(Rect dirty) {
+ return internalLockCanvas(dirty);
+ }
+
+ private final Canvas internalLockCanvas(Rect dirty) {
+ if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
+ throw new BadSurfaceTypeException(
+ "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
+ }
+ mSurfaceLock.lock();
+
+ if (localLOGV) Log.i(TAG, "Locking canvas... stopped="
+ + mDrawingStopped + ", win=" + mWindow);
+
+ Canvas c = null;
+ if (!mDrawingStopped && mWindow != null) {
+ Rect frame = dirty != null ? dirty : mSurfaceFrame;
+ try {
+ c = mSurface.lockCanvas(frame);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception locking surface", e);
+ }
+ }
+
+ if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
+ if (c != null) {
+ mLastLockTime = SystemClock.uptimeMillis();
+ return c;
+ }
+
+ // If the Surface is not ready to be drawn, then return null,
+ // but throttle calls to this function so it isn't called more
+ // than every 100ms.
+ long now = SystemClock.uptimeMillis();
+ long nextTime = mLastLockTime + 100;
+ if (nextTime > now) {
+ try {
+ Thread.sleep(nextTime-now);
+ } catch (InterruptedException e) {
+ }
+ now = SystemClock.uptimeMillis();
+ }
+ mLastLockTime = now;
+ mSurfaceLock.unlock();
+
+ return null;
+ }
+
+ public void unlockCanvasAndPost(Canvas canvas) {
+ mSurface.unlockCanvasAndPost(canvas);
+ mSurfaceLock.unlock();
+ }
+
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ public Rect getSurfaceFrame() {
+ return mSurfaceFrame;
+ }
+ };
+}
+