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