FloatingActionButtonController.java revision 435dbb93a393399972acf2b9ecad5f7c89ac4e6a
16cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati/*
26cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * Copyright (C) 2014 The Android Open Source Project
36cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati *
46cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * Licensed under the Apache License, Version 2.0 (the "License");
56cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * you may not use this file except in compliance with the License.
66cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * You may obtain a copy of the License at
76cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati *
86cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati *      http://www.apache.org/licenses/LICENSE-2.0
96cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati *
106cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * Unless required by applicable law or agreed to in writing, software
116cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * distributed under the License is distributed on an "AS IS" BASIS,
126cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * See the License for the specific language governing permissions and
146cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * limitations under the License.
156cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati */
166cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
176cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatipackage com.android.contacts.common.widget;
186cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
196cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatiimport android.app.Activity;
206cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatiimport android.content.res.Resources;
216cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatiimport android.view.animation.AnimationUtils;
226cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatiimport android.view.animation.Interpolator;
236cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatiimport android.view.View;
246cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
256cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatiimport com.android.contacts.common.util.ViewUtil;
266cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatiimport com.android.contacts.common.R;
277c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Leeimport com.android.phone.common.animation.AnimUtils;
286cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
296cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati/**
306cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati * Controls the movement and appearance of the FAB (Floating Action Button).
316cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati */
326cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapatipublic class FloatingActionButtonController {
336cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    public static final int ALIGN_MIDDLE = 0;
34a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee    public static final int ALIGN_QUARTER_END = 1;
35a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee    public static final int ALIGN_END = 2;
366cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
37435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    private static final int FAB_FADE_DURATION = 266;
38435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    private static final int FAB_FADE_IN_DELAY = 100;
39435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee
406cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    private final int mAnimationDuration;
416cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    private final int mFloatingActionButtonWidth;
426cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    private final int mFloatingActionButtonMarginRight;
436cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    private final View mFloatingActionButtonContainer;
44435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    private final View mFloatingActionButton;
456cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    private final Interpolator mFabInterpolator;
466cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    private int mScreenWidth;
476cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
48435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    public FloatingActionButtonController(Activity activity, View container, View button) {
496cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        Resources resources = activity.getResources();
506cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFabInterpolator = AnimationUtils.loadInterpolator(activity,
516cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                android.R.interpolator.fast_out_slow_in);
526cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonWidth = resources.getDimensionPixelSize(
536cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                R.dimen.floating_action_button_width);
546cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonMarginRight = resources.getDimensionPixelOffset(
556cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                R.dimen.floating_action_button_margin_right);
566cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mAnimationDuration = resources.getInteger(
576cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                R.integer.floating_action_button_animation_duration);
586cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonContainer = container;
59435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee        mFloatingActionButton = button;
606cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        ViewUtil.setupFloatingActionButton(mFloatingActionButtonContainer, resources);
616cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    }
626cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
636cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    /**
646cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * Passes the screen width into the class. Necessary for translation calculations.
656cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * Should be called as soon as parent View width is available.
666cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     *
676cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param screenWidth The width of the screen in pixels.
686cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     */
696cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    public void setScreenWidth(int screenWidth) {
706cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mScreenWidth = screenWidth;
716cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    }
726cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
736cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    /**
746cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * Sets FAB as View.VISIBLE or View.GONE.
756cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     *
766cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param visible Whether or not to make the container visible.
776cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     */
786cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    public void setVisible(boolean visible) {
796cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
806cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    }
816cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
826cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    /**
836cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * Updates the FAB location (middle to right position) as the PageView scrolls.
846cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     *
856cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param positionOffset A fraction used to calculate position of the FAB during page scroll.
866cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     */
876cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    public void onPageScrolled(float positionOffset) {
886cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        // As the page is scrolling, if we're on the first tab, update the FAB position so it
896cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        // moves along with it.
906cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonContainer.setTranslationX(
91a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee                (int) (positionOffset * getTranslationXForAlignment(ALIGN_END)));
926cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonContainer.setTranslationY(0);
936cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    }
946cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
956cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    /**
966cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * Aligns the FAB to the described location plus specified additional offsets.
976cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     *
986cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
996cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param offsetX Additional offsetX to translate by.
1006cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param animate Whether or not to animate the transition.
1016cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     */
1026cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    public void align(int align, int offsetX, int offsetY, boolean animate) {
1037c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee        if (mScreenWidth == 0) {
1047c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee            return;
1057c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee        }
1067c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee
1076cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        int translationX = getTranslationXForAlignment(align);
1086cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        if (animate) {
1096cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati            mFloatingActionButtonContainer.animate()
1106cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                    .translationX(translationX + offsetX)
1116cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                    .translationY(offsetY)
1126cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                    .setInterpolator(mFabInterpolator)
1137c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee                    .setDuration(mAnimationDuration)
1147c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee                    .start();
1156cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        } else {
1166cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati            mFloatingActionButtonContainer.setTranslationX(translationX + offsetX);
1176cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati            mFloatingActionButtonContainer.setTranslationY(offsetY);
1186cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        }
1196cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    }
1206cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
1216cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    /**
1227c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee     * Resizes width and height of the floating action bar container.
1237c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee     * @param dimension The new dimensions for the width and height.
1247c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee     * @param animate Whether to animate this change.
1257c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee     */
1267c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee    public void resize(int dimension, boolean animate) {
1277c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee        if (animate) {
1287c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee            AnimUtils.changeDimensions(mFloatingActionButtonContainer, dimension, dimension);
1297c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee        } else {
1307c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee            mFloatingActionButtonContainer.getLayoutParams().width = dimension;
1317c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee            mFloatingActionButtonContainer.getLayoutParams().height = dimension;
1327c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee            mFloatingActionButtonContainer.requestLayout();
1337c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee        }
1347c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee    }
1357c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee
1367c7eaa7651588ae6e278446d5ebea56fe6dcc84dAndrew Lee    /**
137435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee     * Scales the floating action button from no height and width to its actual dimensions. This is
138435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee     * an animation for showing the floating action button.
139435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee     */
140435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    public void scaleIn() {
141435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee        AnimUtils.scaleIn(mFloatingActionButtonContainer, mAnimationDuration);
142435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee        AnimUtils.fadeIn(mFloatingActionButton, FAB_FADE_DURATION, FAB_FADE_IN_DELAY, null);
143435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    }
144435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee
145435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    /**
146435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee     * Scales the floating action button from its actual dimensions to no height and width. This is
147435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee     * an animation for hiding the floating action button.
148435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee     */
149435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    public void scaleOut() {
150435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee        AnimUtils.scaleOut(mFloatingActionButtonContainer, mAnimationDuration);
151435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    }
152435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee
153435dbb93a393399972acf2b9ecad5f7c89ac4e6aAndrew Lee    /**
154a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee     * Calculates the X offset of the FAB to the given alignment, adjusted for whether or not the
155a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee     * view is in RTL mode.
1566cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     *
1576cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
1586cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @return The translationX for the given alignment.
1596cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     */
1606cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    public int getTranslationXForAlignment(int align) {
161a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee        int result = 0;
1626cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        switch (align) {
1636cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati            case ALIGN_MIDDLE:
1646cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                // Moves the FAB to exactly center screen.
1656cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                return 0;
166a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee            case ALIGN_QUARTER_END:
1676cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                // Moves the FAB a quarter of the screen width.
168a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee                result = mScreenWidth / 4;
169a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee                break;
170a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee            case ALIGN_END:
1716cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                // Moves the FAB half the screen width. Same as aligning right with a marginRight.
172a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee                result = mScreenWidth / 2
1736cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                        - mFloatingActionButtonWidth / 2
1746cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati                        - mFloatingActionButtonMarginRight;
175a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee                break;
176a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee        }
177a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee        if (isLayoutRtl()) {
178a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee            result *= -1;
1796cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        }
180a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee        return result;
1816cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    }
1826cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati
1836cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    /**
1846cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * Manually translates the FAB without animation.
1856cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     *
1866cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param translationX The x distance to translate.
1876cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     * @param translationY The y distance to translate.
1886cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati     */
1896cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    public void manuallyTranslate(int translationX, int translationY) {
1906cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonContainer.setTranslationX(translationX);
1916cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati        mFloatingActionButtonContainer.setTranslationY(translationY);
1926cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati    }
193a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee
194a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee    private boolean isLayoutRtl() {
195923859f758f551ce8e32e4690d120e912bfd1777Yorke Lee        return mFloatingActionButtonContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
196a2fa4ba1d5032620be04691f8fda6d31b92bc50fYorke Lee    }
1976cff9cf3437ffb6214f9e95023c52fa2253e887dSai Cheemalapati}
198