blob: f649808ced3053d14f706f69eb3db2e28c4edac0 [file] [log] [blame]
/*
* 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;
}
}