/* * Copyright (C) 2009 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 com.android.internal.view; import android.graphics.Canvas; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.SystemClock; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; public abstract class BaseSurfaceHolder implements SurfaceHolder { private static final String TAG = "BaseSurfaceHolder"; static final boolean DEBUG = false; public final ArrayList mCallbacks = new ArrayList(); SurfaceHolder.Callback[] mGottenCallbacks; boolean mHaveGottenCallbacks; public final ReentrantLock mSurfaceLock = new ReentrantLock(); public Surface mSurface = new Surface(); int mRequestedWidth = -1; int mRequestedHeight = -1; /** @hide */ protected int mRequestedFormat = PixelFormat.OPAQUE; int mRequestedType = -1; long mLastLockTime = 0; int mType = -1; final Rect mSurfaceFrame = new Rect(); Rect mTmpDirty; public abstract void onUpdateSurface(); public abstract void onRelayoutContainer(); public abstract boolean onAllowLockCanvas(); public int getRequestedWidth() { return mRequestedWidth; } public int getRequestedHeight() { return mRequestedHeight; } public int getRequestedFormat() { return mRequestedFormat; } public int getRequestedType() { return mRequestedType; } 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 SurfaceHolder.Callback[] getCallbacks() { if (mHaveGottenCallbacks) { return mGottenCallbacks; } synchronized (mCallbacks) { final int N = mCallbacks.size(); if (N > 0) { if (mGottenCallbacks == null || mGottenCallbacks.length != N) { mGottenCallbacks = new SurfaceHolder.Callback[N]; } mCallbacks.toArray(mGottenCallbacks); } else { mGottenCallbacks = null; } mHaveGottenCallbacks = true; } return mGottenCallbacks; } public void ungetCallbacks() { mHaveGottenCallbacks = false; } public void setFixedSize(int width, int height) { if (mRequestedWidth != width || mRequestedHeight != height) { mRequestedWidth = width; mRequestedHeight = height; onRelayoutContainer(); } } public void setSizeFromLayout() { if (mRequestedWidth != -1 || mRequestedHeight != -1) { mRequestedWidth = mRequestedHeight = -1; onRelayoutContainer(); } } public void setFormat(int format) { if (mRequestedFormat != format) { mRequestedFormat = format; onUpdateSurface(); } } public void setType(int type) { switch (type) { case SURFACE_TYPE_HARDWARE: case SURFACE_TYPE_GPU: // these are deprecated, treat as "NORMAL" type = SURFACE_TYPE_NORMAL; break; } switch (type) { case SURFACE_TYPE_NORMAL: case SURFACE_TYPE_PUSH_BUFFERS: if (mRequestedType != type) { mRequestedType = type; onUpdateSurface(); } break; } } @Override public Canvas lockCanvas() { return internalLockCanvas(null, false); } @Override public Canvas lockCanvas(Rect dirty) { return internalLockCanvas(dirty, false); } @Override public Canvas lockHardwareCanvas() { return internalLockCanvas(null, true); } private final Canvas internalLockCanvas(Rect dirty, boolean hardware) { if (mType == SURFACE_TYPE_PUSH_BUFFERS) { throw new BadSurfaceTypeException( "Surface type is SURFACE_TYPE_PUSH_BUFFERS"); } mSurfaceLock.lock(); if (DEBUG) Log.i(TAG, "Locking canvas..,"); Canvas c = null; if (onAllowLockCanvas()) { if (dirty == null) { if (mTmpDirty == null) { mTmpDirty = new Rect(); } mTmpDirty.set(mSurfaceFrame); dirty = mTmpDirty; } try { if (hardware) { c = mSurface.lockHardwareCanvas(); } else { c = mSurface.lockCanvas(dirty); } } catch (Exception e) { Log.e(TAG, "Exception locking surface", e); } } if (DEBUG) 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; } public void setSurfaceFrameSize(int width, int height) { mSurfaceFrame.top = 0; mSurfaceFrame.left = 0; mSurfaceFrame.right = width; mSurfaceFrame.bottom = height; } };