| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.cooliris.media; |
| |
| import java.util.ArrayList; |
| |
| import javax.microedition.khronos.opengles.GL11; |
| |
| import android.graphics.Typeface; |
| import android.view.MotionEvent; |
| |
| import com.cooliris.app.App; |
| import com.cooliris.app.Res; |
| |
| public final class PathBarLayer extends Layer { |
| private static final StringTexture.Config sPathFormat = new StringTexture.Config(); |
| private final ArrayList<Component> mComponents = new ArrayList<Component>(); |
| private static final int FILL = Res.drawable.pathbar_bg; |
| private static final int JOIN = Res.drawable.pathbar_join; |
| private static final int CAP = Res.drawable.pathbar_cap; |
| private Component mTouchItem = null; |
| |
| static { |
| sPathFormat.fontSize = 18f * App.PIXEL_DENSITY; |
| } |
| |
| public PathBarLayer() { |
| } |
| |
| public void pushLabel(int icon, String label, Runnable action) { |
| synchronized (mComponents) { |
| mComponents.add(new Component(icon, label, action, 0)); |
| } |
| recomputeComponents(); |
| } |
| |
| public void setAnimatedIcons(final int[] icons) { |
| synchronized (mComponents) { |
| final int numComponents = mComponents.size(); |
| for (int i = 0; i < numComponents; ++i) { |
| final Component component = mComponents.get(i); |
| if (component != null) { |
| if (component.animatedIcons != null) { |
| component.animatedIcons = null; |
| } |
| if (i == numComponents - 1) { |
| component.animatedIcons = icons; |
| } |
| } |
| } |
| } |
| } |
| |
| public void changeLabel(String label) { |
| if (label == null || label.length() == 0) |
| return; |
| Component component = popLabel(); |
| if (component != null) { |
| pushLabel(component.icon, label, component.action); |
| } |
| } |
| |
| public String getCurrentLabel() { |
| final ArrayList<Component> components = mComponents; |
| synchronized (components) { |
| int lastIndex = components.size() - 1; |
| if (lastIndex < 0) { |
| return ""; |
| } |
| Component retVal = components.get(lastIndex); |
| return retVal.origString; |
| } |
| } |
| |
| public Component popLabel() { |
| final ArrayList<Component> components = mComponents; |
| synchronized (components) { |
| int lastIndex = components.size() - 1; |
| if (lastIndex < 0) { |
| return null; |
| } |
| Component retVal = components.get(lastIndex); |
| components.remove(lastIndex); |
| return retVal; |
| } |
| } |
| |
| private static final class Component { |
| public String origString; |
| public int icon; |
| public Runnable action; |
| public StringTexture texture; |
| public float width; |
| public float animWidth; |
| public float x; |
| public int[] animatedIcons; |
| public float timeElapsed; |
| private static final float ICON_WIDTH = 38.0f; |
| |
| Component(int icon, String label, Runnable action, float widthLeft) { |
| this.action = action; |
| origString = label; |
| this.icon = icon; |
| computeLabel(widthLeft); |
| } |
| |
| public final void computeLabel(float widthLeft) { |
| Typeface typeface = sPathFormat.bold ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT; |
| String label = ""; |
| if (origString != null) { |
| label = origString.substring(0, StringTexture.lengthToFit(sPathFormat.fontSize, widthLeft, typeface, origString)); |
| if (label.length() != origString.length()) { |
| label += "..."; |
| } |
| } |
| this.texture = new StringTexture(label, sPathFormat); |
| } |
| |
| public final boolean update(float timeElapsed) { |
| this.timeElapsed += timeElapsed; |
| if (animWidth == 0.0f) { |
| animWidth = width; |
| } |
| animWidth = FloatUtils.animate(animWidth, width, timeElapsed); |
| if (animatedIcons != null && animatedIcons.length > 1) |
| return true; |
| if (animWidth == width) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| public float getIconWidth() { |
| return ICON_WIDTH * App.PIXEL_DENSITY; |
| } |
| } |
| |
| @Override |
| public void generate(RenderView view, RenderView.Lists lists) { |
| lists.blendedList.add(this); |
| lists.hitTestList.add(this); |
| lists.updateList.add(this); |
| } |
| |
| private void layout() { |
| synchronized (mComponents) { |
| int numComponents = mComponents.size(); |
| for (int i = 0; i < numComponents; ++i) { |
| Component component = mComponents.get(i); |
| if (component == null) |
| continue; |
| float iconWidth = (component.icon == 0) ? 0 : component.getIconWidth(); |
| if (iconWidth == 0) { |
| iconWidth = 8 * App.PIXEL_DENSITY; |
| } |
| float offset = 5 * App.PIXEL_DENSITY; |
| float thisComponentWidth = (i != numComponents - 1) ? iconWidth + offset : component.texture.computeTextWidth() |
| + iconWidth + offset; |
| component.width = thisComponentWidth; |
| } |
| } |
| } |
| |
| @Override |
| public boolean update(RenderView view, float timeElapsed) { |
| layout(); |
| boolean retVal = false; |
| synchronized (mComponents) { |
| int numComponents = mComponents.size(); |
| for (int i = 0; i < numComponents; i++) { |
| Component component = mComponents.get(i); |
| retVal |= component.update(timeElapsed); |
| } |
| } |
| return retVal; |
| } |
| |
| @Override |
| public void renderBlended(RenderView view, GL11 gl) { |
| // Draw components. |
| final Texture fill = view.getResource(FILL); |
| final Texture join = view.getResource(JOIN); |
| final Texture cap = view.getResource(CAP); |
| final float y = mY + 3; |
| int x = (int) (3 * App.PIXEL_DENSITY); |
| float height = mHeight; |
| synchronized (mComponents) { |
| int numComponents = mComponents.size(); |
| for (int i = 0; i < numComponents; ++i) { |
| Component component = mComponents.get(i); |
| component.x = x; |
| // Draw the left join if not the first component, and the fill. |
| // TODO: Draw the pressed background for mTouchItem. |
| final int width = (int) component.animWidth; |
| if (i != 0) { |
| view.draw2D(join, x - join.getWidth(), y); |
| if (view.bind(fill)) { |
| view.draw2D(x, y, 0f, width, height); |
| } |
| } else if (view.bind(fill)) { |
| view.draw2D(0f, y, 0f, x + width, height); |
| } |
| |
| if (i == numComponents - 1) { |
| // Draw the cap on the right edge. |
| view.draw2D(cap, x + width, y); |
| } |
| float xOffset = 5 * App.PIXEL_DENSITY; |
| // Draw the label. |
| final int[] icons = component.animatedIcons; |
| |
| // Cycles animated icons. |
| final int iconId = (icons != null && icons.length > 0) ? icons[(int) (component.timeElapsed * 20.0f) % icons.length] |
| : component.icon; |
| final Texture icon = view.getResource(iconId); |
| if (icon != null) { |
| view.loadTexture(icon); |
| view.draw2D(icon, x + xOffset, y - 2 * App.PIXEL_DENSITY); |
| } |
| if (i == numComponents - 1) { |
| final StringTexture texture = component.texture; |
| view.loadTexture(texture); |
| float iconWidth = component.getIconWidth(); |
| if (texture.computeTextWidth() <= (width - iconWidth)) { |
| float textOffset = (iconWidth == 0) ? 8 * App.PIXEL_DENSITY : iconWidth; |
| view.draw2D(texture, x + textOffset, y + 5); |
| } |
| } |
| x += (int) (width + (21 * App.PIXEL_DENSITY + 0.5f)); |
| } |
| } |
| } |
| |
| private Component hitTestItems(float x, float y) { |
| if (y >= mY && y < mY + mHeight) { |
| synchronized (mComponents) { |
| int numComponents = mComponents.size(); |
| for (int i = 0; i < numComponents; i++) { |
| final Component component = mComponents.get(i); |
| float componentx = component.x; |
| if (x >= componentx && x < componentx + component.width) { |
| return component; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent event) { |
| final float x = event.getX(); |
| final float y = event.getY(); |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| mTouchItem = hitTestItems(x, y); |
| break; |
| case MotionEvent.ACTION_MOVE: |
| break; |
| case MotionEvent.ACTION_UP: |
| if (mTouchItem != null) { |
| mTouchItem.action.run(); |
| } |
| case MotionEvent.ACTION_CANCEL: |
| mTouchItem = null; |
| break; |
| } |
| return true; |
| } |
| |
| public void recomputeComponents() { |
| float width = mWidth; |
| width -= 20f * App.PIXEL_DENSITY; |
| synchronized (mComponents) { |
| int numComponents = mComponents.size(); |
| for (int i = 0; i < numComponents; i++) { |
| Component component = mComponents.get(i); |
| width -= (component.getIconWidth() + 20.0f * App.PIXEL_DENSITY); |
| component.computeLabel(width); |
| } |
| } |
| } |
| |
| public int getNumLevels() { |
| synchronized (mComponents) { |
| return mComponents.size(); |
| } |
| } |
| |
| public void clear() { |
| synchronized (mComponents) { |
| mComponents.clear(); |
| } |
| } |
| } |