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 
     17 package com.android.contacts.common.widget;
     18 
     19 import android.app.Activity;
     20 import android.content.res.Resources;
     21 import android.view.animation.AnimationUtils;
     22 import android.view.animation.Interpolator;
     23 import android.view.View;
     24 
     25 import com.android.contacts.common.util.ViewUtil;
     26 import com.android.contacts.common.R;
     27 import com.android.phone.common.animation.AnimUtils;
     28 
     29 /**
     30  * Controls the movement and appearance of the FAB (Floating Action Button).
     31  */
     32 public class FloatingActionButtonController {
     33     public static final int ALIGN_MIDDLE = 0;
     34     public static final int ALIGN_QUARTER_END = 1;
     35     public static final int ALIGN_END = 2;
     36 
     37     private static final int FAB_SCALE_IN_DURATION = 266;
     38     private static final int FAB_SCALE_IN_FADE_IN_DELAY = 100;
     39     private static final int FAB_ICON_FADE_OUT_DURATION = 66;
     40 
     41     private final int mAnimationDuration;
     42     private final int mFloatingActionButtonWidth;
     43     private final int mFloatingActionButtonMarginRight;
     44     private final View mFloatingActionButtonContainer;
     45     private final View mFloatingActionButton;
     46     private final Interpolator mFabInterpolator;
     47     private int mScreenWidth;
     48 
     49     public FloatingActionButtonController(Activity activity, View container, View button) {
     50         Resources resources = activity.getResources();
     51         mFabInterpolator = AnimationUtils.loadInterpolator(activity,
     52                 android.R.interpolator.fast_out_slow_in);
     53         mFloatingActionButtonWidth = resources.getDimensionPixelSize(
     54                 R.dimen.floating_action_button_width);
     55         mFloatingActionButtonMarginRight = resources.getDimensionPixelOffset(
     56                 R.dimen.floating_action_button_margin_right);
     57         mAnimationDuration = resources.getInteger(
     58                 R.integer.floating_action_button_animation_duration);
     59         mFloatingActionButtonContainer = container;
     60         mFloatingActionButton = button;
     61         ViewUtil.setupFloatingActionButton(mFloatingActionButtonContainer, resources);
     62     }
     63 
     64     /**
     65      * Passes the screen width into the class. Necessary for translation calculations.
     66      * Should be called as soon as parent View width is available.
     67      *
     68      * @param screenWidth The width of the screen in pixels.
     69      */
     70     public void setScreenWidth(int screenWidth) {
     71         mScreenWidth = screenWidth;
     72     }
     73 
     74     /**
     75      * Sets FAB as View.VISIBLE or View.GONE.
     76      *
     77      * @param visible Whether or not to make the container visible.
     78      */
     79     public void setVisible(boolean visible) {
     80         mFloatingActionButtonContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
     81     }
     82 
     83     /**
     84      * Updates the FAB location (middle to right position) as the PageView scrolls.
     85      *
     86      * @param positionOffset A fraction used to calculate position of the FAB during page scroll.
     87      */
     88     public void onPageScrolled(float positionOffset) {
     89         // As the page is scrolling, if we're on the first tab, update the FAB position so it
     90         // moves along with it.
     91         mFloatingActionButtonContainer.setTranslationX(
     92                 (int) (positionOffset * getTranslationXForAlignment(ALIGN_END)));
     93         mFloatingActionButtonContainer.setTranslationY(0);
     94     }
     95 
     96     /**
     97      * Aligns the FAB to the described location plus specified additional offsets.
     98      *
     99      * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
    100      * @param offsetX Additional offsetX to translate by.
    101      * @param animate Whether or not to animate the transition.
    102      */
    103     public void align(int align, int offsetX, int offsetY, boolean animate) {
    104         if (mScreenWidth == 0) {
    105             return;
    106         }
    107 
    108         int translationX = getTranslationXForAlignment(align);
    109 
    110         // Skip animation if container is not shown; animation causes container to show again.
    111         if (animate && mFloatingActionButtonContainer.isShown()) {
    112             mFloatingActionButtonContainer.animate()
    113                     .translationX(translationX + offsetX)
    114                     .translationY(offsetY)
    115                     .setInterpolator(mFabInterpolator)
    116                     .setDuration(mAnimationDuration)
    117                     .start();
    118         } else {
    119             mFloatingActionButtonContainer.setTranslationX(translationX + offsetX);
    120             mFloatingActionButtonContainer.setTranslationY(offsetY);
    121         }
    122     }
    123 
    124     /**
    125      * Resizes width and height of the floating action bar container.
    126      * @param dimension The new dimensions for the width and height.
    127      * @param animate Whether to animate this change.
    128      */
    129     public void resize(int dimension, boolean animate) {
    130         if (animate) {
    131             AnimUtils.changeDimensions(mFloatingActionButtonContainer, dimension, dimension);
    132         } else {
    133             mFloatingActionButtonContainer.getLayoutParams().width = dimension;
    134             mFloatingActionButtonContainer.getLayoutParams().height = dimension;
    135             mFloatingActionButtonContainer.requestLayout();
    136         }
    137     }
    138 
    139     /**
    140      * Scales the floating action button from no height and width to its actual dimensions. This is
    141      * an animation for showing the floating action button.
    142      * @param delayMs The delay for the effect, in milliseconds.
    143      */
    144     public void scaleIn(int delayMs) {
    145         setVisible(true);
    146         AnimUtils.scaleIn(mFloatingActionButtonContainer, FAB_SCALE_IN_DURATION, delayMs);
    147         AnimUtils.fadeIn(mFloatingActionButton, FAB_SCALE_IN_DURATION,
    148                 delayMs + FAB_SCALE_IN_FADE_IN_DELAY, null);
    149     }
    150 
    151     /**
    152      * Scales the floating action button from its actual dimensions to no height and width. This is
    153      * an animation for hiding the floating action button.
    154      */
    155     public void scaleOut() {
    156         AnimUtils.scaleOut(mFloatingActionButtonContainer, mAnimationDuration);
    157         // Fade out the icon faster than the scale out animation, so that the icon scaling is less
    158         // obvious. We don't want it to scale, but the resizing the container is not as performant.
    159         AnimUtils.fadeOut(mFloatingActionButton, FAB_ICON_FADE_OUT_DURATION, null);
    160     }
    161 
    162     /**
    163      * Calculates the X offset of the FAB to the given alignment, adjusted for whether or not the
    164      * view is in RTL mode.
    165      *
    166      * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
    167      * @return The translationX for the given alignment.
    168      */
    169     public int getTranslationXForAlignment(int align) {
    170         int result = 0;
    171         switch (align) {
    172             case ALIGN_MIDDLE:
    173                 // Moves the FAB to exactly center screen.
    174                 return 0;
    175             case ALIGN_QUARTER_END:
    176                 // Moves the FAB a quarter of the screen width.
    177                 result = mScreenWidth / 4;
    178                 break;
    179             case ALIGN_END:
    180                 // Moves the FAB half the screen width. Same as aligning right with a marginRight.
    181                 result = mScreenWidth / 2
    182                         - mFloatingActionButtonWidth / 2
    183                         - mFloatingActionButtonMarginRight;
    184                 break;
    185         }
    186         if (isLayoutRtl()) {
    187             result *= -1;
    188         }
    189         return result;
    190     }
    191 
    192     private boolean isLayoutRtl() {
    193         return mFloatingActionButtonContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
    194     }
    195 }
    196