1 /*
2  * Copyright (C) 2012 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 android.animation.cts;
17 
18 import android.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.ArgbEvaluator;
21 import android.animation.Keyframe;
22 import android.animation.ObjectAnimator;
23 import android.animation.PropertyValuesHolder;
24 import android.animation.ValueAnimator;
25 import android.graphics.drawable.ShapeDrawable;
26 import android.test.ActivityInstrumentationTestCase2;
27 import android.util.Property;
28 import android.view.View;
29 import android.view.animation.AccelerateInterpolator;
30 
31 import java.util.concurrent.CountDownLatch;
32 import java.util.concurrent.TimeUnit;
33 
34 public class PropertyValuesHolderTest extends
35         ActivityInstrumentationTestCase2<AnimationActivity> {
36     private AnimationActivity mActivity;
37     private Animator mAnimator;
38     private long mDuration = 1000;
39     private float mStartY;
40     private float mEndY;
41     private Object mObject;
42     private String mProperty;
43 
PropertyValuesHolderTest()44     public PropertyValuesHolderTest() {
45         super(AnimationActivity.class);
46     }
47 
setUp()48     public void setUp() throws Exception {
49          super.setUp();
50          setActivityInitialTouchMode(false);
51          mActivity = getActivity();
52          mAnimator = mActivity.createAnimatorWithDuration(mDuration);
53          mProperty = "y";
54          mStartY = mActivity.mStartY;
55          mEndY = mActivity.mStartY + mActivity.mDeltaY;
56          mObject = mActivity.view.newBall;
57     }
58 
testGetPropertyName()59     public void testGetPropertyName() {
60         float[] values = {mStartY, mEndY};
61         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat(mProperty, values);
62         assertEquals(mProperty, pVHolder.getPropertyName());
63     }
64 
testSetPropertyName()65     public void testSetPropertyName() {
66         float[] values = {mStartY, mEndY};
67         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat("", values);
68         pVHolder.setPropertyName(mProperty);
69         assertEquals(mProperty, pVHolder.getPropertyName());
70     }
71 
testClone()72     public void testClone() {
73         float[] values = {mStartY, mEndY};
74         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat(mProperty, values);
75         PropertyValuesHolder cloneHolder = pVHolder.clone();
76         assertEquals(pVHolder.getPropertyName(), cloneHolder.getPropertyName());
77     }
78 
testSetValues()79     public void testSetValues() throws Throwable {
80         float[] dummyValues = {100, 150};
81         float[] values = {mStartY, mEndY};
82         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat(mProperty, dummyValues);
83         pVHolder.setFloatValues(values);
84 
85         ObjectAnimator objAnimator = ObjectAnimator.ofPropertyValuesHolder(mObject,pVHolder);
86         assertTrue(objAnimator != null);
87         setAnimatorProperties(objAnimator);
88 
89         startAnimation(objAnimator);
90         assertTrue(objAnimator != null);
91         float[] yArray = getYPosition();
92         assertResults(yArray, mStartY, mEndY);
93     }
94 
createAnimator(Keyframe... keyframes)95     private ObjectAnimator createAnimator(Keyframe... keyframes) {
96         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofKeyframe(mProperty, keyframes);
97         ObjectAnimator objAnimator = ObjectAnimator.ofPropertyValuesHolder(mObject,pVHolder);
98         objAnimator.setDuration(mDuration);
99         objAnimator.setInterpolator(new AccelerateInterpolator());
100         return objAnimator;
101     }
102 
waitUntilFinished(ObjectAnimator objectAnimator, long timeoutMilliseconds)103     private void waitUntilFinished(ObjectAnimator objectAnimator, long timeoutMilliseconds)
104             throws InterruptedException {
105         final CountDownLatch latch = new CountDownLatch(1);
106         objectAnimator.addListener(new AnimatorListenerAdapter() {
107             @Override
108             public void onAnimationEnd(Animator animation) {
109                 super.onAnimationEnd(animation);
110                 latch.countDown();
111             }
112         });
113         latch.await(timeoutMilliseconds, TimeUnit.MILLISECONDS);
114         getInstrumentation().waitForIdleSync();
115     }
116 
setTarget(final Animator animator, final Object target)117     private void setTarget(final Animator animator, final Object target) throws Throwable {
118         runTestOnUiThread(new Runnable() {
119             @Override
120             public void run() {
121                 animator.setTarget(target);
122             }
123         });
124     }
125 
startSingleAnimation(final Animator animator)126     private void startSingleAnimation(final Animator animator) throws Throwable {
127         runTestOnUiThread(new Runnable() {
128             @Override
129             public void run() {
130                 mActivity.startSingleAnimation(animator);
131             }
132         });
133     }
134 
testResetValues()135     public void testResetValues() throws Throwable {
136         final float initialY = mActivity.view.newBall.getY();
137         Keyframe emptyKeyframe1 = Keyframe.ofFloat(.0f);
138         ObjectAnimator objAnimator1 = createAnimator(emptyKeyframe1, Keyframe.ofFloat(1f, 100f));
139         startSingleAnimation(objAnimator1);
140         assertTrue("Keyframe should be assigned a value", emptyKeyframe1.hasValue());
141         assertEquals("Keyframe should get the value from the target", emptyKeyframe1.getValue(),
142                 initialY);
143         waitUntilFinished(objAnimator1, mDuration * 2);
144         assertEquals(100f, mActivity.view.newBall.getY());
145         startSingleAnimation(objAnimator1);
146         waitUntilFinished(objAnimator1, mDuration * 2);
147 
148         // run another ObjectAnimator that will move the Y value to something else
149         Keyframe emptyKeyframe2 = Keyframe.ofFloat(.0f);
150         ObjectAnimator objAnimator2 = createAnimator(emptyKeyframe2, Keyframe.ofFloat(1f, 200f));
151         startSingleAnimation(objAnimator2);
152         assertTrue("Keyframe should be assigned a value", emptyKeyframe2.hasValue());
153         assertEquals("Keyframe should get the value from the target", emptyKeyframe2.getValue(), 100f);
154         waitUntilFinished(objAnimator2, mDuration * 2);
155         assertEquals(200f, mActivity.view.newBall.getY());
156 
157         // re-run first object animator. since its target did not change, it should have the same
158         // start value for kf1
159         startSingleAnimation(objAnimator1);
160         assertEquals(emptyKeyframe1.getValue(), initialY);
161         waitUntilFinished(objAnimator1, mDuration * 2);
162 
163         Keyframe fullKeyframe = Keyframe.ofFloat(.0f, 333f);
164         ObjectAnimator objAnimator3 = createAnimator(fullKeyframe, Keyframe.ofFloat(1f, 500f));
165         startSingleAnimation(objAnimator3);
166         assertEquals("When keyframe has value, should not be assigned from the target object",
167                 fullKeyframe.getValue(), 333f);
168         waitUntilFinished(objAnimator3, mDuration * 2);
169 
170         // now, null out the target of the first animator
171         float updatedY = mActivity.view.newBall.getY();
172         setTarget(objAnimator1, null);
173         startSingleAnimation(objAnimator1);
174         assertTrue("Keyframe should get a value", emptyKeyframe1.hasValue());
175         assertEquals("Keyframe should get the updated Y value", emptyKeyframe1.getValue(), updatedY);
176         waitUntilFinished(objAnimator1, mDuration * 2);
177         assertEquals("Animation should run as expected", 100f, mActivity.view.newBall.getY());
178 
179         // now, reset the target of the fully defined animation.
180         setTarget(objAnimator3, null);
181         startSingleAnimation(objAnimator3);
182         assertEquals("When keyframe is fully defined, its value should not change when target is"
183                 + " reset", fullKeyframe.getValue(), 333f);
184         waitUntilFinished(objAnimator3, mDuration * 2);
185 
186         // run the other one to change Y value
187         startSingleAnimation(objAnimator2);
188         waitUntilFinished(objAnimator2, mDuration * 2);
189         // now, set another target w/ the same View type. it should still reset
190         ShapeHolder view = new ShapeHolder(new ShapeDrawable());
191         updatedY = mActivity.view.newBall.getY();
192         setTarget(objAnimator1, view);
193         startSingleAnimation(objAnimator1);
194         assertTrue("Keyframe should get a value when target is set to another view of the same"
195                 + " class", emptyKeyframe1.hasValue());
196         assertEquals("Keyframe should get the updated Y value when target is set to another view"
197                 + " of the same class", emptyKeyframe1.getValue(), updatedY);
198         waitUntilFinished(objAnimator1, mDuration * 2);
199         assertEquals("Animation should run as expected", 100f, mActivity.view.newBall.getY());
200     }
201 
testOffloat()202     public void testOffloat() throws Throwable {
203         float[] values = {mStartY, mEndY};
204         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat(mProperty, values);
205         assertNotNull(pVHolder);
206         ObjectAnimator objAnimator = ObjectAnimator.ofPropertyValuesHolder(mObject,pVHolder);
207         assertTrue(objAnimator != null);
208 
209         setAnimatorProperties(objAnimator);
210         startAnimation(objAnimator);
211         assertTrue(objAnimator != null);
212         float[] yArray = getYPosition();
213         assertResults(yArray, mStartY, mEndY);
214     }
215 
testOfFloat_Property()216     public void testOfFloat_Property() throws Throwable {
217         float[] values = {mStartY, mEndY};
218         ShapeHolderYProperty property=new ShapeHolderYProperty(ShapeHolder.class.getClass(),"y");
219         property.setObject(mObject);
220         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat(property, values);
221         assertNotNull(pVHolder);
222         ObjectAnimator objAnimator = ObjectAnimator.ofPropertyValuesHolder(mObject,pVHolder);
223         assertTrue(objAnimator != null);
224 
225         setAnimatorProperties(objAnimator);
226         startAnimation(objAnimator);
227         assertTrue(objAnimator != null);
228         float[] yArray = getYPosition();
229         assertResults(yArray, mStartY, mEndY);
230     }
231 
testOfInt()232     public void testOfInt() throws Throwable {
233         int start = 0;
234         int end = 10;
235         int[] values = {start, end};
236         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofInt(mProperty, values);
237         assertNotNull(pVHolder);
238         final ObjectAnimator objAnimator = ObjectAnimator.ofPropertyValuesHolder(mObject,pVHolder);
239         assertTrue(objAnimator != null);
240         setAnimatorProperties(objAnimator);
241         this.runTestOnUiThread(new Runnable(){
242             public void run() {
243                 objAnimator.start();
244             }
245         });
246         Thread.sleep(1000);
247         assertTrue(objAnimator.isRunning());
248         Integer animatedValue = (Integer) objAnimator.getAnimatedValue();
249         assertTrue(animatedValue >= start);
250         assertTrue(animatedValue <= end);
251     }
252 
testOfInt_Property()253     public void testOfInt_Property() throws Throwable{
254         Object object = mActivity.view;
255         String property = "backgroundColor";
256         int startColor = mActivity.view.RED;
257         int endColor = mActivity.view.BLUE;
258         int values[] = {startColor, endColor};
259 
260         ViewColorProperty colorProperty=new ViewColorProperty(Integer.class.getClass(),property);
261         colorProperty.setObject(object);
262         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofInt(colorProperty, values);
263         assertNotNull(pVHolder);
264 
265         ObjectAnimator colorAnimator = ObjectAnimator.ofPropertyValuesHolder(object,pVHolder);
266         colorAnimator.setDuration(1000);
267         colorAnimator.setEvaluator(new ArgbEvaluator());
268         colorAnimator.setRepeatCount(ValueAnimator.INFINITE);
269         colorAnimator.setRepeatMode(ValueAnimator.REVERSE);
270 
271         ObjectAnimator objectAnimator = (ObjectAnimator) mActivity.createAnimatorWithDuration(
272             mDuration);
273         startAnimation(objectAnimator, colorAnimator);
274         Thread.sleep(1000);
275         Integer i = (Integer) colorAnimator.getAnimatedValue();
276         //We are going from less negative value to a more negative value
277         assertTrue(i.intValue() <= startColor);
278         assertTrue(endColor <= i.intValue());
279     }
280 
testSetProperty()281     public void testSetProperty() throws Throwable {
282         float[] values = {mStartY, mEndY};
283         ShapeHolderYProperty property=new ShapeHolderYProperty(ShapeHolder.class.getClass(),"y");
284         property.setObject(mObject);
285         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat("", values);
286         pVHolder.setProperty(property);
287         ObjectAnimator objAnimator = ObjectAnimator.ofPropertyValuesHolder(mObject,pVHolder);
288         setAnimatorProperties(objAnimator);
289         startAnimation(objAnimator);
290         assertTrue(objAnimator != null);
291         float[] yArray = getYPosition();
292         assertResults(yArray, mStartY, mEndY);
293     }
294 
295     class ShapeHolderYProperty extends Property {
296         private ShapeHolder shapeHolder ;
297         private Class type = Float.class.getClass();
298         private String name = "y";
299         @SuppressWarnings("unchecked")
ShapeHolderYProperty(Class type, String name)300         public ShapeHolderYProperty(Class type, String name) throws Exception {
301             super(Float.class, name );
302             if(!( type.equals(this.type) || ( name.equals(this.name))) ){
303                 throw new Exception("Type or name provided does not match with " +
304                         this.type.getName() + " or " + this.name);
305             }
306         }
307 
setObject(Object object)308         public void setObject(Object object){
309             shapeHolder = (ShapeHolder) object;
310         }
311 
312         @Override
get(Object object)313         public Object get(Object object) {
314             return shapeHolder;
315         }
316 
317         @Override
getName()318         public String getName() {
319             return "y";
320         }
321 
322         @Override
getType()323         public Class getType() {
324             return super.getType();
325         }
326 
327         @Override
isReadOnly()328         public boolean isReadOnly() {
329             return false;
330         }
331 
332         @Override
set(Object object, Object value)333         public void set(Object object, Object value) {
334             shapeHolder.setY((Float)value);
335         }
336 
337     }
338 
339     class ViewColorProperty extends Property {
340         private View view ;
341         private Class type = Integer.class.getClass();
342         private String name = "backgroundColor";
343         @SuppressWarnings("unchecked")
ViewColorProperty(Class type, String name)344         public ViewColorProperty(Class type, String name) throws Exception {
345             super(Integer.class, name );
346             if(!( type.equals(this.type) || ( name.equals(this.name))) ){
347                 throw new Exception("Type or name provided does not match with " +
348                         this.type.getName() + " or " + this.name);
349             }
350         }
351 
setObject(Object object)352         public void setObject(Object object){
353             view = (View) object;
354         }
355 
356         @Override
get(Object object)357         public Object get(Object object) {
358             return view;
359         }
360 
361         @Override
getName()362         public String getName() {
363             return name;
364         }
365 
366         @Override
getType()367         public Class getType() {
368             return super.getType();
369         }
370 
371         @Override
isReadOnly()372         public boolean isReadOnly() {
373             return false;
374         }
375 
376         @Override
set(Object object, Object value)377         public void set(Object object, Object value) {
378             view.setBackgroundColor((Integer)value);
379         }
380     }
381 
setAnimatorProperties(ObjectAnimator objAnimator)382     private void setAnimatorProperties(ObjectAnimator objAnimator) {
383         objAnimator.setDuration(mDuration);
384         objAnimator.setRepeatCount(ValueAnimator.INFINITE);
385         objAnimator.setInterpolator(new AccelerateInterpolator());
386         objAnimator.setRepeatMode(ValueAnimator.REVERSE);
387     }
388 
getYPosition()389     public float[] getYPosition() throws Throwable{
390         float[] yArray = new float[3];
391         for(int i = 0; i < 3; i++) {
392             float y = mActivity.view.newBall.getY();
393             yArray[i] = y;
394             Thread.sleep(300);
395         }
396         return yArray;
397     }
398 
assertResults(float[] yArray,float startY, float endY)399     public void assertResults(float[] yArray,float startY, float endY) {
400         for(int i = 0; i < 3; i++){
401             float y = yArray[i];
402             assertTrue(y >= startY);
403             assertTrue(y <= endY);
404             if(i < 2) {
405                 float yNext = yArray[i+1];
406                 assertTrue(y != yNext);
407             }
408         }
409     }
410 
startAnimation(final Animator animator)411     private void startAnimation(final Animator animator) throws Throwable {
412         this.runTestOnUiThread(new Runnable() {
413             public void run() {
414                 mActivity.startAnimation(animator);
415             }
416         });
417     }
418 
startAnimation(final ObjectAnimator mObjectAnimator, final ObjectAnimator colorAnimator)419     private void startAnimation(final ObjectAnimator mObjectAnimator,
420             final ObjectAnimator colorAnimator) throws Throwable {
421         Thread mAnimationRunnable = new Thread() {
422             public void run() {
423                 mActivity.startAnimation(mObjectAnimator, colorAnimator);
424             }
425         };
426         this.runTestOnUiThread(mAnimationRunnable);
427     }
428 }
429 
430