• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 package com.android.launcher3.icons;
17 
18 import static com.android.launcher3.icons.FastBitmapDrawable.CLICK_FEEDBACK_DURATION;
19 import static com.android.launcher3.icons.FastBitmapDrawable.HOVERED_SCALE;
20 import static com.android.launcher3.icons.FastBitmapDrawable.HOVER_FEEDBACK_DURATION;
21 import static com.android.launcher3.icons.FastBitmapDrawable.PRESSED_SCALE;
22 import static com.android.launcher3.icons.FastBitmapDrawable.SCALE;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertTrue;
28 import static org.mockito.Mockito.clearInvocations;
29 import static org.mockito.Mockito.spy;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.graphics.Bitmap;
34 import android.graphics.drawable.Drawable;
35 import android.view.animation.AccelerateInterpolator;
36 import android.view.animation.DecelerateInterpolator;
37 import android.view.animation.PathInterpolator;
38 
39 import androidx.test.annotation.UiThreadTest;
40 import androidx.test.filters.SmallTest;
41 
42 import com.android.launcher3.util.LauncherMultivalentJUnit;
43 
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Mock;
48 import org.mockito.MockitoAnnotations;
49 import org.mockito.Spy;
50 
51 /**
52  * Tests for FastBitmapDrawable.
53  */
54 @SmallTest
55 @RunWith(LauncherMultivalentJUnit.class)
56 @UiThreadTest
57 public class FastBitmapDrawableTest {
58     private static final float EPSILON = 0.00001f;
59 
60     @Spy
61     FastBitmapDrawable mFastBitmapDrawable =
62             spy(new FastBitmapDrawable(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)));
63     @Mock Drawable mBadge;
64 
65     @Before
setUp()66     public void setUp() {
67         MockitoAnnotations.initMocks(this);
68         FastBitmapDrawable.setFlagHoverEnabled(true);
69         when(mFastBitmapDrawable.isVisible()).thenReturn(true);
70         mFastBitmapDrawable.mIsPressed = false;
71         mFastBitmapDrawable.mIsHovered = false;
72         mFastBitmapDrawable.resetScale();
73     }
74 
75     @Test
testOnStateChange_noState()76     public void testOnStateChange_noState() {
77         int[] state = new int[]{};
78 
79         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
80 
81         // No scale changes without state change.
82         assertFalse("State change handled.", isHandled);
83         assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation);
84     }
85 
86     @Test
testOnStateChange_statePressed()87     public void testOnStateChange_statePressed() {
88         int[] state = new int[]{android.R.attr.state_pressed};
89 
90         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
91 
92         // Animate to state pressed.
93         assertTrue("State change not handled.", isHandled);
94         assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
95                 CLICK_FEEDBACK_DURATION);
96         mFastBitmapDrawable.mScaleAnimation.end();
97         assertEquals("End value not correct.",
98                 (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
99         assertTrue("Wrong interpolator used.",
100                 mFastBitmapDrawable.mScaleAnimation.getInterpolator()
101                         instanceof AccelerateInterpolator);
102     }
103 
104     @Test
testOnStateChange_stateHovered()105     public void testOnStateChange_stateHovered() {
106         int[] state = new int[]{android.R.attr.state_hovered};
107 
108         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
109 
110         // Animate to state hovered.
111         assertTrue("State change not handled.", isHandled);
112         assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
113                 HOVER_FEEDBACK_DURATION);
114         mFastBitmapDrawable.mScaleAnimation.end();
115         assertEquals("End value not correct.",
116                 (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
117         assertTrue("Wrong interpolator used.",
118                 mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator);
119     }
120 
121     @Test
testOnStateChange_stateHoveredFlagDisabled()122     public void testOnStateChange_stateHoveredFlagDisabled() {
123         FastBitmapDrawable.setFlagHoverEnabled(false);
124         int[] state = new int[]{android.R.attr.state_hovered};
125 
126         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
127 
128         // No state change with flag disabled.
129         assertFalse("Hover state change handled with flag disabled.", isHandled);
130         assertNull("Animation should not run with hover flag disabled.",
131                 mFastBitmapDrawable.mScaleAnimation);
132     }
133 
134     @Test
testOnStateChange_statePressedAndHovered()135     public void testOnStateChange_statePressedAndHovered() {
136         int[] state = new int[]{android.R.attr.state_pressed, android.R.attr.state_hovered};
137 
138         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
139 
140         // Animate to pressed state only.
141         assertTrue("State change not handled.", isHandled);
142         assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
143                 CLICK_FEEDBACK_DURATION);
144         mFastBitmapDrawable.mScaleAnimation.end();
145         assertEquals("End value not correct.",
146                 (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
147         assertTrue("Wrong interpolator used.",
148                 mFastBitmapDrawable.mScaleAnimation.getInterpolator()
149                         instanceof AccelerateInterpolator);
150     }
151 
152     @Test
testOnStateChange_stateHoveredAndPressed()153     public void testOnStateChange_stateHoveredAndPressed() {
154         int[] state = new int[]{android.R.attr.state_hovered, android.R.attr.state_pressed};
155 
156         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
157 
158         // Animate to pressed state only.
159         assertTrue("State change not handled.", isHandled);
160         assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
161                 CLICK_FEEDBACK_DURATION);
162         mFastBitmapDrawable.mScaleAnimation.end();
163         assertEquals("End value not correct.",
164                 (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
165         assertTrue("Wrong interpolator used.",
166                 mFastBitmapDrawable.mScaleAnimation.getInterpolator()
167                         instanceof AccelerateInterpolator);
168     }
169 
170     @Test
testOnStateChange_stateHoveredAndPressedToPressed()171     public void testOnStateChange_stateHoveredAndPressedToPressed() {
172         mFastBitmapDrawable.mIsPressed = true;
173         mFastBitmapDrawable.mIsHovered = true;
174         SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
175         int[] state = new int[]{android.R.attr.state_pressed};
176 
177         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
178 
179         // No scale change from pressed state to pressed state.
180         assertTrue("State not changed.", isHandled);
181         assertEquals("End value not correct.",
182                 (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
183     }
184 
185     @Test
testOnStateChange_stateHoveredAndPressedToHovered()186     public void testOnStateChange_stateHoveredAndPressedToHovered() {
187         mFastBitmapDrawable.mIsPressed = true;
188         mFastBitmapDrawable.mIsHovered = true;
189         SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
190         int[] state = new int[]{android.R.attr.state_hovered};
191 
192         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
193 
194         // No scale change from pressed state to hovered state.
195         assertTrue("State not changed.", isHandled);
196         assertEquals("End value not correct.",
197                 (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
198     }
199 
200     @Test
testOnStateChange_stateHoveredToPressed()201     public void testOnStateChange_stateHoveredToPressed() {
202         mFastBitmapDrawable.mIsHovered = true;
203         SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE);
204         int[] state = new int[]{android.R.attr.state_pressed};
205 
206         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
207 
208         // No scale change from pressed state to hovered state.
209         assertTrue("State not changed.", isHandled);
210         assertEquals("End value not correct.",
211                 (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
212     }
213 
214     @Test
testOnStateChange_statePressedToHovered()215     public void testOnStateChange_statePressedToHovered() {
216         mFastBitmapDrawable.mIsPressed = true;
217         SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
218         int[] state = new int[]{android.R.attr.state_hovered};
219 
220         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
221 
222         // No scale change from pressed state to hovered state.
223         assertTrue("State not changed.", isHandled);
224         assertEquals("End value not correct.",
225                 (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
226     }
227 
228     @Test
testOnStateChange_stateDefaultFromPressed()229     public void testOnStateChange_stateDefaultFromPressed() {
230         mFastBitmapDrawable.mIsPressed = true;
231         SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
232         int[] state = new int[]{};
233 
234         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
235 
236         // Animate to default state from pressed state.
237         assertTrue("State change not handled.", isHandled);
238         assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
239                 CLICK_FEEDBACK_DURATION);
240         mFastBitmapDrawable.mScaleAnimation.end();
241         assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
242         assertTrue("Wrong interpolator used.",
243                 mFastBitmapDrawable.mScaleAnimation.getInterpolator()
244                         instanceof DecelerateInterpolator);
245     }
246 
247     @Test
testOnStateChange_stateDefaultFromHovered()248     public void testOnStateChange_stateDefaultFromHovered() {
249         mFastBitmapDrawable.mIsHovered = true;
250         SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE);
251         int[] state = new int[]{};
252 
253         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
254 
255         // Animate to default state from hovered state.
256         assertTrue("State change not handled.", isHandled);
257         assertEquals("Duration not correct.", mFastBitmapDrawable.mScaleAnimation.getDuration(),
258                 HOVER_FEEDBACK_DURATION);
259         mFastBitmapDrawable.mScaleAnimation.end();
260         assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
261         assertTrue("Wrong interpolator used.",
262                 mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator);
263     }
264 
265     @Test
testOnStateChange_stateHoveredWhilePartiallyScaled()266     public void testOnStateChange_stateHoveredWhilePartiallyScaled() {
267         SCALE.setValue(mFastBitmapDrawable, 0.5f);
268         int[] state = new int[]{android.R.attr.state_hovered};
269 
270         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
271 
272         // Animate to hovered state from midway to pressed state.
273         assertTrue("State change not handled.", isHandled);
274         assertEquals("Duration not correct.",
275                 mFastBitmapDrawable.mScaleAnimation.getDuration(), HOVER_FEEDBACK_DURATION);
276         mFastBitmapDrawable.mScaleAnimation.end();
277         assertEquals("End value not correct.",
278                 (float) SCALE.get(mFastBitmapDrawable), HOVERED_SCALE, EPSILON);
279         assertTrue("Wrong interpolator used.",
280                 mFastBitmapDrawable.mScaleAnimation.getInterpolator() instanceof PathInterpolator);
281     }
282 
283     @Test
testOnStateChange_statePressedWhilePartiallyScaled()284     public void testOnStateChange_statePressedWhilePartiallyScaled() {
285         SCALE.setValue(mFastBitmapDrawable, 0.5f);
286         int[] state = new int[]{android.R.attr.state_pressed};
287 
288         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
289 
290         // Animate to pressed state from midway to hovered state.
291         assertTrue("State change not handled.", isHandled);
292         assertEquals("Duration not correct.",
293                 mFastBitmapDrawable.mScaleAnimation.getDuration(), CLICK_FEEDBACK_DURATION);
294         mFastBitmapDrawable.mScaleAnimation.end();
295         assertEquals("End value not correct.",
296                 (float) SCALE.get(mFastBitmapDrawable), PRESSED_SCALE, EPSILON);
297         assertTrue("Wrong interpolator used.",
298                 mFastBitmapDrawable.mScaleAnimation.getInterpolator()
299                         instanceof AccelerateInterpolator);
300     }
301 
302     @Test
testOnStateChange_stateDefaultFromPressedNotVisible()303     public void testOnStateChange_stateDefaultFromPressedNotVisible() {
304         when(mFastBitmapDrawable.isVisible()).thenReturn(false);
305         mFastBitmapDrawable.mIsPressed = true;
306         SCALE.setValue(mFastBitmapDrawable, PRESSED_SCALE);
307         clearInvocations(mFastBitmapDrawable);
308         int[] state = new int[]{};
309 
310         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
311 
312         // No animations when state was pressed but drawable no longer visible. Set values directly.
313         assertTrue("State change not handled.", isHandled);
314         assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation);
315         assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
316         verify(mFastBitmapDrawable).invalidateSelf();
317     }
318 
319     @Test
testOnStateChange_stateDefaultFromHoveredNotVisible()320     public void testOnStateChange_stateDefaultFromHoveredNotVisible() {
321         when(mFastBitmapDrawable.isVisible()).thenReturn(false);
322         mFastBitmapDrawable.mIsHovered = true;
323         SCALE.setValue(mFastBitmapDrawable, HOVERED_SCALE);
324         clearInvocations(mFastBitmapDrawable);
325         int[] state = new int[]{};
326 
327         boolean isHandled = mFastBitmapDrawable.onStateChange(state);
328 
329         // No animations when state was hovered but drawable no longer visible. Set values directly.
330         assertTrue("State change not handled.", isHandled);
331         assertNull("Scale animation not null.", mFastBitmapDrawable.mScaleAnimation);
332         assertEquals("End value not correct.", (float) SCALE.get(mFastBitmapDrawable), 1f, EPSILON);
333         verify(mFastBitmapDrawable).invalidateSelf();
334     }
335 
336     @Test
testUpdateBadgeAlpha()337     public void testUpdateBadgeAlpha() {
338         mFastBitmapDrawable.setBadge(mBadge);
339 
340         mFastBitmapDrawable.setAlpha(1);
341         mFastBitmapDrawable.setAlpha(0);
342 
343         verify(mBadge).setAlpha(1);
344         verify(mBadge).setAlpha(0);
345     }
346 }
347