1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.view;
18
19import android.graphics.Canvas;
20import android.graphics.PixelFormat;
21import android.graphics.Rect;
22import android.os.SystemClock;
23import android.util.Log;
24import android.view.Surface;
25import android.view.SurfaceHolder;
26
27import java.util.ArrayList;
28import java.util.concurrent.locks.ReentrantLock;
29
30public abstract class BaseSurfaceHolder implements SurfaceHolder {
31    private static final String TAG = "BaseSurfaceHolder";
32    static final boolean DEBUG = false;
33
34    public final ArrayList<SurfaceHolder.Callback> mCallbacks
35            = new ArrayList<SurfaceHolder.Callback>();
36    SurfaceHolder.Callback[] mGottenCallbacks;
37    boolean mHaveGottenCallbacks;
38
39    public final ReentrantLock mSurfaceLock = new ReentrantLock();
40    public Surface mSurface = new Surface();
41
42    int mRequestedWidth = -1;
43    int mRequestedHeight = -1;
44    /** @hide */
45    protected int mRequestedFormat = PixelFormat.OPAQUE;
46    int mRequestedType = -1;
47
48    long mLastLockTime = 0;
49
50    int mType = -1;
51    final Rect mSurfaceFrame = new Rect();
52    Rect mTmpDirty;
53
54    public abstract void onUpdateSurface();
55    public abstract void onRelayoutContainer();
56    public abstract boolean onAllowLockCanvas();
57
58    public int getRequestedWidth() {
59        return mRequestedWidth;
60    }
61
62    public int getRequestedHeight() {
63        return mRequestedHeight;
64    }
65
66    public int getRequestedFormat() {
67        return mRequestedFormat;
68    }
69
70    public int getRequestedType() {
71        return mRequestedType;
72    }
73
74    public void addCallback(Callback callback) {
75        synchronized (mCallbacks) {
76            // This is a linear search, but in practice we'll
77            // have only a couple callbacks, so it doesn't matter.
78            if (mCallbacks.contains(callback) == false) {
79                mCallbacks.add(callback);
80            }
81        }
82    }
83
84    public void removeCallback(Callback callback) {
85        synchronized (mCallbacks) {
86            mCallbacks.remove(callback);
87        }
88    }
89
90    public SurfaceHolder.Callback[] getCallbacks() {
91        if (mHaveGottenCallbacks) {
92            return mGottenCallbacks;
93        }
94
95        synchronized (mCallbacks) {
96            final int N = mCallbacks.size();
97            if (N > 0) {
98                if (mGottenCallbacks == null || mGottenCallbacks.length != N) {
99                    mGottenCallbacks = new SurfaceHolder.Callback[N];
100                }
101                mCallbacks.toArray(mGottenCallbacks);
102            } else {
103                mGottenCallbacks = null;
104            }
105            mHaveGottenCallbacks = true;
106        }
107
108        return mGottenCallbacks;
109    }
110
111    public void ungetCallbacks() {
112        mHaveGottenCallbacks = false;
113    }
114
115    public void setFixedSize(int width, int height) {
116        if (mRequestedWidth != width || mRequestedHeight != height) {
117            mRequestedWidth = width;
118            mRequestedHeight = height;
119            onRelayoutContainer();
120        }
121    }
122
123    public void setSizeFromLayout() {
124        if (mRequestedWidth != -1 || mRequestedHeight != -1) {
125            mRequestedWidth = mRequestedHeight = -1;
126            onRelayoutContainer();
127        }
128    }
129
130    public void setFormat(int format) {
131        if (mRequestedFormat != format) {
132            mRequestedFormat = format;
133            onUpdateSurface();
134        }
135    }
136
137    public void setType(int type) {
138        switch (type) {
139        case SURFACE_TYPE_HARDWARE:
140        case SURFACE_TYPE_GPU:
141            // these are deprecated, treat as "NORMAL"
142            type = SURFACE_TYPE_NORMAL;
143            break;
144        }
145        switch (type) {
146        case SURFACE_TYPE_NORMAL:
147        case SURFACE_TYPE_PUSH_BUFFERS:
148            if (mRequestedType != type) {
149                mRequestedType = type;
150                onUpdateSurface();
151            }
152            break;
153        }
154    }
155
156    public Canvas lockCanvas() {
157        return internalLockCanvas(null);
158    }
159
160    public Canvas lockCanvas(Rect dirty) {
161        return internalLockCanvas(dirty);
162    }
163
164    private final Canvas internalLockCanvas(Rect dirty) {
165        if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
166            throw new BadSurfaceTypeException(
167                    "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
168        }
169        mSurfaceLock.lock();
170
171        if (DEBUG) Log.i(TAG, "Locking canvas..,");
172
173        Canvas c = null;
174        if (onAllowLockCanvas()) {
175            if (dirty == null) {
176                if (mTmpDirty == null) {
177                    mTmpDirty = new Rect();
178                }
179                mTmpDirty.set(mSurfaceFrame);
180                dirty = mTmpDirty;
181            }
182
183            try {
184                c = mSurface.lockCanvas(dirty);
185            } catch (Exception e) {
186                Log.e(TAG, "Exception locking surface", e);
187            }
188        }
189
190        if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
191        if (c != null) {
192            mLastLockTime = SystemClock.uptimeMillis();
193            return c;
194        }
195
196        // If the Surface is not ready to be drawn, then return null,
197        // but throttle calls to this function so it isn't called more
198        // than every 100ms.
199        long now = SystemClock.uptimeMillis();
200        long nextTime = mLastLockTime + 100;
201        if (nextTime > now) {
202            try {
203                Thread.sleep(nextTime-now);
204            } catch (InterruptedException e) {
205            }
206            now = SystemClock.uptimeMillis();
207        }
208        mLastLockTime = now;
209        mSurfaceLock.unlock();
210
211        return null;
212    }
213
214    public void unlockCanvasAndPost(Canvas canvas) {
215        mSurface.unlockCanvasAndPost(canvas);
216        mSurfaceLock.unlock();
217    }
218
219    public Surface getSurface() {
220        return mSurface;
221    }
222
223    public Rect getSurfaceFrame() {
224        return mSurfaceFrame;
225    }
226
227    public void setSurfaceFrameSize(int width, int height) {
228        mSurfaceFrame.top = 0;
229        mSurfaceFrame.left = 0;
230        mSurfaceFrame.right = width;
231        mSurfaceFrame.bottom = height;
232    }
233};
234