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