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 com.android.camera.IconListPreference;
20import com.android.camera.PreferenceGroup;
21import com.android.camera.R;
22import com.android.camera.Util;
23
24import android.content.Context;
25import android.util.AttributeSet;
26import android.view.MotionEvent;
27import android.view.View;
28import android.widget.ImageView;
29
30/**
31 * A view that contains camera setting indicators which are spread over a
32 * vertical bar in preview frame.
33 */
34public class SecondLevelIndicatorControlBar extends IndicatorControl implements
35        View.OnClickListener, AbstractIndicatorButton.IndicatorChangeListener {
36    private static final String TAG = "SecondLevelIndicatorControlBar";
37    private static int ICON_SPACING = Util.dpToPixel(16);
38    private View mCloseIcon;
39    private View mDivider; // the divider line
40    private View mPopupedIndicator;
41    int mOrientation = 0;
42    int mSelectedIndex = -1;
43    // There are some views in the ViewGroup before adding the indicator buttons,
44    // such as Close icon, divider line and the hightlight bar, we need to
45    // remember the count of the non-indicator buttons for getTouchViewIndex().
46    int mNonIndicatorButtonCount;
47
48    public SecondLevelIndicatorControlBar(Context context, AttributeSet attrs) {
49        super(context, attrs);
50    }
51
52    @Override
53    protected void onFinishInflate() {
54        mDivider = findViewById(R.id.divider);
55        mCloseIcon = findViewById(R.id.back_to_first_level);
56        mCloseIcon.setOnClickListener(this);
57    }
58
59    public void initialize(Context context, PreferenceGroup group,
60            String[] keys, String[] otherSettingKeys) {
61
62        setPreferenceGroup(group);
63        mNonIndicatorButtonCount = getChildCount();
64        addControls(keys, otherSettingKeys);
65        if (mOrientation != 0) setOrientation(mOrientation);
66    }
67
68    public void onClick(View view) {
69        dismissSettingPopup();
70        mOnIndicatorEventListener.onIndicatorEvent(
71                    OnIndicatorEventListener.EVENT_LEAVE_SECOND_LEVEL_INDICATOR_BAR);
72    }
73
74    private int getTouchViewIndex(int x, int width) {
75        // If the current touch location is on close icon and above.
76        if (x > mCloseIcon.getLeft()) return indexOfChild(mCloseIcon);
77
78        // Calculate if the touch event is on the indicator buttons.
79        int count = getChildCount();
80        if (count == mNonIndicatorButtonCount) return -1;
81        // The baseline will be the first indicator button's top minus spacing.
82        View firstIndicatorButton = getChildAt(mNonIndicatorButtonCount);
83        int baselineX = firstIndicatorButton.getRight() + (ICON_SPACING / 2);
84        if (x > baselineX) return -1;
85        int iconWidth = firstIndicatorButton.getMeasuredWidth();
86        int buttonRange = iconWidth + ICON_SPACING;
87        return (mNonIndicatorButtonCount + ((baselineX - x) / buttonRange));
88    }
89
90    private void dispatchRelativeTouchEvent(View view, MotionEvent event) {
91        event.offsetLocation(-view.getLeft(), -view.getTop());
92        view.dispatchTouchEvent(event);
93        event.offsetLocation(view.getLeft(), view.getTop());
94    }
95
96    @Override
97    public boolean dispatchTouchEvent(MotionEvent event) {
98        if (!onFilterTouchEventForSecurity(event)) return false;
99
100        int action = event.getAction();
101        if (!isEnabled()) return false;
102
103        double x = (double) event.getX();
104        int width = getWidth();
105        if (width == 0) return false; // the event is sent before onMeasure()
106        if (x > width) x = width;
107
108        int index = getTouchViewIndex((int) x, width);
109
110        // Cancel the previous target if we moved out of it
111        if ((mSelectedIndex != -1) && (index != mSelectedIndex)) {
112            View p = getChildAt(mSelectedIndex);
113
114            int oldAction = event.getAction();
115            event.setAction(MotionEvent.ACTION_CANCEL);
116            dispatchRelativeTouchEvent(p, event);
117            event.setAction(oldAction);
118
119            if (p instanceof AbstractIndicatorButton) {
120                AbstractIndicatorButton b = (AbstractIndicatorButton) p;
121                b.dismissPopup();
122            }
123        }
124
125        // Send event to the target
126        View v = getChildAt(index);
127        if (v == null) return true;
128
129        // Change MOVE to DOWN if this is a new target
130        if (mSelectedIndex != index && action == MotionEvent.ACTION_MOVE) {
131            event.setAction(MotionEvent.ACTION_DOWN);
132        }
133        dispatchRelativeTouchEvent(v, event);
134        mSelectedIndex = index;
135        return true;
136    }
137
138    @Override
139    public IndicatorButton addIndicator(Context context, IconListPreference pref) {
140        IndicatorButton b = super.addIndicator(context, pref);
141        b.setBackgroundResource(R.drawable.bg_pressed);
142        b.setIndicatorChangeListener(this);
143        return b;
144    }
145
146    @Override
147    public OtherSettingIndicatorButton addOtherSettingIndicator(Context context,
148            int resId, String[] keys) {
149        OtherSettingIndicatorButton b =
150                super.addOtherSettingIndicator(context, resId, keys);
151        b.setBackgroundResource(R.drawable.bg_pressed);
152        b.setIndicatorChangeListener(this);
153        return b;
154    }
155
156    @Override
157    public void onShowIndicator(View view, boolean showed) {
158        // Ignore those events if not current popup.
159        if (!showed && (mPopupedIndicator != view)) return;
160        mPopupedIndicator = (showed ? view : null);
161        // Show or dismiss the side indicator highlight.
162        requestLayout();
163    }
164
165    @Override
166    public void setOrientation(int orientation) {
167        mOrientation = orientation;
168        super.setOrientation(orientation);
169    }
170
171    @Override
172    protected void onLayout(
173            boolean changed, int left, int top, int right, int bottom) {
174        int count = getChildCount();
175        if (count == 0) return;
176        int width = right - left;
177        int height = bottom - top;
178        int iconWidth = mCloseIcon.getMeasuredWidth();
179        int padding = getPaddingLeft();
180
181        // Layout from the last icon up.
182        int offsetX = padding;
183        int increment = iconWidth + ICON_SPACING;
184        for (int i = count - 1; i >= mNonIndicatorButtonCount; --i) {
185            getChildAt(i).layout(offsetX, 0, offsetX + iconWidth, height);
186            offsetX += increment;
187        }
188
189        // And layout the divider line.
190        offsetX = width - iconWidth - 2 * padding;
191        mDivider.layout(offsetX, padding, (offsetX + mDivider.getMeasuredWidth()),
192                (height - padding));
193
194        offsetX = width - iconWidth - padding;
195        // The first icon is close button.
196        mCloseIcon.layout(offsetX, 0, (offsetX + iconWidth), height);
197   }
198
199    @Override
200    public void setEnabled(boolean enabled) {
201        super.setEnabled(enabled);
202        if (mCurrentMode == MODE_VIDEO) {
203            mCloseIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
204        }
205        mCloseIcon.setEnabled(enabled);
206    }
207}
208