1 /* 2 * Copyright (C) 2010 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 17 package com.android.camera.ui; 18 19 import android.graphics.Matrix; 20 import android.graphics.Rect; 21 import android.os.SystemClock; 22 import android.view.MotionEvent; 23 import android.view.animation.Animation; 24 import android.view.animation.Transformation; 25 26 import java.util.ArrayList; 27 28 import javax.microedition.khronos.opengles.GL11; 29 30 public class GLView { 31 @SuppressWarnings("unused") 32 private static final String TAG = "GLView"; 33 34 public static final int VISIBLE = 0; 35 public static final int INVISIBLE = 1; 36 37 public static final int FLAG_INVISIBLE = 1; 38 public static final int FLAG_SET_MEASURED_SIZE = 2; 39 public static final int FLAG_LAYOUT_REQUESTED = 4; 40 41 protected final Rect mBounds = new Rect(); 42 protected final Rect mPaddings = new Rect(); 43 44 private GLRootView mRootView; 45 private GLView mParent; 46 private ArrayList<GLView> mComponents; 47 private GLView mMotionTarget; 48 49 private OnTouchListener mOnTouchListener; 50 private Animation mAnimation; 51 52 protected int mViewFlags = 0; 53 54 protected int mMeasuredWidth = 0; 55 protected int mMeasuredHeight = 0; 56 57 private int mLastWidthSpec = -1; 58 private int mLastHeightSpec = -1; 59 60 protected int mScrollY = 0; 61 protected int mScrollX = 0; 62 protected int mScrollHeight = 0; 63 protected int mScrollWidth = 0; 64 startAnimation(Animation animation)65 public void startAnimation(Animation animation) { 66 GLRootView root = getGLRootView(); 67 if (root == null) throw new IllegalStateException(); 68 69 mAnimation = animation; 70 animation.initialize(getWidth(), 71 getHeight(), mParent.getWidth(), mParent.getHeight()); 72 mAnimation.start(); 73 root.registerLaunchedAnimation(animation); 74 invalidate(); 75 } 76 setVisibility(int visibility)77 public void setVisibility(int visibility) { 78 if (visibility == getVisibility()) return; 79 if (visibility == VISIBLE) { 80 mViewFlags &= ~FLAG_INVISIBLE; 81 } else { 82 mViewFlags |= FLAG_INVISIBLE; 83 } 84 onVisibilityChanged(visibility); 85 invalidate(); 86 } 87 getVisibility()88 public int getVisibility() { 89 return (mViewFlags & FLAG_INVISIBLE) == 0 ? VISIBLE : INVISIBLE; 90 } 91 92 public static interface OnTouchListener { onTouch(GLView view, MotionEvent event)93 public boolean onTouch(GLView view, MotionEvent event); 94 } 95 setBounds(int left, int top, int right, int bottom)96 private boolean setBounds(int left, int top, int right, int bottom) { 97 boolean sizeChanged = (right - left) != (mBounds.right - mBounds.left) 98 || (bottom - top) != (mBounds.bottom - mBounds.top); 99 mBounds.set(left, top, right, bottom); 100 return sizeChanged; 101 } 102 onAddToParent(GLView parent)103 protected void onAddToParent(GLView parent) { 104 // TODO: enable the check 105 // if (mParent != null) throw new IllegalStateException(); 106 mParent = parent; 107 if (parent != null && parent.mRootView != null) { 108 onAttachToRoot(parent.mRootView); 109 } 110 } 111 onRemoveFromParent(GLView parent)112 protected void onRemoveFromParent(GLView parent) { 113 if (parent != null && parent.mMotionTarget == this) { 114 long now = SystemClock.uptimeMillis(); 115 dispatchTouchEvent(MotionEvent.obtain( 116 now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0)); 117 parent.mMotionTarget = null; 118 } 119 onDetachFromRoot(); 120 mParent = null; 121 } 122 clearComponents()123 public void clearComponents() { 124 mComponents = null; 125 } 126 getComponentCount()127 public int getComponentCount() { 128 return mComponents == null ? 0 : mComponents.size(); 129 } 130 getComponent(int index)131 public GLView getComponent(int index) { 132 if (mComponents == null) { 133 throw new ArrayIndexOutOfBoundsException(index); 134 } 135 return mComponents.get(index); 136 } 137 addComponent(GLView component)138 public void addComponent(GLView component) { 139 if (mComponents == null) { 140 mComponents = new ArrayList<GLView>(); 141 } 142 mComponents.add(component); 143 component.onAddToParent(this); 144 } 145 removeComponent(GLView component)146 public boolean removeComponent(GLView component) { 147 if (mComponents == null) return false; 148 if (mComponents.remove(component)) { 149 component.onRemoveFromParent(this); 150 return true; 151 } 152 return false; 153 } 154 bounds()155 public Rect bounds() { 156 return mBounds; 157 } 158 getWidth()159 public int getWidth() { 160 return mBounds.right - mBounds.left; 161 } 162 getHeight()163 public int getHeight() { 164 return mBounds.bottom - mBounds.top; 165 } 166 getGLRootView()167 public GLRootView getGLRootView() { 168 return mRootView; 169 } 170 setOnTouchListener(OnTouchListener listener)171 public void setOnTouchListener(OnTouchListener listener) { 172 mOnTouchListener = listener; 173 } 174 invalidate()175 public void invalidate() { 176 GLRootView root = getGLRootView(); 177 if (root != null) root.requestRender(); 178 } 179 requestLayout()180 public void requestLayout() { 181 mViewFlags |= FLAG_LAYOUT_REQUESTED; 182 if (mParent != null) { 183 mParent.requestLayout(); 184 } else { 185 // Is this a content pane ? 186 GLRootView root = getGLRootView(); 187 if (root != null) root.requestLayoutContentPane(); 188 } 189 } 190 render(GLRootView view, GL11 gl)191 protected void render(GLRootView view, GL11 gl) { 192 renderBackground(view, gl); 193 for (int i = 0, n = getComponentCount(); i < n; ++i) { 194 GLView component = getComponent(i); 195 if (component.getVisibility() != GLView.VISIBLE 196 && component.mAnimation == null) continue; 197 renderChild(view, gl, component); 198 } 199 } 200 renderBackground(GLRootView view, GL11 gl)201 protected void renderBackground(GLRootView view, GL11 gl) { 202 } 203 renderChild(GLRootView root, GL11 gl, GLView component)204 protected void renderChild(GLRootView root, GL11 gl, GLView component) { 205 int xoffset = component.mBounds.left - mScrollX; 206 int yoffset = component.mBounds.top - mScrollY; 207 208 Transformation transform = root.getTransformation(); 209 Matrix matrix = transform.getMatrix(); 210 matrix.preTranslate(xoffset, yoffset); 211 212 Animation anim = component.mAnimation; 213 if (anim != null) { 214 long now = root.currentAnimationTimeMillis(); 215 Transformation temp = root.obtainTransformation(); 216 if (!anim.getTransformation(now, temp)) { 217 component.mAnimation = null; 218 } 219 invalidate(); 220 root.pushTransform(); 221 transform.compose(temp); 222 root.freeTransformation(temp); 223 } 224 component.render(root, gl); 225 if (anim != null) root.popTransform(); 226 matrix.preTranslate(-xoffset, -yoffset); 227 } 228 onTouch(MotionEvent event)229 protected boolean onTouch(MotionEvent event) { 230 if (mOnTouchListener != null) { 231 return mOnTouchListener.onTouch(this, event); 232 } 233 return false; 234 } 235 dispatchTouchEvent(MotionEvent event, int x, int y, GLView component, boolean checkBounds)236 private boolean dispatchTouchEvent(MotionEvent event, 237 int x, int y, GLView component, boolean checkBounds) { 238 Rect rect = component.mBounds; 239 int left = rect.left; 240 int top = rect.top; 241 if (!checkBounds || rect.contains(x, y)) { 242 event.offsetLocation(-left, -top); 243 if (component.dispatchTouchEvent(event)) { 244 event.offsetLocation(left, top); 245 return true; 246 } 247 event.offsetLocation(left, top); 248 } 249 return false; 250 } 251 dispatchTouchEvent(MotionEvent event)252 protected boolean dispatchTouchEvent(MotionEvent event) { 253 int x = (int) event.getX(); 254 int y = (int) event.getY(); 255 int action = event.getAction(); 256 if (mMotionTarget != null) { 257 if (action == MotionEvent.ACTION_DOWN) { 258 MotionEvent cancel = MotionEvent.obtain(event); 259 cancel.setAction(MotionEvent.ACTION_CANCEL); 260 mMotionTarget = null; 261 } else { 262 dispatchTouchEvent(event, x, y, mMotionTarget, false); 263 if (action == MotionEvent.ACTION_CANCEL 264 || action == MotionEvent.ACTION_UP) { 265 mMotionTarget = null; 266 } 267 return true; 268 } 269 } 270 if (action == MotionEvent.ACTION_DOWN) { 271 for (int i = 0, n = getComponentCount(); i < n; ++i) { 272 GLView component = getComponent(i); 273 if (component.getVisibility() != GLView.VISIBLE) continue; 274 if (dispatchTouchEvent(event, x, y, component, true)) { 275 mMotionTarget = component; 276 return true; 277 } 278 } 279 } 280 return onTouch(event); 281 } 282 getPaddings()283 public Rect getPaddings() { 284 return mPaddings; 285 } 286 setPaddings(Rect paddings)287 public void setPaddings(Rect paddings) { 288 mPaddings.set(paddings); 289 } 290 setPaddings(int left, int top, int right, int bottom)291 public void setPaddings(int left, int top, int right, int bottom) { 292 mPaddings.set(left, top, right, bottom); 293 } 294 layout(int left, int top, int right, int bottom)295 public void layout(int left, int top, int right, int bottom) { 296 boolean sizeChanged = setBounds(left, top, right, bottom); 297 if (sizeChanged) { 298 mViewFlags &= ~FLAG_LAYOUT_REQUESTED; 299 onLayout(true, left, top, right, bottom); 300 } else if ((mViewFlags & FLAG_LAYOUT_REQUESTED)!= 0) { 301 mViewFlags &= ~FLAG_LAYOUT_REQUESTED; 302 onLayout(false, left, top, right, bottom); 303 } 304 } 305 measure(int widthSpec, int heightSpec)306 public void measure(int widthSpec, int heightSpec) { 307 if (widthSpec == mLastWidthSpec && heightSpec == mLastHeightSpec 308 && (mViewFlags & FLAG_LAYOUT_REQUESTED) == 0) { 309 return; 310 } 311 312 mLastWidthSpec = widthSpec; 313 mLastHeightSpec = heightSpec; 314 315 mViewFlags &= ~FLAG_SET_MEASURED_SIZE; 316 onMeasure(widthSpec, heightSpec); 317 if ((mViewFlags & FLAG_SET_MEASURED_SIZE) == 0) { 318 throw new IllegalStateException(getClass().getName() 319 + " should call setMeasuredSize() in onMeasure()"); 320 } 321 } 322 onMeasure(int widthSpec, int heightSpec)323 protected void onMeasure(int widthSpec, int heightSpec) { 324 } 325 setMeasuredSize(int width, int height)326 protected void setMeasuredSize(int width, int height) { 327 mViewFlags |= FLAG_SET_MEASURED_SIZE; 328 mMeasuredWidth = width; 329 mMeasuredHeight = height; 330 } 331 getMeasuredWidth()332 public int getMeasuredWidth() { 333 return mMeasuredWidth; 334 } 335 getMeasuredHeight()336 public int getMeasuredHeight() { 337 return mMeasuredHeight; 338 } 339 onLayout( boolean changeSize, int left, int top, int right, int bottom)340 protected void onLayout( 341 boolean changeSize, int left, int top, int right, int bottom) { 342 } 343 344 /** 345 * Gets the bounds of the given descendant that relative to this view. 346 */ getBoundsOf(GLView descendant, Rect out)347 public boolean getBoundsOf(GLView descendant, Rect out) { 348 int xoffset = 0; 349 int yoffset = 0; 350 GLView view = descendant; 351 while (view != this) { 352 if (view == null) return false; 353 Rect bounds = view.mBounds; 354 xoffset += bounds.left; 355 yoffset += bounds.top; 356 view = view.mParent; 357 } 358 out.set(xoffset, yoffset, xoffset + descendant.getWidth(), 359 yoffset + descendant.getHeight()); 360 return true; 361 } 362 onVisibilityChanged(int visibility)363 protected void onVisibilityChanged(int visibility) { 364 for (int i = 0, n = getComponentCount(); i < n; ++i) { 365 GLView child = getComponent(i); 366 if (child.getVisibility() == GLView.VISIBLE) { 367 child.onVisibilityChanged(visibility); 368 } 369 } 370 } 371 onAttachToRoot(GLRootView root)372 protected void onAttachToRoot(GLRootView root) { 373 mRootView = root; 374 for (int i = 0, n = getComponentCount(); i < n; ++i) { 375 getComponent(i).onAttachToRoot(root); 376 } 377 } 378 onDetachFromRoot()379 protected void onDetachFromRoot() { 380 for (int i = 0, n = getComponentCount(); i < n; ++i) { 381 getComponent(i).onDetachFromRoot(); 382 } 383 mRootView = null; 384 } 385 } 386