content: /*
   * 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 */
      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;
      }
  };
  