/*
 * 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<SurfaceHolder.Callback> mCallbacks
            = new ArrayList<SurfaceHolder.Callback>();
    SurfaceHolder.Callback[] mGottenCallbacks;
    boolean mHaveGottenCallbacks;
    
    public final ReentrantLock mSurfaceLock = new ReentrantLock();
    public Surface mSurface = new Surface();

    int mRequestedWidth = -1;
    int mRequestedHeight = -1;
    
@hide
/** @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; } };