Home | History | Annotate | Download | only in media
      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 
     17 package com.cooliris.media;
     18 
     19 import java.util.ArrayList;
     20 
     21 import javax.microedition.khronos.opengles.GL11;
     22 
     23 import android.graphics.Typeface;
     24 import android.view.MotionEvent;
     25 
     26 import com.cooliris.app.App;
     27 import com.cooliris.app.Res;
     28 
     29 public final class PathBarLayer extends Layer {
     30     private static final StringTexture.Config sPathFormat = new StringTexture.Config();
     31     private final ArrayList<Component> mComponents = new ArrayList<Component>();
     32     private static final int FILL = Res.drawable.pathbar_bg;
     33     private static final int JOIN = Res.drawable.pathbar_join;
     34     private static final int CAP = Res.drawable.pathbar_cap;
     35     private Component mTouchItem = null;
     36 
     37     static {
     38         sPathFormat.fontSize = 18f * App.PIXEL_DENSITY;
     39     }
     40 
     41     public PathBarLayer() {
     42     }
     43 
     44     public void pushLabel(int icon, String label, Runnable action) {
     45         synchronized (mComponents) {
     46             mComponents.add(new Component(icon, label, action, 0));
     47         }
     48         recomputeComponents();
     49     }
     50 
     51     public void setAnimatedIcons(final int[] icons) {
     52         synchronized (mComponents) {
     53             final int numComponents = mComponents.size();
     54             for (int i = 0; i < numComponents; ++i) {
     55                 final Component component = mComponents.get(i);
     56                 if (component != null) {
     57                     if (component.animatedIcons != null) {
     58                         component.animatedIcons = null;
     59                     }
     60                     if (i == numComponents - 1) {
     61                         component.animatedIcons = icons;
     62                     }
     63                 }
     64             }
     65         }
     66     }
     67 
     68     public void changeLabel(String label) {
     69         if (label == null || label.length() == 0)
     70             return;
     71         Component component = popLabel();
     72         if (component != null) {
     73             pushLabel(component.icon, label, component.action);
     74         }
     75     }
     76 
     77     public String getCurrentLabel() {
     78         final ArrayList<Component> components = mComponents;
     79         synchronized (components) {
     80             int lastIndex = components.size() - 1;
     81             if (lastIndex < 0) {
     82                 return "";
     83             }
     84             Component retVal = components.get(lastIndex);
     85             return retVal.origString;
     86         }
     87     }
     88 
     89     public Component popLabel() {
     90         final ArrayList<Component> components = mComponents;
     91         synchronized (components) {
     92             int lastIndex = components.size() - 1;
     93             if (lastIndex < 0) {
     94                 return null;
     95             }
     96             Component retVal = components.get(lastIndex);
     97             components.remove(lastIndex);
     98             return retVal;
     99         }
    100     }
    101 
    102     private static final class Component {
    103         public String origString;
    104         public int icon;
    105         public Runnable action;
    106         public StringTexture texture;
    107         public float width;
    108         public float animWidth;
    109         public float x;
    110         public int[] animatedIcons;
    111         public float timeElapsed;
    112         private static final float ICON_WIDTH = 38.0f;
    113 
    114         Component(int icon, String label, Runnable action, float widthLeft) {
    115             this.action = action;
    116             origString = label;
    117             this.icon = icon;
    118             computeLabel(widthLeft);
    119         }
    120 
    121         public final void computeLabel(float widthLeft) {
    122             Typeface typeface = sPathFormat.bold ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;
    123             String label = "";
    124             if (origString != null) {
    125                 label = origString.substring(0, StringTexture.lengthToFit(sPathFormat.fontSize, widthLeft, typeface, origString));
    126                 if (label.length() != origString.length()) {
    127                     label += "...";
    128                 }
    129             }
    130             this.texture = new StringTexture(label, sPathFormat);
    131         }
    132 
    133         public final boolean update(float timeElapsed) {
    134             this.timeElapsed += timeElapsed;
    135             if (animWidth == 0.0f) {
    136                 animWidth = width;
    137             }
    138             animWidth = FloatUtils.animate(animWidth, width, timeElapsed);
    139             if (animatedIcons != null && animatedIcons.length > 1)
    140                 return true;
    141             if (animWidth == width) {
    142                 return false;
    143             } else {
    144                 return true;
    145             }
    146         }
    147 
    148         public float getIconWidth() {
    149             return ICON_WIDTH * App.PIXEL_DENSITY;
    150         }
    151     }
    152 
    153     @Override
    154     public void generate(RenderView view, RenderView.Lists lists) {
    155         lists.blendedList.add(this);
    156         lists.hitTestList.add(this);
    157         lists.updateList.add(this);
    158     }
    159 
    160     private void layout() {
    161         synchronized (mComponents) {
    162             int numComponents = mComponents.size();
    163             for (int i = 0; i < numComponents; ++i) {
    164                 Component component = mComponents.get(i);
    165                 if (component == null)
    166                     continue;
    167                 float iconWidth = (component.icon == 0) ? 0 : component.getIconWidth();
    168                 if (iconWidth == 0) {
    169                     iconWidth = 8 * App.PIXEL_DENSITY;
    170                 }
    171                 float offset = 5 * App.PIXEL_DENSITY;
    172                 float thisComponentWidth = (i != numComponents - 1) ? iconWidth + offset : component.texture.computeTextWidth()
    173                         + iconWidth + offset;
    174                 component.width = thisComponentWidth;
    175             }
    176         }
    177     }
    178 
    179     @Override
    180     public boolean update(RenderView view, float timeElapsed) {
    181         layout();
    182         boolean retVal = false;
    183         synchronized (mComponents) {
    184             int numComponents = mComponents.size();
    185             for (int i = 0; i < numComponents; i++) {
    186                 Component component = mComponents.get(i);
    187                 retVal |= component.update(timeElapsed);
    188             }
    189         }
    190         return retVal;
    191     }
    192 
    193     @Override
    194     public void renderBlended(RenderView view, GL11 gl) {
    195         // Draw components.
    196         final Texture fill = view.getResource(FILL);
    197         final Texture join = view.getResource(JOIN);
    198         final Texture cap = view.getResource(CAP);
    199         final float y = mY + 3;
    200         int x = (int) (3 * App.PIXEL_DENSITY);
    201         float height = mHeight;
    202         synchronized (mComponents) {
    203             int numComponents = mComponents.size();
    204             for (int i = 0; i < numComponents; ++i) {
    205                 Component component = mComponents.get(i);
    206                 component.x = x;
    207                 // Draw the left join if not the first component, and the fill.
    208                 // TODO: Draw the pressed background for mTouchItem.
    209                 final int width = (int) component.animWidth;
    210                 if (i != 0) {
    211                     view.draw2D(join, x - join.getWidth(), y);
    212                     if (view.bind(fill)) {
    213                         view.draw2D(x, y, 0f, width, height);
    214                     }
    215                 } else if (view.bind(fill)) {
    216                     view.draw2D(0f, y, 0f, x + width, height);
    217                 }
    218 
    219                 if (i == numComponents - 1) {
    220                     // Draw the cap on the right edge.
    221                     view.draw2D(cap, x + width, y);
    222                 }
    223                 float xOffset = 5 * App.PIXEL_DENSITY;
    224                 // Draw the label.
    225                 final int[] icons = component.animatedIcons;
    226 
    227                 // Cycles animated icons.
    228                 final int iconId = (icons != null && icons.length > 0) ? icons[(int) (component.timeElapsed * 20.0f) % icons.length]
    229                         : component.icon;
    230                 final Texture icon = view.getResource(iconId);
    231                 if (icon != null) {
    232                     view.loadTexture(icon);
    233                     view.draw2D(icon, x + xOffset, y - 2 * App.PIXEL_DENSITY);
    234                 }
    235                 if (i == numComponents - 1) {
    236                     final StringTexture texture = component.texture;
    237                     view.loadTexture(texture);
    238                     float iconWidth = component.getIconWidth();
    239                     if (texture.computeTextWidth() <= (width - iconWidth)) {
    240                         float textOffset = (iconWidth == 0) ? 8 * App.PIXEL_DENSITY : iconWidth;
    241                         view.draw2D(texture, x + textOffset, y + 5);
    242                     }
    243                 }
    244                 x += (int) (width + (21 * App.PIXEL_DENSITY + 0.5f));
    245             }
    246         }
    247     }
    248 
    249     private Component hitTestItems(float x, float y) {
    250         if (y >= mY && y < mY + mHeight) {
    251             synchronized (mComponents) {
    252                 int numComponents = mComponents.size();
    253                 for (int i = 0; i < numComponents; i++) {
    254                     final Component component = mComponents.get(i);
    255                     float componentx = component.x;
    256                     if (x >= componentx && x < componentx + component.width) {
    257                         return component;
    258                     }
    259                 }
    260             }
    261         }
    262         return null;
    263     }
    264 
    265     @Override
    266     public boolean onTouchEvent(MotionEvent event) {
    267         final float x = event.getX();
    268         final float y = event.getY();
    269         switch (event.getAction()) {
    270         case MotionEvent.ACTION_DOWN:
    271             mTouchItem = hitTestItems(x, y);
    272             break;
    273         case MotionEvent.ACTION_MOVE:
    274             break;
    275         case MotionEvent.ACTION_UP:
    276             if (mTouchItem != null) {
    277                 mTouchItem.action.run();
    278             }
    279         case MotionEvent.ACTION_CANCEL:
    280             mTouchItem = null;
    281             break;
    282         }
    283         return true;
    284     }
    285 
    286     public void recomputeComponents() {
    287         float width = mWidth;
    288         width -= 20f * App.PIXEL_DENSITY;
    289         synchronized (mComponents) {
    290             int numComponents = mComponents.size();
    291             for (int i = 0; i < numComponents; i++) {
    292                 Component component = mComponents.get(i);
    293                 width -= (component.getIconWidth() + 20.0f * App.PIXEL_DENSITY);
    294                 component.computeLabel(width);
    295             }
    296         }
    297     }
    298 
    299     public int getNumLevels() {
    300         synchronized (mComponents) {
    301             return mComponents.size();
    302         }
    303     }
    304 
    305     public void clear() {
    306         synchronized (mComponents) {
    307             mComponents.clear();
    308         }
    309     }
    310 }
    311