/* * 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.util.DisplayMetrics; import com.android.internal.view.BaseIWindow; import android.content.Context; import android.content.res.Configuration; import android.content.res.CompatibilityInfo.Translator; 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.Log; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; /** * 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 * *
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. * *
Access to the underlying surface is provided via the SurfaceHolder interface, * which can be retrieved by calling {@link #getHolder}. * *
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. * *
One of the purposes of this class is to provide a surface in which a * secondary thread can render into the screen. If you are going to use it * this way, you need to be aware of some threading semantics: * *
Note that this must be set before the surface view's containing * window is attached to the window manager. * *
Calling this overrides any previous call to {@link #setZOrderOnTop}. */ public void setZOrderMediaOverlay(boolean isMediaOverlay) { mWindowType = isMediaOverlay ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; } /** * Control whether the surface view's surface is placed on top of its * window. Normally it is placed behind the window, to allow it to * (for the most part) appear to composite with the views in the * hierarchy. By setting this, you cause it to be placed above the * window. This means that none of the contents of the window this * SurfaceView is in will be visible on top of its surface. * *
Note that this must be set before the surface view's containing * window is attached to the window manager. * *
Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
*/
public void setZOrderOnTop(boolean onTop) {
if (onTop) {
mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
// ensures the surface is placed below the IME
mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
}
/**
* Hack to allow special layering of windows. The type is one of the
* types in WindowManager.LayoutParams. This is a hack so:
* @hide
*/
public void setWindowType(int type) {
mWindowType = type;
}
private void updateWindow(boolean force, boolean redrawNeeded) {
if (!mHaveFrame) {
return;
}
ViewAncestor viewRoot = (ViewAncestor) getRootView().getParent();
if (viewRoot != null) {
mTranslator = viewRoot.mTranslator;
}
if (mTranslator != null) {
mSurface.setCompatibilityTranslator(mTranslator);
}
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;
if (force || creating || formatChanged || sizeChanged || visibleChanged
|| mLeft != mLocation[0] || mTop != mLocation[1]
|| mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
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;
// Scaling/Translate window's layout here because mLayout is not used elsewhere.
// Places the window relative
mLayout.x = mLeft;
mLayout.y = mTop;
mLayout.width = getWidth();
mLayout.height = getHeight();
if (mTranslator != null) {
mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
}
mLayout.format = mRequestedFormat;
mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_SCALED
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
;
if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
}
if (mWindow == null) {
mWindow = new MyWindow(this);
mLayout.type = mWindowType;
mLayout.gravity = Gravity.LEFT|Gravity.TOP;
mSession.addWithoutInputChannel(mWindow, mLayout,
mVisible ? VISIBLE : GONE, mContentInsets);
}
if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
reportSurfaceDestroyed();
}
mNewSurfaceNeeded = false;
boolean realSizeChanged;
boolean reportDrawNeeded;
mSurfaceLock.lock();
try {
mUpdateWindowNeeded = false;
reportDrawNeeded = mReportDrawNeeded;
mReportDrawNeeded = false;
mDrawingStopped = !visible;
final int relayoutResult = mSession.relayout(
mWindow, mLayout, mWidth, mHeight,
visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
mVisibleInsets, mConfiguration, mSurface);
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
mReportDrawNeeded = true;
}
if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+ ", vis=" + visible + ", frame=" + mWinFrame);
mSurfaceFrame.left = 0;
mSurfaceFrame.top = 0;
if (mTranslator == null) {
mSurfaceFrame.right = mWinFrame.width();
mSurfaceFrame.bottom = mWinFrame.height();
} else {
float appInvertedScale = mTranslator.applicationInvertedScale;
mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
}
final int surfaceWidth = mSurfaceFrame.right;
final int surfaceHeight = mSurfaceFrame.bottom;
realSizeChanged = mLastSurfaceWidth != surfaceWidth
|| mLastSurfaceHeight != surfaceHeight;
mLastSurfaceWidth = surfaceWidth;
mLastSurfaceHeight = surfaceHeight;
} finally {
mSurfaceLock.unlock();
}
try {
redrawNeeded |= creating | reportDrawNeeded;
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 || realSizeChanged) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
}
}
if (redrawNeeded) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
} else {
mSurface.release();
}
} finally {
mIsCreating = false;
if (redrawNeeded) {
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, false);
}
/**
* Check to see if the surface has fixed size dimensions or if the surface's
* dimensions are dimensions are dependent on its current layout.
*
* @return true if the surface has dimensions that are fixed in size
* @hide
*/
public boolean isFixedSize() {
return (mRequestedWidth != -1 || mRequestedHeight != -1);
}
private static class MyWindow extends BaseIWindow {
private final WeakReference