1 /*
2  * Copyright (C) 2014 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 package com.android.camera.widget;
17 
18 import android.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.AnimatorSet;
21 import android.animation.ValueAnimator;
22 import android.content.Context;
23 import android.content.res.Configuration;
24 import android.graphics.Canvas;
25 import android.graphics.Paint;
26 import android.graphics.RectF;
27 import android.util.AttributeSet;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.widget.FrameLayout;
31 import android.widget.ImageButton;
32 
33 import com.android.camera.MultiToggleImageButton;
34 import com.android.camera.ui.RadioOptions;
35 import com.android.camera.ui.TopRightWeightedLayout;
36 import com.android.camera.util.Gusterpolator;
37 import com.android.camera2.R;
38 
39 import java.util.ArrayList;
40 
41 public class ModeOptions extends FrameLayout {
42     private int mBackgroundColor;
43     private final Paint mPaint = new Paint();
44     private boolean mIsHiddenOrHiding;
45     private RectF mAnimateFrom = new RectF();
46     private View mViewToShowHide;
47     private TopRightWeightedLayout mModeOptionsButtons;
48     private RadioOptions mModeOptionsPano;
49     private RadioOptions mModeOptionsExposure;
50 
51     private AnimatorSet mVisibleAnimator;
52     private AnimatorSet mHiddenAnimator;
53     private boolean mDrawCircle;
54     private boolean mFill;
55     private static final int RADIUS_ANIMATION_TIME = 250;
56     private static final int SHOW_ALPHA_ANIMATION_TIME = 350;
57     private static final int HIDE_ALPHA_ANIMATION_TIME = 200;
58     private static final int PADDING_ANIMATION_TIME = 350;
59 
60     private ViewGroup mMainBar;
61     private ViewGroup mActiveBar;
62     public static final int BAR_INVALID = -1;
63     public static final int BAR_STANDARD = 0;
64     public static final int BAR_PANO = 1;
65 
66     private boolean mIsPortrait;
67     private float mRadius = 0f;
68 
ModeOptions(Context context, AttributeSet attrs)69     public ModeOptions(Context context, AttributeSet attrs) {
70         super(context, attrs);
71     }
72 
setViewToShowHide(View v)73     public void setViewToShowHide(View v) {
74         mViewToShowHide = v;
75     }
76 
77     @Override
onFinishInflate()78     public void onFinishInflate() {
79         mIsHiddenOrHiding = true;
80         mBackgroundColor = getResources().getColor(R.color.mode_options_background);
81         mPaint.setAntiAlias(true);
82         mPaint.setColor(mBackgroundColor);
83         mModeOptionsButtons = (TopRightWeightedLayout) findViewById(R.id.mode_options_buttons);
84         mModeOptionsPano = (RadioOptions) findViewById(R.id.mode_options_pano);
85         mModeOptionsExposure = (RadioOptions) findViewById(R.id.mode_options_exposure);
86         mMainBar = mActiveBar = mModeOptionsButtons;
87 
88         ImageButton exposureButton = (ImageButton) findViewById(R.id.exposure_button);
89         exposureButton.setOnClickListener(new View.OnClickListener() {
90             @Override
91             public void onClick(View v) {
92                 mActiveBar = mModeOptionsExposure;
93                 mMainBar.setVisibility(View.INVISIBLE);
94                 mActiveBar.setVisibility(View.VISIBLE);
95             }
96         });
97     }
98 
setMainBar(int b)99     public void setMainBar(int b) {
100         for (int i = 0; i < getChildCount(); i++) {
101             getChildAt(i).setVisibility(View.INVISIBLE);
102         }
103         switch (b) {
104         case BAR_STANDARD:
105             mMainBar = mActiveBar = mModeOptionsButtons;
106             break;
107         case BAR_PANO:
108             mMainBar = mActiveBar = mModeOptionsPano;
109             break;
110         }
111         mMainBar.setVisibility(View.VISIBLE);
112     }
113 
getMainBar()114     public int getMainBar() {
115         if (mMainBar == mModeOptionsButtons) {
116             return BAR_STANDARD;
117         }
118         if (mMainBar == mModeOptionsPano) {
119             return BAR_PANO;
120         }
121         return BAR_INVALID;
122     }
123 
124     @Override
onWindowVisibilityChanged(int visibility)125     public void onWindowVisibilityChanged(int visibility) {
126         super.onWindowVisibilityChanged(visibility);
127         if (visibility != VISIBLE && !mIsHiddenOrHiding) {
128             // Collapse mode options when window is not visible.
129             setVisibility(INVISIBLE);
130             if (mMainBar != null) {
131                 mMainBar.setVisibility(VISIBLE);
132             }
133             if (mActiveBar != null && mActiveBar != mMainBar) {
134                 mActiveBar.setVisibility(INVISIBLE);
135             }
136             if (mViewToShowHide != null) {
137                 mViewToShowHide.setVisibility(VISIBLE);
138             }
139             mIsHiddenOrHiding = true;
140         }
141     }
142 
143     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)144     public void onLayout(boolean changed, int left, int top, int right, int bottom) {
145         if (changed) {
146             mIsPortrait = (getResources().getConfiguration().orientation ==
147                            Configuration.ORIENTATION_PORTRAIT);
148 
149             int buttonSize = getResources()
150                 .getDimensionPixelSize(R.dimen.option_button_circle_size);
151             int buttonPadding = getResources()
152                 .getDimensionPixelSize(R.dimen.mode_options_toggle_padding);
153 
154             float rLeft, rRight, rTop, rBottom;
155             if (mIsPortrait) {
156                 rLeft = getWidth() - buttonPadding - buttonSize;
157                 rTop = (getHeight() - buttonSize) / 2.0f;
158             } else {
159                 rLeft = buttonPadding;
160                 rTop = buttonPadding;
161             }
162             rRight = rLeft + buttonSize;
163             rBottom = rTop + buttonSize;
164             mAnimateFrom.set(rLeft, rTop, rRight, rBottom);
165 
166             setupAnimators();
167             setupToggleButtonParams();
168         }
169 
170         super.onLayout(changed, left, top, right, bottom);
171     }
172 
173     @Override
onDraw(Canvas canvas)174     public void onDraw(Canvas canvas) {
175         if (mDrawCircle) {
176             canvas.drawCircle(mAnimateFrom.centerX(), mAnimateFrom.centerY(), mRadius, mPaint);
177         } else if (mFill) {
178             canvas.drawPaint(mPaint);
179         }
180         super.onDraw(canvas);
181     }
182 
setupToggleButtonParams()183     private void setupToggleButtonParams() {
184         int size = (mIsPortrait ? getHeight() : getWidth());
185 
186         for (int i = 0; i < mModeOptionsButtons.getChildCount(); i++) {
187             View button = mModeOptionsButtons.getChildAt(i);
188             if (button instanceof MultiToggleImageButton) {
189                 MultiToggleImageButton toggleButton = (MultiToggleImageButton) button;
190                 toggleButton.setParentSize(size);
191                 toggleButton.setAnimDirection(mIsPortrait ?
192                         MultiToggleImageButton.ANIM_DIRECTION_VERTICAL :
193                         MultiToggleImageButton.ANIM_DIRECTION_HORIZONTAL);
194             }
195         }
196     }
197 
setupAnimators()198     private void setupAnimators() {
199         if (mVisibleAnimator != null) {
200             mVisibleAnimator.end();
201         }
202         if (mHiddenAnimator != null) {
203             mHiddenAnimator.end();
204         }
205 
206         final float fullSize = (mIsPortrait ? (float) getWidth() : (float) getHeight());
207 
208         // show
209         {
210             final ValueAnimator radiusAnimator =
211                 ValueAnimator.ofFloat(mAnimateFrom.width()/2.0f,
212                     fullSize-mAnimateFrom.width()/2.0f);
213             radiusAnimator.setDuration(RADIUS_ANIMATION_TIME);
214             radiusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
215                 @Override
216                 public void onAnimationUpdate(ValueAnimator animation) {
217                     mRadius = (Float) animation.getAnimatedValue();
218                     mDrawCircle = true;
219                     mFill = false;
220                 }
221             });
222             radiusAnimator.addListener(new AnimatorListenerAdapter() {
223                 @Override
224                 public void onAnimationEnd(Animator animation) {
225                     mDrawCircle = false;
226                     mFill = true;
227                 }
228             });
229 
230             final ValueAnimator alphaAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
231             alphaAnimator.setDuration(SHOW_ALPHA_ANIMATION_TIME);
232             alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
233                 @Override
234                 public void onAnimationUpdate(ValueAnimator animation) {
235                     mActiveBar.setAlpha((Float) animation.getAnimatedValue());
236                 }
237             });
238             alphaAnimator.addListener(new AnimatorListenerAdapter() {
239                 @Override
240                 public void onAnimationEnd(Animator animation) {
241                     mActiveBar.setAlpha(1.0f);
242                 }
243             });
244 
245             final int deltaX = getResources()
246                 .getDimensionPixelSize(R.dimen.mode_options_buttons_anim_delta_x);
247             int childCount = mActiveBar.getChildCount();
248             ArrayList<Animator> paddingAnimators = new ArrayList<Animator>();
249             for (int i = 0; i < childCount; i++) {
250                 final View button;
251                 if (mIsPortrait) {
252                     button = mActiveBar.getChildAt(i);
253                 } else {
254                     button = mActiveBar.getChildAt(childCount-1-i);
255                 }
256 
257                 final ValueAnimator paddingAnimator =
258                     ValueAnimator.ofFloat(deltaX*(childCount-i), 0.0f);
259                 paddingAnimator.setDuration(PADDING_ANIMATION_TIME);
260                 paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
261                     @Override
262                     public void onAnimationUpdate(ValueAnimator animation) {
263                         if (mIsPortrait) {
264                             button.setTranslationX((Float) animation.getAnimatedValue());
265                         } else {
266                             button.setTranslationY(-((Float) animation.getAnimatedValue()));
267                         }
268                         invalidate();
269                     }
270                 });
271 
272                 paddingAnimators.add(paddingAnimator);
273             }
274 
275             AnimatorSet paddingAnimatorSet = new AnimatorSet();
276             paddingAnimatorSet.playTogether(paddingAnimators);
277 
278             mVisibleAnimator = new AnimatorSet();
279             mVisibleAnimator.setInterpolator(Gusterpolator.INSTANCE);
280             mVisibleAnimator.playTogether(radiusAnimator, alphaAnimator, paddingAnimatorSet);
281         }
282 
283         // hide
284         {
285             final ValueAnimator radiusAnimator =
286                 ValueAnimator.ofFloat(fullSize-mAnimateFrom.width()/2.0f,
287                     mAnimateFrom.width()/2.0f);
288             radiusAnimator.setDuration(RADIUS_ANIMATION_TIME);
289             radiusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
290                 @Override
291                 public void onAnimationUpdate(ValueAnimator animation) {
292                     mRadius = (Float) animation.getAnimatedValue();
293                     mDrawCircle = true;
294                     mFill = false;
295                     invalidate();
296                 }
297             });
298             radiusAnimator.addListener(new AnimatorListenerAdapter() {
299                 @Override
300                 public void onAnimationEnd(Animator animation) {
301                     if (mViewToShowHide != null) {
302                         mViewToShowHide.setVisibility(View.VISIBLE);
303                         mDrawCircle = false;
304                         mFill = false;
305                         invalidate();
306                     }
307                 }
308             });
309 
310             final ValueAnimator alphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
311             alphaAnimator.setDuration(HIDE_ALPHA_ANIMATION_TIME);
312             alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
313                 @Override
314                 public void onAnimationUpdate(ValueAnimator animation) {
315                     mActiveBar.setAlpha((Float) animation.getAnimatedValue());
316                     invalidate();
317                 }
318             });
319             alphaAnimator.addListener(new AnimatorListenerAdapter() {
320                 @Override
321                 public void onAnimationEnd(Animator animation) {
322                     setVisibility(View.INVISIBLE);
323                     if (mActiveBar != mMainBar) {
324                         mActiveBar.setAlpha(1.0f);
325                         mActiveBar.setVisibility(View.INVISIBLE);
326                     }
327                     mMainBar.setAlpha(1.0f);
328                     mMainBar.setVisibility(View.VISIBLE);
329                     mActiveBar = mMainBar;
330                     invalidate();
331                 }
332             });
333 
334             mHiddenAnimator = new AnimatorSet();
335             mHiddenAnimator.setInterpolator(Gusterpolator.INSTANCE);
336             mHiddenAnimator.playTogether(radiusAnimator, alphaAnimator);
337         }
338     }
339 
animateVisible()340     public void animateVisible() {
341         if (mIsHiddenOrHiding) {
342             if (mViewToShowHide != null) {
343                 mViewToShowHide.setVisibility(View.INVISIBLE);
344             }
345             mHiddenAnimator.cancel();
346             mVisibleAnimator.end();
347             setVisibility(View.VISIBLE);
348             mVisibleAnimator.start();
349         }
350         mIsHiddenOrHiding = false;
351     }
352 
animateHidden()353     public void animateHidden() {
354         if (!mIsHiddenOrHiding) {
355             mVisibleAnimator.cancel();
356             mHiddenAnimator.end();
357             mHiddenAnimator.start();
358         }
359         mIsHiddenOrHiding = true;
360     }
361 }