BottomBar.java revision 3b92e2f8e6d91af1b83c8bee95bd93e7153b96da
1/*
2 * Copyright (C) 2013 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 android.content.Context;
20import android.content.res.Configuration;
21import android.util.AttributeSet;
22import android.view.Gravity;
23import android.view.MotionEvent;
24import android.view.View;
25import android.view.ViewGroup;
26import android.widget.FrameLayout;
27
28import com.android.camera2.R;
29import com.android.camera.ToggleImageButton;
30
31/**
32 * BottomBar swaps its width and height on rotation. In addition, it also changes
33 * gravity and layout orientation based on the new orientation. Specifically, in
34 * landscape it aligns to the right side of its parent and lays out its children
35 * vertically, whereas in portrait, it stays at the bottom of the parent and has
36 * a horizontal layout orientation.
37 */
38public class BottomBar extends FrameLayout
39        implements PreviewStatusListener.PreviewAreaSizeChangedListener {
40
41    private static final String TAG = "BottomBar";
42    private int mWidth;
43    private int mHeight;
44    private float mOffsetShorterEdge;
45    private float mOffsetLongerEdge;
46
47    private final int mOptimalHeight;
48    private boolean mOverLayBottomBar;
49
50    private TopRightMostOverlay mSettingsOverlay;
51    private TopRightWeightedLayout mSettingsLayout;
52    private FrameLayout mCaptureLayout;
53    private TopRightWeightedLayout mIntentLayout;
54
55    public BottomBar(Context context, AttributeSet attrs) {
56        super(context, attrs);
57        mOptimalHeight = getResources().getDimensionPixelSize(R.dimen.bottom_bar_height_optimal);
58    }
59
60    @Override
61    public void onFinishInflate() {
62        mSettingsOverlay
63            = (TopRightMostOverlay) findViewById(R.id.bottombar_settings_overlay);
64        mSettingsLayout
65            = (TopRightWeightedLayout) findViewById(R.id.bottombar_settings);
66        mCaptureLayout
67            = (FrameLayout) findViewById(R.id.bottombar_capture);
68        mIntentLayout
69            = (TopRightWeightedLayout) findViewById(R.id.bottombar_intent);
70    }
71
72    /**
73     * Initializes the bottom bar toggle for switching between
74     * capture and the bottom bar settings toggles.
75     */
76    public void setupToggle(final boolean isCaptureIntent) {
77        // Of type ToggleImageButton because ToggleButton
78        // has a non-removable spacing for text on the right-hand side.
79        ToggleImageButton toggle = (ToggleImageButton) findViewById(R.id.bottombar_settings_toggle);
80        toggle.setState(0, false);
81        toggle.setOnStateChangeListener(new ToggleImageButton.OnStateChangeListener() {
82            @Override
83            public void stateChanged(View view, boolean toSettings) {
84                if (toSettings) {
85                    if (isCaptureIntent) {
86                        hideIntentLayout();
87                    }
88                    transitionToSettings();
89                } else {
90                    if (isCaptureIntent) {
91                        transitionToIntentLayout();
92                    } else {
93                        transitionToCapture();
94                    }
95                }
96            }
97        });
98        mSettingsOverlay.setReferenceViewParent(mSettingsLayout);
99    }
100
101    /**
102     * Hide the intent layout.  This is necessary for switching between
103     * the intent capture layout and the bottom bar settings.
104     */
105    private void hideIntentLayout() {
106        mIntentLayout.setVisibility(View.INVISIBLE);
107    }
108
109    /**
110     * Perform a transition from the bottom bar capture layout to the
111     * bottom bar settings layout.
112     */
113    private void transitionToCapture() {
114        mSettingsLayout.setVisibility(View.INVISIBLE);
115        mCaptureLayout.setVisibility(View.VISIBLE);
116    }
117
118    /**
119     * Perform a transition from the bottom bar settings layout to the
120     * bottom bar capture layout.
121     */
122    private void transitionToSettings() {
123        mSettingsLayout.setVisibility(View.VISIBLE);
124        mCaptureLayout.setVisibility(View.INVISIBLE);
125    }
126
127    /**
128     * Perform a transition to the global intent layout.  The current
129     * layout state of the bottom bar is irrelevant.
130     */
131    public void transitionToIntentLayout() {
132        mCaptureLayout.setVisibility(View.VISIBLE);
133        mSettingsLayout.setVisibility(View.INVISIBLE);
134        mSettingsOverlay.setVisibility(View.VISIBLE);
135        mIntentLayout.setVisibility(View.VISIBLE);
136
137        View button;
138        button = mIntentLayout.findViewById(R.id.done_button);
139        button.setVisibility(View.INVISIBLE);
140        button = mIntentLayout.findViewById(R.id.retake_button);
141        button.setVisibility(View.INVISIBLE);
142    }
143
144    /**
145     * Perform a transition to the global intent review layout.
146     * The current layout state of the bottom bar is irrelevant.
147     */
148    public void transitionToIntentReviewLayout() {
149        mCaptureLayout.setVisibility(View.INVISIBLE);
150        mSettingsLayout.setVisibility(View.INVISIBLE);
151        mSettingsOverlay.setVisibility(View.INVISIBLE);
152
153        View button;
154        button = mIntentLayout.findViewById(R.id.done_button);
155        button.setVisibility(View.VISIBLE);
156        button = mIntentLayout.findViewById(R.id.retake_button);
157        button.setVisibility(View.VISIBLE);
158        mIntentLayout.setVisibility(View.VISIBLE);
159    }
160
161    @Override
162    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
163        mWidth = MeasureSpec.getSize(widthMeasureSpec);
164        mHeight = MeasureSpec.getSize(heightMeasureSpec);
165        if (mWidth == 0 || mHeight == 0) {
166            return;
167        }
168
169        if (mOffsetShorterEdge != 0 && mOffsetLongerEdge != 0) {
170            float previewAspectRatio =
171                    mOffsetLongerEdge / mOffsetShorterEdge;
172            if (previewAspectRatio < 1.0) {
173                previewAspectRatio = 1.0f/previewAspectRatio;
174            }
175            float screenAspectRatio = (float) mWidth / (float) mHeight;
176            if (screenAspectRatio < 1.0) {
177                screenAspectRatio = 1.0f/screenAspectRatio;
178            }
179            if (previewAspectRatio >= screenAspectRatio) {
180                mOverLayBottomBar = true;
181                setAlpha(0.5f);
182            } else {
183                mOverLayBottomBar = false;
184                setAlpha(1.0f);
185            }
186        }
187
188        // Calculates the width and height needed for the bar.
189        int barWidth, barHeight;
190        if (mWidth > mHeight) {
191            ((LayoutParams) getLayoutParams()).gravity = Gravity.RIGHT;
192            if ((mOffsetLongerEdge == 0 && mOffsetShorterEdge == 0) || mOverLayBottomBar) {
193                barWidth = mOptimalHeight;
194                barHeight = mHeight;
195            } else {
196                barWidth = (int) (mWidth - mOffsetLongerEdge);
197                barHeight = mHeight;
198            }
199        } else {
200            ((LayoutParams) getLayoutParams()).gravity = Gravity.BOTTOM;
201            if ((mOffsetLongerEdge == 0 && mOffsetShorterEdge == 0) || mOverLayBottomBar) {
202                barWidth = mWidth;
203                barHeight = mOptimalHeight;
204            } else {
205                barWidth = mWidth;
206                barHeight = (int) (mHeight - mOffsetLongerEdge);
207            }
208        }
209        super.onMeasure(MeasureSpec.makeMeasureSpec(barWidth, MeasureSpec.EXACTLY),
210                MeasureSpec.makeMeasureSpec(barHeight, MeasureSpec.EXACTLY));
211    }
212
213    private void adjustBottomBar(float scaledTextureWidth,
214                                 float scaledTextureHeight) {
215        setOffset(scaledTextureWidth, scaledTextureHeight);
216    }
217
218    @Override
219    public void onPreviewAreaSizeChanged(float scaledTextureWidth,
220                                         float scaledTextureHeight) {
221        adjustBottomBar(scaledTextureWidth, scaledTextureHeight);
222    }
223
224    private void setOffset(float scaledTextureWidth, float scaledTextureHeight) {
225        float offsetLongerEdge, offsetShorterEdge;
226        if (scaledTextureHeight > scaledTextureWidth) {
227            offsetLongerEdge = scaledTextureHeight;
228            offsetShorterEdge = scaledTextureWidth;
229        } else {
230            offsetLongerEdge = scaledTextureWidth;
231            offsetShorterEdge = scaledTextureHeight;
232        }
233        if (mOffsetLongerEdge != offsetLongerEdge || mOffsetShorterEdge != offsetShorterEdge) {
234            mOffsetLongerEdge = offsetLongerEdge;
235            mOffsetShorterEdge = offsetShorterEdge;
236            requestLayout();
237        }
238    }
239
240    @Override
241    protected void onConfigurationChanged(Configuration config) {
242        super.onConfigurationChanged(config);
243    }
244
245    // prevent touches on bottom bar (not its children)
246    // from triggering a touch event on preview area
247    @Override
248    public boolean onTouchEvent(MotionEvent event) {
249        return true;
250    }
251}
252