| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.contacts.widget; |
| |
| import android.app.Activity; |
| import android.content.res.Resources; |
| import android.graphics.drawable.Drawable; |
| import android.view.View; |
| import android.view.animation.AnimationUtils; |
| import android.view.animation.Interpolator; |
| import android.widget.ImageButton; |
| |
| import com.android.contacts.R; |
| import com.android.contacts.util.ViewUtil; |
| import com.android.phone.common.animation.AnimUtils; |
| |
| /** |
| * Controls the movement and appearance of the FAB (Floating Action Button). |
| */ |
| public class FloatingActionButtonController { |
| public static final int ALIGN_MIDDLE = 0; |
| public static final int ALIGN_QUARTER_END = 1; |
| public static final int ALIGN_END = 2; |
| |
| private static final int FAB_SCALE_IN_DURATION = 186; |
| private static final int FAB_SCALE_IN_FADE_IN_DELAY = 70; |
| private static final int FAB_ICON_FADE_OUT_DURATION = 46; |
| |
| private final int mAnimationDuration; |
| private final int mFloatingActionButtonWidth; |
| private final int mFloatingActionButtonMarginRight; |
| private final View mFloatingActionButtonContainer; |
| private final ImageButton mFloatingActionButton; |
| private final Interpolator mFabInterpolator; |
| private int mScreenWidth; |
| |
| public FloatingActionButtonController(Activity activity, View container, ImageButton button) { |
| Resources resources = activity.getResources(); |
| mFabInterpolator = AnimationUtils.loadInterpolator(activity, |
| android.R.interpolator.fast_out_slow_in); |
| mFloatingActionButtonWidth = resources.getDimensionPixelSize( |
| R.dimen.floating_action_button_width); |
| mFloatingActionButtonMarginRight = resources.getDimensionPixelOffset( |
| R.dimen.floating_action_button_margin_right); |
| mAnimationDuration = resources.getInteger( |
| R.integer.floating_action_button_animation_duration); |
| mFloatingActionButtonContainer = container; |
| mFloatingActionButton = button; |
| ViewUtil.setupFloatingActionButton(mFloatingActionButtonContainer, resources); |
| } |
| |
| /** |
| * Passes the screen width into the class. Necessary for translation calculations. |
| * Should be called as soon as parent View width is available. |
| * |
| * @param screenWidth The width of the screen in pixels. |
| */ |
| public void setScreenWidth(int screenWidth) { |
| mScreenWidth = screenWidth; |
| } |
| |
| /** |
| * Sets FAB as View.VISIBLE or View.GONE. |
| * |
| * @param visible Whether or not to make the container visible. |
| */ |
| public void setVisible(boolean visible) { |
| mFloatingActionButtonContainer.setVisibility(visible ? View.VISIBLE : View.GONE); |
| } |
| |
| public boolean isVisible() { |
| return mFloatingActionButtonContainer.getVisibility() == View.VISIBLE; |
| } |
| |
| public void changeIcon(Drawable icon, String description) { |
| if (mFloatingActionButton.getDrawable() != icon |
| || !mFloatingActionButton.getContentDescription().equals(description)) { |
| mFloatingActionButton.setImageDrawable(icon); |
| mFloatingActionButton.setContentDescription(description); |
| } |
| } |
| |
| /** |
| * Updates the FAB location (middle to right position) as the PageView scrolls. |
| * |
| * @param positionOffset A fraction used to calculate position of the FAB during page scroll. |
| */ |
| public void onPageScrolled(float positionOffset) { |
| // As the page is scrolling, if we're on the first tab, update the FAB position so it |
| // moves along with it. |
| mFloatingActionButtonContainer.setTranslationX( |
| (int) (positionOffset * getTranslationXForAlignment(ALIGN_END))); |
| } |
| |
| /** |
| * Aligns the FAB to the described location |
| * |
| * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT. |
| * @param animate Whether or not to animate the transition. |
| */ |
| public void align(int align, boolean animate) { |
| align(align, 0 /*offsetX */, 0 /* offsetY */, animate); |
| } |
| |
| /** |
| * Aligns the FAB to the described location plus specified additional offsets. |
| * |
| * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT. |
| * @param offsetX Additional offsetX to translate by. |
| * @param offsetY Additional offsetY to translate by. |
| * @param animate Whether or not to animate the transition. |
| */ |
| public void align(int align, int offsetX, int offsetY, boolean animate) { |
| if (mScreenWidth == 0) { |
| return; |
| } |
| |
| int translationX = getTranslationXForAlignment(align); |
| |
| // Skip animation if container is not shown; animation causes container to show again. |
| if (animate && mFloatingActionButtonContainer.isShown()) { |
| mFloatingActionButtonContainer.animate() |
| .translationX(translationX + offsetX) |
| .translationY(offsetY) |
| .setInterpolator(mFabInterpolator) |
| .setDuration(mAnimationDuration) |
| .start(); |
| } else { |
| mFloatingActionButtonContainer.setTranslationX(translationX + offsetX); |
| mFloatingActionButtonContainer.setTranslationY(offsetY); |
| } |
| } |
| |
| /** |
| * Resizes width and height of the floating action bar container. |
| * @param dimension The new dimensions for the width and height. |
| * @param animate Whether to animate this change. |
| */ |
| public void resize(int dimension, boolean animate) { |
| if (animate) { |
| AnimUtils.changeDimensions(mFloatingActionButtonContainer, dimension, dimension); |
| } else { |
| mFloatingActionButtonContainer.getLayoutParams().width = dimension; |
| mFloatingActionButtonContainer.getLayoutParams().height = dimension; |
| mFloatingActionButtonContainer.requestLayout(); |
| } |
| } |
| |
| /** |
| * Scales the floating action button from no height and width to its actual dimensions. This is |
| * an animation for showing the floating action button. |
| * @param delayMs The delay for the effect, in milliseconds. |
| */ |
| public void scaleIn(int delayMs) { |
| setVisible(true); |
| AnimUtils.scaleIn(mFloatingActionButtonContainer, FAB_SCALE_IN_DURATION, delayMs); |
| AnimUtils.fadeIn(mFloatingActionButton, FAB_SCALE_IN_DURATION, |
| delayMs + FAB_SCALE_IN_FADE_IN_DELAY, null); |
| } |
| |
| /** |
| * Immediately remove the affects of the last call to {@link #scaleOut}. |
| */ |
| public void resetIn() { |
| mFloatingActionButton.setAlpha(1f); |
| mFloatingActionButton.setVisibility(View.VISIBLE); |
| mFloatingActionButtonContainer.setScaleX(1); |
| mFloatingActionButtonContainer.setScaleY(1); |
| } |
| |
| /** |
| * Scales the floating action button from its actual dimensions to no height and width. This is |
| * an animation for hiding the floating action button. |
| */ |
| public void scaleOut() { |
| AnimUtils.scaleOut(mFloatingActionButtonContainer, mAnimationDuration); |
| // Fade out the icon faster than the scale out animation, so that the icon scaling is less |
| // obvious. We don't want it to scale, but the resizing the container is not as performant. |
| AnimUtils.fadeOut(mFloatingActionButton, FAB_ICON_FADE_OUT_DURATION, null); |
| } |
| |
| /** |
| * Calculates the X offset of the FAB to the given alignment, adjusted for whether or not the |
| * view is in RTL mode. |
| * |
| * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT. |
| * @return The translationX for the given alignment. |
| */ |
| public int getTranslationXForAlignment(int align) { |
| int result = 0; |
| switch (align) { |
| case ALIGN_MIDDLE: |
| // Moves the FAB to exactly center screen. |
| return 0; |
| case ALIGN_QUARTER_END: |
| // Moves the FAB a quarter of the screen width. |
| result = mScreenWidth / 4; |
| break; |
| case ALIGN_END: |
| // Moves the FAB half the screen width. Same as aligning right with a marginRight. |
| result = mScreenWidth / 2 |
| - mFloatingActionButtonWidth / 2 |
| - mFloatingActionButtonMarginRight; |
| break; |
| } |
| if (isLayoutRtl()) { |
| result *= -1; |
| } |
| return result; |
| } |
| |
| private boolean isLayoutRtl() { |
| return mFloatingActionButtonContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; |
| } |
| } |