Home | History | Annotate | Download | only in ui
      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 
     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 
     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 
     88     public int getVisibility() {
     89         return (mViewFlags & FLAG_INVISIBLE) == 0 ? VISIBLE : INVISIBLE;
     90     }
     91 
     92     public static interface OnTouchListener {
     93         public boolean onTouch(GLView view, MotionEvent event);
     94     }
     95 
     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 
    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 
    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 
    123     public void clearComponents() {
    124         mComponents = null;
    125     }
    126 
    127     public int getComponentCount() {
    128         return mComponents == null ? 0 : mComponents.size();
    129     }
    130 
    131     public GLView getComponent(int index) {
    132         if (mComponents == null) {
    133             throw new ArrayIndexOutOfBoundsException(index);
    134         }
    135         return mComponents.get(index);
    136     }
    137 
    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 
    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 
    155     public Rect bounds() {
    156         return mBounds;
    157     }
    158 
    159     public int getWidth() {
    160         return mBounds.right - mBounds.left;
    161     }
    162 
    163     public int getHeight() {
    164         return mBounds.bottom - mBounds.top;
    165     }
    166 
    167     public GLRootView getGLRootView() {
    168         return mRootView;
    169     }
    170 
    171     public void setOnTouchListener(OnTouchListener listener) {
    172         mOnTouchListener = listener;
    173     }
    174 
    175     public void invalidate() {
    176         GLRootView root = getGLRootView();
    177         if (root != null) root.requestRender();
    178     }
    179 
    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 
    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 
    201     protected void renderBackground(GLRootView view, GL11 gl) {
    202     }
    203 
    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 
    229     protected boolean onTouch(MotionEvent event) {
    230         if (mOnTouchListener != null) {
    231             return mOnTouchListener.onTouch(this, event);
    232         }
    233         return false;
    234     }
    235 
    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 
    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 
    283     public Rect getPaddings() {
    284         return mPaddings;
    285     }
    286 
    287     public void setPaddings(Rect paddings) {
    288         mPaddings.set(paddings);
    289     }
    290 
    291     public void setPaddings(int left, int top, int right, int bottom) {
    292         mPaddings.set(left, top, right, bottom);
    293     }
    294 
    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 
    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 
    323     protected void onMeasure(int widthSpec, int heightSpec) {
    324     }
    325 
    326     protected void setMeasuredSize(int width, int height) {
    327         mViewFlags |= FLAG_SET_MEASURED_SIZE;
    328         mMeasuredWidth = width;
    329         mMeasuredHeight = height;
    330     }
    331 
    332     public int getMeasuredWidth() {
    333         return mMeasuredWidth;
    334     }
    335 
    336     public int getMeasuredHeight() {
    337         return mMeasuredHeight;
    338     }
    339 
    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      */
    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 
    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 
    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 
    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