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    @Override
157    public Canvas lockCanvas() {
158        return internalLockCanvas(null, false);
159    }
160
161    @Override
162    public Canvas lockCanvas(Rect dirty) {
163        return internalLockCanvas(dirty, false);
164    }
165
166    @Override
167    public Canvas lockHardwareCanvas() {
168        return internalLockCanvas(null, true);
169    }
170
171    private final Canvas internalLockCanvas(Rect dirty, boolean hardware) {
172        if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
173            throw new BadSurfaceTypeException(
174                    "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
175        }
176        mSurfaceLock.lock();
177
178        if (DEBUG) Log.i(TAG, "Locking canvas..,");
179
180        Canvas c = null;
181        if (onAllowLockCanvas()) {
182            if (dirty == null) {
183                if (mTmpDirty == null) {
184                    mTmpDirty = new Rect();
185                }
186                mTmpDirty.set(mSurfaceFrame);
187                dirty = mTmpDirty;
188            }
189
190            try {
191                if (hardware) {
192                    c = mSurface.lockHardwareCanvas();
193                } else {
194                    c = mSurface.lockCanvas(dirty);
195                }
196            } catch (Exception e) {
197                Log.e(TAG, "Exception locking surface", e);
198            }
199        }
200
201        if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
202        if (c != null) {
203            mLastLockTime = SystemClock.uptimeMillis();
204            return c;
205        }
206
207        // If the Surface is not ready to be drawn, then return null,
208        // but throttle calls to this function so it isn't called more
209        // than every 100ms.
210        long now = SystemClock.uptimeMillis();
211        long nextTime = mLastLockTime + 100;
212        if (nextTime > now) {
213            try {
214                Thread.sleep(nextTime-now);
215            } catch (InterruptedException e) {
216            }
217            now = SystemClock.uptimeMillis();
218        }
219        mLastLockTime = now;
220        mSurfaceLock.unlock();
221
222        return null;
223    }
224
225    public void unlockCanvasAndPost(Canvas canvas) {
226        mSurface.unlockCanvasAndPost(canvas);
227        mSurfaceLock.unlock();
228    }
229
230    public Surface getSurface() {
231        return mSurface;
232    }
233
234    public Rect getSurfaceFrame() {
235        return mSurfaceFrame;
236    }
237
238    public void setSurfaceFrameSize(int width, int height) {
239        mSurfaceFrame.top = 0;
240        mSurfaceFrame.left = 0;
241        mSurfaceFrame.right = width;
242        mSurfaceFrame.bottom = height;
243    }
244};
245