blob: f649808ced3053d14f706f69eb3db2e28c4edac0 [file] [log] [blame]
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -07001/*
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
Gary Mai69c182a2016-12-05 13:07:03 -080017package com.android.contacts.widget;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070018
19import android.app.Activity;
20import android.content.res.Resources;
Andrew Leebf6731b2015-05-05 13:48:51 -070021import android.graphics.drawable.Drawable;
Gary Mai0a49afa2016-12-05 15:53:58 -080022import android.view.View;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070023import android.view.animation.AnimationUtils;
24import android.view.animation.Interpolator;
Andrew Leebf6731b2015-05-05 13:48:51 -070025import android.widget.ImageButton;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070026
Arthur Wang3f6a2442016-12-05 14:51:59 -080027import com.android.contacts.R;
Gary Mai0a49afa2016-12-05 15:53:58 -080028import com.android.contacts.util.ViewUtil;
Andrew Lee7c7eaa72014-08-14 17:50:43 -070029import com.android.phone.common.animation.AnimUtils;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070030
31/**
32 * Controls the movement and appearance of the FAB (Floating Action Button).
33 */
34public class FloatingActionButtonController {
35 public static final int ALIGN_MIDDLE = 0;
Yorke Leea2fa4ba2014-07-22 10:52:07 -070036 public static final int ALIGN_QUARTER_END = 1;
37 public static final int ALIGN_END = 2;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070038
Wenyi Wange383ecf2016-07-22 10:57:01 -070039 private static final int FAB_SCALE_IN_DURATION = 186;
40 private static final int FAB_SCALE_IN_FADE_IN_DELAY = 70;
41 private static final int FAB_ICON_FADE_OUT_DURATION = 46;
Andrew Lee435dbb92014-08-19 11:37:37 -070042
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070043 private final int mAnimationDuration;
44 private final int mFloatingActionButtonWidth;
45 private final int mFloatingActionButtonMarginRight;
46 private final View mFloatingActionButtonContainer;
Andrew Leebf6731b2015-05-05 13:48:51 -070047 private final ImageButton mFloatingActionButton;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070048 private final Interpolator mFabInterpolator;
49 private int mScreenWidth;
50
Andrew Leebf6731b2015-05-05 13:48:51 -070051 public FloatingActionButtonController(Activity activity, View container, ImageButton button) {
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070052 Resources resources = activity.getResources();
Wenyi Wang1ba9d8a2016-02-21 10:48:40 -080053 mFabInterpolator = AnimationUtils.loadInterpolator(activity,
54 android.R.interpolator.fast_out_slow_in);
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070055 mFloatingActionButtonWidth = resources.getDimensionPixelSize(
56 R.dimen.floating_action_button_width);
57 mFloatingActionButtonMarginRight = resources.getDimensionPixelOffset(
58 R.dimen.floating_action_button_margin_right);
59 mAnimationDuration = resources.getInteger(
60 R.integer.floating_action_button_animation_duration);
61 mFloatingActionButtonContainer = container;
Andrew Lee435dbb92014-08-19 11:37:37 -070062 mFloatingActionButton = button;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070063 ViewUtil.setupFloatingActionButton(mFloatingActionButtonContainer, resources);
64 }
65
66 /**
67 * Passes the screen width into the class. Necessary for translation calculations.
68 * Should be called as soon as parent View width is available.
69 *
70 * @param screenWidth The width of the screen in pixels.
71 */
72 public void setScreenWidth(int screenWidth) {
73 mScreenWidth = screenWidth;
74 }
75
76 /**
77 * Sets FAB as View.VISIBLE or View.GONE.
78 *
79 * @param visible Whether or not to make the container visible.
80 */
81 public void setVisible(boolean visible) {
82 mFloatingActionButtonContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
83 }
84
Andrew Leed01d2382015-06-25 14:55:09 -070085 public boolean isVisible() {
Andrew Lee41e16412015-06-16 14:15:55 -070086 return mFloatingActionButtonContainer.getVisibility() == View.VISIBLE;
87 }
88
Andrew Leebf6731b2015-05-05 13:48:51 -070089 public void changeIcon(Drawable icon, String description) {
90 if (mFloatingActionButton.getDrawable() != icon
91 || !mFloatingActionButton.getContentDescription().equals(description)) {
92 mFloatingActionButton.setImageDrawable(icon);
93 mFloatingActionButton.setContentDescription(description);
94 }
95 }
96
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -070097 /**
98 * Updates the FAB location (middle to right position) as the PageView scrolls.
99 *
100 * @param positionOffset A fraction used to calculate position of the FAB during page scroll.
101 */
102 public void onPageScrolled(float positionOffset) {
103 // As the page is scrolling, if we're on the first tab, update the FAB position so it
104 // moves along with it.
105 mFloatingActionButtonContainer.setTranslationX(
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700106 (int) (positionOffset * getTranslationXForAlignment(ALIGN_END)));
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700107 }
108
109 /**
Andrew Lee365fb202015-06-16 15:30:26 -0700110 * Aligns the FAB to the described location
111 *
112 * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
113 * @param animate Whether or not to animate the transition.
114 */
115 public void align(int align, boolean animate) {
116 align(align, 0 /*offsetX */, 0 /* offsetY */, animate);
117 }
118
119 /**
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700120 * Aligns the FAB to the described location plus specified additional offsets.
121 *
122 * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
123 * @param offsetX Additional offsetX to translate by.
Andrew Lee365fb202015-06-16 15:30:26 -0700124 * @param offsetY Additional offsetY to translate by.
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700125 * @param animate Whether or not to animate the transition.
126 */
127 public void align(int align, int offsetX, int offsetY, boolean animate) {
Andrew Lee7c7eaa72014-08-14 17:50:43 -0700128 if (mScreenWidth == 0) {
129 return;
130 }
131
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700132 int translationX = getTranslationXForAlignment(align);
Andrew Lee955e10d2014-08-28 15:12:19 -0700133
134 // Skip animation if container is not shown; animation causes container to show again.
135 if (animate && mFloatingActionButtonContainer.isShown()) {
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700136 mFloatingActionButtonContainer.animate()
137 .translationX(translationX + offsetX)
138 .translationY(offsetY)
139 .setInterpolator(mFabInterpolator)
Andrew Lee7c7eaa72014-08-14 17:50:43 -0700140 .setDuration(mAnimationDuration)
141 .start();
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700142 } else {
143 mFloatingActionButtonContainer.setTranslationX(translationX + offsetX);
144 mFloatingActionButtonContainer.setTranslationY(offsetY);
145 }
146 }
147
148 /**
Andrew Lee7c7eaa72014-08-14 17:50:43 -0700149 * Resizes width and height of the floating action bar container.
150 * @param dimension The new dimensions for the width and height.
151 * @param animate Whether to animate this change.
152 */
153 public void resize(int dimension, boolean animate) {
154 if (animate) {
155 AnimUtils.changeDimensions(mFloatingActionButtonContainer, dimension, dimension);
156 } else {
157 mFloatingActionButtonContainer.getLayoutParams().width = dimension;
158 mFloatingActionButtonContainer.getLayoutParams().height = dimension;
159 mFloatingActionButtonContainer.requestLayout();
160 }
161 }
162
163 /**
Andrew Lee435dbb92014-08-19 11:37:37 -0700164 * Scales the floating action button from no height and width to its actual dimensions. This is
165 * an animation for showing the floating action button.
Andrew Leed2bbc2c2014-08-29 16:42:19 -0700166 * @param delayMs The delay for the effect, in milliseconds.
Andrew Lee435dbb92014-08-19 11:37:37 -0700167 */
Andrew Leed2bbc2c2014-08-29 16:42:19 -0700168 public void scaleIn(int delayMs) {
169 setVisible(true);
170 AnimUtils.scaleIn(mFloatingActionButtonContainer, FAB_SCALE_IN_DURATION, delayMs);
171 AnimUtils.fadeIn(mFloatingActionButton, FAB_SCALE_IN_DURATION,
172 delayMs + FAB_SCALE_IN_FADE_IN_DELAY, null);
Andrew Lee435dbb92014-08-19 11:37:37 -0700173 }
174
175 /**
Brian Attwell143c1892015-05-04 12:34:00 -0700176 * Immediately remove the affects of the last call to {@link #scaleOut}.
177 */
178 public void resetIn() {
Brian Attwell6aa50bc2015-05-11 14:25:00 -0700179 mFloatingActionButton.setAlpha(1f);
Brian Attwell143c1892015-05-04 12:34:00 -0700180 mFloatingActionButton.setVisibility(View.VISIBLE);
181 mFloatingActionButtonContainer.setScaleX(1);
182 mFloatingActionButtonContainer.setScaleY(1);
183 }
184
185 /**
Andrew Lee435dbb92014-08-19 11:37:37 -0700186 * Scales the floating action button from its actual dimensions to no height and width. This is
187 * an animation for hiding the floating action button.
188 */
189 public void scaleOut() {
190 AnimUtils.scaleOut(mFloatingActionButtonContainer, mAnimationDuration);
Andrew Leed2bbc2c2014-08-29 16:42:19 -0700191 // Fade out the icon faster than the scale out animation, so that the icon scaling is less
192 // obvious. We don't want it to scale, but the resizing the container is not as performant.
193 AnimUtils.fadeOut(mFloatingActionButton, FAB_ICON_FADE_OUT_DURATION, null);
Andrew Lee435dbb92014-08-19 11:37:37 -0700194 }
195
196 /**
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700197 * Calculates the X offset of the FAB to the given alignment, adjusted for whether or not the
198 * view is in RTL mode.
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700199 *
200 * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
201 * @return The translationX for the given alignment.
202 */
203 public int getTranslationXForAlignment(int align) {
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700204 int result = 0;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700205 switch (align) {
206 case ALIGN_MIDDLE:
207 // Moves the FAB to exactly center screen.
208 return 0;
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700209 case ALIGN_QUARTER_END:
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700210 // Moves the FAB a quarter of the screen width.
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700211 result = mScreenWidth / 4;
212 break;
213 case ALIGN_END:
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700214 // Moves the FAB half the screen width. Same as aligning right with a marginRight.
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700215 result = mScreenWidth / 2
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700216 - mFloatingActionButtonWidth / 2
217 - mFloatingActionButtonMarginRight;
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700218 break;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700219 }
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700220 if (isLayoutRtl()) {
221 result *= -1;
222 }
223 return result;
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700224 }
225
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700226 private boolean isLayoutRtl() {
Yorke Lee923859f2014-07-25 14:40:52 -0700227 return mFloatingActionButtonContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
Yorke Leea2fa4ba2014-07-22 10:52:07 -0700228 }
Sai Cheemalapati6cff9cf2014-06-04 16:30:31 -0700229}