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
17package com.android.camera.ui;
18
19import javax.microedition.khronos.opengles.GL11;
20
21import android.graphics.Rect;
22import android.view.MotionEvent;
23import android.view.View.MeasureSpec;
24import android.view.animation.AlphaAnimation;
25
26class IndicatorBar extends GLView {
27
28    public static final int INDEX_NONE = -1;
29
30    private NinePatchTexture mBackground;
31    private Texture mHighlight;
32    private int mSelectedIndex = INDEX_NONE;
33
34    private OnItemSelectedListener mSelectedListener;
35    private boolean mActivated = false;
36
37    private boolean mSelectionChanged = false;
38
39    private class Background extends GLView {
40        @Override
41        protected void render(GLRootView root, GL11 gl) {
42            mBackground.draw(root, 0, 0, getWidth(), getHeight());
43
44            if (mActivated && mSelectedIndex != INDEX_NONE
45                    && mHighlight != null) {
46                Rect bounds = IndicatorBar.this.getComponent(
47                        mSelectedIndex + 1).mBounds;
48                mHighlight.draw(root, bounds.left, bounds.top,
49                        bounds.width(), bounds.height());
50            }
51        }
52    }
53
54    public interface OnItemSelectedListener {
55        public void onItemSelected(GLView view, int position);
56        public void onNothingSelected();
57    }
58
59    public IndicatorBar() {
60        GLView background = new Background();
61        background.setVisibility(GLView.INVISIBLE);
62        addComponent(background);
63    }
64
65    public void overrideSettings(String key, String value) {
66        for (int i = 1, n = getComponentCount(); i < n; ++i) {
67            AbstractIndicator indicator = (AbstractIndicator) getComponent(i);
68            indicator.overrideSettings(key, value);
69        }
70    }
71
72    public void setOnItemSelectedListener(OnItemSelectedListener l) {
73        mSelectedListener = l;
74    }
75
76    public void setBackground(NinePatchTexture background) {
77        if (mBackground == background) return;
78        mBackground = background;
79        if (background != null) {
80            setPaddings(background.getPaddings());
81        } else {
82            setPaddings(0, 0, 0, 0);
83        }
84        invalidate();
85    }
86
87    public void setHighlight(Texture highlight) {
88        if (mHighlight == highlight) return;
89        mHighlight = highlight;
90        invalidate();
91    }
92
93    @Override
94    protected void onMeasure(int widthSpec, int heightSpec) {
95        int width = 0;
96        int height = 0;
97        for (int i = 1, n = getComponentCount(); i < n; ++i) {
98            GLView component = getComponent(i);
99            component.measure(
100                    MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
101            width = Math.max(width, component.getMeasuredWidth());
102            height += component.getMeasuredHeight();
103        }
104        new MeasureHelper(this)
105                .setPreferredContentSize(width, height)
106                .measure(widthSpec, heightSpec);
107    }
108
109    @Override
110    protected void onLayout(
111            boolean changed, int left, int top, int right, int bottom) {
112        // Background
113        getComponent(0).layout(0, 0, right - left, bottom - top);
114
115        int count = getComponentCount();
116        Rect p = mPaddings;
117        int cBottom = bottom - top - p.bottom;
118        int cRight = right - left - p.right;
119        int yoffset = mPaddings.top;
120        int xoffset = mPaddings.left;
121        for (int i = 1; i < count; ++i) {
122            int cHeight = (cBottom - yoffset) / (count - i);
123            int nextYoffset = yoffset + cHeight;
124            getComponent(i).layout(xoffset, yoffset, cRight, nextYoffset);
125            yoffset = nextYoffset;
126        }
127    }
128
129    private void setSelectedItem(GLView view, int index) {
130        if (index == mSelectedIndex) return;
131        mSelectionChanged = true;
132        mSelectedIndex = index;
133        if (mSelectedListener != null) {
134            if (index == INDEX_NONE) {
135                mSelectedListener.onNothingSelected();
136            } else {
137                mSelectedListener.onItemSelected(view, index);
138            }
139        }
140        invalidate();
141    }
142
143    public void setSelectedIndex(int index) {
144        if (index == mSelectedIndex) return;
145        setSelectedItem(index == INDEX_NONE ? null :getComponent(index), index);
146    }
147
148    public void setActivated(boolean activated) {
149        if (activated == mActivated) return;
150        mActivated = activated;
151        if (activated) {
152            GLView background = getComponent(0);
153            background.setVisibility(GLView.VISIBLE);
154            AlphaAnimation anim = new AlphaAnimation(0, 1);
155            anim.setDuration(200);
156            background.startAnimation(anim);
157        } else {
158            GLView background = getComponent(0);
159            background.setVisibility(GLView.INVISIBLE);
160            AlphaAnimation anim = new AlphaAnimation(1, 0);
161            anim.setDuration(200);
162            background.startAnimation(anim);
163        }
164    }
165
166    public boolean isActivated() {
167        return mActivated;
168    }
169
170    @Override
171    protected boolean dispatchTouchEvent(MotionEvent event) {
172        // Do not pass motion events to children
173        return onTouch(event);
174    }
175
176    @Override @SuppressWarnings("fallthrough")
177    protected boolean onTouch(MotionEvent event) {
178        int y = (int) event.getY();
179        switch (event.getAction()) {
180            case MotionEvent.ACTION_DOWN:
181                mSelectionChanged = false;
182                setActivated(true);
183            case MotionEvent.ACTION_MOVE:
184                for (int i = 1, n = getComponentCount(); i < n; ++i) {
185                    GLView component = getComponent(i);
186                    if (y <= component.mBounds.bottom) {
187                        setSelectedItem(component, i - 1);
188                        return true;
189                    }
190                }
191                setSelectedItem(null, INDEX_NONE);
192                break;
193            case MotionEvent.ACTION_UP:
194                if (mSelectionChanged == false) {
195                    setSelectedItem(null, INDEX_NONE);
196                }
197        }
198        return true;
199    }
200
201    public void reloadPreferences() {
202        for (int i = 1, n = getComponentCount(); i < n; ++i) {
203            ((AbstractIndicator) getComponent(i)).reloadPreferences();
204        }
205    }
206
207    public void setOrientation(int orientation) {
208        for (int i = 1, n = getComponentCount(); i < n; ++i) {
209            ((AbstractIndicator) getComponent(i)).setOrientation(orientation);
210        }
211    }
212
213    public int getSelectedIndex() {
214        return mSelectedIndex;
215    }
216}
217