• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.dialer.widget;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.res.ColorStateList;
22 import android.content.res.Resources;
23 import android.support.annotation.DrawableRes;
24 import android.support.design.widget.FloatingActionButton;
25 import android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener;
26 import android.view.View;
27 import android.view.animation.AnimationUtils;
28 import android.view.animation.Interpolator;
29 import com.android.contacts.common.R;
30 import com.android.dialer.common.Assert;
31 
32 /** Controls the movement and appearance of the FAB (Floating Action Button). */
33 public class FloatingActionButtonController {
34 
35   public static final int ALIGN_MIDDLE = 0;
36   public static final int ALIGN_QUARTER_END = 1;
37   public static final int ALIGN_END = 2;
38 
39   private final int animationDuration;
40   private final int floatingActionButtonWidth;
41   private final int floatingActionButtonMarginRight;
42   private final FloatingActionButton fab;
43   private final Interpolator fabInterpolator;
44   private int fabIconId = -1;
45   private int screenWidth;
46 
FloatingActionButtonController(Activity activity, FloatingActionButton fab)47   public FloatingActionButtonController(Activity activity, FloatingActionButton fab) {
48     Resources resources = activity.getResources();
49     fabInterpolator =
50         AnimationUtils.loadInterpolator(activity, android.R.interpolator.fast_out_slow_in);
51     floatingActionButtonWidth =
52         resources.getDimensionPixelSize(R.dimen.floating_action_button_width);
53     floatingActionButtonMarginRight =
54         resources.getDimensionPixelOffset(R.dimen.floating_action_button_margin_right);
55     animationDuration = resources.getInteger(R.integer.floating_action_button_animation_duration);
56     this.fab = fab;
57   }
58 
59   /**
60    * Passes the screen width into the class. Necessary for translation calculations. Should be
61    * called as soon as parent View width is available.
62    *
63    * @param screenWidth The width of the screen in pixels.
64    */
setScreenWidth(int screenWidth)65   public void setScreenWidth(int screenWidth) {
66     this.screenWidth = screenWidth;
67   }
68 
69   /** @see FloatingActionButton#isShown() */
isVisible()70   public boolean isVisible() {
71     return fab.isShown();
72   }
73 
74   /**
75    * Sets FAB as shown or hidden.
76    *
77    * @see #scaleIn()
78    * @see #scaleOut()
79    */
setVisible(boolean visible)80   public void setVisible(boolean visible) {
81     if (visible) {
82       scaleIn();
83     } else {
84       scaleOut();
85     }
86   }
87 
changeIcon(Context context, @DrawableRes int iconId, String description)88   public void changeIcon(Context context, @DrawableRes int iconId, String description) {
89     if (this.fabIconId != iconId) {
90       fab.setImageResource(iconId);
91       fab.setImageTintList(
92           ColorStateList.valueOf(context.getResources().getColor(android.R.color.white)));
93       this.fabIconId = iconId;
94     }
95     if (!fab.getContentDescription().equals(description)) {
96       fab.setContentDescription(description);
97     }
98   }
99 
100   /**
101    * Updates the FAB location (middle to right position) as the PageView scrolls.
102    *
103    * @param positionOffset A fraction used to calculate position of the FAB during page scroll.
104    */
onPageScrolled(float positionOffset)105   public void onPageScrolled(float positionOffset) {
106     // As the page is scrolling, if we're on the first tab, update the FAB position so it
107     // moves along with it.
108     fab.setTranslationX(positionOffset * getTranslationXForAlignment(ALIGN_END));
109   }
110 
111   /**
112    * Aligns the FAB to the described location
113    *
114    * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
115    * @param animate Whether or not to animate the transition.
116    */
align(int align, boolean animate)117   public void align(int align, boolean animate) {
118     align(align, 0 /*offsetX */, 0 /* offsetY */, animate);
119   }
120 
121   /**
122    * Aligns the FAB to the described location plus specified additional offsets.
123    *
124    * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
125    * @param offsetX Additional offsetX to translate by.
126    * @param offsetY Additional offsetY to translate by.
127    * @param animate Whether or not to animate the transition.
128    */
align(int align, int offsetX, int offsetY, boolean animate)129   private void align(int align, int offsetX, int offsetY, boolean animate) {
130     if (screenWidth == 0) {
131       return;
132     }
133 
134     int translationX = getTranslationXForAlignment(align);
135 
136     // Skip animation if container is not shown; animation causes container to show again.
137     if (animate && fab.isShown()) {
138       fab.animate()
139           .translationX(translationX + offsetX)
140           .translationY(offsetY)
141           .setInterpolator(fabInterpolator)
142           .setDuration(animationDuration)
143           .start();
144     } else {
145       fab.setTranslationX(translationX + offsetX);
146       fab.setTranslationY(offsetY);
147     }
148   }
149 
150   /** @see FloatingActionButton#show() */
scaleIn()151   public void scaleIn() {
152     fab.show();
153   }
154 
155   /** @see FloatingActionButton#hide() */
scaleOut()156   public void scaleOut() {
157     fab.hide();
158   }
159 
scaleOut(OnVisibilityChangedListener listener)160   public void scaleOut(OnVisibilityChangedListener listener) {
161     fab.hide(listener);
162   }
163 
164   /**
165    * Calculates the X offset of the FAB to the given alignment, adjusted for whether or not the view
166    * is in RTL mode.
167    *
168    * @param align One of ALIGN_MIDDLE, ALIGN_QUARTER_RIGHT, or ALIGN_RIGHT.
169    * @return The translationX for the given alignment.
170    */
getTranslationXForAlignment(int align)171   private int getTranslationXForAlignment(int align) {
172     int result;
173     switch (align) {
174       case ALIGN_MIDDLE:
175         // Moves the FAB to exactly center screen.
176         return 0;
177       case ALIGN_QUARTER_END:
178         // Moves the FAB a quarter of the screen width.
179         result = screenWidth / 4;
180         break;
181       case ALIGN_END:
182         // Moves the FAB half the screen width. Same as aligning right with a marginRight.
183         result = screenWidth / 2 - floatingActionButtonWidth / 2 - floatingActionButtonMarginRight;
184         break;
185       default:
186         throw Assert.createIllegalStateFailException("Invalid alignment value: " + align);
187     }
188     if (isLayoutRtl()) {
189       result *= -1;
190     }
191     return result;
192   }
193 
isLayoutRtl()194   private boolean isLayoutRtl() {
195     return fab.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
196   }
197 }
198